[gedit] Added new plugin: quickopen



commit bfb97f3691cbe2704620e210ab7057d440f2ca73
Author: Jesse van den Kieboom <jesse icecrew nl>
Date:   Sun May 17 13:53:55 2009 +0200

    Added new plugin: quickopen
    
    This plugin allows to quickly open files by partially matching file names from root directories such
    as the users' home, desktop, current document, recent files, open documents and bookmarks.
---
 configure.ac                                       |    2 +
 plugins/Makefile.am                                |    4 +-
 plugins/quickopen/Makefile.am                      |   15 +
 .../quickopen/quickopen.gedit-plugin.desktop.in    |   10 +
 plugins/quickopen/quickopen/Makefile.am            |   13 +
 plugins/quickopen/quickopen/__init__.py            |   46 ++
 plugins/quickopen/quickopen/popup.py               |  453 ++++++++++++++++++++
 plugins/quickopen/quickopen/virtualdirs.py         |   86 ++++
 plugins/quickopen/quickopen/windowhelper.py        |  205 +++++++++
 po/POTFILES.in                                     |    2 +
 10 files changed, 834 insertions(+), 2 deletions(-)

diff --git a/configure.ac b/configure.ac
index 792fffb..74db60a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -438,6 +438,8 @@ plugins/Makefile
 plugins/modelines/Makefile
 plugins/pythonconsole/Makefile
 plugins/pythonconsole/pythonconsole/Makefile
+plugins/quickopen/Makefile
+plugins/quickopen/quickopen/Makefile
 plugins/sample/Makefile
 plugins/snippets/Makefile
 plugins/snippets/snippets/Makefile
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index 9bde214..7069b03 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -23,8 +23,8 @@ SUBDIRS = 		\
 	$(SPELL_PLUGIN_DIR)
 
 if ENABLE_PYTHON
-DIST_SUBDIRS += externaltools pythonconsole snippets
-SUBDIRS      += externaltools pythonconsole snippets
+DIST_SUBDIRS += externaltools pythonconsole snippets quickopen
+SUBDIRS      += externaltools pythonconsole snippets quickopen
 endif
 
 
diff --git a/plugins/quickopen/Makefile.am b/plugins/quickopen/Makefile.am
new file mode 100644
index 0000000..4b5faf0
--- /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 100644
index 0000000..40f7f04
--- /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  <jessevdk gnome org>
+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 100644
index 0000000..88882fd
--- /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 100644
index 0000000..a41c940
--- /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 100644
index 0000000..3528c3f
--- /dev/null
+++ b/plugins/quickopen/quickopen/popup.py
@@ -0,0 +1,453 @@
+# -*- 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 fnmatch
+import gedit
+import xml
+from virtualdirs import VirtualDirectory
+
+from gpdefs import *
+
+try:
+    gettext.bindtextdomain(GETTEXT_PACKAGE, GP_LOCALEDIR)
+    _ = lambda s: gettext.dgettext(GETTEXT_PACKAGE, s);
+except:
+    _ = lambda s: s
+
+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
+
+                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)
+
+                tv.append_column(column)
+                sw.add(tv)
+
+                tv.get_selection().connect('changed', self.on_selection_changed)
+
+                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 _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 gio.Error:
+                        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 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 do_search(self):
+                self.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
+
+                text = self._entry.get_text().strip()
+                self._clear_store()
+
+                if text == '':
+                        self._show_virtuals()
+                        self.window.set_cursor(None)
+                        return
+
+                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 _select_index(self, idx):
+                self._treeview.get_selection().select_path((idx,))
+
+                self._treeview.scroll_to_cell((idx,), None, True, 0.5, 0)
+
+        def _move_selection(self, howmany):
+                model, s = self._treeview.get_selection().get_selected()
+                num = model.iter_n_children(None)
+
+                if num == 0:
+                        return True
+
+                if not s:
+                        if howmany > 0:
+                                self._select_index(0)
+                        else:
+                                self._select_index(num - 1)
+                else:
+                        path = model.get_path(s)
+                        idx = path[0]
+
+                        if idx + howmany < 0:
+                                self._select_index(0)
+                        elif idx + howmany >= num:
+                                self._select_index(num - 1)
+                        else:
+                                self._select_index(idx + howmany)
+
+                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, s = self._treeview.get_selection().get_selected()
+
+                if s:
+                        info = model.get(s, 2, 3)
+
+                        if info[1] != gio.FILE_TYPE_DIRECTORY:
+                                if self._handler(info[0]):
+                                        self.destroy()
+                                        return True
+                                else:
+                                        return False
+                        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
+                else:
+                        gfile = self._direct_file()
+
+                        if gfile and self._handler(gfile):
+                                self.destroy()
+                                return True
+
+                return False
+
+        def on_key_press_event(self, widget, event):
+                if event.keyval == gtk.keysyms.Escape:
+                        self.destroy()
+                        return True
+                elif event.keyval == gtk.keysyms.Down:
+                        return self._move_selection(1)
+                elif event.keyval == gtk.keysyms.Up:
+                        return self._move_selection(-1)
+                elif event.keyval == gtk.keysyms.Page_Down:
+                        return self._move_selection(5)
+                elif event.keyval == gtk.keysyms.Page_Up:
+                        return self._move_selection(-5)
+                elif event.keyval in [gtk.keysyms.Return, gtk.keysyms.KP_Enter, gtk.keysyms.Tab, gtk.keysyms.ISO_Left_Tab]:
+                        return self._activate()
+
+                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, piter = selection.get_selected()
+
+                if not piter:
+                        gfile = self._direct_file()
+                else:
+                        gfile = model.get(piter, 2)[0]
+
+                if gfile:
+                        if gfile.is_native():
+                                fname = xml.sax.saxutils.escape(gfile.get_path())
+                        else:
+                                fname = xml.sax.saxutils.escape(gfile.get_uri())
+                else:
+                        fname = ''
+
+                self._open_button.set_sensitive(fname != '')
+                self._info_label.set_markup(fname)
+
+        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 100644
index 0000000..b15b7e7
--- /dev/null
+++ b/plugins/quickopen/quickopen/virtualdirs.py
@@ -0,0 +1,86 @@
+# -*- 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():
+                        if doc.get_uri():
+                                self.append(gio.File(doc.get_uri()))
+
+# ex:ts=8:et:
diff --git a/plugins/quickopen/quickopen/windowhelper.py b/plugins/quickopen/quickopen/windowhelper.py
new file mode 100644
index 0000000..92e40d4
--- /dev/null
+++ b/plugins/quickopen/quickopen/windowhelper.py
@@ -0,0 +1,205 @@
+# -*- 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
+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>
+"""
+
+from gpdefs import *
+
+try:
+    gettext.bindtextdomain(GETTEXT_PACKAGE, GP_LOCALEDIR)
+    _ = lambda s: gettext.dgettext(GETTEXT_PACKAGE, s);
+except:
+    _ = lambda s: s
+
+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)
+                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 = gio.File(doc.get_uri())
+                        paths.append(gfile.get_parent())
+
+                # File browser root directory
+                if gedit.version[0] >= 2 and 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 gio.Error:
+                                        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:
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 8cc910c..740ada2 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -74,6 +74,8 @@ plugins/modelines/modelines.gedit-plugin.desktop.in
 plugins/pythonconsole/pythonconsole.gedit-plugin.desktop.in
 plugins/pythonconsole/pythonconsole/__init__.py
 [type: gettext/glade]plugins/pythonconsole/pythonconsole/config.ui
+plugins/quickopen/quickopen/popup.py
+plugins/quickopen/quickopen/windowhelper.py
 plugins/sample/gedit-sample-plugin.c
 plugins/sample/sample.gedit-plugin.desktop.in
 plugins/snippets/snippets.gedit-plugin.desktop.in



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]