summaryrefslogtreecommitdiff
path: root/plugins/quickopen
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/quickopen')
-rwxr-xr-xplugins/quickopen/Makefile.am15
-rwxr-xr-xplugins/quickopen/quickopen.gedit-plugin.desktop.in10
-rwxr-xr-xplugins/quickopen/quickopen/Makefile.am13
-rwxr-xr-xplugins/quickopen/quickopen/__init__.py46
-rwxr-xr-xplugins/quickopen/quickopen/popup.py534
-rwxr-xr-xplugins/quickopen/quickopen/virtualdirs.py87
-rwxr-xr-xplugins/quickopen/quickopen/windowhelper.py198
7 files changed, 903 insertions, 0 deletions
diff --git a/plugins/quickopen/Makefile.am b/plugins/quickopen/Makefile.am
new file mode 100755
index 00000000..4b5faf00
--- /dev/null
+++ b/plugins/quickopen/Makefile.am
@@ -0,0 +1,15 @@
+# Quick Open Plugin
+SUBDIRS = quickopen
+plugindir = $(GEDIT_PLUGINS_LIBS_DIR)
+
+plugin_in_files = quickopen.gedit-plugin.desktop.in
+%.gedit-plugin: %.gedit-plugin.desktop.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*po) ; $(INTLTOOL_MERGE) $(top_srcdir)/po $< $@ -d -u -c $(top_builddir)/po/.intltool-merge-cache
+
+plugin_DATA = $(plugin_in_files:.gedit-plugin.desktop.in=.gedit-plugin)
+
+EXTRA_DIST = $(plugin_in_files)
+
+CLEANFILES = $(plugin_DATA)
+DISTCLEANFILES = $(plugin_DATA)
+
+-include $(top_srcdir)/git.mk
diff --git a/plugins/quickopen/quickopen.gedit-plugin.desktop.in b/plugins/quickopen/quickopen.gedit-plugin.desktop.in
new file mode 100755
index 00000000..40f7f040
--- /dev/null
+++ b/plugins/quickopen/quickopen.gedit-plugin.desktop.in
@@ -0,0 +1,10 @@
+[Gedit Plugin]
+Loader=python
+Module=quickopen
+IAge=2
+_Name=Quick Open
+_Description=Quickly open files
+Icon=gtk-open
+Authors=Jesse van den Kieboom <[email protected]>
+Copyright=Copyright © 2009 Jesse van den Kieboom
+Website=http://www.gedit.org
diff --git a/plugins/quickopen/quickopen/Makefile.am b/plugins/quickopen/quickopen/Makefile.am
new file mode 100755
index 00000000..88882fdf
--- /dev/null
+++ b/plugins/quickopen/quickopen/Makefile.am
@@ -0,0 +1,13 @@
+# Quick Open Plugin
+
+plugindir = $(GEDIT_PLUGINS_LIBS_DIR)/quickopen
+plugin_PYTHON = \
+ __init__.py \
+ popup.py \
+ virtualdirs.py \
+ windowhelper.py
+
+CLEANFILES =
+DISTCLEANFILES =
+
+-include $(top_srcdir)/git.mk
diff --git a/plugins/quickopen/quickopen/__init__.py b/plugins/quickopen/quickopen/__init__.py
new file mode 100755
index 00000000..a41c9400
--- /dev/null
+++ b/plugins/quickopen/quickopen/__init__.py
@@ -0,0 +1,46 @@
+# -*- coding: utf-8 -*-
+
+# 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.
+
+import gedit
+from windowhelper import WindowHelper
+
+class QuickOpenPlugin(gedit.Plugin):
+ def __init__(self):
+ gedit.Plugin.__init__(self)
+
+ self._popup_size = (450, 300)
+ self._helpers = {}
+
+ def activate(self, window):
+ self._helpers[window] = WindowHelper(window, self)
+
+ def deactivate(self, window):
+ self._helpers[window].deactivate()
+ del self._helpers[window]
+
+ def update_ui(self, window):
+ self._helpers[window].update_ui()
+
+ def get_popup_size(self):
+ return self._popup_size
+
+ def set_popup_size(self, size):
+ self._popup_size = size
+
+# ex:ts=8:et:
diff --git a/plugins/quickopen/quickopen/popup.py b/plugins/quickopen/quickopen/popup.py
new file mode 100755
index 00000000..a80caf31
--- /dev/null
+++ b/plugins/quickopen/quickopen/popup.py
@@ -0,0 +1,534 @@
+# -*- coding: utf-8 -*-
+
+# 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.
+
+import gtk
+import gtk.gdk
+import gobject
+import os
+import gio
+import pango
+import glib
+import fnmatch
+import gedit
+import xml.sax.saxutils
+from virtualdirs import VirtualDirectory
+
+class Popup(gtk.Dialog):
+ def __init__(self, window, paths, handler):
+ gtk.Dialog.__init__(self,
+ title=_('Quick Open'),
+ parent=window,
+ flags=gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_NO_SEPARATOR | gtk.DIALOG_MODAL)
+
+ self.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
+ self._open_button = self.add_button(gtk.STOCK_OPEN, gtk.RESPONSE_ACCEPT)
+
+ self._handler = handler
+ self._build_ui()
+
+ self._dirs = []
+ self._cache = {}
+ self._theme = None
+ self._cursor = None
+ self._shift_start = None
+
+ accel_group = gtk.AccelGroup()
+ accel_group.connect_group(gtk.keysyms.l, gtk.gdk.CONTROL_MASK, 0, self.on_focus_entry)
+
+ self.add_accel_group(accel_group)
+
+ unique = []
+
+ for path in paths:
+ if not path.get_uri() in unique:
+ self._dirs.append(path)
+ unique.append(path.get_uri())
+
+ def _build_ui(self):
+ vbox = self.get_content_area()
+ vbox.set_spacing(3)
+
+ self._entry = gtk.Entry()
+
+ self._entry.connect('changed', self.on_changed)
+ self._entry.connect('key-press-event', self.on_key_press_event)
+
+ sw = gtk.ScrolledWindow(None, None)
+ sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+ sw.set_shadow_type(gtk.SHADOW_OUT)
+
+ tv = gtk.TreeView()
+ tv.set_headers_visible(False)
+
+ self._store = gtk.ListStore(gio.Icon, str, object, int)
+ tv.set_model(self._store)
+
+ self._treeview = tv
+ tv.connect('row-activated', self.on_row_activated)
+
+ renderer = gtk.CellRendererPixbuf()
+ column = gtk.TreeViewColumn()
+ column.pack_start(renderer, False)
+ column.set_attributes(renderer, gicon=0)
+
+ renderer = gtk.CellRendererText()
+ column.pack_start(renderer, True)
+ column.set_attributes(renderer, markup=1)
+
+ column.set_cell_data_func(renderer, self.on_cell_data_cb)
+
+ tv.append_column(column)
+ sw.add(tv)
+
+ selection = tv.get_selection()
+ selection.connect('changed', self.on_selection_changed)
+ selection.set_mode(gtk.SELECTION_MULTIPLE)
+
+ vbox.pack_start(self._entry, False, False, 0)
+ vbox.pack_start(sw, True, True, 0)
+
+ lbl = gtk.Label()
+ lbl.set_alignment(0, 0.5)
+ lbl.set_ellipsize(pango.ELLIPSIZE_MIDDLE)
+ self._info_label = lbl
+
+ vbox.pack_start(lbl, False, False, 0)
+
+ # Initial selection
+ self.on_selection_changed(tv.get_selection())
+ vbox.show_all()
+
+ def on_cell_data_cb(self, column, cell, model, piter):
+ path = model.get_path(piter)
+
+ if self._cursor and path == self._cursor.get_path():
+ style = self._treeview.get_style()
+ bg = style.bg[gtk.STATE_PRELIGHT]
+
+ cell.set_property('cell-background-gdk', bg)
+ cell.set_property('style', pango.STYLE_ITALIC)
+ else:
+ cell.set_property('cell-background-set', False)
+ cell.set_property('style-set', False)
+
+ def _icon_from_stock(self, stock):
+ theme = gtk.icon_theme_get_default()
+ size = gtk.icon_size_lookup(gtk.ICON_SIZE_MENU)
+ pixbuf = theme.load_icon(stock, size[0], gtk.ICON_LOOKUP_USE_BUILTIN)
+
+ return pixbuf
+
+ def _list_dir(self, gfile):
+ entries = []
+
+ try:
+ entries = gfile.enumerate_children("standard::*")
+ except glib.GError:
+ pass
+
+ children = []
+
+ for entry in entries:
+ if isinstance(gfile, VirtualDirectory):
+ child, entry = entry
+ else:
+ child = gfile.get_child(entry.get_name())
+
+ children.append((child, entry.get_name(), entry.get_file_type(), entry.get_icon()))
+
+ return children
+
+ def _compare_entries(self, a, b, lpart):
+ if lpart in a:
+ if lpart in b:
+ return cmp(a.index(lpart), b.index(lpart))
+ else:
+ return -1
+ elif lpart in b:
+ return 1
+ else:
+ return 0
+
+ def _match_glob(self, s, glob):
+ if glob:
+ glob += '*'
+
+ return fnmatch.fnmatch(s, glob)
+
+ def do_search_dir(self, parts, d):
+ if not parts or not d:
+ return []
+
+ if not d in self._cache:
+ entries = self._list_dir(d)
+ entries.sort(lambda x, y: cmp(x[1].lower(), y[1].lower()))
+
+ self._cache[d] = entries
+ else:
+ entries = self._cache[d]
+
+ found = []
+ newdirs = []
+
+ lpart = parts[0].lower()
+
+ for entry in entries:
+ if not entry:
+ continue
+
+ lentry = entry[1].lower()
+
+ if not lpart or lpart in lentry or self._match_glob(lentry, lpart):
+ if entry[2] == gio.FILE_TYPE_DIRECTORY:
+ if len(parts) > 1:
+ newdirs.append(entry[0])
+ else:
+ found.append(entry)
+ elif entry[2] == gio.FILE_TYPE_REGULAR and \
+ (not lpart or len(parts) == 1):
+ found.append(entry)
+
+ found.sort(lambda a, b: self._compare_entries(a[1].lower(), b[1].lower(), lpart))
+
+ if lpart == '..':
+ newdirs.append(d.get_parent())
+
+ for dd in newdirs:
+ found.extend(self.do_search_dir(parts[1:], dd))
+
+ return found
+
+ def _replace_insensitive(self, s, find, rep):
+ out = ''
+ l = s.lower()
+ find = find.lower()
+ last = 0
+
+ if len(find) == 0:
+ return xml.sax.saxutils.escape(s)
+
+ while True:
+ m = l.find(find, last)
+
+ if m == -1:
+ break
+ else:
+ out += xml.sax.saxutils.escape(s[last:m]) + rep % (xml.sax.saxutils.escape(s[m:m + len(find)]),)
+ last = m + len(find)
+
+ return out + xml.sax.saxutils.escape(s[last:])
+
+
+ def make_markup(self, parts, path):
+ out = []
+
+ for i in range(0, len(parts)):
+ out.append(self._replace_insensitive(path[i], parts[i], "<b>%s</b>"))
+
+ return os.sep.join(out)
+
+ def _get_icon(self, f):
+ query = f.query_info(gio.FILE_ATTRIBUTE_STANDARD_ICON)
+
+ if not query:
+ return None
+ else:
+ return query.get_icon()
+
+ def _make_parts(self, parent, child, pp):
+ parts = []
+
+ # We went from parent, to child, using pp
+ idx = len(pp) - 1
+
+ while idx >= 0:
+ if pp[idx] == '..':
+ parts.insert(0, '..')
+ else:
+ parts.insert(0, child.get_basename())
+ child = child.get_parent()
+
+ idx -= 1
+
+ return parts
+
+ def normalize_relative(self, parts):
+ if not parts:
+ return []
+
+ out = self.normalize_relative(parts[:-1])
+
+ if parts[-1] == '..':
+ if not out or (out[-1] == '..') or len(out) == 1:
+ out.append('..')
+ else:
+ del out[-1]
+ else:
+ out.append(parts[-1])
+
+ return out
+
+ def _append_to_store(self, item):
+ if not item in self._stored_items:
+ self._store.append(item)
+ self._stored_items[item] = True
+
+ def _clear_store(self):
+ self._store.clear()
+ self._stored_items = {}
+
+ def _show_virtuals(self):
+ for d in self._dirs:
+ if isinstance(d, VirtualDirectory):
+ for entry in d.enumerate_children("standard::*"):
+ self._append_to_store((entry[1].get_icon(), xml.sax.saxutils.escape(entry[1].get_name()), entry[0], entry[1].get_file_type()))
+
+ def _remove_cursor(self):
+ if self._cursor:
+ path = self._cursor.get_path()
+ self._cursor = None
+
+ self._store.row_changed(path, self._store.get_iter(path))
+
+ def do_search(self):
+ self.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
+ self._remove_cursor()
+
+ text = self._entry.get_text().strip()
+ self._clear_store()
+
+ if text == '':
+ self._show_virtuals()
+ else:
+ parts = self.normalize_relative(text.split(os.sep))
+ files = []
+
+ for d in self._dirs:
+ for entry in self.do_search_dir(parts, d):
+ pathparts = self._make_parts(d, entry[0], parts)
+ self._append_to_store((entry[3], self.make_markup(parts, pathparts), entry[0], entry[2]))
+
+ piter = self._store.get_iter_first()
+
+ if piter:
+ self._treeview.get_selection().select_path(self._store.get_path(piter))
+
+ self.window.set_cursor(None)
+
+ def do_show(self):
+ gtk.Window.do_show(self)
+
+ self._entry.grab_focus()
+ self._entry.set_text("")
+
+ self.do_search()
+
+ def on_changed(self, editable):
+ self.do_search()
+ self.on_selection_changed(self._treeview.get_selection())
+
+ def _shift_extend(self, towhere):
+ selection = self._treeview.get_selection()
+
+ if not self._shift_start:
+ model, rows = selection.get_selected_rows()
+ start = rows[0]
+
+ self._shift_start = gtk.TreeRowReference(self._store, start)
+ else:
+ start = self._shift_start.get_path()
+
+ selection.unselect_all()
+ selection.select_range(start, towhere)
+
+ def _select_index(self, idx, hasctrl, hasshift):
+ path = (idx,)
+
+ if not (hasctrl or hasshift):
+ self._treeview.get_selection().unselect_all()
+
+ if hasshift:
+ self._shift_extend(path)
+ else:
+ self._shift_start = None
+
+ if not hasctrl:
+ self._treeview.get_selection().select_path(path)
+
+ self._treeview.scroll_to_cell(path, None, True, 0.5, 0)
+ self._remove_cursor()
+
+ if hasctrl or hasshift:
+ self._cursor = gtk.TreeRowReference(self._store, path)
+
+ piter = self._store.get_iter(path)
+ self._store.row_changed(path, piter)
+
+ def _move_selection(self, howmany, hasctrl, hasshift):
+ num = self._store.iter_n_children(None)
+
+ if num == 0:
+ return True
+
+ # Test for cursor
+ path = None
+
+ if self._cursor:
+ path = self._cursor.get_path()
+ else:
+ model, rows = self._treeview.get_selection().get_selected_rows()
+
+ if len(rows) == 1:
+ path = rows[0]
+
+ if not path:
+ if howmany > 0:
+ self._select_index(0, hasctrl, hasshift)
+ else:
+ self._select_index(num - 1, hasctrl, hasshift)
+ else:
+ idx = path[0]
+
+ if idx + howmany < 0:
+ self._select_index(0, hasctrl, hasshift)
+ elif idx + howmany >= num:
+ self._select_index(num - 1, hasctrl, hasshift)
+ else:
+ self._select_index(idx + howmany, hasctrl, hasshift)
+
+ return True
+
+ def _direct_file(self):
+ uri = self._entry.get_text()
+ gfile = None
+
+ if gedit.utils.uri_is_valid(uri):
+ gfile = gio.File(uri)
+ elif os.path.isabs(uri):
+ f = gio.File(uri)
+
+ if f.query_exists():
+ gfile = f
+
+ return gfile
+
+ def _activate(self):
+ model, rows = self._treeview.get_selection().get_selected_rows()
+ ret = True
+
+ for row in rows:
+ s = model.get_iter(row)
+ info = model.get(s, 2, 3)
+
+ if info[1] != gio.FILE_TYPE_DIRECTORY:
+ ret = ret and self._handler(info[0])
+ else:
+ text = self._entry.get_text()
+
+ for i in range(len(text) - 1, -1, -1):
+ if text[i] == os.sep:
+ break
+
+ self._entry.set_text(os.path.join(text[:i], os.path.basename(info[0].get_uri())) + os.sep)
+ self._entry.set_position(-1)
+ self._entry.grab_focus()
+ return True
+
+ if rows and ret:
+ self.destroy()
+
+ if not rows:
+ gfile = self._direct_file()
+
+ if gfile and self._handler(gfile):
+ self.destroy()
+ else:
+ ret = False
+ else:
+ ret = False
+
+ return ret
+
+ def toggle_cursor(self):
+ if not self._cursor:
+ return
+
+ path = self._cursor.get_path()
+ selection = self._treeview.get_selection()
+
+ if selection.path_is_selected(path):
+ selection.unselect_path(path)
+ else:
+ selection.select_path(path)
+
+ def on_key_press_event(self, widget, event):
+ move_mapping = {
+ gtk.keysyms.Down: 1,
+ gtk.keysyms.Up: -1,
+ gtk.keysyms.Page_Down: 5,
+ gtk.keysyms.Page_Up: -5
+ }
+
+ if event.keyval == gtk.keysyms.Escape:
+ self.destroy()
+ return True
+ elif event.keyval in move_mapping:
+ return self._move_selection(move_mapping[event.keyval], event.state & gtk.gdk.CONTROL_MASK, event.state & gtk.gdk.SHIFT_MASK)
+ elif event.keyval in [gtk.keysyms.Return, gtk.keysyms.KP_Enter, gtk.keysyms.Tab, gtk.keysyms.ISO_Left_Tab]:
+ return self._activate()
+ elif event.keyval == gtk.keysyms.space and event.state & gtk.gdk.CONTROL_MASK:
+ self.toggle_cursor()
+
+ return False
+
+ def on_row_activated(self, view, path, column):
+ self._activate()
+
+ def do_response(self, response):
+ if response != gtk.RESPONSE_ACCEPT or not self._activate():
+ self.destroy()
+
+ def on_selection_changed(self, selection):
+ model, rows = selection.get_selected_rows()
+
+ gfile = None
+ fname = None
+
+ if not rows:
+ gfile = self._direct_file()
+ elif len(rows) == 1:
+ gfile = model.get(model.get_iter(rows[0]), 2)[0]
+ else:
+ fname = ''
+
+ if gfile:
+ if gfile.is_native():
+ fname = xml.sax.saxutils.escape(gfile.get_path())
+ else:
+ fname = xml.sax.saxutils.escape(gfile.get_uri())
+
+ self._open_button.set_sensitive(fname != None)
+ self._info_label.set_markup(fname or '')
+
+ def on_focus_entry(self, group, accel, keyval, modifier):
+ self._entry.grab_focus()
+
+gobject.type_register(Popup)
+
+# ex:ts=8:et:
diff --git a/plugins/quickopen/quickopen/virtualdirs.py b/plugins/quickopen/quickopen/virtualdirs.py
new file mode 100755
index 00000000..ef0b8dc4
--- /dev/null
+++ b/plugins/quickopen/quickopen/virtualdirs.py
@@ -0,0 +1,87 @@
+# -*- coding: utf-8 -*-
+
+# 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.
+
+import gtk
+import gio
+
+class VirtualDirectory:
+ def __init__(self, name):
+ self._name = name
+ self._children = []
+
+ def get_uri(self):
+ return 'virtual://' + self._name
+
+ def get_parent(self):
+ return None
+
+ def enumerate_children(self, attr):
+ return self._children
+
+ def append(self, child):
+ if not child.is_native():
+ return
+
+ try:
+ info = child.query_info("standard::*")
+
+ if info:
+ self._children.append((child, info))
+ except:
+ pass
+
+class RecentDocumentsDirectory(VirtualDirectory):
+ def __init__(self, maxitems=10, screen=None):
+ VirtualDirectory.__init__(self, 'recent')
+
+ self._maxitems = maxitems
+ self.fill(screen)
+
+ def fill(self, screen):
+ if screen:
+ manager = gtk.recent_manager_get_for_screen(screen)
+ else:
+ manager = gtk.recent_manager_get_default()
+
+ items = manager.get_items()
+ items.sort(lambda a, b: cmp(b.get_visited(), a.get_visited()))
+
+ added = 0
+
+ for item in items:
+ if item.has_group('gedit'):
+ self.append(gio.File(item.get_uri()))
+ added += 1
+
+ if added >= self._maxitems:
+ break
+
+class CurrentDocumentsDirectory(VirtualDirectory):
+ def __init__(self, window):
+ VirtualDirectory.__init__(self, 'documents')
+
+ self.fill(window)
+
+ def fill(self, window):
+ for doc in window.get_documents():
+ location = doc.get_location()
+ if location:
+ self.append(location)
+
+# ex:ts=8:et:
diff --git a/plugins/quickopen/quickopen/windowhelper.py b/plugins/quickopen/quickopen/windowhelper.py
new file mode 100755
index 00000000..70ea26f0
--- /dev/null
+++ b/plugins/quickopen/quickopen/windowhelper.py
@@ -0,0 +1,198 @@
+# -*- coding: utf-8 -*-
+
+# 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.
+
+import gedit
+import gtk
+from popup import Popup
+import os
+import gedit.commands
+import gio
+import glib
+from virtualdirs import RecentDocumentsDirectory
+from virtualdirs import CurrentDocumentsDirectory
+
+ui_str = """<ui>
+ <menubar name="MenuBar">
+ <menu name="FileMenu" action="File">
+ <placeholder name="FileOps_2">
+ <menuitem name="QuickOpen" action="QuickOpen"/>
+ </placeholder>
+ </menu>
+ </menubar>
+</ui>
+"""
+
+class WindowHelper:
+ def __init__(self, window, plugin):
+ self._window = window
+ self._plugin = plugin
+
+ self._popup = None
+ self._install_menu()
+
+ def deactivate(self):
+ self._uninstall_menu()
+ self._window = None
+ self._plugin = None
+
+ def update_ui(self):
+ pass
+
+ def _uninstall_menu(self):
+ manager = self._window.get_ui_manager()
+
+ manager.remove_ui(self._ui_id)
+ manager.remove_action_group(self._action_group)
+
+ manager.ensure_update()
+
+ def _install_menu(self):
+ manager = self._window.get_ui_manager()
+ self._action_group = gtk.ActionGroup("GeditQuickOpenPluginActions")
+ self._action_group.add_actions([
+ ("QuickOpen", gtk.STOCK_OPEN, _("Quick open"),
+ '<Ctrl><Alt>O', _("Quickly open documents"),
+ self.on_quick_open_activate)
+ ])
+
+ manager.insert_action_group(self._action_group, -1)
+ self._ui_id = manager.add_ui_from_string(ui_str)
+
+ def _create_popup(self):
+ paths = []
+
+ # Open documents
+ paths.append(CurrentDocumentsDirectory(self._window))
+
+ doc = self._window.get_active_document()
+
+ # Current document directory
+ if doc and doc.is_local():
+ gfile = doc.get_location()
+ paths.append(gfile.get_parent())
+
+ # File browser root directory
+ if gedit.version[0] > 2 or (gedit.version[0] == 2 and (gedit.version[1] > 26 or (gedit.version[1] == 26 and gedit.version[2] >= 2))):
+ bus = self._window.get_message_bus()
+
+ try:
+ msg = bus.send_sync('/plugins/filebrowser', 'get_root')
+
+ if msg:
+ uri = msg.get_value('uri')
+
+ if uri:
+ gfile = gio.File(uri)
+
+ if gfile.is_native():
+ paths.append(gfile)
+
+ except StandardError:
+ pass
+
+ # Recent documents
+ paths.append(RecentDocumentsDirectory(screen=self._window.get_screen()))
+
+ # Local bookmarks
+ for path in self._local_bookmarks():
+ paths.append(path)
+
+ # Desktop directory
+ desktopdir = self._desktop_dir()
+
+ if desktopdir:
+ paths.append(gio.File(desktopdir))
+
+ # Home directory
+ paths.append(gio.File(os.path.expanduser('~')))
+
+ self._popup = Popup(self._window, paths, self.on_activated)
+
+ self._popup.set_default_size(*self._plugin.get_popup_size())
+ self._popup.set_transient_for(self._window)
+ self._popup.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
+
+ self._window.get_group().add_window(self._popup)
+
+ self._popup.connect('destroy', self.on_popup_destroy)
+
+ def _local_bookmarks(self):
+ filename = os.path.expanduser('~/.gtk-bookmarks')
+
+ if not os.path.isfile(filename):
+ return []
+
+ paths = []
+
+ for line in file(filename, 'r').xreadlines():
+ uri = line.strip().split(" ")[0]
+ f = gio.File(uri)
+
+ if f.is_native():
+ try:
+ info = f.query_info("standard::type")
+
+ if info and info.get_file_type() == gio.FILE_TYPE_DIRECTORY:
+ paths.append(f)
+ except glib.GError:
+ pass
+
+ return paths
+
+ def _desktop_dir(self):
+ config = os.getenv('XDG_CONFIG_HOME')
+
+ if not config:
+ config = os.path.expanduser('~/.config')
+
+ config = os.path.join(config, 'user-dirs.dirs')
+ desktopdir = None
+
+ if os.path.isfile(config):
+ for line in file(config, 'r').xreadlines():
+ line = line.strip()
+
+ if line.startswith('XDG_DESKTOP_DIR'):
+ parts = line.split('=', 1)
+ desktopdir = os.path.expandvars(parts[1].strip('"').strip("'"))
+ break
+
+ if not desktopdir:
+ desktopdir = os.path.expanduser('~/Desktop')
+
+ return desktopdir
+
+ # Callbacks
+ def on_quick_open_activate(self, action):
+ if not self._popup:
+ self._create_popup()
+
+ self._popup.show()
+
+ def on_popup_destroy(self, popup):
+ alloc = popup.get_allocation()
+ self._plugin.set_popup_size((alloc.width, alloc.height))
+
+ self._popup = None
+
+ def on_activated(self, gfile):
+ gedit.commands.load_uri(self._window, gfile.get_uri(), None, -1)
+ return True
+
+# ex:ts=8:et: