This commit is contained in:
Perberos
2011-11-07 16:46:58 -03:00
commit 528c1e5ff5
532 changed files with 709826 additions and 0 deletions

15
plugins/quickopen/Makefile.am Executable file
View 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

View 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

View 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

View 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:

View 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:

View 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:

View 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: