initial
This commit is contained in:
165
plugins/snippets/snippets/Completion.py
Executable file
165
plugins/snippets/snippets/Completion.py
Executable file
@@ -0,0 +1,165 @@
|
||||
import gtksourceview2 as gsv
|
||||
import gobject
|
||||
import gedit
|
||||
import gtk
|
||||
|
||||
from Library import Library
|
||||
from LanguageManager import get_language_manager
|
||||
from Snippet import Snippet
|
||||
|
||||
class Proposal(gobject.GObject, gsv.CompletionProposal):
|
||||
def __init__(self, snippet):
|
||||
gobject.GObject.__init__(self)
|
||||
self._snippet = Snippet(snippet)
|
||||
|
||||
def snippet(self):
|
||||
return self._snippet.data
|
||||
|
||||
# Interface implementation
|
||||
def do_get_markup(self):
|
||||
return self._snippet.display()
|
||||
|
||||
def do_get_info(self):
|
||||
return self._snippet.data['text']
|
||||
|
||||
class Provider(gobject.GObject, gsv.CompletionProvider):
|
||||
def __init__(self, name, language_id, handler):
|
||||
gobject.GObject.__init__(self)
|
||||
|
||||
self.name = name
|
||||
self.info_widget = None
|
||||
self.proposals = []
|
||||
self.language_id = language_id
|
||||
self.handler = handler
|
||||
self.info_widget = None
|
||||
self.mark = None
|
||||
|
||||
theme = gtk.icon_theme_get_default()
|
||||
w, h = gtk.icon_size_lookup(gtk.ICON_SIZE_MENU)
|
||||
|
||||
self.icon = theme.load_icon(gtk.STOCK_JUSTIFY_LEFT, w, 0)
|
||||
|
||||
def __del__(self):
|
||||
if self.mark:
|
||||
self.mark.get_buffer().delete_mark(self.mark)
|
||||
|
||||
def set_proposals(self, proposals):
|
||||
self.proposals = proposals
|
||||
|
||||
def mark_position(self, it):
|
||||
if not self.mark:
|
||||
self.mark = it.get_buffer().create_mark(None, it, True)
|
||||
else:
|
||||
self.mark.get_buffer().move_mark(self.mark, it)
|
||||
|
||||
def get_word(self, context):
|
||||
it = context.get_iter()
|
||||
|
||||
if it.starts_word() or it.starts_line() or not it.ends_word():
|
||||
return None
|
||||
|
||||
start = it.copy()
|
||||
|
||||
if start.backward_word_start():
|
||||
self.mark_position(start)
|
||||
return start.get_text(it)
|
||||
else:
|
||||
return None
|
||||
|
||||
def do_get_start_iter(self, context, proposal):
|
||||
if not self.mark or self.mark.get_deleted():
|
||||
return None
|
||||
|
||||
return self.mark.get_buffer().get_iter_at_mark(self.mark)
|
||||
|
||||
def do_match(self, context):
|
||||
return True
|
||||
|
||||
def get_proposals(self, word):
|
||||
if self.proposals:
|
||||
proposals = self.proposals
|
||||
else:
|
||||
proposals = Library().get_snippets(None)
|
||||
|
||||
if self.language_id:
|
||||
proposals += Library().get_snippets(self.language_id)
|
||||
|
||||
# Filter based on the current word
|
||||
if word:
|
||||
proposals = filter(lambda x: x['tag'].startswith(word), proposals)
|
||||
|
||||
return map(lambda x: Proposal(x), proposals)
|
||||
|
||||
def do_populate(self, context):
|
||||
proposals = self.get_proposals(self.get_word(context))
|
||||
context.add_proposals(self, proposals, True)
|
||||
|
||||
def do_get_name(self):
|
||||
return self.name
|
||||
|
||||
def do_activate_proposal(self, proposal, piter):
|
||||
return self.handler(proposal, piter)
|
||||
|
||||
def do_get_info_widget(self, proposal):
|
||||
if not self.info_widget:
|
||||
view = gedit.View(gedit.Document())
|
||||
manager = get_language_manager()
|
||||
|
||||
lang = manager.get_language('snippets')
|
||||
view.get_buffer().set_language(lang)
|
||||
|
||||
sw = gtk.ScrolledWindow()
|
||||
sw.add(view)
|
||||
|
||||
self.info_view = view
|
||||
self.info_widget = sw
|
||||
|
||||
return self.info_widget
|
||||
|
||||
def do_update_info(self, proposal, info):
|
||||
buf = self.info_view.get_buffer()
|
||||
|
||||
buf.set_text(proposal.get_info())
|
||||
buf.move_mark(buf.get_insert(), buf.get_start_iter())
|
||||
buf.move_mark(buf.get_selection_bound(), buf.get_start_iter())
|
||||
self.info_view.scroll_to_iter(buf.get_start_iter(), False)
|
||||
|
||||
info.set_sizing(-1, -1, False, False)
|
||||
info.process_resize()
|
||||
|
||||
def do_get_icon(self):
|
||||
return self.icon
|
||||
|
||||
def do_get_activation(self):
|
||||
return gsv.COMPLETION_ACTIVATION_USER_REQUESTED
|
||||
|
||||
class Defaults(gobject.GObject, gsv.CompletionProvider):
|
||||
def __init__(self, handler):
|
||||
gobject.GObject.__init__(self)
|
||||
|
||||
self.handler = handler
|
||||
self.proposals = []
|
||||
|
||||
def set_defaults(self, defaults):
|
||||
self.proposals = []
|
||||
|
||||
for d in defaults:
|
||||
self.proposals.append(gsv.CompletionItem(d))
|
||||
|
||||
def do_get_name(self):
|
||||
return ""
|
||||
|
||||
def do_activate_proposal(self, proposal, piter):
|
||||
return self.handler(proposal, piter)
|
||||
|
||||
def do_populate(self, context):
|
||||
context.add_proposals(self, self.proposals, True)
|
||||
|
||||
def do_get_activation(self):
|
||||
return gsv.COMPLETION_ACTIVATION_NONE
|
||||
|
||||
gobject.type_register(Proposal)
|
||||
gobject.type_register(Provider)
|
||||
gobject.type_register(Defaults)
|
||||
|
||||
# ex:ts=8:et:
|
1089
plugins/snippets/snippets/Document.py
Executable file
1089
plugins/snippets/snippets/Document.py
Executable file
File diff suppressed because it is too large
Load Diff
98
plugins/snippets/snippets/Exporter.py
Executable file
98
plugins/snippets/snippets/Exporter.py
Executable file
@@ -0,0 +1,98 @@
|
||||
import os
|
||||
import tempfile
|
||||
import sys
|
||||
import shutil
|
||||
|
||||
from snippets.Library import *
|
||||
import xml.etree.ElementTree as et
|
||||
from Helper import *
|
||||
|
||||
class Exporter:
|
||||
def __init__(self, filename, snippets):
|
||||
self.filename = filename
|
||||
self.set_snippets(snippets)
|
||||
|
||||
def set_snippets(self, snippets):
|
||||
self.snippets = {}
|
||||
|
||||
for snippet in snippets:
|
||||
lang = snippet.language()
|
||||
|
||||
if lang in self.snippets:
|
||||
self.snippets[lang].append(snippet)
|
||||
else:
|
||||
self.snippets[lang] = [snippet]
|
||||
|
||||
def export_xml(self, dirname, language, snippets):
|
||||
# Create the root snippets node
|
||||
root = et.Element('snippets')
|
||||
|
||||
# Create filename based on language
|
||||
if language:
|
||||
filename = os.path.join(dirname, language + '.xml')
|
||||
|
||||
# Set the language attribute
|
||||
root.attrib['language'] = language
|
||||
else:
|
||||
filename = os.path.join(dirname, 'global.xml')
|
||||
|
||||
# Add all snippets to the root node
|
||||
for snippet in snippets:
|
||||
root.append(snippet.to_xml())
|
||||
|
||||
# Write xml
|
||||
write_xml(root, filename, ('text', 'accelerator'))
|
||||
|
||||
def export_archive(self, cmd):
|
||||
dirname = tempfile.mkdtemp()
|
||||
|
||||
# Save current working directory and change to temporary directory
|
||||
curdir = os.getcwd()
|
||||
|
||||
try:
|
||||
os.chdir(dirname)
|
||||
|
||||
# Write snippet xml files
|
||||
for language, snippets in self.snippets.items():
|
||||
self.export_xml(dirname, language , snippets)
|
||||
|
||||
# Archive files
|
||||
status = os.system('%s "%s" *.xml' % (cmd, self.filename))
|
||||
finally:
|
||||
os.chdir(curdir)
|
||||
|
||||
if status != 0:
|
||||
return _('The archive "%s" could not be created' % self.filename)
|
||||
|
||||
# Remove the temporary directory
|
||||
shutil.rmtree(dirname)
|
||||
|
||||
def export_targz(self):
|
||||
self.export_archive('tar -c --gzip -f')
|
||||
|
||||
def export_tarbz2(self):
|
||||
self.export_archive('tar -c --bzip2 -f')
|
||||
|
||||
def export_tar(self):
|
||||
self.export_archive('tar -cf')
|
||||
|
||||
def run(self):
|
||||
dirname = os.path.dirname(self.filename)
|
||||
if not os.path.exists(dirname):
|
||||
return _('Target directory "%s" does not exist') % dirname
|
||||
|
||||
if not os.path.isdir(dirname):
|
||||
return _('Target directory "%s" is not a valid directory') % dirname
|
||||
|
||||
(root, ext) = os.path.splitext(self.filename)
|
||||
|
||||
actions = {'.tar.gz': self.export_targz,
|
||||
'.tar.bz2': self.export_tarbz2,
|
||||
'.tar': self.export_tar}
|
||||
|
||||
for k, v in actions.items():
|
||||
if self.filename.endswith(k):
|
||||
return v()
|
||||
|
||||
return self.export_targz()
|
||||
# ex:ts=8:et:
|
182
plugins/snippets/snippets/Helper.py
Executable file
182
plugins/snippets/snippets/Helper.py
Executable file
@@ -0,0 +1,182 @@
|
||||
# Gedit snippets plugin
|
||||
# Copyright (C) 2005-2006 Jesse van den Kieboom <jesse@icecrew.nl>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
import string
|
||||
from xml.sax import saxutils
|
||||
from xml.etree.ElementTree import *
|
||||
import re
|
||||
|
||||
import gtk
|
||||
from gtk import gdk
|
||||
|
||||
def message_dialog(par, typ, msg):
|
||||
d = gtk.MessageDialog(par, gtk.DIALOG_MODAL, typ, gtk.BUTTONS_OK, msg)
|
||||
d.set_property('use-markup', True)
|
||||
|
||||
d.run()
|
||||
d.destroy()
|
||||
|
||||
def compute_indentation(view, piter):
|
||||
line = piter.get_line()
|
||||
start = view.get_buffer().get_iter_at_line(line)
|
||||
end = start.copy()
|
||||
|
||||
ch = end.get_char()
|
||||
|
||||
while (ch.isspace() and ch != '\r' and ch != '\n' and \
|
||||
end.compare(piter) < 0):
|
||||
if not end.forward_char():
|
||||
break;
|
||||
|
||||
ch = end.get_char()
|
||||
|
||||
if start.equal(end):
|
||||
return ''
|
||||
|
||||
return start.get_slice(end)
|
||||
|
||||
def markup_escape(text):
|
||||
return saxutils.escape(text)
|
||||
|
||||
def spaces_instead_of_tabs(view, text):
|
||||
if not view.get_insert_spaces_instead_of_tabs():
|
||||
return text
|
||||
|
||||
return text.replace("\t", view.get_tab_width() * ' ')
|
||||
|
||||
def insert_with_indent(view, piter, text, indentfirst = True, context = None):
|
||||
text = spaces_instead_of_tabs(view, text)
|
||||
lines = text.split('\n')
|
||||
|
||||
view.get_buffer().set_data('GeditSnippetsPluginContext', context)
|
||||
|
||||
if len(lines) == 1:
|
||||
view.get_buffer().insert(piter, text)
|
||||
else:
|
||||
# Compute indentation
|
||||
indent = compute_indentation(view, piter)
|
||||
text = ''
|
||||
|
||||
for i in range(0, len(lines)):
|
||||
if indentfirst or i > 0:
|
||||
text += indent + lines[i] + '\n'
|
||||
else:
|
||||
text += lines[i] + '\n'
|
||||
|
||||
view.get_buffer().insert(piter, text[:-1])
|
||||
|
||||
view.get_buffer().set_data('GeditSnippetsPluginContext', None)
|
||||
|
||||
def get_buffer_context(buf):
|
||||
return buf.get_data('GeditSnippetsPluginContext')
|
||||
|
||||
def snippets_debug(*s):
|
||||
return
|
||||
|
||||
def write_xml(node, f, cdata_nodes=()):
|
||||
assert node is not None
|
||||
|
||||
if not hasattr(f, "write"):
|
||||
f = open(f, "wb")
|
||||
|
||||
# Encoding
|
||||
f.write("<?xml version='1.0' encoding='utf-8'?>\n")
|
||||
|
||||
_write_node(node, f, cdata_nodes)
|
||||
|
||||
def _write_indent(file, text, indent):
|
||||
file.write(' ' * indent + text)
|
||||
|
||||
def _write_node(node, file, cdata_nodes=(), indent=0):
|
||||
# write XML to file
|
||||
tag = node.tag
|
||||
|
||||
if node is Comment:
|
||||
_write_indent(file, "<!-- %s -->\n" % saxutils.escape(node.text.encode('utf-8')), indent)
|
||||
elif node is ProcessingInstruction:
|
||||
_write_indent(file, "<?%s?>\n" % saxutils.escape(node.text.encode('utf-8')), indent)
|
||||
else:
|
||||
items = node.items()
|
||||
|
||||
if items or node.text or len(node):
|
||||
_write_indent(file, "<" + tag.encode('utf-8'), indent)
|
||||
|
||||
if items:
|
||||
items.sort() # lexical order
|
||||
for k, v in items:
|
||||
file.write(" %s=%s" % (k.encode('utf-8'), saxutils.quoteattr(v.encode('utf-8'))))
|
||||
if node.text or len(node):
|
||||
file.write(">")
|
||||
if node.text and node.text.strip() != "":
|
||||
if tag in cdata_nodes:
|
||||
file.write(_cdata(node.text))
|
||||
else:
|
||||
file.write(saxutils.escape(node.text.encode('utf-8')))
|
||||
else:
|
||||
file.write("\n")
|
||||
|
||||
for n in node:
|
||||
_write_node(n, file, cdata_nodes, indent + 1)
|
||||
|
||||
if not len(node):
|
||||
file.write("</" + tag.encode('utf-8') + ">\n")
|
||||
else:
|
||||
_write_indent(file, "</" + tag.encode('utf-8') + ">\n", \
|
||||
indent)
|
||||
else:
|
||||
file.write(" />\n")
|
||||
|
||||
if node.tail and node.tail.strip() != "":
|
||||
file.write(saxutils.escape(node.tail.encode('utf-8')))
|
||||
|
||||
def _cdata(text, replace=string.replace):
|
||||
text = text.encode('utf-8')
|
||||
return '<![CDATA[' + replace(text, ']]>', ']]]]><![CDATA[>') + ']]>'
|
||||
|
||||
def buffer_word_boundary(buf):
|
||||
iter = buf.get_iter_at_mark(buf.get_insert())
|
||||
start = iter.copy()
|
||||
|
||||
if not iter.starts_word() and (iter.inside_word() or iter.ends_word()):
|
||||
start.backward_word_start()
|
||||
|
||||
if not iter.ends_word() and iter.inside_word():
|
||||
iter.forward_word_end()
|
||||
|
||||
return (start, iter)
|
||||
|
||||
def buffer_line_boundary(buf):
|
||||
iter = buf.get_iter_at_mark(buf.get_insert())
|
||||
start = iter.copy()
|
||||
start.set_line_offset(0)
|
||||
|
||||
if not iter.ends_line():
|
||||
iter.forward_to_line_end()
|
||||
|
||||
return (start, iter)
|
||||
|
||||
def drop_get_uris(selection):
|
||||
lines = re.split('\\s*[\\n\\r]+\\s*', selection.data.strip())
|
||||
result = []
|
||||
|
||||
for line in lines:
|
||||
if not line.startswith('#'):
|
||||
result.append(line)
|
||||
|
||||
return result
|
||||
|
||||
# ex:ts=8:et:
|
100
plugins/snippets/snippets/Importer.py
Executable file
100
plugins/snippets/snippets/Importer.py
Executable file
@@ -0,0 +1,100 @@
|
||||
import os
|
||||
import tempfile
|
||||
import sys
|
||||
import shutil
|
||||
|
||||
from snippets.Library import *
|
||||
|
||||
class Importer:
|
||||
def __init__(self, filename):
|
||||
self.filename = filename
|
||||
|
||||
def import_destination(self, filename):
|
||||
userdir = Library().userdir
|
||||
|
||||
filename = os.path.basename(filename)
|
||||
(root, ext) = os.path.splitext(filename)
|
||||
|
||||
filename = os.path.join(userdir, root + ext)
|
||||
i = 1
|
||||
|
||||
while os.path.exists(filename):
|
||||
filename = os.path.join(userdir, root + '_' + str(i) + ext)
|
||||
i += 1
|
||||
|
||||
return filename
|
||||
|
||||
def import_file(self, filename):
|
||||
if not os.path.exists(filename):
|
||||
return _('File "%s" does not exist') % filename
|
||||
|
||||
if not os.path.isfile(filename):
|
||||
return _('File "%s" is not a valid snippets file') % filename
|
||||
|
||||
# Find destination for file to copy to
|
||||
dest = self.import_destination(filename)
|
||||
|
||||
# Copy file
|
||||
shutil.copy(filename, dest)
|
||||
|
||||
# Add library
|
||||
if not Library().add_user_library(dest):
|
||||
return _('Imported file "%s" is not a valid snippets file') % os.path.basename(dest)
|
||||
|
||||
def import_xml(self):
|
||||
return self.import_file(self.filename)
|
||||
|
||||
def import_archive(self, cmd):
|
||||
dirname = tempfile.mkdtemp()
|
||||
status = os.system('cd %s; %s "%s"' % (dirname, cmd, self.filename))
|
||||
|
||||
if status != 0:
|
||||
return _('The archive "%s" could not be extracted' % self.filename)
|
||||
|
||||
errors = []
|
||||
|
||||
# Now import all the files from the archive
|
||||
for f in os.listdir(dirname):
|
||||
f = os.path.join(dirname, f)
|
||||
|
||||
if os.path.isfile(f):
|
||||
if self.import_file(f):
|
||||
errors.append(os.path.basename(f))
|
||||
else:
|
||||
sys.stderr.write('Skipping %s, not a valid snippets file' % os.path.basename(f))
|
||||
|
||||
# Remove the temporary directory
|
||||
shutil.rmtree(dirname)
|
||||
|
||||
if len(errors) > 0:
|
||||
return _('The following files could not be imported: %s') % ', '.join(errors)
|
||||
|
||||
def import_targz(self):
|
||||
self.import_archive('tar -x --gzip -f')
|
||||
|
||||
def import_tarbz2(self):
|
||||
self.import_archive('tar -x --bzip2 -f')
|
||||
|
||||
def import_tar(self):
|
||||
self.import_archive('tar -xf')
|
||||
|
||||
def run(self):
|
||||
if not os.path.exists(self.filename):
|
||||
return _('File "%s" does not exist') % self.filename
|
||||
|
||||
if not os.path.isfile(self.filename):
|
||||
return _('File "%s" is not a valid snippets archive') % self.filename
|
||||
|
||||
(root, ext) = os.path.splitext(self.filename)
|
||||
|
||||
actions = {'.tar.gz': self.import_targz,
|
||||
'.tar.bz2': self.import_tarbz2,
|
||||
'.xml': self.import_xml,
|
||||
'.tar': self.import_tar}
|
||||
|
||||
for k, v in actions.items():
|
||||
if self.filename.endswith(k):
|
||||
return v()
|
||||
|
||||
return _('File "%s" is not a valid snippets archive') % self.filename
|
||||
# ex:ts=8:et:
|
21
plugins/snippets/snippets/LanguageManager.py
Executable file
21
plugins/snippets/snippets/LanguageManager.py
Executable file
@@ -0,0 +1,21 @@
|
||||
import gtksourceview2 as gsv
|
||||
import os
|
||||
|
||||
from Library import Library
|
||||
|
||||
global manager
|
||||
manager = None
|
||||
|
||||
def get_language_manager():
|
||||
global manager
|
||||
|
||||
if not manager:
|
||||
dirs = []
|
||||
|
||||
for d in Library().systemdirs:
|
||||
dirs.append(os.path.join(d, 'lang'))
|
||||
|
||||
manager = gsv.LanguageManager()
|
||||
manager.set_search_path(dirs + manager.get_search_path())
|
||||
|
||||
return manager
|
993
plugins/snippets/snippets/Library.py
Executable file
993
plugins/snippets/snippets/Library.py
Executable file
@@ -0,0 +1,993 @@
|
||||
# Gedit snippets plugin
|
||||
# Copyright (C) 2005-2006 Jesse van den Kieboom <jesse@icecrew.nl>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
import os
|
||||
import weakref
|
||||
import sys
|
||||
import tempfile
|
||||
import re
|
||||
|
||||
import gtk
|
||||
|
||||
import xml.etree.ElementTree as et
|
||||
from Helper import *
|
||||
|
||||
class NamespacedId:
|
||||
def __init__(self, namespace, id):
|
||||
if not id:
|
||||
self.id = None
|
||||
else:
|
||||
if namespace:
|
||||
self.id = namespace + '-'
|
||||
else:
|
||||
self.id = 'global-'
|
||||
|
||||
self.id += id
|
||||
|
||||
class SnippetData:
|
||||
PROPS = {'tag': '', 'text': '', 'description': 'New snippet',
|
||||
'accelerator': '', 'drop-targets': ''}
|
||||
|
||||
def __init__(self, node, library):
|
||||
self.priv_id = node.attrib.get('id')
|
||||
|
||||
self.set_library(library)
|
||||
self.valid = False
|
||||
self.set_node(node)
|
||||
|
||||
def can_modify(self):
|
||||
return (self.library and (isinstance(self.library(), SnippetsUserFile)))
|
||||
|
||||
def set_library(self, library):
|
||||
if library:
|
||||
self.library = weakref.ref(library)
|
||||
else:
|
||||
self.library = None
|
||||
|
||||
self.id = NamespacedId(self.language(), self.priv_id).id
|
||||
|
||||
def set_node(self, node):
|
||||
if self.can_modify():
|
||||
self.node = node
|
||||
else:
|
||||
self.node = None
|
||||
|
||||
self.init_snippet_data(node)
|
||||
|
||||
def init_snippet_data(self, node):
|
||||
if node == None:
|
||||
return
|
||||
|
||||
self.override = node.attrib.get('override')
|
||||
|
||||
self.properties = {}
|
||||
props = SnippetData.PROPS.copy()
|
||||
|
||||
# Store all properties present
|
||||
for child in node:
|
||||
if child.tag in props:
|
||||
del props[child.tag]
|
||||
|
||||
# Normalize accelerator
|
||||
if child.tag == 'accelerator' and child.text != None:
|
||||
keyval, mod = gtk.accelerator_parse(child.text)
|
||||
|
||||
if gtk.accelerator_valid(keyval, mod):
|
||||
child.text = gtk.accelerator_name(keyval, mod)
|
||||
else:
|
||||
child.text = ''
|
||||
|
||||
if self.can_modify():
|
||||
self.properties[child.tag] = child
|
||||
else:
|
||||
self.properties[child.tag] = child.text or ''
|
||||
|
||||
# Create all the props that were not found so we stay consistent
|
||||
for prop in props:
|
||||
if self.can_modify():
|
||||
child = et.SubElement(node, prop)
|
||||
|
||||
child.text = props[prop]
|
||||
self.properties[prop] = child
|
||||
else:
|
||||
self.properties[prop] = props[prop]
|
||||
|
||||
self.check_validation()
|
||||
|
||||
def check_validation(self):
|
||||
if not self['tag'] and not self['accelerator'] and not self['drop-targets']:
|
||||
return False
|
||||
|
||||
library = Library()
|
||||
keyval, mod = gtk.accelerator_parse(self['accelerator'])
|
||||
|
||||
self.valid = library.valid_tab_trigger(self['tag']) and \
|
||||
(not self['accelerator'] or library.valid_accelerator(keyval, mod))
|
||||
|
||||
def _format_prop(self, prop, value):
|
||||
if prop == 'drop-targets' and value != '':
|
||||
return re.split('\\s*[,;]\\s*', value)
|
||||
else:
|
||||
return value
|
||||
|
||||
def __getitem__(self, prop):
|
||||
if prop in self.properties:
|
||||
if self.can_modify():
|
||||
return self._format_prop(prop, self.properties[prop].text or '')
|
||||
else:
|
||||
return self._format_prop(prop, self.properties[prop] or '')
|
||||
|
||||
return self._format_prop(prop, '')
|
||||
|
||||
def __setitem__(self, prop, value):
|
||||
if not prop in self.properties:
|
||||
return
|
||||
|
||||
if isinstance(value, list):
|
||||
value = ','.join(value)
|
||||
|
||||
if not self.can_modify() and self.properties[prop] != value:
|
||||
# ohoh, this is not can_modify, but it needs to be changed...
|
||||
# make sure it is transfered to the changes file and set all the
|
||||
# fields.
|
||||
# This snippet data container will effectively become the container
|
||||
# for the newly created node, but transparently to whoever uses
|
||||
# it
|
||||
self._override()
|
||||
|
||||
if self.can_modify() and self.properties[prop].text != value:
|
||||
if self.library():
|
||||
self.library().tainted = True
|
||||
|
||||
oldvalue = self.properties[prop].text
|
||||
self.properties[prop].text = value
|
||||
|
||||
if prop == 'tag' or prop == 'accelerator' or prop == 'drop-targets':
|
||||
container = Library().container(self.language())
|
||||
container.prop_changed(self, prop, oldvalue)
|
||||
|
||||
self.check_validation()
|
||||
|
||||
def language(self):
|
||||
if self.library and self.library():
|
||||
return self.library().language
|
||||
else:
|
||||
return None
|
||||
|
||||
def is_override(self):
|
||||
return self.override and Library().overridden[self.override]
|
||||
|
||||
def to_xml(self):
|
||||
return self._create_xml()
|
||||
|
||||
def _create_xml(self, parent=None, update=False, attrib={}):
|
||||
# Create a new node
|
||||
if parent != None:
|
||||
element = et.SubElement(parent, 'snippet', attrib)
|
||||
else:
|
||||
element = et.Element('snippet')
|
||||
|
||||
# Create all the properties
|
||||
for p in self.properties:
|
||||
prop = et.SubElement(element, p)
|
||||
prop.text = self[p]
|
||||
|
||||
if update:
|
||||
self.properties[p] = prop
|
||||
|
||||
return element
|
||||
|
||||
def _override(self):
|
||||
# Find the user file
|
||||
target = Library().get_user_library(self.language())
|
||||
|
||||
# Create a new node there with override
|
||||
element = self._create_xml(target.root, True, {'override': self.id})
|
||||
|
||||
# Create an override snippet data, feed it element so that it stores
|
||||
# all the values and then set the node to None so that it only contains
|
||||
# the values in .properties
|
||||
override = SnippetData(element, self.library())
|
||||
override.set_node(None)
|
||||
override.id = self.id
|
||||
|
||||
# Set our node to the new element
|
||||
self.node = element
|
||||
|
||||
# Set the override to our id
|
||||
self.override = self.id
|
||||
self.id = None
|
||||
|
||||
# Set the new library
|
||||
self.set_library(target)
|
||||
|
||||
# The library is tainted because we added this snippet
|
||||
target.tainted = True
|
||||
|
||||
# Add the override
|
||||
Library().overridden[self.override] = override
|
||||
|
||||
def revert(self, snippet):
|
||||
userlib = self.library()
|
||||
self.set_library(snippet.library())
|
||||
|
||||
userlib.remove(self.node)
|
||||
|
||||
self.set_node(None)
|
||||
|
||||
# Copy the properties
|
||||
self.properties = snippet.properties
|
||||
|
||||
# Set the id
|
||||
self.id = snippet.id
|
||||
|
||||
# Reset the override flag
|
||||
self.override = None
|
||||
|
||||
class SnippetsTreeBuilder(et.TreeBuilder):
|
||||
def __init__(self, start=None, end=None):
|
||||
et.TreeBuilder.__init__(self)
|
||||
self.set_start(start)
|
||||
self.set_end(end)
|
||||
|
||||
def set_start(self, start):
|
||||
self._start_cb = start
|
||||
|
||||
def set_end(self, end):
|
||||
self._end_cb = end
|
||||
|
||||
def start(self, tag, attrs):
|
||||
result = et.TreeBuilder.start(self, tag, attrs)
|
||||
|
||||
if self._start_cb:
|
||||
self._start_cb(result)
|
||||
|
||||
return result
|
||||
|
||||
def end(self, tag):
|
||||
result = et.TreeBuilder.end(self, tag)
|
||||
|
||||
if self._end_cb:
|
||||
self._end_cb(result)
|
||||
|
||||
return result
|
||||
|
||||
class LanguageContainer:
|
||||
def __init__(self, language):
|
||||
self.language = language
|
||||
self.snippets = []
|
||||
self.snippets_by_prop = {'tag': {}, 'accelerator': {}, 'drop-targets': {}}
|
||||
self.accel_group = gtk.AccelGroup()
|
||||
self._refs = 0
|
||||
|
||||
def _add_prop(self, snippet, prop, value=0):
|
||||
if value == 0:
|
||||
value = snippet[prop]
|
||||
|
||||
if not value or value == '':
|
||||
return
|
||||
|
||||
snippets_debug('Added ', prop ,' ', value, ' to ', str(self.language))
|
||||
|
||||
if prop == 'accelerator':
|
||||
keyval, mod = gtk.accelerator_parse(value)
|
||||
self.accel_group.connect_group(keyval, mod, 0, \
|
||||
Library().accelerator_activated)
|
||||
|
||||
snippets = self.snippets_by_prop[prop]
|
||||
|
||||
if not isinstance(value, list):
|
||||
value = [value]
|
||||
|
||||
for val in value:
|
||||
if val in snippets:
|
||||
snippets[val].append(snippet)
|
||||
else:
|
||||
snippets[val] = [snippet]
|
||||
|
||||
def _remove_prop(self, snippet, prop, value=0):
|
||||
if value == 0:
|
||||
value = snippet[prop]
|
||||
|
||||
if not value or value == '':
|
||||
return
|
||||
|
||||
snippets_debug('Removed ', prop, ' ', value, ' from ', str(self.language))
|
||||
|
||||
if prop == 'accelerator':
|
||||
keyval, mod = gtk.accelerator_parse(value)
|
||||
self.accel_group.disconnect_key(keyval, mod)
|
||||
|
||||
snippets = self.snippets_by_prop[prop]
|
||||
|
||||
if not isinstance(value, list):
|
||||
value = [value]
|
||||
|
||||
for val in value:
|
||||
try:
|
||||
snippets[val].remove(snippet)
|
||||
except:
|
||||
True
|
||||
|
||||
def append(self, snippet):
|
||||
tag = snippet['tag']
|
||||
accelerator = snippet['accelerator']
|
||||
|
||||
self.snippets.append(snippet)
|
||||
|
||||
self._add_prop(snippet, 'tag')
|
||||
self._add_prop(snippet, 'accelerator')
|
||||
self._add_prop(snippet, 'drop-targets')
|
||||
|
||||
return snippet
|
||||
|
||||
def remove(self, snippet):
|
||||
try:
|
||||
self.snippets.remove(snippet)
|
||||
except:
|
||||
True
|
||||
|
||||
self._remove_prop(snippet, 'tag')
|
||||
self._remove_prop(snippet, 'accelerator')
|
||||
self._remove_prop(snippet, 'drop-targets')
|
||||
|
||||
def prop_changed(self, snippet, prop, oldvalue):
|
||||
snippets_debug('PROP CHANGED (', prop, ')', oldvalue)
|
||||
|
||||
self._remove_prop(snippet, prop, oldvalue)
|
||||
self._add_prop(snippet, prop)
|
||||
|
||||
def from_prop(self, prop, value):
|
||||
snippets = self.snippets_by_prop[prop]
|
||||
|
||||
if prop == 'drop-targets':
|
||||
s = []
|
||||
|
||||
# FIXME: change this to use
|
||||
# matevfs.mime_type_get_equivalence when it comes
|
||||
# available
|
||||
for key, val in snippets.items():
|
||||
if not value.startswith(key):
|
||||
continue
|
||||
|
||||
for snippet in snippets[key]:
|
||||
if not snippet in s:
|
||||
s.append(snippet)
|
||||
|
||||
return s
|
||||
else:
|
||||
if value in snippets:
|
||||
return snippets[value]
|
||||
else:
|
||||
return []
|
||||
|
||||
def ref(self):
|
||||
self._refs += 1
|
||||
|
||||
return True
|
||||
|
||||
def unref(self):
|
||||
if self._refs > 0:
|
||||
self._refs -= 1
|
||||
|
||||
return self._refs != 0
|
||||
|
||||
class SnippetsSystemFile:
|
||||
def __init__(self, path=None):
|
||||
self.path = path
|
||||
self.loaded = False
|
||||
self.language = None
|
||||
self.ok = True
|
||||
self.need_id = True
|
||||
|
||||
def load_error(self, message):
|
||||
sys.stderr.write("An error occurred loading " + self.path + ":\n")
|
||||
sys.stderr.write(message + "\nSnippets in this file will not be " \
|
||||
"available, please correct or remove the file.\n")
|
||||
|
||||
def _add_snippet(self, element):
|
||||
if not self.need_id or element.attrib.get('id'):
|
||||
self.loading_elements.append(element)
|
||||
|
||||
def set_language(self, element):
|
||||
self.language = element.attrib.get('language')
|
||||
|
||||
if self.language:
|
||||
self.language = self.language.lower()
|
||||
|
||||
def _set_root(self, element):
|
||||
self.set_language(element)
|
||||
|
||||
def _preprocess_element(self, element):
|
||||
if not self.loaded:
|
||||
if not element.tag == "snippets":
|
||||
self.load_error("Root element should be `snippets' instead " \
|
||||
"of `%s'" % element.tag)
|
||||
return False
|
||||
else:
|
||||
self._set_root(element)
|
||||
self.loaded = True
|
||||
elif element.tag != 'snippet' and not self.insnippet:
|
||||
self.load_error("Element should be `snippet' instead of `%s'" \
|
||||
% element.tag)
|
||||
return False
|
||||
else:
|
||||
self.insnippet = True
|
||||
|
||||
return True
|
||||
|
||||
def _process_element(self, element):
|
||||
if element.tag == 'snippet':
|
||||
self._add_snippet(element)
|
||||
self.insnippet = False
|
||||
|
||||
return True
|
||||
|
||||
def ensure(self):
|
||||
if not self.ok or self.loaded:
|
||||
return
|
||||
|
||||
self.load()
|
||||
|
||||
def parse_xml(self, readsize=16384):
|
||||
if not self.path:
|
||||
return
|
||||
|
||||
elements = []
|
||||
|
||||
builder = SnippetsTreeBuilder( \
|
||||
lambda node: elements.append((node, True)), \
|
||||
lambda node: elements.append((node, False)))
|
||||
|
||||
parser = et.XMLTreeBuilder(target=builder)
|
||||
self.insnippet = False
|
||||
|
||||
try:
|
||||
f = open(self.path, "r")
|
||||
|
||||
while True:
|
||||
data = f.read(readsize)
|
||||
|
||||
if not data:
|
||||
break
|
||||
|
||||
parser.feed(data)
|
||||
|
||||
for element in elements:
|
||||
yield element
|
||||
|
||||
del elements[:]
|
||||
|
||||
f.close()
|
||||
except IOError:
|
||||
self.ok = False
|
||||
|
||||
def load(self):
|
||||
if not self.ok:
|
||||
return
|
||||
|
||||
snippets_debug("Loading library (" + str(self.language) + "): " + \
|
||||
self.path)
|
||||
|
||||
self.loaded = False
|
||||
self.ok = False
|
||||
self.loading_elements = []
|
||||
|
||||
for element in self.parse_xml():
|
||||
if element[1]:
|
||||
if not self._preprocess_element(element[0]):
|
||||
del self.loading_elements[:]
|
||||
return
|
||||
else:
|
||||
if not self._process_element(element[0]):
|
||||
del self.loading_elements[:]
|
||||
return
|
||||
|
||||
for element in self.loading_elements:
|
||||
snippet = Library().add_snippet(self, element)
|
||||
|
||||
del self.loading_elements[:]
|
||||
self.ok = True
|
||||
|
||||
# This function will get the language for a file by just inspecting the
|
||||
# root element of the file. This is provided so that a cache can be built
|
||||
# for which file contains which language.
|
||||
# It returns the name of the language
|
||||
def ensure_language(self):
|
||||
if not self.loaded:
|
||||
self.ok = False
|
||||
|
||||
for element in self.parse_xml(256):
|
||||
if element[1]:
|
||||
if element[0].tag == 'snippets':
|
||||
self.set_language(element[0])
|
||||
self.ok = True
|
||||
|
||||
break
|
||||
|
||||
def unload(self):
|
||||
snippets_debug("Unloading library (" + str(self.language) + "): " + \
|
||||
self.path)
|
||||
self.language = None
|
||||
self.loaded = False
|
||||
self.ok = True
|
||||
|
||||
class SnippetsUserFile(SnippetsSystemFile):
|
||||
def __init__(self, path=None):
|
||||
SnippetsSystemFile.__init__(self, path)
|
||||
self.tainted = False
|
||||
self.need_id = False
|
||||
|
||||
def _set_root(self, element):
|
||||
SnippetsSystemFile._set_root(self, element)
|
||||
self.root = element
|
||||
|
||||
def add_prop(self, node, tag, data):
|
||||
if data[tag]:
|
||||
prop = et.SubElement(node, tag)
|
||||
prop.text = data[tag]
|
||||
|
||||
return prop
|
||||
else:
|
||||
return None
|
||||
|
||||
def new_snippet(self, properties=None):
|
||||
if (not self.ok) or self.root == None:
|
||||
return None
|
||||
|
||||
element = et.SubElement(self.root, 'snippet')
|
||||
|
||||
if properties:
|
||||
for prop in properties:
|
||||
sub = et.SubElement(element, prop)
|
||||
sub.text = properties[prop]
|
||||
|
||||
self.tainted = True
|
||||
|
||||
return Library().add_snippet(self, element)
|
||||
|
||||
def set_language(self, element):
|
||||
SnippetsSystemFile.set_language(self, element)
|
||||
|
||||
filename = os.path.basename(self.path).lower()
|
||||
|
||||
if not self.language and filename == "global.xml":
|
||||
self.modifier = True
|
||||
elif self.language and filename == self.language + ".xml":
|
||||
self.modifier = True
|
||||
else:
|
||||
self.modifier = False
|
||||
|
||||
def create_root(self, language):
|
||||
if self.loaded:
|
||||
snippets_debug('Not creating root, already loaded')
|
||||
return
|
||||
|
||||
if language:
|
||||
root = et.Element('snippets', {'language': language})
|
||||
self.path = os.path.join(Library().userdir, language.lower() + '.xml')
|
||||
else:
|
||||
root = et.Element('snippets')
|
||||
self.path = os.path.join(Library().userdir, 'global.xml')
|
||||
|
||||
self._set_root(root)
|
||||
self.loaded = True
|
||||
self.ok = True
|
||||
self.tainted = True
|
||||
self.save()
|
||||
|
||||
def remove(self, element):
|
||||
try:
|
||||
self.root.remove(element)
|
||||
self.tainted = True
|
||||
except:
|
||||
return
|
||||
|
||||
try:
|
||||
first = self.root[0]
|
||||
except:
|
||||
# No more elements, this library is useless now
|
||||
Library().remove_library(self)
|
||||
|
||||
def save(self):
|
||||
if not self.ok or self.root == None or not self.tainted:
|
||||
return
|
||||
|
||||
path = os.path.dirname(self.path)
|
||||
|
||||
try:
|
||||
if not os.path.isdir(path):
|
||||
os.makedirs(path, 0755)
|
||||
except OSError:
|
||||
# TODO: this is bad...
|
||||
sys.stderr.write("Error in making dirs\n")
|
||||
|
||||
try:
|
||||
write_xml(self.root, self.path, ('text', 'accelerator'))
|
||||
self.tainted = False
|
||||
except IOError:
|
||||
# Couldn't save, what to do
|
||||
sys.stderr.write("Could not save user snippets file to " + \
|
||||
self.path + "\n")
|
||||
|
||||
def unload(self):
|
||||
SnippetsSystemFile.unload(self)
|
||||
self.root = None
|
||||
|
||||
class Singleton(object):
|
||||
_instance = None
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
if not cls._instance:
|
||||
cls._instance = super(Singleton, cls).__new__(
|
||||
cls, *args, **kwargs)
|
||||
cls._instance.__init_once__()
|
||||
|
||||
return cls._instance
|
||||
|
||||
class Library(Singleton):
|
||||
def __init_once__(self):
|
||||
self._accelerator_activated_cb = None
|
||||
self.loaded = False
|
||||
self.check_buffer = gtk.TextBuffer()
|
||||
|
||||
def set_dirs(self, userdir, systemdirs):
|
||||
self.userdir = userdir
|
||||
self.systemdirs = systemdirs
|
||||
|
||||
self.libraries = {}
|
||||
self.containers = {}
|
||||
self.overridden = {}
|
||||
self.loaded_ids = []
|
||||
|
||||
self.loaded = False
|
||||
|
||||
def set_accelerator_callback(self, cb):
|
||||
self._accelerator_activated_cb = cb
|
||||
|
||||
def accelerator_activated(self, group, obj, keyval, mod):
|
||||
ret = False
|
||||
|
||||
if self._accelerator_activated_cb:
|
||||
ret = self._accelerator_activated_cb(group, obj, keyval, mod)
|
||||
|
||||
return ret
|
||||
|
||||
def add_snippet(self, library, element):
|
||||
container = self.container(library.language)
|
||||
overrided = self.overrided(library, element)
|
||||
|
||||
if overrided:
|
||||
overrided.set_library(library)
|
||||
snippets_debug('Snippet is overriden: ' + overrided['description'])
|
||||
return None
|
||||
|
||||
snippet = SnippetData(element, library)
|
||||
|
||||
if snippet.id in self.loaded_ids:
|
||||
snippets_debug('Not added snippet ' + str(library.language) + \
|
||||
'::' + snippet['description'] + ' (duplicate)')
|
||||
return None
|
||||
|
||||
snippet = container.append(snippet)
|
||||
snippets_debug('Added snippet ' + str(library.language) + '::' + \
|
||||
snippet['description'])
|
||||
|
||||
if snippet and snippet.override:
|
||||
self.add_override(snippet)
|
||||
|
||||
if snippet.id:
|
||||
self.loaded_ids.append(snippet.id)
|
||||
|
||||
return snippet
|
||||
|
||||
def container(self, language):
|
||||
language = self.normalize_language(language)
|
||||
|
||||
if not language in self.containers:
|
||||
self.containers[language] = LanguageContainer(language)
|
||||
|
||||
return self.containers[language]
|
||||
|
||||
def get_user_library(self, language):
|
||||
target = None
|
||||
|
||||
if language in self.libraries:
|
||||
for library in self.libraries[language]:
|
||||
if isinstance(library, SnippetsUserFile) and library.modifier:
|
||||
target = library
|
||||
elif not isinstance(library, SnippetsUserFile):
|
||||
break
|
||||
|
||||
if not target:
|
||||
# Create a new user file then
|
||||
snippets_debug('Creating a new user file for language ' + \
|
||||
str(language))
|
||||
target = SnippetsUserFile()
|
||||
target.create_root(language)
|
||||
self.add_library(target)
|
||||
|
||||
return target
|
||||
|
||||
def new_snippet(self, language, properties=None):
|
||||
language = self.normalize_language(language)
|
||||
library = self.get_user_library(language)
|
||||
|
||||
return library.new_snippet(properties)
|
||||
|
||||
def revert_snippet(self, snippet):
|
||||
# This will revert the snippet to the one it overrides
|
||||
if not snippet.can_modify() or not snippet.override in self.overridden:
|
||||
# It can't be reverted, shouldn't happen, but oh..
|
||||
return
|
||||
|
||||
# The snippet in self.overriden only contains the property contents and
|
||||
# the library it belongs to
|
||||
revertto = self.overridden[snippet.override]
|
||||
del self.overridden[snippet.override]
|
||||
|
||||
if revertto:
|
||||
snippet.revert(revertto)
|
||||
|
||||
if revertto.id:
|
||||
self.loaded_ids.append(revertto.id)
|
||||
|
||||
def remove_snippet(self, snippet):
|
||||
if not snippet.can_modify() or snippet.is_override():
|
||||
return
|
||||
|
||||
# Remove from the library
|
||||
userlib = snippet.library()
|
||||
userlib.remove(snippet.node)
|
||||
|
||||
# Remove from the container
|
||||
container = self.containers[userlib.language]
|
||||
container.remove(snippet)
|
||||
|
||||
def overrided(self, library, element):
|
||||
id = NamespacedId(library.language, element.attrib.get('id')).id
|
||||
|
||||
if id in self.overridden:
|
||||
snippet = SnippetData(element, None)
|
||||
snippet.set_node(None)
|
||||
|
||||
self.overridden[id] = snippet
|
||||
return snippet
|
||||
else:
|
||||
return None
|
||||
|
||||
def add_override(self, snippet):
|
||||
snippets_debug('Add override:', snippet.override)
|
||||
if not snippet.override in self.overridden:
|
||||
self.overridden[snippet.override] = None
|
||||
|
||||
def add_library(self, library):
|
||||
library.ensure_language()
|
||||
|
||||
if not library.ok:
|
||||
snippets_debug('Library in wrong format, ignoring')
|
||||
return False
|
||||
|
||||
snippets_debug('Adding library (' + str(library.language) + '): ' + \
|
||||
library.path)
|
||||
|
||||
if library.language in self.libraries:
|
||||
# Make sure all the user files are before the system files
|
||||
if isinstance(library, SnippetsUserFile):
|
||||
self.libraries[library.language].insert(0, library)
|
||||
else:
|
||||
self.libraries[library.language].append(library)
|
||||
else:
|
||||
self.libraries[library.language] = [library]
|
||||
|
||||
return True
|
||||
|
||||
def remove_library(self, library):
|
||||
if not library.ok:
|
||||
return
|
||||
|
||||
if library.path and os.path.isfile(library.path):
|
||||
os.unlink(library.path)
|
||||
|
||||
try:
|
||||
self.libraries[library.language].remove(library)
|
||||
except KeyError:
|
||||
True
|
||||
|
||||
container = self.containers[library.language]
|
||||
|
||||
for snippet in list(container.snippets):
|
||||
if snippet.library() == library:
|
||||
container.remove(snippet)
|
||||
|
||||
def add_user_library(self, path):
|
||||
library = SnippetsUserFile(path)
|
||||
return self.add_library(library)
|
||||
|
||||
def add_system_library(self, path):
|
||||
library = SnippetsSystemFile(path)
|
||||
return self.add_library(library)
|
||||
|
||||
def find_libraries(self, path, searched, addcb):
|
||||
snippets_debug("Finding in: " + path)
|
||||
|
||||
if not os.path.isdir(path):
|
||||
return searched
|
||||
|
||||
files = os.listdir(path)
|
||||
searched.append(path)
|
||||
|
||||
for f in files:
|
||||
f = os.path.realpath(os.path.join(path, f))
|
||||
|
||||
# Determine what language this file provides snippets for
|
||||
if os.path.isfile(f):
|
||||
addcb(f)
|
||||
|
||||
return searched
|
||||
|
||||
def normalize_language(self, language):
|
||||
if language:
|
||||
return language.lower()
|
||||
|
||||
return language
|
||||
|
||||
def remove_container(self, language):
|
||||
for snippet in self.containers[language].snippets:
|
||||
if snippet.id in self.loaded_ids:
|
||||
self.loaded_ids.remove(snippet.id)
|
||||
|
||||
if snippet.override in self.overridden:
|
||||
del self.overridden[snippet.override]
|
||||
|
||||
del self.containers[language]
|
||||
|
||||
def get_accel_group(self, language):
|
||||
language = self.normalize_language(language)
|
||||
container = self.container(language)
|
||||
|
||||
self.ensure(language)
|
||||
return container.accel_group
|
||||
|
||||
def save(self, language):
|
||||
language = self.normalize_language(language)
|
||||
|
||||
if language in self.libraries:
|
||||
for library in self.libraries[language]:
|
||||
if isinstance(library, SnippetsUserFile):
|
||||
library.save()
|
||||
else:
|
||||
break
|
||||
|
||||
def ref(self, language):
|
||||
language = self.normalize_language(language)
|
||||
|
||||
snippets_debug('Ref:', language)
|
||||
self.container(language).ref()
|
||||
|
||||
def unref(self, language):
|
||||
language = self.normalize_language(language)
|
||||
|
||||
snippets_debug('Unref:', language)
|
||||
|
||||
if language in self.containers:
|
||||
if not self.containers[language].unref() and \
|
||||
language in self.libraries:
|
||||
|
||||
for library in self.libraries[language]:
|
||||
library.unload()
|
||||
|
||||
self.remove_container(language)
|
||||
|
||||
def ensure(self, language):
|
||||
self.ensure_files()
|
||||
language = self.normalize_language(language)
|
||||
|
||||
# Ensure language as well as the global snippets (None)
|
||||
for lang in (None, language):
|
||||
if lang in self.libraries:
|
||||
# Ensure the container exists
|
||||
self.container(lang)
|
||||
|
||||
for library in self.libraries[lang]:
|
||||
library.ensure()
|
||||
|
||||
def ensure_files(self):
|
||||
if self.loaded:
|
||||
return
|
||||
|
||||
searched = []
|
||||
searched = self.find_libraries(self.userdir, searched, \
|
||||
self.add_user_library)
|
||||
|
||||
for d in self.systemdirs:
|
||||
searched = self.find_libraries(d, searched, \
|
||||
self.add_system_library)
|
||||
|
||||
self.loaded = True
|
||||
|
||||
def valid_accelerator(self, keyval, mod):
|
||||
mod &= gtk.accelerator_get_default_mod_mask()
|
||||
|
||||
return (mod and (gdk.keyval_to_unicode(keyval) or \
|
||||
keyval in range(gtk.keysyms.F1, gtk.keysyms.F12 + 1)))
|
||||
|
||||
def valid_tab_trigger(self, trigger):
|
||||
if not trigger:
|
||||
return True
|
||||
|
||||
if trigger.isdigit():
|
||||
return False
|
||||
|
||||
self.check_buffer.set_text(trigger)
|
||||
|
||||
start, end = self.check_buffer.get_bounds()
|
||||
text = self.check_buffer.get_text(start, end)
|
||||
|
||||
s = start.copy()
|
||||
e = end.copy()
|
||||
|
||||
end.backward_word_start()
|
||||
start.forward_word_end()
|
||||
|
||||
return (s.equal(end) and e.equal(start)) or (len(text) == 1 and not (text.isalnum() or text.isspace()))
|
||||
|
||||
# Snippet getters
|
||||
# ===============
|
||||
def _from_prop(self, prop, value, language=None):
|
||||
self.ensure_files()
|
||||
|
||||
result = []
|
||||
language = self.normalize_language(language)
|
||||
|
||||
if not language in self.containers:
|
||||
return []
|
||||
|
||||
self.ensure(language)
|
||||
result = self.containers[language].from_prop(prop, value)
|
||||
|
||||
if len(result) == 0 and language and None in self.containers:
|
||||
result = self.containers[None].from_prop(prop, value)
|
||||
|
||||
return result
|
||||
|
||||
# Get snippets for a given language
|
||||
def get_snippets(self, language=None):
|
||||
self.ensure_files()
|
||||
language = self.normalize_language(language)
|
||||
|
||||
if not language in self.libraries:
|
||||
return []
|
||||
|
||||
snippets = []
|
||||
self.ensure(language)
|
||||
|
||||
return list(self.containers[language].snippets)
|
||||
|
||||
# Get snippets for a given accelerator
|
||||
def from_accelerator(self, accelerator, language=None):
|
||||
return self._from_prop('accelerator', accelerator, language)
|
||||
|
||||
# Get snippets for a given tag
|
||||
def from_tag(self, tag, language=None):
|
||||
return self._from_prop('tag', tag, language)
|
||||
|
||||
# Get snippets for a given drop target
|
||||
def from_drop_target(self, drop_target, language=None):
|
||||
return self._from_prop('drop-targets', drop_target, language)
|
||||
|
||||
# ex:ts=8:et:
|
28
plugins/snippets/snippets/Makefile.am
Executable file
28
plugins/snippets/snippets/Makefile.am
Executable file
@@ -0,0 +1,28 @@
|
||||
# Python snippets plugin
|
||||
plugindir = $(GEDIT_PLUGINS_LIBS_DIR)/snippets
|
||||
|
||||
plugin_PYTHON = \
|
||||
__init__.py \
|
||||
WindowHelper.py \
|
||||
Document.py \
|
||||
Library.py \
|
||||
Snippet.py \
|
||||
Parser.py \
|
||||
Placeholder.py \
|
||||
Manager.py \
|
||||
Helper.py \
|
||||
SubstitutionParser.py \
|
||||
Importer.py \
|
||||
Exporter.py \
|
||||
LanguageManager.py \
|
||||
Completion.py
|
||||
|
||||
uidir = $(GEDIT_PLUGINS_DATA_DIR)/snippets/ui
|
||||
ui_DATA = snippets.ui
|
||||
|
||||
EXTRA_DIST = $(ui_DATA)
|
||||
|
||||
CLEANFILES = *.bak *.gladep *.pyc
|
||||
DISTCLEANFILES = *.bak *.gladep *.pyc
|
||||
|
||||
-include $(top_srcdir)/git.mk
|
1157
plugins/snippets/snippets/Manager.py
Executable file
1157
plugins/snippets/snippets/Manager.py
Executable file
File diff suppressed because it is too large
Load Diff
259
plugins/snippets/snippets/Parser.py
Executable file
259
plugins/snippets/snippets/Parser.py
Executable file
@@ -0,0 +1,259 @@
|
||||
# Gedit snippets plugin
|
||||
# Copyright (C) 2006-2007 Jesse van den Kieboom <jesse@icecrew.nl>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from SubstitutionParser import SubstitutionParser
|
||||
|
||||
class Token:
|
||||
def __init__(self, klass, data):
|
||||
self.klass = klass
|
||||
self.data = data
|
||||
|
||||
def __str__(self):
|
||||
return '%s: [%s]' % (self.klass, self.data)
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.klass == other.klass and self.data == other.data
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self.__eq__(other)
|
||||
|
||||
class Parser:
|
||||
SREG_ENV = '[A-Z_]+'
|
||||
SREG_ID = '[0-9]+'
|
||||
|
||||
REG_ESCAPE = re.compile('(\\$(%s|\\(|\\{|<|%s)|`|\\\\)' % (SREG_ENV, SREG_ID))
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
for k, v in kwargs.items():
|
||||
setattr(self, k, v)
|
||||
|
||||
self.position = 0
|
||||
self.data_length = len(self.data)
|
||||
|
||||
self.RULES = (self._match_env, self._match_regex, self._match_placeholder, self._match_shell, self._match_eval, self._text)
|
||||
|
||||
def remains(self):
|
||||
return self.data[self.position:]
|
||||
|
||||
def next_char(self):
|
||||
if self.position + 1 >= self.data_length:
|
||||
return ''
|
||||
else:
|
||||
return self.data[self.position + 1]
|
||||
|
||||
def char(self):
|
||||
if self.position >= self.data_length:
|
||||
return ''
|
||||
else:
|
||||
return self.data[self.position]
|
||||
|
||||
def token(self):
|
||||
self.tktext = ''
|
||||
|
||||
while self.position < self.data_length:
|
||||
try:
|
||||
# Get first character
|
||||
func = {'$': self._rule,
|
||||
'`': self._try_match_shell}[self.char()]
|
||||
except:
|
||||
func = self._text
|
||||
|
||||
# Detect end of text token
|
||||
if func != self._text and self.tktext != '':
|
||||
return Token('text', self.tktext)
|
||||
|
||||
tk = func()
|
||||
|
||||
if tk:
|
||||
return tk
|
||||
|
||||
if self.tktext != '':
|
||||
return Token('text', self.tktext)
|
||||
|
||||
def _need_escape(self):
|
||||
text = self.remains()[1:]
|
||||
|
||||
if text == '':
|
||||
return False
|
||||
|
||||
return self.REG_ESCAPE.match(text)
|
||||
|
||||
def _escape(self):
|
||||
if not self._need_escape():
|
||||
return
|
||||
|
||||
# Increase position with 1
|
||||
self.position += 1
|
||||
|
||||
def _text(self):
|
||||
if self.char() == '\\':
|
||||
self._escape()
|
||||
|
||||
self.tktext += self.char()
|
||||
self.position += 1
|
||||
|
||||
def _rule(self):
|
||||
for rule in self.RULES:
|
||||
res = rule()
|
||||
|
||||
if res:
|
||||
return res
|
||||
|
||||
def _match_env(self):
|
||||
text = self.remains()
|
||||
match = re.match('\\$(%s)' % self.SREG_ENV, text) or re.match('\\${(%s)}' % self.SREG_ENV, text)
|
||||
|
||||
if match:
|
||||
self.position += len(match.group(0))
|
||||
return Token('environment', match.group(1))
|
||||
|
||||
def _parse_list(self, lst):
|
||||
pos = 0
|
||||
length = len(lst)
|
||||
items = []
|
||||
last = None
|
||||
|
||||
while pos < length:
|
||||
char = lst[pos]
|
||||
next = pos < length - 1 and lst[pos + 1]
|
||||
|
||||
if char == '\\' and (next == ',' or next == ']'):
|
||||
char = next
|
||||
pos += 1
|
||||
elif char == ',':
|
||||
if last != None:
|
||||
items.append(last)
|
||||
|
||||
last = None
|
||||
pos += 1
|
||||
continue
|
||||
|
||||
last = (last != None and last + char) or char
|
||||
pos += 1
|
||||
|
||||
if last != None:
|
||||
items.append(last)
|
||||
|
||||
return items
|
||||
|
||||
def _parse_default(self, default):
|
||||
match = re.match('^\\s*(\\\\)?(\\[((\\\\]|[^\\]])+)\\]\\s*)$', default)
|
||||
|
||||
if not match:
|
||||
return [default]
|
||||
|
||||
groups = match.groups()
|
||||
|
||||
if groups[0]:
|
||||
return [groups[1]]
|
||||
|
||||
return self._parse_list(groups[2])
|
||||
|
||||
def _match_placeholder(self):
|
||||
text = self.remains()
|
||||
|
||||
match = re.match('\\${(%s)(:((\\\\\\}|[^}])+))?}' % self.SREG_ID, text) or re.match('\\$(%s)' % self.SREG_ID, text)
|
||||
|
||||
if not match:
|
||||
return None
|
||||
|
||||
groups = match.groups()
|
||||
default = ''
|
||||
tabstop = int(groups[0])
|
||||
self.position += len(match.group(0))
|
||||
|
||||
if len(groups) > 1 and groups[2]:
|
||||
default = self._parse_default(groups[2].replace('\\}', '}'))
|
||||
|
||||
return Token('placeholder', {'tabstop': tabstop, 'default': default})
|
||||
|
||||
def _match_shell(self):
|
||||
text = self.remains()
|
||||
match = re.match('`((%s):)?((\\\\`|[^`])+?)`' % self.SREG_ID, text) or re.match('\\$\\(((%s):)?((\\\\\\)|[^\\)])+?)\\)' % self.SREG_ID, text)
|
||||
|
||||
if not match:
|
||||
return None
|
||||
|
||||
groups = match.groups()
|
||||
tabstop = (groups[1] and int(groups[1])) or -1
|
||||
self.position += len(match.group(0))
|
||||
|
||||
if text[0] == '`':
|
||||
contents = groups[2].replace('\\`', '`')
|
||||
else:
|
||||
contents = groups[2].replace('\\)', ')')
|
||||
|
||||
return Token('shell', {'tabstop': tabstop, 'contents': contents})
|
||||
|
||||
def _try_match_shell(self):
|
||||
return self._match_shell() or self._text()
|
||||
|
||||
def _eval_options(self, options):
|
||||
reg = re.compile(self.SREG_ID)
|
||||
tabstop = -1
|
||||
depend = []
|
||||
|
||||
options = options.split(':')
|
||||
|
||||
for opt in options:
|
||||
if reg.match(opt):
|
||||
tabstop = int(opt)
|
||||
else:
|
||||
depend += self._parse_list(opt[1:-1])
|
||||
|
||||
return (tabstop, depend)
|
||||
|
||||
def _match_eval(self):
|
||||
text = self.remains()
|
||||
|
||||
options = '((%s)|\\[([0-9, ]+)\\])' % self.SREG_ID
|
||||
match = re.match('\\$<((%s:)*)((\\\\>|[^>])+?)>' % options, text)
|
||||
|
||||
if not match:
|
||||
return None
|
||||
|
||||
groups = match.groups()
|
||||
(tabstop, depend) = (groups[0] and self._eval_options(groups[0][:-1])) or (-1, [])
|
||||
self.position += len(match.group(0))
|
||||
|
||||
return Token('eval', {'tabstop': tabstop, 'dependencies': depend, 'contents': groups[5].replace('\\>', '>')})
|
||||
|
||||
def _match_regex(self):
|
||||
text = self.remains()
|
||||
|
||||
content = '((?:\\\\[/]|\\\\}|[^/}])+)'
|
||||
match = re.match('\\${(?:(%s):)?\\s*(%s|\\$([A-Z_]+))?[/]%s[/]%s(?:[/]([a-zA-Z]*))?}' % (self.SREG_ID, self.SREG_ID, content, content), text)
|
||||
|
||||
if not match:
|
||||
return None
|
||||
|
||||
groups = match.groups()
|
||||
tabstop = (groups[0] and int(groups[0])) or -1
|
||||
inp = (groups[2] or (groups[1] and int(groups[1]))) or ''
|
||||
|
||||
pattern = re.sub('\\\\([/}])', '\\1', groups[3])
|
||||
substitution = re.sub('\\\\([/}])', '\\1', groups[4])
|
||||
modifiers = groups[5] or ''
|
||||
|
||||
self.position += len(match.group(0))
|
||||
|
||||
return Token('regex', {'tabstop': tabstop, 'input': inp, 'pattern': pattern, 'substitution': substitution, 'modifiers': modifiers})
|
||||
|
||||
# ex:ts=8:et:
|
700
plugins/snippets/snippets/Placeholder.py
Executable file
700
plugins/snippets/snippets/Placeholder.py
Executable file
@@ -0,0 +1,700 @@
|
||||
# Gedit snippets plugin
|
||||
# Copyright (C) 2005-2006 Jesse van den Kieboom <jesse@icecrew.nl>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
import traceback
|
||||
import re
|
||||
import os
|
||||
import sys
|
||||
import signal
|
||||
import select
|
||||
import locale
|
||||
import subprocess
|
||||
from SubstitutionParser import SubstitutionParser
|
||||
import gobject
|
||||
|
||||
from Helper import *
|
||||
|
||||
# These are places in a view where the cursor can go and do things
|
||||
class Placeholder:
|
||||
def __init__(self, view, tabstop, defaults, begin):
|
||||
self.ok = True
|
||||
self.done = False
|
||||
self.buf = view.get_buffer()
|
||||
self.view = view
|
||||
self.has_references = False
|
||||
self.mirrors = []
|
||||
self.leave_mirrors = []
|
||||
self.tabstop = tabstop
|
||||
self.set_default(defaults)
|
||||
self.prev_contents = self.default
|
||||
self.set_mark_gravity()
|
||||
|
||||
if begin:
|
||||
self.begin = self.buf.create_mark(None, begin, self.mark_gravity[0])
|
||||
else:
|
||||
self.begin = None
|
||||
|
||||
self.end = None
|
||||
|
||||
def __str__(self):
|
||||
return '%s (%s)' % (str(self.__class__), str(self.default))
|
||||
|
||||
def set_mark_gravity(self):
|
||||
self.mark_gravity = [True, False]
|
||||
|
||||
def set_default(self, defaults):
|
||||
self.default = None
|
||||
self.defaults = []
|
||||
|
||||
if not defaults:
|
||||
return
|
||||
|
||||
for d in defaults:
|
||||
dm = self.expand_environment(d)
|
||||
|
||||
if dm:
|
||||
self.defaults.append(dm)
|
||||
|
||||
if not self.default:
|
||||
self.default = dm
|
||||
|
||||
if dm != d:
|
||||
break
|
||||
|
||||
|
||||
def literal(self, s):
|
||||
return repr(s)
|
||||
|
||||
def format_environment(self, s):
|
||||
return s
|
||||
|
||||
def re_environment(self, m):
|
||||
if m.group(1) or not m.group(2) in os.environ:
|
||||
return '$' + m.group(2)
|
||||
else:
|
||||
return self.format_environment(os.environ[m.group(2)])
|
||||
|
||||
def expand_environment(self, text):
|
||||
if not text:
|
||||
return text
|
||||
|
||||
return re.sub('(\\\\)?\\$([A-Z_]+)', self.re_environment, text)
|
||||
|
||||
def get_iter(self, mark):
|
||||
if mark and not mark.get_deleted():
|
||||
return self.buf.get_iter_at_mark(mark)
|
||||
else:
|
||||
return None
|
||||
|
||||
def begin_iter(self):
|
||||
return self.get_iter(self.begin)
|
||||
|
||||
def end_iter(self):
|
||||
return self.get_iter(self.end)
|
||||
|
||||
def run_last(self, placeholders):
|
||||
begin = self.begin_iter()
|
||||
self.end = self.buf.create_mark(None, begin, self.mark_gravity[1])
|
||||
|
||||
if self.default:
|
||||
insert_with_indent(self.view, begin, self.default, False, self)
|
||||
|
||||
def remove(self, force = False):
|
||||
if self.begin and not self.begin.get_deleted():
|
||||
self.buf.delete_mark(self.begin)
|
||||
|
||||
if self.end and not self.end.get_deleted():
|
||||
self.buf.delete_mark(self.end)
|
||||
|
||||
# Do something on beginning this placeholder
|
||||
def enter(self):
|
||||
if not self.begin or self.begin.get_deleted():
|
||||
return
|
||||
|
||||
self.buf.move_mark(self.buf.get_insert(), self.begin_iter())
|
||||
|
||||
if self.end:
|
||||
self.buf.move_mark(self.buf.get_selection_bound(), self.end_iter())
|
||||
else:
|
||||
self.buf.move_mark(self.buf.get_selection_bound(), self.begin_iter())
|
||||
|
||||
def get_text(self):
|
||||
if self.begin and self.end:
|
||||
biter = self.begin_iter()
|
||||
eiter = self.end_iter()
|
||||
|
||||
if biter and eiter:
|
||||
return self.buf.get_text(self.begin_iter(), self.end_iter())
|
||||
else:
|
||||
return ''
|
||||
else:
|
||||
return ''
|
||||
|
||||
def add_mirror(self, mirror, onleave = False):
|
||||
mirror.has_references = True
|
||||
|
||||
if onleave:
|
||||
self.leave_mirrors.append(mirror)
|
||||
else:
|
||||
self.mirrors.append(mirror)
|
||||
|
||||
def set_text(self, text):
|
||||
if self.begin.get_deleted() or self.end.get_deleted():
|
||||
return
|
||||
|
||||
# Set from self.begin to self.end to text!
|
||||
self.buf.begin_user_action()
|
||||
# Remove everything between self.begin and self.end
|
||||
begin = self.begin_iter()
|
||||
self.buf.delete(begin, self.end_iter())
|
||||
|
||||
# Insert the text from the mirror
|
||||
insert_with_indent(self.view, begin, text, True, self)
|
||||
self.buf.end_user_action()
|
||||
|
||||
self.update_contents()
|
||||
|
||||
def update_contents(self):
|
||||
prev = self.prev_contents
|
||||
self.prev_contents = self.get_text()
|
||||
|
||||
if prev != self.get_text():
|
||||
for mirror in self.mirrors:
|
||||
if not mirror.update(self):
|
||||
return
|
||||
|
||||
def update_leave_mirrors(self):
|
||||
# Notify mirrors
|
||||
for mirror in self.leave_mirrors:
|
||||
if not mirror.update(self):
|
||||
return
|
||||
|
||||
# Do something on ending this placeholder
|
||||
def leave(self):
|
||||
self.update_leave_mirrors()
|
||||
|
||||
def find_mirrors(self, text, placeholders):
|
||||
mirrors = []
|
||||
|
||||
while (True):
|
||||
m = re.search('(\\\\)?\\$(?:{([0-9]+)}|([0-9]+))', text)
|
||||
|
||||
if not m:
|
||||
break
|
||||
|
||||
# Skip escaped mirrors
|
||||
if m.group(1):
|
||||
text = text[m.end():]
|
||||
continue
|
||||
|
||||
tabstop = int(m.group(2) or m.group(3))
|
||||
|
||||
if tabstop in placeholders:
|
||||
if not tabstop in mirrors:
|
||||
mirrors.append(tabstop)
|
||||
|
||||
text = text[m.end():]
|
||||
else:
|
||||
self.ok = False
|
||||
return None
|
||||
|
||||
return mirrors
|
||||
|
||||
# This is an placeholder which inserts a mirror of another Placeholder
|
||||
class PlaceholderMirror(Placeholder):
|
||||
def __init__(self, view, tabstop, begin):
|
||||
Placeholder.__init__(self, view, -1, None, begin)
|
||||
self.mirror_stop = tabstop
|
||||
|
||||
def update(self, mirror):
|
||||
self.set_text(mirror.get_text())
|
||||
return True
|
||||
|
||||
def run_last(self, placeholders):
|
||||
Placeholder.run_last(self, placeholders)
|
||||
|
||||
if self.mirror_stop in placeholders:
|
||||
mirror = placeholders[self.mirror_stop]
|
||||
|
||||
mirror.add_mirror(self)
|
||||
|
||||
if mirror.default:
|
||||
self.set_text(mirror.default)
|
||||
else:
|
||||
self.ok = False
|
||||
|
||||
# This placeholder indicates the end of a snippet
|
||||
class PlaceholderEnd(Placeholder):
|
||||
def __init__(self, view, begin, default):
|
||||
Placeholder.__init__(self, view, 0, default, begin)
|
||||
|
||||
def run_last(self, placeholders):
|
||||
Placeholder.run_last(self, placeholders)
|
||||
|
||||
# Remove the begin mark and set the begin mark
|
||||
# to the end mark, this is needed so the end placeholder won't contain
|
||||
# any text
|
||||
|
||||
if not self.default:
|
||||
self.mark_gravity[0] = False
|
||||
self.buf.delete_mark(self.begin)
|
||||
self.begin = self.buf.create_mark(None, self.end_iter(), self.mark_gravity[0])
|
||||
|
||||
def enter(self):
|
||||
if self.begin and not self.begin.get_deleted():
|
||||
self.buf.move_mark(self.buf.get_insert(), self.begin_iter())
|
||||
|
||||
if self.end and not self.end.get_deleted():
|
||||
self.buf.move_mark(self.buf.get_selection_bound(), self.end_iter())
|
||||
|
||||
def leave(self):
|
||||
self.enter()
|
||||
|
||||
# This placeholder is used to expand a command with embedded mirrors
|
||||
class PlaceholderExpand(Placeholder):
|
||||
def __init__(self, view, tabstop, begin, s):
|
||||
Placeholder.__init__(self, view, tabstop, None, begin)
|
||||
|
||||
self.mirror_text = {0: ''}
|
||||
self.timeout_id = None
|
||||
self.cmd = s
|
||||
self.instant_update = False
|
||||
|
||||
def __str__(self):
|
||||
s = Placeholder.__str__(self)
|
||||
|
||||
return s + ' ' + self.cmd
|
||||
|
||||
def get_mirrors(self, placeholders):
|
||||
return self.find_mirrors(self.cmd, placeholders)
|
||||
|
||||
# Check if all substitution placeholders are accounted for
|
||||
def run_last(self, placeholders):
|
||||
Placeholder.run_last(self, placeholders)
|
||||
|
||||
self.ok = True
|
||||
mirrors = self.get_mirrors(placeholders)
|
||||
|
||||
if mirrors:
|
||||
allDefault = True
|
||||
|
||||
for mirror in mirrors:
|
||||
p = placeholders[mirror]
|
||||
p.add_mirror(self, not self.instant_update)
|
||||
self.mirror_text[p.tabstop] = p.default
|
||||
|
||||
if not p.default and not isinstance(p, PlaceholderExpand):
|
||||
allDefault = False
|
||||
|
||||
if allDefault:
|
||||
self.update(None)
|
||||
self.default = self.get_text() or None
|
||||
else:
|
||||
self.update(None)
|
||||
self.default = self.get_text() or None
|
||||
|
||||
if self.tabstop == -1:
|
||||
self.done = True
|
||||
|
||||
def re_placeholder(self, m, formatter):
|
||||
if m.group(1):
|
||||
return '"$' + m.group(2) + '"'
|
||||
else:
|
||||
if m.group(3):
|
||||
index = int(m.group(3))
|
||||
else:
|
||||
index = int(m.group(4))
|
||||
|
||||
return formatter(self.mirror_text[index])
|
||||
|
||||
def remove_timeout(self):
|
||||
if self.timeout_id != None:
|
||||
gobject.source_remove(self.timeout_id)
|
||||
self.timeout_id = None
|
||||
|
||||
def install_timeout(self):
|
||||
self.remove_timeout()
|
||||
self.timeout_id = gobject.timeout_add(1000, self.timeout_cb)
|
||||
|
||||
def timeout_cb(self):
|
||||
self.timeout_id = None
|
||||
|
||||
return False
|
||||
|
||||
def format_environment(self, text):
|
||||
return self.literal(text)
|
||||
|
||||
def substitute(self, text, formatter = None):
|
||||
formatter = formatter or self.literal
|
||||
|
||||
# substitute all mirrors, but also environmental variables
|
||||
text = re.sub('(\\\\)?\\$({([0-9]+)}|([0-9]+))', lambda m: self.re_placeholder(m, formatter),
|
||||
text)
|
||||
|
||||
return self.expand_environment(text)
|
||||
|
||||
def run_update(self):
|
||||
text = self.substitute(self.cmd)
|
||||
|
||||
if text:
|
||||
ret = self.expand(text)
|
||||
|
||||
if ret:
|
||||
self.update_leave_mirrors()
|
||||
else:
|
||||
ret = True
|
||||
|
||||
return ret
|
||||
|
||||
def update(self, mirror):
|
||||
text = None
|
||||
|
||||
if mirror:
|
||||
self.mirror_text[mirror.tabstop] = mirror.get_text()
|
||||
|
||||
# Check if all substitutions have been made
|
||||
for tabstop in self.mirror_text:
|
||||
if tabstop == 0:
|
||||
continue
|
||||
|
||||
if self.mirror_text[tabstop] == None:
|
||||
return False
|
||||
|
||||
return self.run_update()
|
||||
|
||||
def expand(self, text):
|
||||
return True
|
||||
|
||||
# The shell placeholder executes commands in a subshell
|
||||
class PlaceholderShell(PlaceholderExpand):
|
||||
def __init__(self, view, tabstop, begin, s):
|
||||
PlaceholderExpand.__init__(self, view, tabstop, begin, s)
|
||||
|
||||
self.shell = None
|
||||
self.remove_me = False
|
||||
|
||||
def close_shell(self):
|
||||
self.shell.stdout.close()
|
||||
self.shell = None
|
||||
|
||||
def timeout_cb(self):
|
||||
PlaceholderExpand.timeout_cb(self)
|
||||
self.remove_timeout()
|
||||
|
||||
if not self.shell:
|
||||
return False
|
||||
|
||||
gobject.source_remove(self.watch_id)
|
||||
self.close_shell()
|
||||
|
||||
if self.remove_me:
|
||||
PlaceholderExpand.remove(self)
|
||||
|
||||
message_dialog(None, gtk.MESSAGE_ERROR, 'Execution of the shell ' \
|
||||
'command (%s) exceeded the maximum time; ' \
|
||||
'execution aborted.' % self.command)
|
||||
|
||||
return False
|
||||
|
||||
def process_close(self):
|
||||
self.close_shell()
|
||||
self.remove_timeout()
|
||||
|
||||
self.set_text(str.join('', self.shell_output).rstrip('\n'))
|
||||
|
||||
if self.default == None:
|
||||
self.default = self.get_text()
|
||||
self.leave()
|
||||
|
||||
if self.remove_me:
|
||||
PlaceholderExpand.remove(self, True)
|
||||
|
||||
def process_cb(self, source, condition):
|
||||
if condition & gobject.IO_IN:
|
||||
line = source.readline()
|
||||
|
||||
if len(line) > 0:
|
||||
try:
|
||||
line = unicode(line, 'utf-8')
|
||||
except:
|
||||
line = unicode(line, locale.getdefaultlocale()[1],
|
||||
'replace')
|
||||
|
||||
self.shell_output += line
|
||||
self.install_timeout()
|
||||
|
||||
return True
|
||||
|
||||
self.process_close()
|
||||
return False
|
||||
|
||||
def literal_replace(self, match):
|
||||
return "\\%s" % (match.group(0))
|
||||
|
||||
def literal(self, text):
|
||||
return '"' + re.sub('([\\\\"])', self.literal_replace, text) + '"'
|
||||
|
||||
def expand(self, text):
|
||||
self.remove_timeout()
|
||||
|
||||
if self.shell:
|
||||
gobject.source_remove(self.watch_id)
|
||||
self.close_shell()
|
||||
|
||||
popen_args = {
|
||||
'cwd' : None,
|
||||
'shell': True,
|
||||
'env' : os.environ,
|
||||
'stdout': subprocess.PIPE
|
||||
}
|
||||
|
||||
self.command = text
|
||||
self.shell = subprocess.Popen(text, **popen_args)
|
||||
self.shell_output = ''
|
||||
self.watch_id = gobject.io_add_watch(self.shell.stdout, gobject.IO_IN | \
|
||||
gobject.IO_HUP, self.process_cb)
|
||||
self.install_timeout()
|
||||
|
||||
return True
|
||||
|
||||
def remove(self, force = False):
|
||||
if not force and self.shell:
|
||||
# Still executing shell command
|
||||
self.remove_me = True
|
||||
else:
|
||||
if force:
|
||||
self.remove_timeout()
|
||||
|
||||
if self.shell:
|
||||
self.close_shell()
|
||||
|
||||
PlaceholderExpand.remove(self, force)
|
||||
|
||||
class TimeoutError(Exception):
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
|
||||
def __str__(self):
|
||||
return repr(self.value)
|
||||
|
||||
# The python placeholder evaluates commands in python
|
||||
class PlaceholderEval(PlaceholderExpand):
|
||||
def __init__(self, view, tabstop, refs, begin, s, namespace):
|
||||
PlaceholderExpand.__init__(self, view, tabstop, begin, s)
|
||||
|
||||
self.fdread = 0
|
||||
self.remove_me = False
|
||||
self.namespace = namespace
|
||||
|
||||
self.refs = []
|
||||
|
||||
if refs:
|
||||
for ref in refs:
|
||||
self.refs.append(int(ref.strip()))
|
||||
|
||||
def get_mirrors(self, placeholders):
|
||||
mirrors = PlaceholderExpand.get_mirrors(self, placeholders)
|
||||
|
||||
if not self.ok:
|
||||
return None
|
||||
|
||||
for ref in self.refs:
|
||||
if ref in placeholders:
|
||||
if ref not in mirrors:
|
||||
mirrors.append(ref)
|
||||
else:
|
||||
self.ok = False
|
||||
return None
|
||||
|
||||
return mirrors
|
||||
|
||||
# SIGALRM is not supported on all platforms (e.g. windows). Timeout
|
||||
# with SIGALRM will not be used on those platforms. This will
|
||||
# potentially block gedit if you have a placeholder which gets stuck,
|
||||
# but it's better than not supporting them at all. At some point we
|
||||
# might have proper thread support and we can fix this in a better way
|
||||
def timeout_supported(self):
|
||||
return hasattr(signal, 'SIGALRM')
|
||||
|
||||
def timeout_cb(self, signum = 0, frame = 0):
|
||||
raise TimeoutError, "Operation timed out (>2 seconds)"
|
||||
|
||||
def install_timeout(self):
|
||||
if not self.timeout_supported():
|
||||
return
|
||||
|
||||
if self.timeout_id != None:
|
||||
self.remove_timeout()
|
||||
|
||||
self.timeout_id = signal.signal(signal.SIGALRM, self.timeout_cb)
|
||||
signal.alarm(2)
|
||||
|
||||
def remove_timeout(self):
|
||||
if not self.timeout_supported():
|
||||
return
|
||||
|
||||
if self.timeout_id != None:
|
||||
signal.alarm(0)
|
||||
|
||||
signal.signal(signal.SIGALRM, self.timeout_id)
|
||||
|
||||
self.timeout_id = None
|
||||
|
||||
def expand(self, text):
|
||||
self.remove_timeout()
|
||||
|
||||
text = text.strip()
|
||||
self.command = text
|
||||
|
||||
if not self.command or self.command == '':
|
||||
self.set_text('')
|
||||
return
|
||||
|
||||
text = "def process_snippet():\n\t" + "\n\t".join(text.split("\n"))
|
||||
|
||||
if 'process_snippet' in self.namespace:
|
||||
del self.namespace['process_snippet']
|
||||
|
||||
try:
|
||||
exec text in self.namespace
|
||||
except:
|
||||
traceback.print_exc()
|
||||
|
||||
if 'process_snippet' in self.namespace:
|
||||
try:
|
||||
# Install a sigalarm signal. This is a HACK to make sure
|
||||
# gedit doesn't get freezed by someone creating a python
|
||||
# placeholder which for instance loops indefinately. Since
|
||||
# the code is executed synchronously it will hang gedit. With
|
||||
# the alarm signal we raise an exception and catch this
|
||||
# (see below). We show an error message and return False.
|
||||
# ___this is a HACK___ and should be fixed properly (I just
|
||||
# don't know how)
|
||||
self.install_timeout()
|
||||
result = self.namespace['process_snippet']()
|
||||
self.remove_timeout()
|
||||
except TimeoutError:
|
||||
self.remove_timeout()
|
||||
|
||||
message_dialog(None, gtk.MESSAGE_ERROR, \
|
||||
_('Execution of the Python command (%s) exceeds the maximum ' \
|
||||
'time, execution aborted.') % self.command)
|
||||
|
||||
return False
|
||||
except Exception, detail:
|
||||
self.remove_timeout()
|
||||
|
||||
message_dialog(None, gtk.MESSAGE_ERROR,
|
||||
_('Execution of the Python command (%s) failed: %s') %
|
||||
(self.command, detail))
|
||||
|
||||
return False
|
||||
|
||||
if result == None:
|
||||
# sys.stderr.write("%s:\n>> %s\n" % (_('The following python code, run in a snippet, does not return a value'), "\n>> ".join(self.command.split("\n"))))
|
||||
result = ''
|
||||
|
||||
self.set_text(str(result))
|
||||
|
||||
return True
|
||||
|
||||
# Regular expression placeholder
|
||||
class PlaceholderRegex(PlaceholderExpand):
|
||||
def __init__(self, view, tabstop, begin, inp, pattern, substitution, modifiers):
|
||||
PlaceholderExpand.__init__(self, view, tabstop, begin, '')
|
||||
|
||||
self.instant_update = True
|
||||
self.inp = inp
|
||||
self.pattern = pattern
|
||||
self.substitution = substitution
|
||||
|
||||
self.init_modifiers(modifiers)
|
||||
|
||||
def init_modifiers(self, modifiers):
|
||||
mods = {'I': re.I,
|
||||
'L': re.L,
|
||||
'M': re.M,
|
||||
'S': re.S,
|
||||
'U': re.U,
|
||||
'X': re.X}
|
||||
|
||||
self.modifiers = 0
|
||||
|
||||
for modifier in modifiers:
|
||||
if modifier in mods:
|
||||
self.modifiers |= mods[modifier]
|
||||
|
||||
def get_mirrors(self, placeholders):
|
||||
mirrors = self.find_mirrors(self.pattern, placeholders) + self.find_mirrors(self.substitution, placeholders)
|
||||
|
||||
if isinstance(self.inp, int):
|
||||
if self.inp not in placeholders:
|
||||
self.ok = False
|
||||
return None
|
||||
elif self.inp not in mirrors:
|
||||
mirrors.append(self.inp)
|
||||
|
||||
return mirrors
|
||||
|
||||
def literal(self, s):
|
||||
return re.escape(s)
|
||||
|
||||
def get_input(self):
|
||||
if isinstance(self.inp, int):
|
||||
return self.mirror_text[self.inp]
|
||||
elif self.inp in os.environ:
|
||||
return os.environ[self.inp]
|
||||
else:
|
||||
return ''
|
||||
|
||||
def run_update(self):
|
||||
pattern = self.substitute(self.pattern)
|
||||
substitution = self.substitute(self.substitution, SubstitutionParser.escape_substitution)
|
||||
|
||||
if pattern:
|
||||
return self.expand(pattern, substitution)
|
||||
|
||||
return True
|
||||
|
||||
def expand(self, pattern, substitution):
|
||||
# Try to compile pattern
|
||||
try:
|
||||
regex = re.compile(pattern, self.modifiers)
|
||||
except re.error, message:
|
||||
sys.stderr.write('Could not compile regular expression: %s\n%s\n' % (pattern, message))
|
||||
return False
|
||||
|
||||
inp = self.get_input()
|
||||
match = regex.search(inp)
|
||||
|
||||
if not match:
|
||||
self.set_text(inp)
|
||||
else:
|
||||
groups = match.groupdict()
|
||||
|
||||
idx = 0
|
||||
for group in match.groups():
|
||||
groups[str(idx + 1)] = group
|
||||
idx += 1
|
||||
|
||||
groups['0'] = match.group(0)
|
||||
|
||||
parser = SubstitutionParser(substitution, groups)
|
||||
self.set_text(parser.parse())
|
||||
|
||||
return True
|
||||
# ex:ts=8:et:
|
355
plugins/snippets/snippets/Snippet.py
Executable file
355
plugins/snippets/snippets/Snippet.py
Executable file
@@ -0,0 +1,355 @@
|
||||
# Gedit snippets plugin
|
||||
# Copyright (C) 2005-2006 Jesse van den Kieboom <jesse@icecrew.nl>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
import os
|
||||
import gio
|
||||
|
||||
from Placeholder import *
|
||||
from Parser import Parser, Token
|
||||
from Helper import *
|
||||
|
||||
class EvalUtilities:
|
||||
def __init__(self, view=None):
|
||||
self.view = view
|
||||
self._init_namespace()
|
||||
|
||||
def _init_namespace(self):
|
||||
self.namespace = {
|
||||
'__builtins__': __builtins__,
|
||||
'align': self.util_align,
|
||||
'readfile': self.util_readfile,
|
||||
'filesize': self.util_filesize
|
||||
}
|
||||
|
||||
def _real_len(self, s, tablen = 0):
|
||||
if tablen == 0:
|
||||
tablen = self.view.get_tab_width()
|
||||
|
||||
return len(s.expandtabs(tablen))
|
||||
|
||||
def _filename_to_uri(self, filename):
|
||||
gfile = gio.File(filename)
|
||||
|
||||
return gfile.get_uri()
|
||||
|
||||
def util_readfile(self, filename):
|
||||
stream = gio.File(filename).read()
|
||||
|
||||
if not stream:
|
||||
return ''
|
||||
|
||||
res = stream.read()
|
||||
stream.close()
|
||||
|
||||
return res
|
||||
|
||||
def util_filesize(self, filename):
|
||||
gfile = gio.File(filename)
|
||||
info = gfile.query_info(gio.FILE_ATTRIBUTE_STANDARD_SIZE)
|
||||
|
||||
if not info:
|
||||
return 0
|
||||
|
||||
return info.get_size()
|
||||
|
||||
def util_align(self, items):
|
||||
maxlen = []
|
||||
tablen = self.view.get_tab_width()
|
||||
|
||||
for row in range(0, len(items)):
|
||||
for col in range(0, len(items[row]) - 1):
|
||||
if row == 0:
|
||||
maxlen.append(0)
|
||||
|
||||
items[row][col] += "\t"
|
||||
rl = self._real_len(items[row][col], tablen)
|
||||
|
||||
if (rl > maxlen[col]):
|
||||
maxlen[col] = rl
|
||||
|
||||
result = ''
|
||||
|
||||
for row in range(0, len(items)):
|
||||
for col in range(0, len(items[row]) - 1):
|
||||
item = items[row][col]
|
||||
|
||||
result += item + ("\t" * ((maxlen[col] - \
|
||||
self._real_len(item, tablen)) / tablen))
|
||||
|
||||
result += items[row][len(items[row]) - 1]
|
||||
|
||||
if row != len(items) - 1:
|
||||
result += "\n"
|
||||
|
||||
return result
|
||||
|
||||
class Snippet:
|
||||
def __init__(self, data):
|
||||
self.data = data
|
||||
|
||||
def __getitem__(self, prop):
|
||||
return self.data[prop]
|
||||
|
||||
def __setitem__(self, prop, value):
|
||||
self.data[prop] = value
|
||||
|
||||
def accelerator_display(self):
|
||||
accel = self['accelerator']
|
||||
|
||||
if accel:
|
||||
keyval, mod = gtk.accelerator_parse(accel)
|
||||
accel = gtk.accelerator_get_label(keyval, mod)
|
||||
|
||||
return accel or ''
|
||||
|
||||
def display(self):
|
||||
nm = markup_escape(self['description'])
|
||||
|
||||
tag = self['tag']
|
||||
accel = self.accelerator_display()
|
||||
detail = []
|
||||
|
||||
if tag and tag != '':
|
||||
detail.append(tag)
|
||||
|
||||
if accel and accel != '':
|
||||
detail.append(accel)
|
||||
|
||||
if not detail:
|
||||
return nm
|
||||
else:
|
||||
return nm + ' (<b>' + markup_escape(str.join(', ', detail)) + \
|
||||
'</b>)'
|
||||
|
||||
def _add_placeholder(self, placeholder):
|
||||
if placeholder.tabstop in self.placeholders:
|
||||
if placeholder.tabstop == -1:
|
||||
self.placeholders[-1].append(placeholder)
|
||||
self.plugin_data.ordered_placeholders.append(placeholder)
|
||||
elif placeholder.tabstop == -1:
|
||||
self.placeholders[-1] = [placeholder]
|
||||
self.plugin_data.ordered_placeholders.append(placeholder)
|
||||
else:
|
||||
self.placeholders[placeholder.tabstop] = placeholder
|
||||
self.plugin_data.ordered_placeholders.append(placeholder)
|
||||
|
||||
def _insert_text(self, text):
|
||||
# Insert text keeping indentation in mind
|
||||
indented = unicode.join('\n' + unicode(self._indent), spaces_instead_of_tabs(self._view, text).split('\n'))
|
||||
self._view.get_buffer().insert(self._insert_iter(), indented)
|
||||
|
||||
def _insert_iter(self):
|
||||
return self._view.get_buffer().get_iter_at_mark(self._insert_mark)
|
||||
|
||||
def _create_environment(self, data):
|
||||
val = ((data in os.environ) and os.environ[data]) or ''
|
||||
|
||||
# Get all the current indentation
|
||||
all_indent = compute_indentation(self._view, self._insert_iter())
|
||||
|
||||
# Substract initial indentation to get the snippet indentation
|
||||
indent = all_indent[len(self._indent):]
|
||||
|
||||
# Keep indentation
|
||||
return unicode.join('\n' + unicode(indent), val.split('\n'))
|
||||
|
||||
def _create_placeholder(self, data):
|
||||
tabstop = data['tabstop']
|
||||
begin = self._insert_iter()
|
||||
|
||||
if tabstop == 0:
|
||||
# End placeholder
|
||||
return PlaceholderEnd(self._view, begin, data['default'])
|
||||
elif tabstop in self.placeholders:
|
||||
# Mirror placeholder
|
||||
return PlaceholderMirror(self._view, tabstop, begin)
|
||||
else:
|
||||
# Default placeholder
|
||||
return Placeholder(self._view, tabstop, data['default'], begin)
|
||||
|
||||
def _create_shell(self, data):
|
||||
begin = self._insert_iter()
|
||||
return PlaceholderShell(self._view, data['tabstop'], begin, data['contents'])
|
||||
|
||||
def _create_eval(self, data):
|
||||
begin = self._insert_iter()
|
||||
return PlaceholderEval(self._view, data['tabstop'], data['dependencies'], begin, data['contents'], self._utils.namespace)
|
||||
|
||||
def _create_regex(self, data):
|
||||
begin = self._insert_iter()
|
||||
return PlaceholderRegex(self._view, data['tabstop'], begin, data['input'], data['pattern'], data['substitution'], data['modifiers'])
|
||||
|
||||
def _create_text(self, data):
|
||||
return data
|
||||
|
||||
def _invalid_placeholder(self, placeholder, remove):
|
||||
buf = self._view.get_buffer()
|
||||
|
||||
# Remove the text because this placeholder is invalid
|
||||
if placeholder.default and remove:
|
||||
buf.delete(placeholder.begin_iter(), placeholder.end_iter())
|
||||
|
||||
placeholder.remove()
|
||||
|
||||
if placeholder.tabstop == -1:
|
||||
index = self.placeholders[-1].index(placeholder)
|
||||
del self.placeholders[-1][index]
|
||||
else:
|
||||
del self.placeholders[placeholder.tabstop]
|
||||
|
||||
self.plugin_data.ordered_placeholders.remove(placeholder)
|
||||
|
||||
def _parse(self, plugin_data):
|
||||
# Initialize current variables
|
||||
self._view = plugin_data.view
|
||||
self._indent = compute_indentation(self._view, self._view.get_buffer().get_iter_at_mark(self.begin_mark))
|
||||
self._utils = EvalUtilities(self._view)
|
||||
self.placeholders = {}
|
||||
self._insert_mark = self.end_mark
|
||||
self.plugin_data = plugin_data
|
||||
|
||||
# Create parser
|
||||
parser = Parser(data=self['text'])
|
||||
|
||||
# Parse tokens
|
||||
while (True):
|
||||
token = parser.token()
|
||||
|
||||
if not token:
|
||||
break
|
||||
|
||||
try:
|
||||
val = {'environment': self._create_environment,
|
||||
'placeholder': self._create_placeholder,
|
||||
'shell': self._create_shell,
|
||||
'eval': self._create_eval,
|
||||
'regex': self._create_regex,
|
||||
'text': self._create_text}[token.klass](token.data)
|
||||
except:
|
||||
sys.stderr.write('Token class not supported: %s\n' % token.klass)
|
||||
continue
|
||||
|
||||
if isinstance(val, basestring):
|
||||
# Insert text
|
||||
self._insert_text(val)
|
||||
else:
|
||||
# Insert placeholder
|
||||
self._add_placeholder(val)
|
||||
|
||||
# Create end placeholder if there isn't one yet
|
||||
if 0 not in self.placeholders:
|
||||
self.placeholders[0] = PlaceholderEnd(self._view, self.end_iter(), None)
|
||||
self.plugin_data.ordered_placeholders.append(self.placeholders[0])
|
||||
|
||||
# Make sure run_last is ran for all placeholders and remove any
|
||||
# non `ok` placeholders
|
||||
for tabstop in self.placeholders.copy():
|
||||
ph = (tabstop == -1 and list(self.placeholders[-1])) or [self.placeholders[tabstop]]
|
||||
|
||||
for placeholder in ph:
|
||||
placeholder.run_last(self.placeholders)
|
||||
|
||||
if not placeholder.ok or placeholder.done:
|
||||
self._invalid_placeholder(placeholder, not placeholder.ok)
|
||||
|
||||
# Remove all the Expand placeholders which have a tabstop because
|
||||
# they can be used to mirror, but they shouldn't be real tabstops
|
||||
# (if they have mirrors installed). This is problably a bit of
|
||||
# a dirty hack :)
|
||||
if -1 not in self.placeholders:
|
||||
self.placeholders[-1] = []
|
||||
|
||||
for tabstop in self.placeholders.copy():
|
||||
placeholder = self.placeholders[tabstop]
|
||||
|
||||
if tabstop != -1:
|
||||
if isinstance(placeholder, PlaceholderExpand) and \
|
||||
placeholder.has_references:
|
||||
# Add to anonymous placeholders
|
||||
self.placeholders[-1].append(placeholder)
|
||||
|
||||
# Remove placeholder
|
||||
del self.placeholders[tabstop]
|
||||
|
||||
self.plugin_data = None
|
||||
|
||||
def insert_into(self, plugin_data, insert):
|
||||
buf = plugin_data.view.get_buffer()
|
||||
last_index = 0
|
||||
|
||||
# Find closest mark at current insertion, so that we may insert
|
||||
# our marks in the correct order
|
||||
(current, next) = plugin_data.next_placeholder()
|
||||
|
||||
if current:
|
||||
# Insert AFTER current
|
||||
last_index = plugin_data.placeholders.index(current) + 1
|
||||
elif next:
|
||||
# Insert BEFORE next
|
||||
last_index = plugin_data.placeholders.index(next)
|
||||
else:
|
||||
# Insert at first position
|
||||
last_index = 0
|
||||
|
||||
# lastIndex now contains the position of the last mark
|
||||
# Create snippet bounding marks
|
||||
self.begin_mark = buf.create_mark(None, insert, True)
|
||||
self.end_mark = buf.create_mark(None, insert, False)
|
||||
|
||||
# Now parse the contents of this snippet, create Placeholders
|
||||
# and insert the placholder marks in the marks array of plugin_data
|
||||
self._parse(plugin_data)
|
||||
|
||||
# So now all of the snippet is in the buffer, we have all our
|
||||
# placeholders right here, what's next, put all marks in the
|
||||
# plugin_data.marks
|
||||
k = self.placeholders.keys()
|
||||
k.sort(reverse=True)
|
||||
|
||||
plugin_data.placeholders.insert(last_index, self.placeholders[0])
|
||||
last_iter = self.placeholders[0].end_iter()
|
||||
|
||||
for tabstop in k:
|
||||
if tabstop != -1 and tabstop != 0:
|
||||
placeholder = self.placeholders[tabstop]
|
||||
end_iter = placeholder.end_iter()
|
||||
|
||||
if last_iter.compare(end_iter) < 0:
|
||||
last_iter = end_iter
|
||||
|
||||
# Inserting placeholder
|
||||
plugin_data.placeholders.insert(last_index, placeholder)
|
||||
|
||||
# Move end mark to last placeholder
|
||||
buf.move_mark(self.end_mark, last_iter)
|
||||
|
||||
return self
|
||||
|
||||
def deactivate(self):
|
||||
buf = self.begin_mark.get_buffer()
|
||||
|
||||
buf.delete_mark(self.begin_mark)
|
||||
buf.delete_mark(self.end_mark)
|
||||
|
||||
self.placeholders = {}
|
||||
|
||||
def begin_iter(self):
|
||||
return self.begin_mark.get_buffer().get_iter_at_mark(self.begin_mark)
|
||||
|
||||
def end_iter(self):
|
||||
return self.end_mark.get_buffer().get_iter_at_mark(self.end_mark)
|
||||
# ex:ts=8:et:
|
202
plugins/snippets/snippets/SubstitutionParser.py
Executable file
202
plugins/snippets/snippets/SubstitutionParser.py
Executable file
@@ -0,0 +1,202 @@
|
||||
# Gedit snippets plugin
|
||||
# Copyright (C) 2006-2007 Jesse van den Kieboom <jesse@icecrew.nl>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
import re
|
||||
|
||||
class ParseError(Exception):
|
||||
def __str__(self):
|
||||
return 'Parse error, resume next'
|
||||
|
||||
class Modifiers:
|
||||
def _first_char(s):
|
||||
first = (s != '' and s[0]) or ''
|
||||
rest = (len(s) > 1 and s[1:]) or ''
|
||||
|
||||
return first, rest
|
||||
|
||||
def upper_first(s):
|
||||
first, rest = Modifiers._first_char(s)
|
||||
|
||||
return '%s%s' % (first.upper(), rest)
|
||||
|
||||
def upper(s):
|
||||
return s.upper()
|
||||
|
||||
def lower_first(s):
|
||||
first, rest = Modifiers._first_char(s)
|
||||
|
||||
return '%s%s' % (first.lower(), rest)
|
||||
|
||||
def lower(s):
|
||||
return s.lower()
|
||||
|
||||
def title(s):
|
||||
return s.title()
|
||||
|
||||
upper_first = staticmethod(upper_first)
|
||||
upper = staticmethod(upper)
|
||||
lower_first = staticmethod(lower_first)
|
||||
lower = staticmethod(lower)
|
||||
title = staticmethod(title)
|
||||
_first_char = staticmethod(_first_char)
|
||||
|
||||
class SubstitutionParser:
|
||||
REG_ID = '[0-9]+'
|
||||
REG_NAME = '[a-zA-Z_]+'
|
||||
REG_MOD = '[a-zA-Z]+'
|
||||
REG_ESCAPE = '\\\\|\\(\\?|,|\\)'
|
||||
|
||||
def __init__(self, pattern, groups = {}, modifiers = {}):
|
||||
self.pattern = pattern
|
||||
self.groups = groups
|
||||
|
||||
self.REG_GROUP = '(?:(%s)|<(%s|%s)(?:,(%s))?>)' % (self.REG_ID, self.REG_ID, self.REG_NAME, self.REG_MOD)
|
||||
self.modifiers = {'u': Modifiers.upper_first,
|
||||
'U': Modifiers.upper,
|
||||
'l': Modifiers.lower_first,
|
||||
'L': Modifiers.lower,
|
||||
't': Modifiers.title}
|
||||
|
||||
for k, v in modifiers.items():
|
||||
self.modifiers[k] = v
|
||||
|
||||
def parse(self):
|
||||
result, tokens = self._parse(self.pattern, None)
|
||||
|
||||
return result
|
||||
|
||||
def _parse(self, tokens, terminator):
|
||||
result = ''
|
||||
|
||||
while tokens != '':
|
||||
if self._peek(tokens) == '' or self._peek(tokens) == terminator:
|
||||
tokens = self._remains(tokens)
|
||||
break
|
||||
|
||||
try:
|
||||
res, tokens = self._expr(tokens, terminator)
|
||||
except ParseError:
|
||||
res, tokens = self._text(tokens)
|
||||
|
||||
result += res
|
||||
|
||||
return result, tokens
|
||||
|
||||
def _peek(self, tokens, num = 0):
|
||||
return (num < len(tokens) and tokens[num])
|
||||
|
||||
def _token(self, tokens):
|
||||
if tokens == '':
|
||||
return '', '';
|
||||
|
||||
return tokens[0], (len(tokens) > 1 and tokens[1:]) or ''
|
||||
|
||||
def _remains(self, tokens, num = 1):
|
||||
return (num < len(tokens) and tokens[num:]) or ''
|
||||
|
||||
def _expr(self, tokens, terminator):
|
||||
if tokens == '':
|
||||
return ''
|
||||
|
||||
try:
|
||||
return {'\\': self._escape,
|
||||
'(': self._condition}[self._peek(tokens)](tokens, terminator)
|
||||
except KeyError:
|
||||
raise ParseError
|
||||
|
||||
def _text(self, tokens):
|
||||
return self._token(tokens)
|
||||
|
||||
def _substitute(self, group, modifiers = ''):
|
||||
result = (self.groups.has_key(group) and self.groups[group]) or ''
|
||||
|
||||
for modifier in modifiers:
|
||||
if self.modifiers.has_key(modifier):
|
||||
result = self.modifiers[modifier](result)
|
||||
|
||||
return result
|
||||
|
||||
def _match_group(self, tokens):
|
||||
match = re.match('\\\\%s' % self.REG_GROUP, tokens)
|
||||
|
||||
if not match:
|
||||
return None, tokens
|
||||
|
||||
return self._substitute(match.group(1) or match.group(2), match.group(3) or ''), tokens[match.end():]
|
||||
|
||||
def _escape(self, tokens, terminator):
|
||||
# Try to match a group
|
||||
result, tokens = self._match_group(tokens)
|
||||
|
||||
if result != None:
|
||||
return result, tokens
|
||||
|
||||
s = self.REG_GROUP
|
||||
|
||||
if terminator:
|
||||
s += '|%s' % re.escape(terminator)
|
||||
|
||||
match = re.match('\\\\(\\\\%s|%s)' % (s, self.REG_ESCAPE), tokens)
|
||||
|
||||
if not match:
|
||||
raise ParseError
|
||||
|
||||
return match.group(1), tokens[match.end():]
|
||||
|
||||
def _condition_value(self, tokens):
|
||||
match = re.match('\\\\?%s\s*' % self.REG_GROUP, tokens)
|
||||
|
||||
if not match:
|
||||
return None, tokens
|
||||
|
||||
groups = match.groups()
|
||||
name = groups[0] or groups[1]
|
||||
|
||||
return self.groups.has_key(name) and self.groups[name] != None, tokens[match.end():]
|
||||
|
||||
def _condition(self, tokens, terminator):
|
||||
# Match ? after (
|
||||
if self._peek(tokens, 1) != '?':
|
||||
raise ParseError
|
||||
|
||||
# Remove initial (? token
|
||||
tokens = self._remains(tokens, 2)
|
||||
condition, tokens = self._condition_value(tokens)
|
||||
|
||||
if condition == None or self._peek(tokens) != ',':
|
||||
raise ParseError
|
||||
|
||||
truepart, tokens = self._parse(self._remains(tokens), ',')
|
||||
|
||||
if truepart == None:
|
||||
raise ParseError
|
||||
|
||||
falsepart, tokens = self._parse(tokens, ')')
|
||||
|
||||
if falsepart == None:
|
||||
raise ParseError
|
||||
|
||||
if condition:
|
||||
return truepart, tokens
|
||||
else:
|
||||
return falsepart, tokens
|
||||
|
||||
def escape_substitution(substitution):
|
||||
return re.sub('(%s|%s)' % (self.REG_GROUP, self.REG_ESCAPE), '\\\\\\1', substitution)
|
||||
|
||||
escapesubstitution = staticmethod(escape_substitution)
|
||||
# ex:ts=8:et:
|
209
plugins/snippets/snippets/WindowHelper.py
Executable file
209
plugins/snippets/snippets/WindowHelper.py
Executable file
@@ -0,0 +1,209 @@
|
||||
# Gedit snippets plugin
|
||||
# Copyright (C) 2005-2006 Jesse van den Kieboom <jesse@icecrew.nl>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
import re
|
||||
import os
|
||||
import gettext
|
||||
|
||||
import gtk
|
||||
from gtk import gdk
|
||||
import gedit
|
||||
|
||||
from Document import Document
|
||||
from Library import Library
|
||||
|
||||
class WindowHelper:
|
||||
def __init__(self, plugin):
|
||||
self.plugin = plugin
|
||||
self.current_controller = None
|
||||
self.current_language = None
|
||||
self.signal_ids = {}
|
||||
|
||||
def run(self, window):
|
||||
self.window = window
|
||||
|
||||
self.insert_menu()
|
||||
self.register_messages()
|
||||
|
||||
self.accel_group = Library().get_accel_group(None)
|
||||
|
||||
window.add_accel_group(self.accel_group)
|
||||
window.connect('tab-added', self.on_tab_added)
|
||||
|
||||
# Add controllers to all the current views
|
||||
for view in self.window.get_views():
|
||||
if isinstance(view, gedit.View) and not self.has_controller(view):
|
||||
view._snippet_controller = Document(self, view)
|
||||
|
||||
self.update()
|
||||
|
||||
def stop(self):
|
||||
self.window.remove_accel_group(self.accel_group)
|
||||
self.accel_group = None
|
||||
|
||||
#self.window.remove_accel_group(accel)
|
||||
self.remove_menu()
|
||||
self.unregister_messages()
|
||||
|
||||
# Iterate over all the tabs and remove every controller
|
||||
for view in self.window.get_views():
|
||||
if isinstance(view, gedit.View) and self.has_controller(view):
|
||||
view._snippet_controller.stop()
|
||||
view._snippet_controller = None
|
||||
|
||||
self.window = None
|
||||
self.plugin = None
|
||||
|
||||
def register_messages(self):
|
||||
bus = self.window.get_message_bus()
|
||||
|
||||
self.messages = {
|
||||
'activate': bus.register('/plugins/snippets', 'activate', ('view', 'iter'), trigger=str, view=gedit.View, iter=gtk.TextIter),
|
||||
'parse-and-activate': bus.register('/plugins/snippets', 'parse-and-activate', ('view', 'iter'), snippet=str, view=gedit.View, iter=gtk.TextIter)
|
||||
}
|
||||
|
||||
bus.connect('/plugins/snippets', 'activate', self.on_message_activate)
|
||||
bus.connect('/plugins/snippets', 'parse-and-activate', self.on_message_parse_and_activate)
|
||||
|
||||
def unregister_messages(self):
|
||||
bus = self.window.get_message_bus()
|
||||
|
||||
for name in self.messages:
|
||||
bus.unregister(self.messages[name])
|
||||
|
||||
self.messages = {}
|
||||
|
||||
def on_message_activate(self, bus, message):
|
||||
if message.has_key('view'):
|
||||
view = message.view
|
||||
else:
|
||||
view = self.window.get_active_view()
|
||||
|
||||
if not self.has_controller(view):
|
||||
return
|
||||
|
||||
if message.has_key('iter'):
|
||||
iter = message.iter
|
||||
else:
|
||||
iter = view.get_buffer().get_iter_at_mark(view.get_buffer().get_insert())
|
||||
|
||||
controller = view._snippet_controller
|
||||
controller.run_snippet_trigger(message.trigger, (iter, iter))
|
||||
|
||||
def on_message_parse_and_activate(self, bus, message):
|
||||
if message.has_key('view'):
|
||||
view = message.view
|
||||
else:
|
||||
view = self.window.get_active_view()
|
||||
|
||||
if not self.has_controller(view):
|
||||
return
|
||||
|
||||
if message.has_key('iter'):
|
||||
iter = message.iter
|
||||
else:
|
||||
iter = view.get_buffer().get_iter_at_mark(view.get_buffer().get_insert())
|
||||
|
||||
controller = view._snippet_controller
|
||||
controller.parse_and_run_snippet(message.snippet, iter)
|
||||
|
||||
def insert_menu(self):
|
||||
manager = self.window.get_ui_manager()
|
||||
|
||||
self.action_group = gtk.ActionGroup("GeditSnippetPluginActions")
|
||||
self.action_group.set_translation_domain('gedit')
|
||||
self.action_group.add_actions([('ManageSnippets', None,
|
||||
_('Manage _Snippets...'), \
|
||||
None, _('Manage snippets'), \
|
||||
self.on_action_snippets_activate)])
|
||||
|
||||
self.merge_id = manager.new_merge_id()
|
||||
manager.insert_action_group(self.action_group, -1)
|
||||
manager.add_ui(self.merge_id, '/MenuBar/ToolsMenu/ToolsOps_5', \
|
||||
'ManageSnippets', 'ManageSnippets', gtk.UI_MANAGER_MENUITEM, False)
|
||||
|
||||
def remove_menu(self):
|
||||
manager = self.window.get_ui_manager()
|
||||
manager.remove_ui(self.merge_id)
|
||||
manager.remove_action_group(self.action_group)
|
||||
self.action_group = None
|
||||
|
||||
def find_snippet(self, snippets, tag):
|
||||
result = []
|
||||
|
||||
for snippet in snippets:
|
||||
if Snippet(snippet)['tag'] == tag:
|
||||
result.append(snippet)
|
||||
|
||||
return result
|
||||
|
||||
def has_controller(self, view):
|
||||
return hasattr(view, '_snippet_controller') and view._snippet_controller
|
||||
|
||||
def update_language(self):
|
||||
if not self.window:
|
||||
return
|
||||
|
||||
if self.current_language:
|
||||
accel_group = Library().get_accel_group( \
|
||||
self.current_language)
|
||||
self.window.remove_accel_group(accel_group)
|
||||
|
||||
if self.current_controller:
|
||||
self.current_language = self.current_controller.language_id
|
||||
|
||||
if self.current_language != None:
|
||||
accel_group = Library().get_accel_group( \
|
||||
self.current_language)
|
||||
self.window.add_accel_group(accel_group)
|
||||
else:
|
||||
self.current_language = None
|
||||
|
||||
def language_changed(self, controller):
|
||||
if controller == self.current_controller:
|
||||
self.update_language()
|
||||
|
||||
def update(self):
|
||||
view = self.window.get_active_view()
|
||||
|
||||
if not view or not self.has_controller(view):
|
||||
return
|
||||
|
||||
controller = view._snippet_controller
|
||||
|
||||
if controller != self.current_controller:
|
||||
self.current_controller = controller
|
||||
self.update_language()
|
||||
|
||||
# Callbacks
|
||||
|
||||
def on_tab_added(self, window, tab):
|
||||
# Create a new controller for this tab if it has a standard gedit view
|
||||
view = tab.get_view()
|
||||
|
||||
if isinstance(view, gedit.View) and not self.has_controller(view):
|
||||
view._snippet_controller = Document(self, view)
|
||||
|
||||
self.update()
|
||||
|
||||
def on_action_snippets_activate(self, item):
|
||||
self.plugin.create_configure_dialog()
|
||||
|
||||
def accelerator_activated(self, keyval, mod):
|
||||
return self.current_controller.accelerator_activate(keyval, mod)
|
||||
|
||||
# ex:ts=8:et:
|
101
plugins/snippets/snippets/__init__.py
Executable file
101
plugins/snippets/snippets/__init__.py
Executable file
@@ -0,0 +1,101 @@
|
||||
# Gedit snippets plugin
|
||||
# Copyright (C) 2005-2006 Jesse van den Kieboom <jesse@icecrew.nl>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
import sys
|
||||
import os
|
||||
import shutil
|
||||
|
||||
import gtk
|
||||
from gtk import gdk
|
||||
import gedit
|
||||
import platform
|
||||
|
||||
from WindowHelper import WindowHelper
|
||||
from Library import Library
|
||||
from Manager import Manager
|
||||
from Snippet import Snippet
|
||||
|
||||
class SnippetsPlugin(gedit.Plugin):
|
||||
def __init__(self):
|
||||
gedit.Plugin.__init__(self)
|
||||
|
||||
self.dlg = None
|
||||
|
||||
library = Library()
|
||||
library.set_accelerator_callback(self.accelerator_activated)
|
||||
|
||||
if platform.platform() == 'Windows':
|
||||
snippetsdir = os.path.expanduser('~/gedit/snippets')
|
||||
else:
|
||||
userdir = os.getenv('MATE22_USER_DIR')
|
||||
if userdir:
|
||||
snippetsdir = os.path.join(userdir, 'gedit/snippets')
|
||||
else:
|
||||
snippetsdir = os.path.expanduser('~/.mate2/gedit/snippets')
|
||||
|
||||
library.set_dirs(snippetsdir, self.system_dirs())
|
||||
|
||||
def system_dirs(self):
|
||||
if platform.platform() != 'Windows':
|
||||
if 'XDG_DATA_DIRS' in os.environ:
|
||||
datadirs = os.environ['XDG_DATA_DIRS']
|
||||
else:
|
||||
datadirs = '/usr/local/share' + os.pathsep + '/usr/share'
|
||||
|
||||
dirs = []
|
||||
|
||||
for d in datadirs.split(os.pathsep):
|
||||
d = os.path.join(d, 'gedit-2', 'plugins', 'snippets')
|
||||
|
||||
if os.path.isdir(d):
|
||||
dirs.append(d)
|
||||
|
||||
dirs.append(self.get_data_dir())
|
||||
return dirs
|
||||
|
||||
def activate(self, window):
|
||||
data = WindowHelper(self)
|
||||
window._snippets_plugin_data = data
|
||||
data.run(window)
|
||||
|
||||
def deactivate(self, window):
|
||||
window._snippets_plugin_data.stop()
|
||||
window._snippets_plugin_data = None
|
||||
|
||||
def update_ui(self, window):
|
||||
window._snippets_plugin_data.update()
|
||||
|
||||
def create_configure_dialog(self):
|
||||
if not self.dlg:
|
||||
self.dlg = Manager(self.get_data_dir())
|
||||
else:
|
||||
self.dlg.run()
|
||||
|
||||
window = gedit.app_get_default().get_active_window()
|
||||
|
||||
if window:
|
||||
self.dlg.dlg.set_transient_for(window)
|
||||
|
||||
return self.dlg.dlg
|
||||
|
||||
def accelerator_activated(self, group, obj, keyval, mod):
|
||||
ret = False
|
||||
|
||||
if hasattr(obj, '_snippets_plugin_data'):
|
||||
ret = obj._snippets_plugin_data.accelerator_activated(keyval, mod)
|
||||
|
||||
return ret
|
647
plugins/snippets/snippets/snippets.ui
Executable file
647
plugins/snippets/snippets/snippets.ui
Executable file
@@ -0,0 +1,647 @@
|
||||
<?xml version="1.0"?>
|
||||
<!--*- mode: xml -*-->
|
||||
<interface>
|
||||
<object class="GtkListStore" id="model1">
|
||||
<columns>
|
||||
<column type="gchararray"/>
|
||||
</columns>
|
||||
<data>
|
||||
<row>
|
||||
<col id="0">text</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0">text/plain</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0">text/xml</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0">image</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0">image/png</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0">image/jpeg</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0">audio</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0">video</col>
|
||||
</row>
|
||||
</data>
|
||||
</object>
|
||||
<object class="GeditDocument" id="source_buffer">
|
||||
<property name="highlight-matching-brackets">True</property>
|
||||
</object>
|
||||
<object class="GtkDialog" id="dialog_snippets">
|
||||
<property name="title" translatable="yes">Snippets Manager</property>
|
||||
<property name="type">GTK_WINDOW_TOPLEVEL</property>
|
||||
<property name="window_position">GTK_WIN_POS_NONE</property>
|
||||
<property name="modal">False</property>
|
||||
<property name="default_width">750</property>
|
||||
<property name="default_height">500</property>
|
||||
<property name="resizable">True</property>
|
||||
<property name="destroy_with_parent">True</property>
|
||||
<property name="decorated">True</property>
|
||||
<property name="skip_taskbar_hint">True</property>
|
||||
<property name="skip_pager_hint">False</property>
|
||||
<property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
|
||||
<property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
|
||||
<property name="focus_on_map">True</property>
|
||||
<property name="urgency_hint">False</property>
|
||||
<property name="has_separator">False</property>
|
||||
<signal handler="on_dialog_snippets_response" last_modification_time="Mon, 19 Dec 2005 11:20:00 GMT" name="response"/>
|
||||
<signal handler="on_dialog_snippets_destroy" last_modification_time="Sun, 22 Jun 2008 13:22:00 GMT" name="destroy"/>
|
||||
<child internal-child="vbox">
|
||||
<object class="GtkVBox" id="dialog-vbox1">
|
||||
<property name="visible">True</property>
|
||||
<property name="homogeneous">False</property>
|
||||
<property name="spacing">0</property>
|
||||
<child internal-child="action_area">
|
||||
<object class="GtkHButtonBox" id="dialog-action_area1">
|
||||
<property name="visible">True</property>
|
||||
<property name="layout_style">GTK_BUTTONBOX_END</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="closebutton1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_default">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="label">gtk-close</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="relief">GTK_RELIEF_NORMAL</property>
|
||||
<property name="focus_on_click">True</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="button1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_default">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="label">gtk-help</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="relief">GTK_RELIEF_NORMAL</property>
|
||||
<property name="focus_on_click">True</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack_type">GTK_PACK_END</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkHPaned" id="hpaned_paned">
|
||||
<property name="border_width">6</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="position">275</property>
|
||||
<child>
|
||||
<object class="GtkVBox" id="vbox_selection">
|
||||
<property name="width_request">230</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="homogeneous">False</property>
|
||||
<property name="spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">_Snippets:</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="use_markup">False</property>
|
||||
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||
<property name="wrap">False</property>
|
||||
<property name="selectable">False</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="yalign">0.5</property>
|
||||
<property name="xpad">0</property>
|
||||
<property name="ypad">0</property>
|
||||
<property name="mnemonic_widget">tree_view_snippets</property>
|
||||
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
|
||||
<property name="width_chars">-1</property>
|
||||
<property name="single_line_mode">False</property>
|
||||
<property name="angle">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="scrolled_window_snippets">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
|
||||
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
|
||||
<property name="shadow_type">GTK_SHADOW_IN</property>
|
||||
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="tree_view_snippets">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="headers_visible">False</property>
|
||||
<property name="rules_hint">False</property>
|
||||
<property name="reorderable">False</property>
|
||||
<property name="enable_search">True</property>
|
||||
<property name="fixed_height_mode">False</property>
|
||||
<property name="hover_selection">False</property>
|
||||
<property name="hover_expand">False</property>
|
||||
<signal handler="on_tree_view_snippets_row_expanded" last_modification_time="Tue, 03 Jan 2006 22:06:02 GMT" name="row_expanded"/>
|
||||
<signal handler="on_tree_view_snippets_key_press" last_modification_time="Tue, 03 Jan 2006 22:07:00 GMT" name="key_press_event"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkHBox" id="hbox_buttons">
|
||||
<property name="visible">True</property>
|
||||
<property name="homogeneous">False</property>
|
||||
<property name="spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="button_new_snippet">
|
||||
<property name="visible">True</property>
|
||||
<property name="tooltip-text" translatable="yes">Create new snippet</property>
|
||||
<property name="can_default">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="relief">GTK_RELIEF_NORMAL</property>
|
||||
<property name="focus_on_click">True</property>
|
||||
<signal handler="on_button_new_snippet_clicked" last_modification_time="Tue, 20 Dec 2005 19:50:58 GMT" name="clicked"/>
|
||||
<child>
|
||||
<object class="GtkImage" id="image1">
|
||||
<property name="visible">True</property>
|
||||
<property name="stock">gtk-new</property>
|
||||
<property name="icon_size">4</property>
|
||||
<property name="xalign">0.5</property>
|
||||
<property name="yalign">0.5</property>
|
||||
<property name="xpad">0</property>
|
||||
<property name="ypad">0</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="button_import_snippets">
|
||||
<property name="visible">True</property>
|
||||
<property name="tooltip-text" translatable="yes">Import snippets</property>
|
||||
<property name="can_default">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="relief">GTK_RELIEF_NORMAL</property>
|
||||
<property name="focus_on_click">True</property>
|
||||
<signal handler="on_button_import_snippets_clicked" last_modification_time="Tue, 10 Jul 2007 18:37:11 GMT" name="clicked"/>
|
||||
<child>
|
||||
<object class="GtkImage" id="image5">
|
||||
<property name="visible">True</property>
|
||||
<property name="stock">gtk-open</property>
|
||||
<property name="icon_size">4</property>
|
||||
<property name="xalign">0.5</property>
|
||||
<property name="yalign">0.5</property>
|
||||
<property name="xpad">0</property>
|
||||
<property name="ypad">0</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="button_export_snippets">
|
||||
<property name="visible">True</property>
|
||||
<property name="tooltip-text" translatable="yes">Export selected snippets</property>
|
||||
<property name="can_default">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="relief">GTK_RELIEF_NORMAL</property>
|
||||
<property name="focus_on_click">True</property>
|
||||
<signal handler="on_button_export_snippets_clicked" last_modification_time="Tue, 10 Jul 2007 18:37:25 GMT" name="clicked"/>
|
||||
<child>
|
||||
<object class="GtkImage" id="image4">
|
||||
<property name="visible">True</property>
|
||||
<property name="stock">gtk-save</property>
|
||||
<property name="icon_size">4</property>
|
||||
<property name="xalign">0.5</property>
|
||||
<property name="yalign">0.5</property>
|
||||
<property name="xpad">0</property>
|
||||
<property name="ypad">0</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="button_remove_snippet">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="tooltip-text" translatable="yes">Delete selected snippet</property>
|
||||
<property name="can_default">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="relief">GTK_RELIEF_NORMAL</property>
|
||||
<property name="focus_on_click">True</property>
|
||||
<signal handler="on_button_remove_snippet_clicked" last_modification_time="Mon, 19 Dec 2005 13:15:14 GMT" name="clicked"/>
|
||||
<child>
|
||||
<object class="GtkImage" id="image_remove">
|
||||
<property name="visible">True</property>
|
||||
<property name="stock">gtk-delete</property>
|
||||
<property name="icon_size">4</property>
|
||||
<property name="xalign">0.5</property>
|
||||
<property name="yalign">0.5</property>
|
||||
<property name="xpad">0</property>
|
||||
<property name="ypad">0</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="pack_type">GTK_PACK_END</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="shrink">False</property>
|
||||
<property name="resize">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkVBox" id="vbox_snippet">
|
||||
<property name="visible">True</property>
|
||||
<property name="homogeneous">False</property>
|
||||
<property name="spacing">12</property>
|
||||
<child>
|
||||
<object class="GtkVBox" id="vbox2">
|
||||
<property name="visible">True</property>
|
||||
<property name="homogeneous">False</property>
|
||||
<property name="spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label4">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">_Edit:</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="use_markup">False</property>
|
||||
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||
<property name="wrap">False</property>
|
||||
<property name="selectable">False</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="yalign">0.5</property>
|
||||
<property name="xpad">0</property>
|
||||
<property name="ypad">0</property>
|
||||
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
|
||||
<property name="width_chars">-1</property>
|
||||
<property name="single_line_mode">False</property>
|
||||
<property name="angle">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="scrolled_window_snippet">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
|
||||
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
|
||||
<property name="shadow_type">GTK_SHADOW_IN</property>
|
||||
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
|
||||
<child>
|
||||
<object class="GeditView" id="source_view_snippet">
|
||||
<property name="buffer">source_buffer</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="auto-indent">True</property>
|
||||
<property name="insert-spaces-instead-of-tabs">False</property>
|
||||
<property name="smart-home-end">GTK_SOURCE_SMART_HOME_END_AFTER</property>
|
||||
<property name="tab-width">2</property>
|
||||
<property name="highlight-current-line">True</property>
|
||||
<property name="show-right-margin">False</property>
|
||||
<property name="show-line-numbers">False</property>
|
||||
|
||||
<signal handler="on_source_view_snippet_focus_out" last_modification_time="Sat, 07 Jan 2006 17:13:24 GMT" name="focus_out_event"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkVBox" id="vbox1">
|
||||
<property name="visible">True</property>
|
||||
<property name="homogeneous">False</property>
|
||||
<property name="spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label3">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Activation</property>
|
||||
<property name="use_underline">False</property>
|
||||
<property name="use_markup">True</property>
|
||||
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||
<property name="wrap">False</property>
|
||||
<property name="selectable">False</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="yalign">0.5</property>
|
||||
<property name="xpad">0</property>
|
||||
<property name="ypad">0</property>
|
||||
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
|
||||
<property name="width_chars">-1</property>
|
||||
<property name="single_line_mode">False</property>
|
||||
<property name="angle">0</property>
|
||||
<attributes>
|
||||
<attribute name="weight" value="PANGO_WEIGHT_BOLD"/>
|
||||
</attributes>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkHBox" id="hbox1">
|
||||
<property name="visible">True</property>
|
||||
<property name="homogeneous">False</property>
|
||||
<property name="spacing">0</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label2">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes"> </property>
|
||||
<property name="use_underline">False</property>
|
||||
<property name="use_markup">False</property>
|
||||
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||
<property name="wrap">False</property>
|
||||
<property name="selectable">False</property>
|
||||
<property name="xalign">0.5</property>
|
||||
<property name="yalign">0.5</property>
|
||||
<property name="xpad">0</property>
|
||||
<property name="ypad">0</property>
|
||||
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
|
||||
<property name="width_chars">-1</property>
|
||||
<property name="single_line_mode">False</property>
|
||||
<property name="angle">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTable" id="table1">
|
||||
<property name="visible">True</property>
|
||||
<property name="n_rows">3</property>
|
||||
<property name="n_columns">2</property>
|
||||
<property name="homogeneous">False</property>
|
||||
<property name="row_spacing">6</property>
|
||||
<property name="column_spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_tab_trigger">
|
||||
<property name="visible">True</property>
|
||||
<property comments=""tab" here means the tab key, not the notebook tab!" name="label" translatable="yes">_Tab trigger:</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="use_markup">False</property>
|
||||
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||
<property name="wrap">False</property>
|
||||
<property name="selectable">False</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="yalign">0.5</property>
|
||||
<property name="xpad">0</property>
|
||||
<property name="ypad">0</property>
|
||||
<property name="mnemonic_widget">entry_tab_trigger</property>
|
||||
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
|
||||
<property name="width_chars">-1</property>
|
||||
<property name="single_line_mode">False</property>
|
||||
<property name="angle">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="right_attach">1</property>
|
||||
<property name="top_attach">0</property>
|
||||
<property name="bottom_attach">1</property>
|
||||
<property name="x_options">fill</property>
|
||||
<property name="y_options"/>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkHBox" id="hbox_tab_trigger">
|
||||
<property name="visible">True</property>
|
||||
<child>
|
||||
<object class="GtkEntry" id="entry_tab_trigger">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="tooltip-text" translatable="yes">Single word the snippet is activated with after pressing Tab</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="editable">True</property>
|
||||
<property name="visibility">True</property>
|
||||
<property name="max_length">0</property>
|
||||
<property name="text" translatable="yes"/>
|
||||
<property name="has_frame">True</property>
|
||||
<property name="invisible_char">*</property>
|
||||
<property name="activates_default">False</property>
|
||||
<signal handler="on_entry_tab_trigger_focus_out" last_modification_time="Wed, 04 Jan 2006 14:07:29 GMT" name="focus_out_event"/>
|
||||
<signal handler="on_entry_tab_trigger_changed" last_modification_time="Fri, 28 Apr 2006 16:50:34 GMT" name="changed"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImage" id="image_tab_trigger">
|
||||
<property name="visible">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">1</property>
|
||||
<property name="padding">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="top_attach">0</property>
|
||||
<property name="bottom_attach">1</property>
|
||||
<property name="y_options"/>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="entry_accelerator">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="tooltip-text" translatable="yes">Shortcut key with which the snippet is activated</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="editable">False</property>
|
||||
<property name="visibility">True</property>
|
||||
<property name="max_length">0</property>
|
||||
<property name="text" translatable="yes"/>
|
||||
<property name="has_frame">True</property>
|
||||
<property name="invisible_char">*</property>
|
||||
<property name="activates_default">False</property>
|
||||
<signal handler="on_entry_accelerator_focus_out" last_modification_time="Wed, 04 Jan 2006 14:07:20 GMT" name="focus_out_event"/>
|
||||
<signal handler="on_entry_accelerator_key_press" last_modification_time="Wed, 04 Jan 2006 14:07:23 GMT" name="key_press_event"/>
|
||||
<signal handler="on_entry_accelerator_focus_in" last_modification_time="Wed, 04 Jan 2006 14:09:06 GMT" name="focus_in_event"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="bottom_attach">2</property>
|
||||
<property name="y_options"/>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_accelerator">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">S_hortcut key:</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="use_markup">False</property>
|
||||
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||
<property name="wrap">False</property>
|
||||
<property name="selectable">False</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="yalign">0.5</property>
|
||||
<property name="xpad">0</property>
|
||||
<property name="ypad">0</property>
|
||||
<property name="mnemonic_widget">entry_accelerator</property>
|
||||
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
|
||||
<property name="width_chars">-1</property>
|
||||
<property name="single_line_mode">False</property>
|
||||
<property name="angle">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="right_attach">1</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="bottom_attach">2</property>
|
||||
<property name="x_options">fill</property>
|
||||
<property name="y_options"/>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_drop_targets">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">_Drop targets:</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="use_markup">False</property>
|
||||
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||
<property name="wrap">False</property>
|
||||
<property name="selectable">False</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="yalign">0.5</property>
|
||||
<property name="xpad">0</property>
|
||||
<property name="ypad">0</property>
|
||||
<property name="mnemonic_widget">entry_accelerator</property>
|
||||
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
|
||||
<property name="width_chars">-1</property>
|
||||
<property name="single_line_mode">False</property>
|
||||
<property name="angle">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="right_attach">1</property>
|
||||
<property name="top_attach">2</property>
|
||||
<property name="bottom_attach">3</property>
|
||||
<property name="x_options">fill</property>
|
||||
<property name="y_options"/>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkComboBoxEntry" id="combo_drop_targets">
|
||||
<property name="visible">True</property>
|
||||
<property name="add_tearoffs">False</property>
|
||||
<property name="has_frame">True</property>
|
||||
<property name="focus_on_click">True</property>
|
||||
<property name="model">model1</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="renderer1"/>
|
||||
<attributes>
|
||||
<attribute name="text">0</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="top_attach">2</property>
|
||||
<property name="bottom_attach">3</property>
|
||||
<property name="x_options">fill</property>
|
||||
<property name="y_options">fill</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="shrink">True</property>
|
||||
<property name="resize">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<action-widgets>
|
||||
<action-widget response="-7">closebutton1</action-widget>
|
||||
<action-widget response="-11">button1</action-widget>
|
||||
</action-widgets>
|
||||
</object>
|
||||
</interface>
|
Reference in New Issue
Block a user