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 / SnippetsLibrary.py < prev   
Encoding:
Python Source  |  2006-08-27  |  22.9 KB  |  948 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 ElementTree as et
  19. import os
  20. import weakref
  21. import sys
  22. import gtk
  23. from functions import *
  24.  
  25. class NamespacedId:
  26.     def __init__(self, namespace, id):
  27.         if not id:
  28.             self.id = None
  29.         else:
  30.             if namespace:
  31.                 self.id = namespace + '-'
  32.             else:
  33.                 self.id = 'global-'
  34.         
  35.             self.id += id
  36.  
  37. class SnippetData:
  38.     PROPS = {'tag': '', 'text': '', 'description': 'New snippet', 
  39.             'accelerator': ''}
  40.  
  41.     def __init__(self, node, library):
  42.         self.priv_id = node.attrib.get('id')
  43.  
  44.         self.set_library(library)
  45.         self.valid = False
  46.         self.set_node(node)
  47.  
  48.     def can_modify(self):
  49.         return (self.library and (isinstance(self.library(), SnippetsUserFile)))
  50.  
  51.     def set_library(self, library):
  52.         if library:
  53.             self.library = weakref.ref(library)
  54.         else:
  55.             self.library = None
  56.  
  57.         self.id = NamespacedId(self.language(), self.priv_id).id
  58.  
  59.     def set_node(self, node):
  60.         if self.can_modify():
  61.             self.node = node
  62.         else:
  63.             self.node = None
  64.         
  65.         self.init_snippet_data(node)
  66.         
  67.     def init_snippet_data(self, node):
  68.         if node == None:
  69.             return
  70.  
  71.         self.override = node.attrib.get('override')
  72.  
  73.         self.properties = {}
  74.         props = SnippetData.PROPS.copy()
  75.         
  76.         # Store all properties present
  77.         for child in node:
  78.             if props.has_key(child.tag):
  79.                 del props[child.tag]
  80.                 
  81.                 # Normalize accelerator
  82.                 if child.tag == 'accelerator' and child.text != None:
  83.                     keyval, mod = gtk.accelerator_parse(child.text)
  84.                     
  85.                     if not gtk.accelerator_valid(keyval, mod):
  86.                         child.text = ''
  87.                     else:
  88.                         child.text = gtk.accelerator_name(keyval, mod)
  89.                 
  90.                 if self.can_modify():
  91.                     self.properties[child.tag] = child
  92.                 else:
  93.                     self.properties[child.tag] = child.text or ''
  94.         
  95.         # Create all the props that were not found so we stay consistent
  96.         for prop in props:
  97.             if self.can_modify():
  98.                 child = et.SubElement(node, prop)
  99.  
  100.                 child.text = props[prop]
  101.                 self.properties[prop] = child
  102.             else:
  103.                 self.properties[prop] = props[prop]
  104.         
  105.         self.check_validation()
  106.     
  107.     def check_validation(self):
  108.         if not self['tag'] and not self['accelerator']:
  109.             return False
  110.  
  111.         library = SnippetsLibrary()
  112.         keyval, mod = gtk.accelerator_parse(self['accelerator'])
  113.         
  114.         self.valid = library.valid_tab_trigger(self['tag']) and \
  115.                 (not self['accelerator'] or library.valid_accelerator(keyval, mod))
  116.     
  117.     def __getitem__(self, prop):
  118.         if prop in self.properties:
  119.             if self.can_modify():
  120.                 return self.properties[prop].text or ''
  121.             else:
  122.                 return self.properties[prop] or ''
  123.         
  124.         return ''
  125.     
  126.     def __setitem__(self, prop, value):
  127.         if not prop in self.properties:
  128.             return
  129.             
  130.         if not self.can_modify() and self.properties[prop] != value:
  131.             # ohoh, this is not can_modify, but it needs to be changed...
  132.             # make sure it is transfered to the changes file and set all the
  133.             # fields.
  134.             # This snippet data container will effectively become the container
  135.             # for the newly created node, but transparently to whoever uses
  136.             # it
  137.             
  138.             self._override()
  139.  
  140.         if self.can_modify() and self.properties[prop].text != value:
  141.             if self.library():
  142.                 self.library().tainted = True
  143.  
  144.             oldvalue = self.properties[prop].text
  145.             self.properties[prop].text = value
  146.             
  147.             if prop == 'tag' or prop == 'accelerator':
  148.                 container = SnippetsLibrary().container(self.language())
  149.                 container.prop_changed(self, prop, oldvalue)
  150.         
  151.         self.check_validation()
  152.  
  153.     def language(self):
  154.         if self.library and self.library():
  155.             return self.library().language
  156.         else:
  157.             return None
  158.     
  159.     def is_override(self):
  160.         return self.override and SnippetsLibrary().overridden[self.override]
  161.  
  162.     def _override(self):
  163.         # Find the user file
  164.         target = SnippetsLibrary().get_user_library(self.language())
  165.  
  166.         # Create a new node there with override
  167.         element = et.SubElement(target.root, 'snippet', \
  168.                 {'override': self.id})
  169.         
  170.         # Create all the properties
  171.         for p in self.properties:
  172.             prop = et.SubElement(element, p)
  173.             prop.text = self.properties[p]
  174.             self.properties[p] = prop
  175.  
  176.         # Create an override snippet data, feed it element so that it stores
  177.         # all the values and then set the node to None so that it only contains
  178.         # the values in .properties
  179.         override = SnippetData(element, self.library())
  180.         override.set_node(None)
  181.         override.id = self.id
  182.         
  183.         # Set our node to the new element
  184.         self.node = element
  185.         
  186.         # Set the override to our id
  187.         self.override = self.id
  188.         self.id = None
  189.         
  190.         # Set the new library
  191.         self.set_library(target)
  192.         
  193.         # The library is tainted because we added this snippet
  194.         target.tainted = True
  195.         
  196.         # Add the override
  197.         SnippetsLibrary().overridden[self.override] = override
  198.     
  199.     def revert(self, snippet):
  200.         userlib = self.library()
  201.         self.set_library(snippet.library())
  202.         
  203.         userlib.remove(self.node)
  204.         
  205.         self.set_node(None)
  206.  
  207.         # Copy the properties
  208.         self.properties = snippet.properties
  209.         
  210.         # Set the id
  211.         self.id = snippet.id
  212.  
  213.         # Reset the override flag
  214.         self.override = None
  215.  
  216. class SnippetsTreeBuilder(et.TreeBuilder):
  217.     def __init__(self, start=None, end=None):
  218.         et.TreeBuilder.__init__(self)
  219.         self.set_start(start)
  220.         self.set_end(end)
  221.  
  222.     def set_start(self, start):
  223.         self._start_cb = start
  224.     
  225.     def set_end(self, end):
  226.         self._end_cb = end
  227.  
  228.     def start(self, tag, attrs):
  229.         result = et.TreeBuilder.start(self, tag, attrs)
  230.     
  231.         if self._start_cb:
  232.             self._start_cb(result)
  233.     
  234.         return result
  235.         
  236.     def end(self, tag):
  237.         result = et.TreeBuilder.end(self, tag)
  238.     
  239.         if self._end_cb:
  240.             self._end_cb(result)
  241.     
  242.         return result
  243.  
  244. class LanguageContainer:
  245.     def __init__(self, language):
  246.         self.language = language
  247.         self.snippets = []
  248.         self.snippets_by_prop = {'tag': {}, 'accelerator': {}}
  249.         self.accel_group = gtk.AccelGroup()
  250.         self._refs = 0
  251.  
  252.     def _add_prop(self, snippet, prop, value=0):
  253.         if value == 0:
  254.             value = snippet[prop]
  255.         
  256.         if not value or value == '':
  257.             return
  258.  
  259.         snippets_debug('Added ' + prop + ' ' + value + ' to ' + \
  260.                 str(self.language))
  261.         
  262.         if prop == 'accelerator':
  263.             keyval, mod = gtk.accelerator_parse(value)
  264.             self.accel_group.connect_group(keyval, mod, 0, \
  265.                     SnippetsLibrary().accelerator_activated)
  266.         
  267.         snippets = self.snippets_by_prop[prop]
  268.         
  269.         if value in snippets:
  270.             snippets[value].append(snippet)
  271.         else:
  272.             snippets[value] = [snippet]
  273.  
  274.     def _remove_prop(self, snippet, prop, value=0):
  275.         if value == 0:
  276.             value = snippet[prop]
  277.  
  278.         if not value or value == '':
  279.             return
  280.  
  281.         snippets_debug('Removed ' + prop + ' ' + value + ' from ' + \
  282.                 str(self.language))
  283.  
  284.         if prop == 'accelerator':
  285.             keyval, mod = gtk.accelerator_parse(value)
  286.             self.accel_group.disconnect_key(keyval, mod)
  287.  
  288.         snippets = self.snippets_by_prop[prop]
  289.         
  290.         try:
  291.             snippets[value].remove(snippet)
  292.         except:
  293.             True
  294.         
  295.     def append(self, snippet):
  296.         tag = snippet['tag']
  297.         accelerator = snippet['accelerator']
  298.         
  299.         self.snippets.append(snippet)
  300.         
  301.         self._add_prop(snippet, 'tag')
  302.         self._add_prop(snippet, 'accelerator')
  303.  
  304.         return snippet
  305.     
  306.     def remove(self, snippet):
  307.         try:
  308.             self.snippets.remove(snippet)
  309.         except:
  310.             True
  311.             
  312.         self._remove_prop(snippet, 'tag')
  313.         self._remove_prop(snippet, 'accelerator')
  314.     
  315.     def prop_changed(self, snippet, prop, oldvalue):
  316.         snippets_debug('PROP CHANGED (', prop, ')', oldvalue)
  317.  
  318.         self._remove_prop(snippet, prop, oldvalue)
  319.         self._add_prop(snippet, prop)
  320.     
  321.     def from_prop(self, prop, value):
  322.         snippets = self.snippets_by_prop[prop]
  323.         
  324.         if value in snippets:
  325.             return snippets[value]
  326.         else:
  327.             return []
  328.     
  329.     def ref(self):
  330.         self._refs += 1
  331.     
  332.         return True
  333.  
  334.     def unref(self):
  335.         if self._refs > 0:
  336.             self._refs -= 1
  337.         
  338.         return self._refs != 0
  339.  
  340. class SnippetsSystemFile:
  341.     def __init__(self, path=None):
  342.         self.path = path
  343.         self.loaded = False
  344.         self.language = None
  345.         self.ok = True
  346.         
  347.     def load_error(self, message):
  348.         sys.stderr.write("An error occurred loading " + self.path + ":\n")
  349.         sys.stderr.write(message + "\nSnippets in this file will not be " \
  350.                 "available, please correct or remove the file.\n")
  351.  
  352.     def _add_snippet(self, element):
  353.         self.loading_elements.append(element)
  354.  
  355.     def set_language(self, element):
  356.         self.language = element.attrib.get('language')
  357.         
  358.         if self.language:
  359.             self.language = self.language.lower()
  360.     
  361.     def _set_root(self, element):
  362.         self.set_language(element)
  363.         
  364.     def _preprocess_element(self, element):
  365.         if not self.loaded:
  366.             if not element.tag == "snippets":
  367.                 self.load_error("Root element should be `snippets' instead " \
  368.                         "of `%s'" % element.tag)
  369.                 return False
  370.             else:
  371.                 self._set_root(element)
  372.                 self.loaded = True
  373.         elif element.tag != 'snippet' and not self.insnippet:
  374.             self.load_error("Element should be `snippet' instead of `%s'" \
  375.                     % element.tag)
  376.             return False
  377.         else:
  378.             self.insnippet = True
  379.  
  380.         return True
  381.  
  382.     def _process_element(self, element):
  383.         if element.tag == 'snippet':
  384.             self._add_snippet(element)
  385.             self.insnippet = False            
  386.  
  387.         return True
  388.  
  389.     def ensure(self):
  390.         if not self.ok or self.loaded:
  391.             return
  392.         
  393.         self.load()
  394.  
  395.     def parse_xml(self, readsize=16384):
  396.         if not self.path:
  397.             return
  398.             
  399.         elements = []
  400.  
  401.         builder = SnippetsTreeBuilder( \
  402.                 lambda node: elements.append((node, True)), \
  403.                 lambda node: elements.append((node, False)))
  404.  
  405.         parser = et.XMLTreeBuilder(target=builder)
  406.         self.insnippet = False
  407.         
  408.         try:
  409.             f = open(self.path, "r")
  410.             
  411.             while True:
  412.                 data = f.read(readsize)
  413.                 
  414.                 if not data:
  415.                     break
  416.                 
  417.                 parser.feed(data)
  418.                 
  419.                 for element in elements:
  420.                     yield element
  421.                 
  422.                 del elements[:]
  423.             
  424.             f.close()
  425.         except IOError:
  426.             self.ok = False
  427.  
  428.     def load(self):
  429.         if not self.ok:
  430.             return
  431.  
  432.         snippets_debug("Loading library (" + str(self.language) + "): " + \
  433.                 self.path)
  434.         
  435.         self.loaded = False
  436.         self.ok = False
  437.         self.loading_elements = []
  438.         
  439.         for element in self.parse_xml():
  440.             if element[1]:
  441.                 if not self._preprocess_element(element[0]):
  442.                     del self.loading_elements[:]
  443.                     return
  444.             else:
  445.                 if not self._process_element(element[0]):
  446.                     del self.loading_elements[:]
  447.                     return
  448.  
  449.         for element in self.loading_elements:
  450.             snippet = SnippetsLibrary().add_snippet(self, element)
  451.         
  452.         del self.loading_elements[:]
  453.         self.ok = True
  454.  
  455.     # This function will get the language for a file by just inspecting the
  456.     # root element of the file. This is provided so that a cache can be built
  457.     # for which file contains which language.
  458.     # It returns the name of the language
  459.     def ensure_language(self):
  460.         if not self.loaded:
  461.             self.ok = False
  462.             
  463.             for element in self.parse_xml(256):
  464.                 if element[1]:
  465.                     if element[0].tag == 'snippets':
  466.                         self.set_language(element[0])
  467.                         self.ok = True
  468.  
  469.                     break
  470.     
  471.     def unload(self):
  472.         snippets_debug("Unloading library (" + str(self.language) + "): " + \
  473.                 self.path)
  474.         self.language = None
  475.         self.loaded = False
  476.         self.ok = True
  477.  
  478. class SnippetsUserFile(SnippetsSystemFile):
  479.     def __init__(self, path=None):
  480.         SnippetsSystemFile.__init__(self, path)
  481.         self.tainted = False
  482.         
  483.     def _set_root(self, element):
  484.         SnippetsSystemFile._set_root(self, element)
  485.         self.root = element
  486.             
  487.     def add_prop(self, node, tag, data):
  488.         if data[tag]:
  489.             prop = et.SubElement(node, tag)
  490.             prop.text = data[tag]
  491.         
  492.             return prop
  493.         else:
  494.             return None
  495.  
  496.     def new_snippet(self, properties=None):
  497.         if (not self.ok) or self.root == None:
  498.             return None
  499.         
  500.         element = et.SubElement(self.root, 'snippet')
  501.         
  502.         if properties:
  503.             for prop in properties:
  504.                 sub = et.SubElement(element, prop)
  505.                 sub.text = properties[prop]
  506.         
  507.         self.tainted = True
  508.         
  509.         return SnippetsLibrary().add_snippet(self, element)
  510.     
  511.     def set_language(self, element):
  512.         SnippetsSystemFile.set_language(self, element)
  513.         
  514.         filename = os.path.basename(self.path).lower()
  515.         
  516.         if not self.language and filename == "global.xml":
  517.             self.modifier = True
  518.         elif self.language and filename == self.language + ".xml":
  519.             self.modifier = True
  520.         else:
  521.             self.modifier = False
  522.     
  523.     def create_root(self, language):
  524.         if self.loaded:
  525.             snippets_debug('Not creating root, already loaded')
  526.             return
  527.         
  528.         if language:
  529.             root = et.Element('snippets', {'language': language})
  530.             self.path = os.path.join(SnippetsLibrary().userdir, language.lower() + '.xml')
  531.         else:
  532.             root = et.Element('snippets')
  533.             self.path = os.path.join(SnippetsLibrary().userdir, 'global.xml')
  534.         
  535.         self._set_root(root)
  536.         self.loaded = True
  537.         self.ok = True
  538.         self.tainted = True
  539.         self.save()
  540.     
  541.     def remove(self, element):
  542.         try:
  543.             self.root.remove(element)
  544.             self.tainted = True
  545.         except:
  546.             return
  547.         
  548.         try:
  549.             first = self.root[0]
  550.         except:
  551.             # No more elements, this library is useless now
  552.             SnippetsLibrary().remove_library(self)
  553.     
  554.     def save(self):
  555.         if not self.ok or self.root == None or not self.tainted:
  556.             return
  557.  
  558.         path = os.path.dirname(self.path)
  559.         
  560.         try:
  561.             if not os.path.isdir(path):
  562.                 os.makedirs(path, 0755)
  563.         except OSError:
  564.             # TODO: this is bad...
  565.             sys.stderr.write("Error in making dirs\n")
  566.  
  567.         try:
  568.             write_xml(self.root, self.path, ('text', 'accelerator'))
  569.             self.tainted = False
  570.         except IOError:
  571.             # Couldn't save, what to do
  572.             sys.stderr.write("Could not save user snippets file to " + \
  573.                     self.path + "\n")
  574.     
  575.     def unload(self):
  576.         SnippetsSystemFile.unload(self)
  577.         self.root = None
  578.  
  579. class SnippetsLibraryImpl:
  580.     def __init__(self):
  581.         self._accelerator_activated_cb = None
  582.         self.loaded = False
  583.         self.check_buffer = gtk.TextBuffer()
  584.             
  585.     def set_dirs(self, userdir, systemdirs):
  586.         self.userdir = userdir
  587.         self.systemdirs = systemdirs
  588.         
  589.         self.libraries = {}
  590.         self.containers = {}
  591.         self.overridden = {}
  592.         self.loaded_ids = []
  593.  
  594.         self.loaded = False
  595.     
  596.     def set_accelerator_callback(self, cb):
  597.         self._accelerator_activated_cb = cb
  598.     
  599.     def accelerator_activated(self, group, obj, keyval, mod):
  600.         if self._accelerator_activated_cb:
  601.             self._accelerator_activated_cb(group, obj, keyval, mod)
  602.  
  603.     def add_snippet(self, library, element):
  604.         container = self.container(library.language)
  605.         overrided = self.overrided(library, element)
  606.         
  607.         if overrided:
  608.             overrided.set_library(library)
  609.             snippets_debug('Snippet is overriden: ' + overrided['description'])
  610.             return None
  611.         
  612.         snippet = SnippetData(element, library)
  613.         
  614.         if snippet.id in self.loaded_ids:
  615.             snippets_debug('Not added snippet ' + str(library.language) + \
  616.                     '::' + snippet['description'] + ' (duplicate)')
  617.             return None
  618.  
  619.         snippet = container.append(snippet)
  620.         snippets_debug('Added snippet ' + str(library.language) + '::' + \
  621.                 snippet['description'])
  622.         
  623.         if snippet and snippet.override:
  624.             self.add_override(snippet)
  625.         
  626.         if snippet.id:
  627.             self.loaded_ids.append(snippet.id)
  628.  
  629.         return snippet
  630.     
  631.     def container(self, language):
  632.         language = self.normalize_language(language)
  633.         
  634.         if not language in self.containers:
  635.             self.containers[language] = LanguageContainer(language)
  636.         
  637.         return self.containers[language]
  638.     
  639.     def get_user_library(self, language):
  640.         target = None
  641.         
  642.         if language in self.libraries:
  643.             for library in self.libraries[language]:
  644.                 if isinstance(library, SnippetsUserFile) and library.modifier:
  645.                     target = library
  646.                 elif not isinstance(library, SnippetsUserFile):
  647.                     break
  648.         
  649.         if not target:
  650.             # Create a new user file then
  651.             snippets_debug('Creating a new user file for language ' + \
  652.                     str(language))
  653.             target = SnippetsUserFile()
  654.             target.create_root(language)
  655.             self.add_library(target)
  656.     
  657.         return target
  658.     
  659.     def new_snippet(self, language, properties=None):
  660.         language = self.normalize_language(language)
  661.         library = self.get_user_library(language)
  662.  
  663.         return library.new_snippet(properties)
  664.     
  665.     def revert_snippet(self, snippet):
  666.         # This will revert the snippet to the one it overrides
  667.         if not snippet.can_modify() or not snippet.override in self.overridden:
  668.             # It can't be reverted, shouldn't happen, but oh..
  669.             return
  670.         
  671.         # The snippet in self.overriden only contains the property contents and
  672.         # the library it belongs to
  673.         revertto = self.overridden[snippet.override]
  674.         del self.overridden[snippet.override]
  675.         
  676.         if revertto:
  677.             snippet.revert(revertto)
  678.         
  679.             if revertto.id:
  680.                 self.loaded_ids.append(revertto.id)
  681.     
  682.     def remove_snippet(self, snippet):
  683.         if not snippet.can_modify() or snippet.is_override():
  684.             return
  685.         
  686.         # Remove from the library
  687.         userlib = snippet.library()
  688.         userlib.remove(snippet.node)
  689.         
  690.         # Remove from the container
  691.         container = self.containers[userlib.language]
  692.         container.remove(snippet)
  693.     
  694.     def overrided(self, library, element):
  695.         id = NamespacedId(library.language, element.attrib.get('id')).id
  696.         
  697.         if id in self.overridden:
  698.             snippet = SnippetData(element, None)
  699.             snippet.set_node(None)
  700.             
  701.             self.overridden[id] = snippet
  702.             return snippet
  703.         else:
  704.             return None
  705.     
  706.     def add_override(self, snippet):
  707.         snippets_debug('Add override:', snippet.override)
  708.         
  709.         if not snippet.override in self.overridden:
  710.             self.overridden[snippet.override] = None
  711.     
  712.     def add_library(self, library):
  713.         library.ensure_language()
  714.         
  715.         if not library.ok:
  716.             snippets_debug('Library in wrong format, ignoring')
  717.             return
  718.         
  719.         snippets_debug('Adding library (' + str(library.language) + '): ' + \
  720.                 library.path)
  721.  
  722.         if library.language in self.libraries:
  723.             # Make sure all the user files are before the system files
  724.             if isinstance(library, SnippetsUserFile):
  725.                 self.libraries[library.language].insert(0, library)
  726.             else:
  727.                 self.libraries[library.language].append(library)
  728.         else:
  729.             self.libraries[library.language] = [library]
  730.     
  731.     def remove_library(self, library):
  732.         if not library.ok:
  733.             return
  734.         
  735.         if library.path and os.path.isfile(library.path):
  736.             os.unlink(library.path)
  737.         
  738.         try:
  739.             self.libraries[library.language].remove(library)
  740.         except KeyError:
  741.             True
  742.             
  743.         container = self.containers[library.language]
  744.             
  745.         for snippet in list(container.snippets):
  746.             if snippet.library() == library:
  747.                 container.remove(snippet)
  748.     
  749.     def _add_user_library(self, path):
  750.         library = SnippetsUserFile(path)
  751.         self.add_library(library)
  752.         
  753.     def _add_system_library(self, path):
  754.         library = SnippetsSystemFile(path)
  755.         self.add_library(library)
  756.  
  757.     def find_libraries(self, path, searched, addcb):
  758.         snippets_debug("Finding in: " + path)
  759.         
  760.         if not os.path.isdir(path):
  761.             return searched
  762.  
  763.         files = os.listdir(path)
  764.         searched.append(path)
  765.         
  766.         for f in files:
  767.             f = os.path.realpath(os.path.join(path, f))
  768.  
  769.             # Determine what language this file provides snippets for
  770.             if os.path.isfile(f):
  771.                 addcb(f)
  772.             elif os.path.isdir(f) and not f in searched:
  773.                 # Recursion rules!
  774.                 searched = self.find_libraries(self, f, searched, addcb)
  775.         
  776.         return searched
  777.     
  778.     def normalize_language(self, language):
  779.         if language:
  780.             return language.lower()
  781.         
  782.         return language
  783.     
  784.     def remove_container(self, language):
  785.         for snippet in self.containers[language].snippets:
  786.             if snippet.id in self.loaded_ids:
  787.                 self.loaded_ids.remove(snippet.id)
  788.  
  789.             if snippet.override in self.overridden:
  790.                 del self.overridden[snippet.override]
  791.  
  792.         del self.containers[language]
  793.         
  794.     def get_accel_group(self, language):
  795.         language = self.normalize_language(language)
  796.         
  797.         container = self.container(language)
  798.         return container.accel_group
  799.         
  800.     def save(self, language):
  801.         language = self.normalize_language(language)
  802.         
  803.         if language in self.libraries:
  804.             for library in self.libraries[language]:
  805.                 if isinstance(library, SnippetsUserFile):
  806.                     library.save()
  807.                 else:
  808.                     break
  809.     
  810.     def ref(self, language):
  811.         language = self.normalize_language(language)
  812.  
  813.         snippets_debug('Ref:', language)
  814.         self.container(language).ref()
  815.     
  816.     def unref(self, language):
  817.         language = self.normalize_language(language)
  818.         
  819.         snippets_debug('Unref:', language)
  820.         
  821.         if language in self.containers:
  822.             if not self.containers[language].unref() and \
  823.                     language in self.libraries:
  824.  
  825.                 for library in self.libraries[language]:
  826.                     library.unload()
  827.                 
  828.                 self.remove_container(language)
  829.  
  830.     def ensure(self, language):
  831.         language = self.normalize_language(language)
  832.  
  833.         # Ensure language as well as the global snippets (None)
  834.         for lang in (None, language):
  835.             if lang in self.libraries:
  836.                 # Ensure the container exists
  837.                 self.container(lang)
  838.  
  839.                 for library in self.libraries[lang]:
  840.                     library.ensure()
  841.  
  842.     def ensure_files(self):
  843.         if self.loaded:
  844.             return
  845.  
  846.         searched = []
  847.         searched = self.find_libraries(self.userdir, searched, \
  848.                 self._add_user_library)
  849.         
  850.         for d in self.systemdirs:
  851.             searched = self.find_libraries(d, searched, \
  852.                     self._add_system_library)
  853.  
  854.         self.loaded = True
  855.  
  856.     def valid_accelerator(self, keyval, mod):
  857.         mod &= gtk.accelerator_get_default_mod_mask()
  858.     
  859.         return (mod and (gdk.keyval_to_unicode(keyval) or \
  860.                 keyval in range(gtk.keysyms.F1, gtk.keysyms.F12 + 1)))
  861.     
  862.     def valid_tab_trigger(self, trigger):
  863.         if not trigger:
  864.             return True
  865.  
  866.         if trigger.isdigit():
  867.             return False
  868.  
  869.         self.check_buffer.set_text(trigger)
  870.  
  871.         start, end = self.check_buffer.get_bounds()
  872.         text = self.check_buffer.get_text(start, end)
  873.                 
  874.         s = start.copy()
  875.         e = end.copy()
  876.         
  877.         end.backward_word_start()
  878.         start.forward_word_end()
  879.         
  880.         return (s.equal(end) and e.equal(start)) or (len(text) == 1 and not (text.isalnum() or text.isspace()))
  881.  
  882.     # Snippet getters
  883.     # ===============
  884.     
  885.     # Get snippets for a given language
  886.     def get_snippets(self, language=None):
  887.         self.ensure_files()
  888.         language = self.normalize_language(language)
  889.         
  890.         if not language in self.libraries:
  891.             return []
  892.         
  893.         snippets = []
  894.         self.ensure(language)
  895.         
  896.         return list(self.containers[language].snippets)
  897.  
  898.     # Get snippets for a given accelerator
  899.     def from_accelerator(self, accelerator, language=None):
  900.         self.ensure_files()
  901.         
  902.         result = []        
  903.         language = self.normalize_language(language)
  904.             
  905.         if not language in self.containers:
  906.             return []
  907.  
  908.         self.ensure(language)
  909.         result = self.containers[language].from_prop('accelerator', accelerator)
  910.         
  911.         if len(result) == 0 and language and None in self.containers:
  912.             result = self.containers[None].from_prop('accelerator', accelerator)
  913.         
  914.         return result
  915.  
  916.     # Get snippets for a given tag
  917.     def from_tag(self, tag, language=None):
  918.         self.ensure_files()
  919.         
  920.         result = []
  921.         language = self.normalize_language(language)
  922.             
  923.         if not language in self.containers:
  924.             return []
  925.  
  926.         self.ensure(language)
  927.         result = self.containers[language].from_prop('tag', tag)
  928.         
  929.         if len(result) == 0 and language and None in self.containers:
  930.             result = self.containers[None].from_prop('tag', tag)
  931.         
  932.         return result
  933.  
  934. class SnippetsLibrary:
  935.     __instance = None
  936.     
  937.     def __init__(self):
  938.         if not SnippetsLibrary.__instance:
  939.             SnippetsLibrary.__instance = SnippetsLibraryImpl()
  940.     
  941.         self.__dict__['_SnippetsLibrary__instance'] = SnippetsLibrary.__instance
  942.         
  943.     def __getattr__(self, attr):
  944.         return getattr(self.__instance, attr)
  945.     
  946.     def __setattr__(self, attr, value):
  947.         return setattr(self.__instance, attr, value)
  948.