initial
This commit is contained in:
15
plugins/quickopen/Makefile.am
Executable file
15
plugins/quickopen/Makefile.am
Executable file
@@ -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
|
10
plugins/quickopen/quickopen.gedit-plugin.desktop.in
Executable file
10
plugins/quickopen/quickopen.gedit-plugin.desktop.in
Executable file
@@ -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
|
13
plugins/quickopen/quickopen/Makefile.am
Executable file
13
plugins/quickopen/quickopen/Makefile.am
Executable file
@@ -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
|
46
plugins/quickopen/quickopen/__init__.py
Executable file
46
plugins/quickopen/quickopen/__init__.py
Executable file
@@ -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:
|
534
plugins/quickopen/quickopen/popup.py
Executable file
534
plugins/quickopen/quickopen/popup.py
Executable file
@@ -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:
|
87
plugins/quickopen/quickopen/virtualdirs.py
Executable file
87
plugins/quickopen/quickopen/virtualdirs.py
Executable file
@@ -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:
|
198
plugins/quickopen/quickopen/windowhelper.py
Executable file
198
plugins/quickopen/quickopen/windowhelper.py
Executable file
@@ -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:
|
Reference in New Issue
Block a user