home *** CD-ROM | disk | FTP | other *** search
/ PC Welt 2006 November (DVD) / PCWELT_11_2006.ISO / casper / filesystem.squashfs / usr / lib / gedit-2 / plugins / snippets / SnippetsDialog.py < prev    next >
Encoding:
Python Source  |  2006-08-27  |  17.1 KB  |  649 lines

  1. #    Gedit snippets plugin
  2. #    Copyright (C) 2005-2006  Jesse van den Kieboom <jesse@icecrew.nl>
  3. #
  4. #    This program is free software; you can redistribute it and/or modify
  5. #    it under the terms of the GNU General Public License as published by
  6. #    the Free Software Foundation; either version 2 of the License, or
  7. #    (at your option) any later version.
  8. #
  9. #    This program is distributed in the hope that it will be useful,
  10. #    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12. #    GNU General Public License for more details.
  13. #
  14. #    You should have received a copy of the GNU General Public License
  15. #    along with this program; if not, write to the Free Software
  16. #    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  17.  
  18. import gtk
  19. from gtk import glade
  20. from gtk import gdk
  21. import pango
  22. import os
  23. import gedit
  24. import gtksourceview
  25. from Snippet import Snippet
  26. from functions import *
  27. from SnippetsLibrary import *
  28. import gobject
  29.  
  30. class SnippetsDialog:
  31.     NAME_COLUMN = 0
  32.     SORT_COLUMN = 1
  33.     OBJ_COLUMN = 2
  34.  
  35.     model = None
  36.     
  37.     def __init__(self):
  38.         self.snippet = None
  39.         self.dlg = None
  40.         self.key_press_id = 0
  41.         self.tooltips = gtk.Tooltips()
  42.         self.run()
  43.  
  44.     def get_language_snippets(self, path, name = None):
  45.         library = SnippetsLibrary()
  46.         
  47.         name = self.get_language(path)
  48.         nodes = library.get_snippets(name)
  49.  
  50.         return nodes
  51.  
  52.     def add_new_snippet_node(self, parent):
  53.         return self.model.append(parent, ('<i>' + _('Add a new snippet...') + \
  54.                 '</i>', '', None))
  55.  
  56.     def fill_language(self, piter):
  57.         # Remove all children
  58.         child = self.model.iter_children(piter)
  59.         
  60.         while child and    self.model.remove(child):
  61.             True
  62.         
  63.         path = self.model.get_path(piter)
  64.         nodes = self.get_language_snippets(path)
  65.         language = self.get_language(path)
  66.         
  67.         SnippetsLibrary().ref(language)
  68.         
  69.         if nodes:
  70.             for node in nodes:
  71.                 self.add_snippet(piter, node)
  72.         else:
  73.             # Add node that tells there are no snippets currently
  74.             self.add_new_snippet_node(piter)
  75.  
  76.         self.tree_view.expand_row(path, False)
  77.  
  78.     def build_model(self):
  79.         window = gedit.app_get_default().get_active_window()
  80.         
  81.         if window:
  82.             view = window.get_active_view()
  83.  
  84.             if not view:
  85.                 current_lang = None
  86.             else:
  87.                 current_lang = view.get_buffer().get_language()
  88.                 source_view = self['source_view_snippet']
  89.                 
  90.                 source_view.set_auto_indent(view.get_auto_indent())
  91.                 source_view.set_insert_spaces_instead_of_tabs( \
  92.                         view.get_insert_spaces_instead_of_tabs())
  93.                 source_view.set_smart_home_end(view.get_smart_home_end())
  94.                 source_view.set_tabs_width(view.get_tabs_width())
  95.  
  96.         else:
  97.             current_lang = None
  98.  
  99.         tree_view = self['tree_view_snippets']
  100.         expand = None
  101.         
  102.         if not self.model:
  103.             self.model = gtk.TreeStore(str, str, object)
  104.             self.model.set_sort_column_id(self.SORT_COLUMN, gtk.SORT_ASCENDING)
  105.             manager = gtksourceview.SourceLanguagesManager()
  106.             langs = manager.get_available_languages()
  107.             
  108.             piter = self.model.append(None, (_('Global'), '', None))
  109.             # Add dummy node
  110.             self.model.append(piter, ('', '', None))
  111.             
  112.             nm = None
  113.             
  114.             if current_lang:
  115.                 nm = current_lang.get_name()
  116.         
  117.             for lang in langs:
  118.                 name = lang.get_name()
  119.                 parent = self.model.append(None, (name, name, lang))
  120.  
  121.                 # Add dummy node
  122.                 self.model.append(parent, ('', '', None))
  123.  
  124.                 if (nm == name):
  125.                     expand = parent
  126.         else:
  127.             if current_lang:
  128.                 piter = self.model.get_iter_first()
  129.                 nm = current_lang.get_name()
  130.                 
  131.                 while piter:
  132.                     lang = self.model.get_value(piter, \
  133.                             self.SORT_COLUMN)
  134.                     
  135.                     if lang    == nm:
  136.                         expand = piter
  137.                         break;
  138.                         
  139.                     piter = self.model.iter_next(piter)
  140.  
  141.         tree_view.set_model(self.model)
  142.         
  143.         if not expand:
  144.             expand = self.model.get_iter_root()
  145.             
  146.         tree_view.expand_row(self.model.get_path(expand), False)
  147.         self.select_iter(expand)
  148.  
  149.     def get_cell_data_cb(self, column, cell, model, iter):
  150.         s = model.get_value(iter, self.OBJ_COLUMN)
  151.         
  152.         snippet = isinstance(s, SnippetData)
  153.         
  154.         cell.set_property('editable', snippet)
  155.         
  156.         if snippet and not s.valid:
  157.             cell.set_property('foreground-gdk', gdk.color_parse('red'))
  158.         else:
  159.             cell.set_property('foreground-set', False)
  160.         
  161.         cell.set_property('markup', model.get_value(iter, self.NAME_COLUMN))
  162.         
  163.  
  164.     def build_tree_view(self):        
  165.         self.tree_view = self['tree_view_snippets']
  166.         
  167.         self.column = gtk.TreeViewColumn(None)
  168.         self.renderer = gtk.CellRendererText()
  169.         self.column.pack_start(self.renderer, False)
  170.         self.column.set_cell_data_func(self.renderer, self.get_cell_data_cb)
  171.         
  172.         self.tree_view.append_column(self.column)
  173.         
  174.         self.renderer.connect('edited', self.on_cell_edited)
  175.         self.renderer.connect('editing-started', self.on_cell_editing_started)
  176.  
  177.         self.tree_view.get_selection().connect('changed', \
  178.                 self.on_tree_view_selection_changed)
  179.  
  180.     def custom_handler(self, xml, function_name, widget_name, str1, str2, \
  181.             int1 , int2):
  182.         if function_name == 'create_source_view':
  183.             buf = gtksourceview.SourceBuffer()
  184.             buf.set_highlight(True)
  185.             source_view = gtksourceview.SourceView(buf)
  186.             source_view.set_auto_indent(True)
  187.             source_view.set_insert_spaces_instead_of_tabs(False)
  188.             source_view.set_smart_home_end(True)
  189.             source_view.set_tabs_width(4)
  190.             
  191.             return source_view
  192.         else:
  193.             return None
  194.         
  195.     def build(self):
  196.         glade.set_custom_handler(self.custom_handler)
  197.         self.xml = glade.XML(os.path.dirname(__file__) + '/snippets.glade')
  198.         
  199.         handlers_dic = {
  200.                 'on_dialog_snippets_response': \
  201.                     self.on_dialog_snippets_response, \
  202.                 'on_button_new_snippet_clicked': \
  203.                     self.on_button_new_snippet_clicked, \
  204.                 'on_button_remove_snippet_clicked': \
  205.                     self.on_button_remove_snippet_clicked, \
  206.                 'on_entry_tab_trigger_focus_out': \
  207.                     self.on_entry_tab_trigger_focus_out, \
  208.                 'on_entry_tab_trigger_changed': \
  209.                     self.on_entry_tab_trigger_changed, \
  210.                 'on_entry_accelerator_focus_out': \
  211.                     self.on_entry_accelerator_focus_out, \
  212.                 'on_entry_accelerator_focus_in': \
  213.                     self.on_entry_accelerator_focus_in, \
  214.                 'on_entry_accelerator_key_press': \
  215.                     self.on_entry_accelerator_key_press, \
  216.                 'on_source_view_snippet_focus_out': \
  217.                     self.on_source_view_snippet_focus_out, \
  218.                 'on_tree_view_snippets_row_expanded': \
  219.                     self.on_tree_view_snippets_row_expanded, \
  220.                 'on_tree_view_snippets_key_press': \
  221.                     self.on_tree_view_snippets_key_press \
  222.                 }
  223.  
  224.         self.xml.signal_autoconnect(handlers_dic)
  225.         
  226.         self.build_tree_view()
  227.         self.build_model()
  228.  
  229.         button = self['button_remove_snippet']
  230.         button.set_use_stock(True)
  231.         button.set_label(gtk.STOCK_REMOVE)
  232.  
  233.         source_view = self['source_view_snippet']
  234.         source_view.modify_font(pango.FontDescription('Monospace 8'))
  235.  
  236.         self.dlg = self['dialog_snippets']
  237.     
  238.     def __getitem__(self, key):
  239.         return self.xml.get_widget(key)
  240.  
  241.     def is_filled(self, piter):
  242.         if not self.model.iter_has_child(piter):
  243.             return True
  244.         
  245.         child = self.model.iter_children(piter)
  246.         nm = self.model.get_value(child, self.NAME_COLUMN)
  247.         obj = self.model.get_value(child, self.OBJ_COLUMN)
  248.         
  249.         return (obj or nm)
  250.  
  251.     def fill_if_needed(self, piter):
  252.         if not self.is_filled(piter):
  253.             self.fill_language(piter)
  254.  
  255.     def find_iter(self, parent, snippet):
  256.         self.fill_if_needed(parent)
  257.         piter = self.model.iter_children(parent)
  258.         
  259.         while (piter):
  260.             node = self.model.get_value(piter, self.OBJ_COLUMN)
  261.  
  262.             if node == snippet.data:
  263.                 return piter
  264.             
  265.             piter = self.model.iter_next(piter)
  266.         
  267.         return None
  268.  
  269.     def update_remove_button(self):
  270.         button = self['button_remove_snippet']
  271.         
  272.         if not self.snippet:
  273.             button.set_sensitive(False)
  274.             button.set_label(gtk.STOCK_REMOVE)    
  275.         else:
  276.             if self.snippet.data.can_modify():
  277.                 button.set_sensitive(True)
  278.                 
  279.                  if self.snippet.data.is_override():
  280.                     button.set_label(gtk.STOCK_REVERT_TO_SAVED)
  281.                 else:
  282.                     button.set_label(gtk.STOCK_REMOVE)
  283.             else:
  284.                 button.set_sensitive(False)
  285.                 button.set_label(gtk.STOCK_REMOVE)
  286.  
  287.     def snippet_changed(self, piter = None):
  288.         if piter:
  289.             node = self.model.get_value(piter, self.OBJ_COLUMN)
  290.             s = Snippet(node)
  291.         else:
  292.             s = self.snippet
  293.             piter = self.find_iter(self.model.get_iter(self.language_path), s)
  294.  
  295.         if piter:
  296.             nm = s.display()
  297.             
  298.             self.model.set(piter, self.NAME_COLUMN, nm, self.SORT_COLUMN, nm)
  299.             self.update_remove_button()
  300.             self.entry_tab_trigger_update_valid()
  301.  
  302.         return piter
  303.  
  304.     def add_snippet(self, parent, snippet):
  305.         piter = self.model.append(parent, ('', '', snippet))
  306.         
  307.         return self.snippet_changed(piter)
  308.  
  309.     def run(self):
  310.         if not self.dlg:
  311.             self.build()
  312.             self.dlg.show_all()
  313.         else:
  314.             self.build_model()
  315.             self.dlg.present()
  316.     
  317.     def selected_snippet(self):
  318.         (model, piter) = self.tree_view.get_selection().get_selected()
  319.         
  320.         if piter:
  321.             parent = model.iter_parent(piter)
  322.             
  323.             if parent:
  324.                 return parent, piter, \
  325.                         model.get_value(piter, self.OBJ_COLUMN)
  326.             else:
  327.                 return parent, piter, None        
  328.         else:
  329.             return None, None, None
  330.  
  331.     def selection_changed(self):
  332.         if not self.snippet:
  333.             sens = False
  334.  
  335.             self['entry_tab_trigger'].set_text('')
  336.             self['entry_accelerator'].set_text('')            
  337.             self['source_view_snippet'].get_buffer().set_text('')
  338.  
  339.             self.tooltips.disable()
  340.         else:
  341.             sens = True
  342.  
  343.             self['entry_tab_trigger'].set_text(self.snippet['tag'])
  344.             self['entry_accelerator'].set_text( \
  345.                     self.snippet.accelerator_display())
  346.             
  347.             buf = self['source_view_snippet'].get_buffer()
  348.             lang = self.model.get_value(self.model.get_iter( \
  349.                     self.language_path), self.OBJ_COLUMN)
  350.             
  351.             buf.set_language(lang)
  352.             buf.set_text(self.snippet['text'])
  353.             
  354.             self.tooltips.enable()
  355.  
  356.         for name in ['source_view_snippet', 'label_tab_trigger', \
  357.                 'entry_tab_trigger', 'label_accelerator', 'entry_accelerator']:
  358.             self[name].set_sensitive(sens)
  359.         
  360.         self.update_remove_button()
  361.             
  362.     def select_iter(self, piter):
  363.         self.tree_view.get_selection().select_iter(piter)
  364.         self.tree_view.scroll_to_cell(self.model.get_path(piter), None, \
  365.             True, 0.5, 0.5)
  366.  
  367.     def get_language(self, path):
  368.         if path[0] == 0:
  369.             return None
  370.         else:
  371.             return self.model.get_value(self.model.get_iter( \
  372.                     (path[0],)), self.OBJ_COLUMN).get_id()
  373.  
  374.     def new_snippet(self, properties=None):
  375.         if not self.language_path:
  376.             return None
  377.  
  378.         parent = self.model.get_iter(self.language_path)
  379.         snippet = SnippetsLibrary().new_snippet(self.get_language( \
  380.                 self.language_path), properties)
  381.         
  382.         return Snippet(snippet)
  383.  
  384.     def get_dummy(self, parent):
  385.         if not self.model.iter_n_children(parent) == 1:
  386.             return None
  387.         
  388.         dummy = self.model.iter_children(parent)
  389.         
  390.         if not self.model.get_value(dummy, self.OBJ_COLUMN):
  391.             return dummy
  392.     
  393.         return None
  394.     
  395.     def unref_languages(self):
  396.         piter = self.model.get_iter_first()
  397.         library = SnippetsLibrary()
  398.         
  399.         while piter:
  400.             if self.is_filled(piter):
  401.                 language = self.get_language(self.model.get_path(piter))
  402.                 library.save(language)
  403.  
  404.                 library.unref(language)
  405.             
  406.             piter = self.model.iter_next(piter)
  407.  
  408.     # Callbacks
  409.     def on_dialog_snippets_response(self, dlg, resp):
  410.         if resp == gtk.RESPONSE_HELP:
  411.             gedit.help_display(self.dlg, 'gedit.xml', 'gedit-snippets-plugin')
  412.             return
  413.  
  414.         self.unref_languages()    
  415.         self.snippet = None    
  416.         self.model = None
  417.         self.dlg.destroy()
  418.         self.dlg = None
  419.     
  420.     def on_cell_editing_started(self, renderer, editable, path):
  421.         piter = self.model.get_iter(path)
  422.         
  423.         if not self.model.iter_parent(piter):
  424.             renderer.stop_editing(True)
  425.             editable.remove_widget()
  426.         elif isinstance(editable, gtk.Entry):
  427.             if self.snippet:
  428.                 editable.set_text(self.snippet['description'])
  429.             else:
  430.                 # This is the `Add a new snippet...` item
  431.                 editable.set_text('')
  432.             
  433.             editable.grab_focus()
  434.     
  435.     def on_cell_edited(self, cell, path, new_text):        
  436.         if new_text != '':
  437.             piter = self.model.get_iter(path)
  438.             node = self.model.get_value(piter, self.OBJ_COLUMN)
  439.             
  440.             if node:
  441.                 if node == self.snippet.data:
  442.                     s = self.snippet
  443.                 else:
  444.                     s = Snippet(node)
  445.             
  446.                 s['description'] = new_text
  447.                 self.snippet_changed(piter)
  448.                 self.select_iter(piter)
  449.             else:
  450.                 # This is the `Add a new snippet...` item
  451.                 # We create a new snippet
  452.                 snippet = self.new_snippet({'description': new_text})
  453.                 
  454.                 if snippet:
  455.                     self.model.set(piter, self.OBJ_COLUMN, snippet.data)
  456.                     self.snippet_changed(piter)
  457.                     self.snippet = snippet
  458.                     self.selection_changed()
  459.     
  460.     def on_entry_accelerator_focus_out(self, entry, event):
  461.         if not self.snippet:
  462.             return
  463.  
  464.         entry.set_text(self.snippet.accelerator_display())
  465.  
  466.     def entry_tab_trigger_update_valid(self):
  467.         entry = self['entry_tab_trigger']
  468.         text = entry.get_text()
  469.         
  470.         if text and not SnippetsLibrary().valid_tab_trigger(text):
  471.             entry.modify_base(gtk.STATE_NORMAL, gtk.gdk.Color(0xffff, 0x6666, \
  472.                     0x6666))
  473.             entry.modify_text(gtk.STATE_NORMAL, gtk.gdk.Color(0xffff, 0xffff, \
  474.                     0xffff))
  475.  
  476.             self.tooltips.set_tip(entry, _('This is not a valid tab trigger. Triggers can either contain letters or a single, non alphanumeric, character like {, [, etcetera.'))
  477.         else:
  478.             entry.modify_base(gtk.STATE_NORMAL, None)
  479.             entry.modify_text(gtk.STATE_NORMAL, None)
  480.  
  481.             self.tooltips.set_tip(entry, None)
  482.         
  483.         return False
  484.  
  485.     def on_entry_tab_trigger_focus_out(self, entry, event):
  486.         if not self.snippet:
  487.             return
  488.  
  489.         text = entry.get_text()
  490.  
  491.         # save tag
  492.         self.snippet['tag'] = text
  493.         self.snippet_changed()
  494.     
  495.     def on_entry_tab_trigger_changed(self, entry):
  496.         self.entry_tab_trigger_update_valid()
  497.     
  498.     def on_source_view_snippet_focus_out(self, source_view, event):
  499.         if not self.snippet:
  500.             return
  501.  
  502.         buf = source_view.get_buffer()
  503.         text = buf.get_text(buf.get_start_iter(), \
  504.                 buf.get_end_iter())
  505.  
  506.         self.snippet['text'] = text
  507.         self.snippet_changed()
  508.     
  509.     def on_button_new_snippet_clicked(self, button):
  510.         snippet = self.new_snippet()
  511.         
  512.         if not snippet:
  513.             return
  514.  
  515.         parent = self.model.get_iter(self.language_path)
  516.         path = self.model.get_path(parent)
  517.         
  518.         dummy = self.get_dummy(parent)
  519.         
  520.         if dummy:
  521.             # Remove the dummy
  522.             self.model.remove(dummy)
  523.         
  524.         # Add the snippet
  525.         piter = self.add_snippet(parent, snippet.data)
  526.         self.select_iter(piter)
  527.  
  528.         if not self.tree_view.row_expanded(path):
  529.             self.tree_view.expand_row(path, False)
  530.             self.select_iter(piter)
  531.  
  532.         self.tree_view.grab_focus()
  533.  
  534.         path = self.model.get_path(piter)
  535.         self.tree_view.set_cursor(path, self.column, True)
  536.         
  537.     def on_button_remove_snippet_clicked(self, button):
  538.         parent, piter, node = self.selected_snippet()
  539.  
  540.         if not self.snippet or not node.can_modify():
  541.             return
  542.         
  543.         if node.is_override():
  544.             SnippetsLibrary().revert_snippet(node)
  545.             self.selection_changed()
  546.         else:
  547.             SnippetsLibrary().remove_snippet(node)
  548.             self.snippet = None
  549.  
  550.             path = self.model.get_path(piter)
  551.         
  552.             if self.model.remove(piter):
  553.                 self.select_iter(piter)
  554.             elif path[-1] != 0:
  555.                 self.select_iter(self.model.get_iter((path[0], path[1] - 1)))
  556.             else:
  557.                 dummy = self.add_new_snippet_node(parent)
  558.                 self.tree_view.expand_row(self.model.get_path(parent), False)
  559.                 self.select_iter(dummy)
  560.         
  561.         self.tree_view.grab_focus()
  562.     
  563.     def set_accelerator(self, keyval, mod):
  564.         accelerator = gtk.accelerator_name(keyval, mod)
  565.         self.snippet['accelerator'] = accelerator
  566.  
  567.         return True
  568.     
  569.     def on_entry_accelerator_key_press(self, entry, event):
  570.         source_view = self['source_view_snippet']
  571.  
  572.         if event.keyval == gdk.keyval_from_name('Escape'):
  573.             # Reset
  574.             entry.set_text(self.snippet.accelerator_display())
  575.             self.tree_view.grab_focus()
  576.             
  577.             return True
  578.         elif event.keyval == gdk.keyval_from_name('Delete') or \
  579.                 event.keyval == gdk.keyval_from_name('BackSpace'):
  580.             # Remove the accelerator
  581.             entry.set_text('')
  582.             self.snippet['accelerator'] = ''
  583.             self.tree_view.grab_focus()
  584.             
  585.             self.snippet_changed()
  586.             return True
  587.         elif SnippetsLibrary().valid_accelerator(event.keyval, event.state):
  588.             # New accelerator
  589.             self.set_accelerator(event.keyval, \
  590.                     event.state & gtk.accelerator_get_default_mod_mask())
  591.             entry.set_text(self.snippet.accelerator_display())
  592.             self.snippet_changed()
  593.             self.tree_view.grab_focus()
  594.  
  595.         else:
  596.             return True
  597.     
  598.     def on_entry_accelerator_focus_in(self, entry, event):
  599.         if self.snippet['accelerator']:
  600.             entry.set_text(_('Type a new accelerator, or press Backspace to clear'))
  601.         else:
  602.             entry.set_text(_('Type a new accelerator'))
  603.     
  604.     def on_tree_view_selection_changed(self, selection):
  605.         parent, piter, node = self.selected_snippet()
  606.         
  607.         if self.snippet:
  608.             self.on_entry_tab_trigger_focus_out(self['entry_tab_trigger'], \
  609.                     None)
  610.             self.on_source_view_snippet_focus_out( \
  611.                     self['source_view_snippet'], None)
  612.         
  613.         if parent:
  614.             self.language_path = self.model.get_path(parent)
  615.         elif piter:
  616.             self.language_path = self.model.get_path(piter)
  617.         else:
  618.             self.language_path = None
  619.  
  620.         if node:
  621.             self.snippet = Snippet(node)
  622.         else:
  623.             self.snippet = None
  624.  
  625.         self.selection_changed()
  626.  
  627.     def iter_after(self, target, after):
  628.         if not after:
  629.             return True
  630.  
  631.         tp = self.model.get_path(target)
  632.         ap = self.model.get_path(after)
  633.         
  634.         if tp[0] > ap[0] or (tp[0] == ap[0] and (len(ap) == 1 or tp[1] > ap[1])):
  635.             return True
  636.         
  637.         return False
  638.         
  639.     def on_tree_view_snippets_key_press(self, treeview, event):
  640.         if self.snippet and event.keyval == gdk.keyval_from_name('Delete') \
  641.                 and    self.snippet:
  642.             self.on_button_remove_snippet_clicked(None)
  643.             return True
  644.  
  645.     def on_tree_view_snippets_row_expanded(self, treeview, piter, path):
  646.         # Check if it is already filled
  647.         self.fill_if_needed(piter)
  648.         self.select_iter(piter)
  649.