[gedit] [snippets] Make better use of new plugin architecture
- From: Jesse van den Kieboom <jessevdk src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gedit] [snippets] Make better use of new plugin architecture
- Date: Fri, 22 Apr 2011 18:49:58 +0000 (UTC)
commit de138c65f93dc35f0b328098e45fd3f2ff058d04
Author: Jesse van den Kieboom <jessevdk gnome org>
Date: Fri Apr 22 20:49:03 2011 +0200
[snippets] Make better use of new plugin architecture
plugins/snippets/snippets/Makefile.am | 3 +-
plugins/snippets/snippets/__init__.py | 1 +
plugins/snippets/snippets/document.py | 176 +++++++++---------------
plugins/snippets/snippets/manager.py | 5 -
plugins/snippets/snippets/shareddata.py | 38 +++++-
plugins/snippets/snippets/signals.py | 92 ++++++++++++
plugins/snippets/snippets/windowactivatable.py | 109 +++++++--------
7 files changed, 245 insertions(+), 179 deletions(-)
---
diff --git a/plugins/snippets/snippets/Makefile.am b/plugins/snippets/snippets/Makefile.am
index 9466996..f8f8dd5 100644
--- a/plugins/snippets/snippets/Makefile.am
+++ b/plugins/snippets/snippets/Makefile.am
@@ -18,7 +18,8 @@ plugin_PYTHON = \
importer.py \
exporter.py \
languagemanager.py \
- completion.py
+ completion.py \
+ signals.py
uidir = $(GEDIT_PLUGINS_DATA_DIR)/snippets/ui
ui_DATA = snippets.ui
diff --git a/plugins/snippets/snippets/__init__.py b/plugins/snippets/snippets/__init__.py
index 2e27417..0d4d92e 100644
--- a/plugins/snippets/snippets/__init__.py
+++ b/plugins/snippets/snippets/__init__.py
@@ -17,5 +17,6 @@
from appactivatable import AppActivatable
from windowactivatable import WindowActivatable
+from document import Document
# ex:ts=8:et:
diff --git a/plugins/snippets/snippets/document.py b/plugins/snippets/snippets/document.py
index 41bd43e..4c6c284 100644
--- a/plugins/snippets/snippets/document.py
+++ b/plugins/snippets/snippets/document.py
@@ -25,25 +25,27 @@ from library import Library
from snippet import Snippet
from placeholder import *
import completion
+from signals import Signals
+from shareddata import SharedData
class DynamicSnippet(dict):
def __init__(self, text):
self['text'] = text
self.valid = True
-class Document:
- TAB_KEY_VAL = (Gdk.KEY_Tab, \
- Gdk.KEY_ISO_Left_Tab)
+class Document(GObject.Object, Gedit.ViewActivatable, Signals):
+ TAB_KEY_VAL = (Gdk.KEY_Tab, Gdk.KEY_ISO_Left_Tab)
SPACE_KEY_VAL = (Gdk.KEY_space,)
- def __init__(self, instance, view):
- self.view = None
- self.instance = instance
+ view = GObject.property(type=Gedit.View)
+
+ def __init__(self):
+ GObject.Object.__init__(self)
+ Signals.__init__(self)
self.placeholders = []
self.active_snippets = []
self.active_placeholder = None
- self.signal_ids = {}
self.ordered_placeholders = []
self.update_placeholders = []
@@ -54,107 +56,58 @@ class Document:
self.provider = completion.Provider(_('Snippets'), self.language_id, self.on_proposal_activated)
self.defaults_provider = completion.Defaults(self.on_default_activated)
+ def do_activate(self):
# Always have a reference to the global snippets
Library().ref(None)
- self.set_view(view)
-
- # Stop controlling the view. Remove all active snippets, remove references
- # to the view and the plugin instance, disconnect all signal handlers
- def stop(self):
- if self.timeout_update_id != 0:
- GObject.source_remove(self.timeout_update_id)
- self.timeout_update_id = 0
- del self.update_placeholders[:]
- del self.jump_placeholders[:]
-
- # Always release the reference to the global snippets
- Library().unref(None)
- self.set_view(None)
- self.instance = None
- self.active_placeholder = None
- def disconnect_signal(self, obj, signal):
- if (obj, signal) in self.signal_ids:
- obj.disconnect(self.signal_ids[(obj, signal)])
- del self.signal_ids[(obj, signal)]
-
- def connect_signal(self, obj, signal, cb):
- self.disconnect_signal(obj, signal)
- self.signal_ids[(obj, signal)] = obj.connect(signal, cb)
-
- def connect_signal_after(self, obj, signal, cb):
- self.disconnect_signal(obj, signal)
- self.signal_ids[(obj, signal)] = obj.connect_after(signal, cb)
-
- # Set the view to be controlled. Installs signal handlers and sets current
- # language. If there is already a view set this function will first remove
- # all currently active snippets and disconnect all current signals. So
- # self.set_view(None) will effectively remove all the control from the
- # current view
- def _set_view(self, view):
- if self.view:
- buf = self.view.get_buffer()
-
- # Remove signals
- signals = {self.view: ('key-press-event', 'destroy',
- 'notify::editable', 'drag-data-received', 'expose-event'),
- buf: ('notify::language', 'changed', 'cursor-moved', 'insert-text'),
- self.view.get_completion(): ('hide',)}
-
- for obj, sig in signals.items():
- if obj:
- for s in sig:
- self.disconnect_signal(obj, s)
-
- # Remove all active snippets
- for snippet in list(self.active_snippets):
- self.deactivate_snippet(snippet, True)
+ buf = self.view.get_buffer()
- completion = self.view.get_completion()
+ self.connect_signal(self.view, 'key-press-event', self.on_view_key_press)
+ self.connect_signal(buf, 'notify::language', self.on_notify_language)
+ self.connect_signal(self.view, 'drag-data-received', self.on_drag_data_received)
- if completion:
- completion.remove_provider(self.provider)
- completion.remove_provider(self.defaults_provider)
+ self.connect_signal_after(self.view, 'draw', self.on_draw)
- self.view = view
+ self.update_language()
- if view != None:
- buf = view.get_buffer()
+ completion = self.view.get_completion()
- self.connect_signal(view, 'destroy', self.on_view_destroy)
+ completion.add_provider(self.provider)
+ completion.add_provider(self.defaults_provider)
- if view.get_editable():
- self.connect_signal(view, 'key-press-event', self.on_view_key_press)
+ self.connect_signal(completion, 'hide', self.on_completion_hide)
- self.connect_signal(buf, 'notify::language', self.on_notify_language)
- self.connect_signal(view, 'notify::editable', self.on_notify_editable)
- self.connect_signal(view, 'drag-data-received', self.on_drag_data_received)
- self.connect_signal_after(view, 'draw', self.on_draw)
+ SharedData().register_controller(self.view, self)
- self.update_language()
+ def do_deactivate(self):
+ if self.timeout_update_id != 0:
+ GObject.source_remove(self.timeout_update_id)
+ self.timeout_update_id = 0
- completion = view.get_completion()
+ del self.update_placeholders[:]
+ del self.jump_placeholders[:]
- completion.add_provider(self.provider)
- completion.add_provider(self.defaults_provider)
+ # Always release the reference to the global snippets
+ Library().unref(None)
+ self.active_placeholder = None
- self.connect_signal(completion, 'hide', self.on_completion_hide)
- elif self.language_id != 0:
- langid = self.language_id
+ self.disconnect_signals(self.view)
+ self.disconnect_signals(self.view.get_buffer())
- self.language_id = None;
- self.provider.language_id = self.language_id
+ # Remove all active snippets
+ for snippet in list(self.active_snippets):
+ self.deactivate_snippet(snippet, True)
- if self.instance:
- self.instance.language_changed(self)
+ completion = self.view.get_completion()
- Library().unref(langid)
+ if completion:
+ completion.remove_provider(self.provider)
+ completion.remove_provider(self.defaults_provider)
- def set_view(self, view):
- if view == self.view:
- return
+ if self.language_id != 0:
+ Library().unref(self.language_id)
- self._set_view(view)
+ SharedData().unregister_controller(self.view, self)
# Call this whenever the language in the view changes. This makes sure that
# the correct language is used when finding snippets
@@ -173,15 +126,14 @@ class Document:
else:
self.language_id = None
- if self.instance:
- self.instance.language_changed(self)
-
if langid != 0:
Library().unref(langid)
Library().ref(self.language_id)
self.provider.language_id = self.language_id
+ SharedData().update_state(self.view.get_toplevel())
+
def accelerator_activate(self, keyval, mod):
if not self.view or not self.view.get_editable():
return False
@@ -190,8 +142,6 @@ class Document:
snippets = Library().from_accelerator(accelerator, \
self.language_id)
- snippets_debug('Accel!')
-
if len(snippets) == 0:
return False
elif len(snippets) == 1:
@@ -558,14 +508,12 @@ class Document:
cur = buf.get_iter_at_mark(buf.get_insert())
last = sn.end_iter()
- # FIXME: get_iter_location doesnt work
+ curloc = self.view.get_iter_location(cur)
+ lastloc = self.view.get_iter_location(last)
- #curloc = self.view.get_iter_location(cur)
- #lastloc = self.view.get_iter_location(last)
-
- #if (lastloc.y + lastloc.height) - curloc.y <= \
- # self.view.get_visible_rect().height:
- # self.view.scroll_mark_onscreen(sn.end_mark)
+ if (lastloc.y + lastloc.height) - curloc.y <= \
+ self.view.get_visible_rect().height:
+ self.view.scroll_mark_onscreen(sn.end_mark)
buf.end_user_action()
self.view.grab_focus()
@@ -604,12 +552,18 @@ class Document:
return (word, start, end)
def parse_and_run_snippet(self, data, iter):
+ if not self.view.get_editable():
+ return
+
self.apply_snippet(DynamicSnippet(data), iter, iter)
def run_snippet_trigger(self, trigger, bounds):
if not self.view:
return False
+ if not self.view.get_editable():
+ return False
+
snippets = Library().from_tag(trigger, self.language_id)
buf = self.view.get_buffer()
@@ -630,6 +584,9 @@ class Document:
if not self.view:
return False
+ if not self.view.get_editable():
+ return False
+
buf = self.view.get_buffer()
# get the word preceding the current insertion position
@@ -702,11 +659,6 @@ class Document:
return False
- # Callbacks
- def on_view_destroy(self, view):
- self.stop()
- return
-
def on_buffer_cursor_moved(self, buf):
piter = buf.get_iter_at_mark(buf.get_insert())
@@ -784,14 +736,14 @@ class Document:
def on_notify_language(self, buf, spec):
self.update_language()
- def on_notify_editable(self, view, spec):
- self._set_view(view)
-
def on_view_key_press(self, view, event):
library = Library()
state = event.get_state()
+ if not self.view.get_editable():
+ return False
+
if not (state & Gdk.ModifierType.CONTROL_MASK) and \
not (state & Gdk.ModifierType.MOD1_MASK) and \
event.keyval in self.TAB_KEY_VAL:
@@ -909,6 +861,9 @@ class Document:
if not (Gtk.targets_include_uri(context.targets) and data.data and self.in_bounds(x, y)):
return
+ if not self.view.get_editable():
+ return
+
uris = drop_get_uris(data)
uris.reverse()
stop = False
@@ -943,6 +898,9 @@ class Document:
self.provider.set_proposals(None)
def on_proposal_activated(self, proposal, piter):
+ if not self.view.get_editable():
+ return False
+
buf = self.view.get_buffer()
bounds = buf.get_selection_bounds()
diff --git a/plugins/snippets/snippets/manager.py b/plugins/snippets/snippets/manager.py
index 7ec4b95..8649a05 100644
--- a/plugins/snippets/snippets/manager.py
+++ b/plugins/snippets/snippets/manager.py
@@ -47,7 +47,6 @@ class Manager(Gtk.Dialog, Gtk.Buildable):
def __init__(self):
self.snippet = None
self._temp_export = None
- self.snippets_doc = None
self.key_press_id = 0
@@ -310,7 +309,6 @@ class Manager(Gtk.Dialog, Gtk.Buildable):
if lang:
source_view.get_buffer().set_highlight_syntax(True)
source_view.get_buffer().set_language(lang)
- self.snippets_doc = Document(None, source_view)
combo = self['combo_drop_targets']
@@ -588,9 +586,6 @@ class Manager(Gtk.Dialog, Gtk.Buildable):
shutil.rmtree(os.path.dirname(self._temp_export))
self._temp_export = None
- if self.snippets_doc:
- self.snippets_doc.stop()
-
self.unref_languages()
self.snippet = None
self.model = None
diff --git a/plugins/snippets/snippets/shareddata.py b/plugins/snippets/snippets/shareddata.py
index f1d8f0d..1b5c9c2 100644
--- a/plugins/snippets/snippets/shareddata.py
+++ b/plugins/snippets/snippets/shareddata.py
@@ -26,6 +26,40 @@ class SharedData(object):
def __init__(self):
self.dlg = None
self.dlg_default_size = None
+ self.controller_registry = {}
+ self.windows = {}
+
+ def register_controller(self, view, controller):
+ self.controller_registry[view] = controller
+
+ def unregister_controller(self, view, controller):
+ if self.controller_registry[view] == controller:
+ del self.controller_registry[view]
+
+ def register_window(self, window):
+ self.windows[window.window] = window
+
+ def unregister_window(self, window):
+ if window.window in self.windows:
+ del self.windows[window.window]
+
+ def update_state(self, window):
+ if window in self.windows:
+ self.windows[window].do_update_state()
+
+ def get_active_controller(self, window):
+ view = window.get_active_view()
+
+ if not view or not view in self.controller_registry:
+ return None
+
+ return self.controller_registry[view]
+
+ def get_controller(self, view):
+ if view in self.controller_registry:
+ return self.controller_registry[view]
+ else:
+ return None
def manager_destroyed(self, dlg):
self.dlg_default_size = [dlg.get_allocation().width, dlg.get_allocation().height]
@@ -42,9 +76,7 @@ class SharedData(object):
if self.dlg_default_size:
self.dlg.set_default_size(self.dlg_default_size[0], self.dlg_default_size[1])
- if window:
- self.dlg.set_transient_for(window)
-
+ self.dlg.set_transient_for(window)
self.dlg.present()
# vi:ex:ts=4:et
diff --git a/plugins/snippets/snippets/signals.py b/plugins/snippets/snippets/signals.py
new file mode 100644
index 0000000..aa895d2
--- /dev/null
+++ b/plugins/snippets/snippets/signals.py
@@ -0,0 +1,92 @@
+# -*- coding: utf-8 -*-
+#
+# signals.py
+#
+# Copyright (C) 2009 - Jesse van den Kieboom
+#
+# 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., 59 Temple Place, Suite 330,
+# Boston, MA 02111-1307, USA.
+
+class Signals:
+ def __init__(self):
+ self._signals = {}
+
+ def _connect(self, obj, name, handler, connector):
+ ret = self._signals.setdefault(obj, {})
+
+ hid = connector(name, handler)
+ ret.setdefault(name, []).append(hid)
+
+ return hid
+
+ def connect_signal(self, obj, name, handler):
+ return self._connect(obj, name, handler, obj.connect)
+
+ def connect_signal_after(self, obj, name, handler):
+ return self._connect(obj, name, handler, obj.connect_after)
+
+ def disconnect_signals(self, obj):
+ if obj not in self._signals:
+ return False
+
+ for name in self._signals[obj]:
+ for hid in self._signals[obj][name]:
+ obj.disconnect(hid)
+
+ del self._signals[obj]
+ return True
+
+ def block_signal(self, obj, name):
+ if obj not in self._signals:
+ return False
+
+ if name not in self._signals[obj]:
+ return False
+
+ for hid in self._signals[obj][name]:
+ obj.handler_block(hid)
+
+ return True
+
+ def unblock_signal(self, obj, name):
+ if obj not in self._signals:
+ return False
+
+ if name not in self._signals[obj]:
+ return False
+
+ for hid in self._signals[obj][name]:
+ obj.handler_unblock(hid)
+
+ return True
+
+ def disconnect_signal(self, obj, name):
+ if obj not in self._signals:
+ return False
+
+ if name not in self._signals[obj]:
+ return False
+
+ for hid in self._signals[obj][name]:
+ obj.disconnect(hid)
+
+ del self._signals[obj][name]
+
+ if len(self._signals[obj]) == 0:
+ del self._signals[obj]
+
+ return True
+
+# ex:ts=4:et:
diff --git a/plugins/snippets/snippets/windowactivatable.py b/plugins/snippets/snippets/windowactivatable.py
index 9b50ed0..856e07d 100644
--- a/plugins/snippets/snippets/windowactivatable.py
+++ b/plugins/snippets/snippets/windowactivatable.py
@@ -24,6 +24,7 @@ from gi.repository import Gtk, Gdk, Gedit, GObject
from document import Document
from library import Library
from shareddata import SharedData
+from signals import Signals
class Activate(Gedit.Message):
view = GObject.property(type=Gedit.View)
@@ -31,14 +32,16 @@ class Activate(Gedit.Message):
# iter = GObject.property(type=Gtk.TextIter)
trigger = GObject.property(type=str)
-class WindowActivatable(GObject.Object, Gedit.WindowActivatable):
+class WindowActivatable(GObject.Object, Gedit.WindowActivatable, Signals):
__gtype_name__ = "GeditSnippetsWindowActivatable"
window = GObject.property(type=Gedit.Window)
def __init__(self):
- self.current_controller = None
- self.current_language = None
+ GObject.Object.__init__(self)
+ Signals.__init__(self)
+
+ self.current_language_accel_group = None
self.signal_ids = {}
def do_activate(self):
@@ -53,15 +56,14 @@ class WindowActivatable(GObject.Object, Gedit.WindowActivatable):
if self.accel_group:
self.window.add_accel_group(self.accel_group)
- self.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.connect_signal(self.window,
+ 'active-tab-changed',
+ self.on_active_tab_changed)
self.do_update_state()
+ SharedData().register_window(self)
+
def do_deactivate(self):
if self.accel_group:
self.window.remove_accel_group(self.accel_group)
@@ -71,26 +73,17 @@ class WindowActivatable(GObject.Object, Gedit.WindowActivatable):
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
-
library = Library()
library.remove_accelerator_callback(self.accelerator_activated)
- def do_update_state(self):
- view = self.window.get_active_view()
+ self.disconnect_signals(self.window)
- if not view or not self.has_controller(view):
- return
+ SharedData().unregister_window(self)
- controller = view._snippet_controller
+ def do_update_state(self):
+ controller = SharedData().get_active_controller(self.window)
- if controller != self.current_controller:
- self.current_controller = controller
- self.update_language()
+ self.update_language(controller)
def register_messages(self):
bus = self.window.get_message_bus()
@@ -111,7 +104,9 @@ class WindowActivatable(GObject.Object, Gedit.WindowActivatable):
if not view:
view = self.window.get_active_view()
- if not self.has_controller(view):
+ controller = SharedData().get_controller(view)
+
+ if not controller:
return
# TODO: fix me as soon as the property fix lands in pygobject
@@ -119,8 +114,6 @@ class WindowActivatable(GObject.Object, Gedit.WindowActivatable):
#if not iter:
iter = view.get_buffer().get_iter_at_mark(view.get_buffer().get_insert())
-
- controller = view._snippet_controller
controller.run_snippet_trigger(message.props.trigger, (iter, iter))
def on_message_parse_and_activate(self, bus, message, userdata):
@@ -129,15 +122,16 @@ class WindowActivatable(GObject.Object, Gedit.WindowActivatable):
if not view:
view = self.window.get_active_view()
- if not self.has_controller(view):
- return
-
- iter = message.props.iter
+ controller = SharedData().get_controller(view)
- if not iter:
- iter = view.get_buffer().get_iter_at_mark(view.get_buffer().get_insert())
+ if not controller:
+ return
- controller = view._snippet_controller
+ # TODO: fix me as soon as the property fix lands in pygobject
+ #iter = message.props.iter
+
+ #if not iter:
+ iter = view.get_buffer().get_iter_at_mark(view.get_buffer().get_insert())
controller.parse_and_run_snippet(message.snippet, iter)
def insert_menu(self):
@@ -170,43 +164,33 @@ class WindowActivatable(GObject.Object, Gedit.WindowActivatable):
return result
- def has_controller(self, view):
- return hasattr(view, '_snippet_controller') and view._snippet_controller
-
- def update_language(self):
+ def update_language(self, controller):
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)
+ if controller:
+ langid = controller.language_id
else:
- self.current_language = None
+ langid = None
- def language_changed(self, controller):
- if controller == self.current_controller:
- self.update_language()
+ if langid != None:
+ accelgroup = Library().get_accel_group(langid)
+ else:
+ accelgroup = None
- # Callbacks
+ if accelgroup != self.current_language_accel_group:
+ if self.current_language_accel_group:
+ self.window.remove_accel_group(self.current_language_accel_group)
- 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 accelgroup:
+ self.window.add_accel_group(accelgroup)
- if isinstance(view, Gedit.View) and not self.has_controller(view):
- view._snippet_controller = Document(self, view)
+ self.current_language_accel_group = accelgroup
- self.do_update_state()
+ def on_active_tab_changed(self, window, tab):
+ self.update_language(SharedData().get_controller(tab.get_view()))
+ # Callbacks
def create_configure_dialog(self):
SharedData().show_manager(self.window, self.plugin_info.get_data_dir())
@@ -215,7 +199,10 @@ class WindowActivatable(GObject.Object, Gedit.WindowActivatable):
def accelerator_activated(self, group, obj, keyval, mod):
if obj == self.window:
- return self.current_controller.accelerator_activate(keyval, mod)
+ controller = SharedData().get_active_controller(self.window)
+
+ if controller:
+ return controller.accelerator_activate(keyval, mod)
else:
return False
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]