Persistent history and tab completion (from the history) support.

git-svn-id: https://samskivert.googlecode.com/svn/trunk@669 6335cc39-0255-0410-8fd6-9bcaacd3b74c
This commit is contained in:
mdb
2002-03-17 21:25:20 +00:00
parent ca29aee202
commit be6c4511ee
4 changed files with 212 additions and 19 deletions
+3
View File
@@ -1,5 +1,8 @@
2002-03-17 Michael Bayne <mdb@samskivert.com> 2002-03-17 Michael Bayne <mdb@samskivert.com>
* src/history.py: Added persistent history support and tab
completion (from the history) to the query box text field. Yay!
* various: Rewrote the whole business in Python, using Glade to * various: Rewrote the whole business in Python, using Glade to
create the user interface. Oh, the joy of being rid of C. create the user interface. Oh, the joy of being rid of C.
+1 -1
View File
@@ -67,6 +67,7 @@
<signal> <signal>
<name>selection_received</name> <name>selection_received</name>
<handler>on_query_selection_received</handler> <handler>on_query_selection_received</handler>
<after>True</after>
<last_modification_time>Sat, 16 Mar 2002 21:46:37 GMT</last_modification_time> <last_modification_time>Sat, 16 Mar 2002 21:46:37 GMT</last_modification_time>
</signal> </signal>
<editable>True</editable> <editable>True</editable>
@@ -83,7 +84,6 @@
<widget> <widget>
<class>GtkButton</class> <class>GtkButton</class>
<name>prefs</name> <name>prefs</name>
<can_focus>True</can_focus>
<signal> <signal>
<name>clicked</name> <name>clicked</name>
<handler>on_prefs_clicked</handler> <handler>on_prefs_clicked</handler>
+90
View File
@@ -0,0 +1,90 @@
#
# $Id: history.py,v 1.1 2002/03/17 21:25:20 mdb Exp $
#
# lookuplet - a utility for quickly looking up information
# Copyright (C) 2001 Michael Bayne
#
# 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.1 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 gnome.config
class History:
"Used to persistently maintain a history of entries for the \
lookuplet query text field."
# the maximum history size
HISTORY_SIZE = 10;
# the actual history entries
history = [];
#
# Loads up the history from persistent preferences and prepares for
# operation.
#
def __init__ (self):
gnome.config.push_prefix("/lookuplet/");
count = gnome.config.get_int("lookuplet/history/count");
for h in range(0, count):
self.history.append(gnome.config.get_string(
"lookuplet/history/entry_%.2u" % h));
# print "loaded %d: %s" % (h, self.history[h]);
gnome.config.pop_prefix();
return;
#
# Adds an entry to the history.
#
def append (self, entry):
# if this entry is already in the history list, we simply want to
# move it to the end
if (entry in self.history):
self.history.remove(entry);
# add our entry
self.history.append(entry);
# trim the history if it's too big
if (len(self.history) > self.HISTORY_SIZE):
self.history.pop(0);
# flush our config to the configuration repository
gnome.config.push_prefix("/lookuplet/");
count = len(self.history);
gnome.config.set_int("lookuplet/history/count", count);
for h in range(0, count):
gnome.config.set_string(
"lookuplet/history/entry_%.2u" % h, self.history[h]);
# print "wrote %d: %s" % (h, self.history[h]);
gnome.config.sync();
gnome.config.drop_all();
gnome.config.pop_prefix();
#
# Scans back from one before the specified index looking for a history
# entry that matches the specified prefix, returning its index if one
# is found or -1 if one is not.
#
def match (self, prefix, index):
count = len(self.history);
for offset in range(1, count+1):
# do some funny math to always ensure a positive value
pos = (index - offset + count) % count;
# print "scanning %s for %s." % (self.history[pos], prefix);
if (self.history[pos].find(prefix) == 0):
# print "found it at %d." % pos;
return pos;
# print "didn't find it.";
return -1;
+118 -18
View File
@@ -1,5 +1,5 @@
# #
# $Id: lookuplet.py,v 1.2 2002/03/17 10:13:25 mdb Exp $ # $Id: lookuplet.py,v 1.3 2002/03/17 21:25:20 mdb Exp $
# #
# lookuplet - a utility for quickly looking up information # lookuplet - a utility for quickly looking up information
# Copyright (C) 2001 Michael Bayne # Copyright (C) 2001 Michael Bayne
@@ -27,6 +27,7 @@ import string
import re import re
import bindings import bindings
import history
import keyval_util import keyval_util
import properties import properties
@@ -37,6 +38,11 @@ class Lookuplet:
SELECTION_PRIMARY = 1; SELECTION_PRIMARY = 1;
CURRENT_TIME = 0; CURRENT_TIME = 0;
# our special key definitions
PREV_HISTORY_KEY = GDK.Up
NEXT_HISTORY_KEY = GDK.Down
AUTO_COMPLETE_KEY = GDK.Tab
# a reference to our key bindings # a reference to our key bindings
bindings = None; bindings = None;
@@ -49,9 +55,17 @@ class Lookuplet:
# whether or not we're running as a Gnome applet # whether or not we're running as a Gnome applet
appletMode = 0; appletMode = 0;
# our query box history
history = None;
# where we are if we're scanning the history
hisidx = -1;
# the prefix we're using to match if we're matching on a prefix
hispref = None;
def __init__ (self, xmlui, bindings, props, appletMode): def __init__ (self, xmlui, bindings, props, appletMode):
window = xmlui.get_widget("lookuplet"); window = xmlui.get_widget("lookuplet");
query = xmlui.get_widget("query");
self.about = xmlui.get_widget("about"); self.about = xmlui.get_widget("about");
self.string_atom = None; self.string_atom = None;
self.bindings = bindings; self.bindings = bindings;
@@ -64,6 +78,9 @@ class Lookuplet:
nameFuncMap[key] = getattr(self, key); nameFuncMap[key] = getattr(self, key);
xmlui.signal_autoconnect(nameFuncMap); xmlui.signal_autoconnect(nameFuncMap);
# create our query history
self.history = history.History();
# if we're in applet mode, extract the UI and stick it into an # if we're in applet mode, extract the UI and stick it into an
# applet widget # applet widget
if (appletMode): if (appletMode):
@@ -93,6 +110,7 @@ class Lookuplet:
# request the selection # request the selection
if (self.string_atom == None): if (self.string_atom == None):
self.string_atom = gtk.atom_intern("STRING", gtk.FALSE); self.string_atom = gtk.atom_intern("STRING", gtk.FALSE);
query = xmlui.get_widget("query");
query.selection_convert(self.SELECTION_PRIMARY, query.selection_convert(self.SELECTION_PRIMARY,
self.string_atom, self.string_atom,
self.CURRENT_TIME); self.CURRENT_TIME);
@@ -109,24 +127,100 @@ class Lookuplet:
def on_prefs_clicked (self, button): def on_prefs_clicked (self, button):
self.props.editProperties(); self.props.editProperties();
def on_query_key_press_event (self, query, event): def handle_special_keys (self, query, event):
# handle special keystrokes such as history and auto-complete # we'll need this later
# if (handle_special_keys(widget, ek)): count = len(self.history.history);
# return true;
# ignore plain or shifted keystrokes # handle special keystrokes such as history and auto-complete
if (event.state <= 1): if (event.keyval == self.NEXT_HISTORY_KEY):
if (self.hisidx >= 0 and self.hisidx < count):
self.hisidx += 1;
else:
return gtk.TRUE;
elif (event.keyval == self.PREV_HISTORY_KEY):
if (self.hisidx > 0):
self.hisidx -= 1;
elif (self.hisidx == -1):
# if we haven't grabbed a prefix as of yet, grab what we've got
if (self.hispref == None):
self.hispref = query.get_text();
# print "set prefix: %s" % self.hispref;
self.hisidx = count-1;
else:
return gtk.TRUE;
elif (event.keyval == self.AUTO_COMPLETE_KEY):
# if we haven't grabbed a prefix as of yet, grab what we've got
if (self.hispref == None):
self.hispref = query.get_text();
# print "set prefix: %s" % self.hispref;
# if our history prefix has not yet been set, set it and start
# scanning through the history
if (self.hisidx == -1):
self.hisidx = count;
self.hisidx = self.history.match(self.hispref, self.hisidx);
else:
return gtk.FALSE; return gtk.FALSE;
# convert the key press into a string # if we found something scrolling or autocompleting, show it
keystr = keyval_util.convert_keyval_state_to_string( if (self.hisidx > -1 and self.hisidx < count):
event.keyval, event.state); # print "displaying history entry %d." % self.hisidx;
text = self.history.history[self.hisidx];
self.set_query(query, text);
return gtk.TRUE;
# look up a binding for that key string # otherwise go back to whatever we were editing
binding = self.bindings.get_match(keystr); if (self.hispref != None):
# print "restoring prefix %s" % self.hispref;
self.set_query(query, self.hispref);
# we always want to claim to have handled the keypress if it was
# the autocomplete key, otherwise GTK will move the focus from our
# query box widget
if (event.keyval == self.AUTO_COMPLETE_KEY):
return gtk.TRUE
else:
return gtk.FALSE;
def set_query (self, query, text):
# display text in the query box and select it for easy erasal
query.set_text(text);
query.select_region(0, len(text));
def on_query_key_press_event (self, query, event):
# handle history browsing and auto-completion
if (self.handle_special_keys(query, event) == gtk.TRUE):
return gtk.TRUE;
# clear out our history prefix because it's in the text field now
# and we'll grab it again if we need it
self.hispref = None;
self.hisidx = -1;
# if they pressed return, map that to a special binding
if (event.keyval == GDK.Return):
binding = bindings.Binding("", bindings.Binding.URL, "", "%T");
else:
# ignore plain or shifted keystrokes
if (event.state <= 1):
return gtk.FALSE;
# convert the key press into a string
keystr = keyval_util.convert_keyval_state_to_string(
event.keyval, event.state);
# look up a binding for that key string
binding = self.bindings.get_match(keystr);
# if we found one, invoke it
if (binding != None): if (binding != None):
# if we found one, invoke it text = query.get_text();
binding.invoke(query.get_text()); binding.invoke(text);
# append this entry to our history
self.history.append(text);
# and either bail or get ready for the next go
if (self.appletMode): if (self.appletMode):
query.set_text(""); query.set_text("");
else: else:
@@ -137,6 +231,12 @@ class Lookuplet:
# otherwise, let GTK know that we didn't handle the key press # otherwise, let GTK know that we didn't handle the key press
return gtk.FALSE; return gtk.FALSE;
def url_p (self, text):
return (re.match("^http:", text) or
re.match("^ftp:", text) or
re.match("^https:", text) or
re.match("^file:", text));
def on_query_selection_received (self, query, selection_data, data): def on_query_selection_received (self, query, selection_data, data):
if (selection_data.length < 0): if (selection_data.length < 0):
# print "Selection retrieval failed."; # print "Selection retrieval failed.";
@@ -156,11 +256,11 @@ class Lookuplet:
# make sure some selection was provided # make sure some selection was provided
if (len(text) > 0): if (len(text) > 0):
# check to see if we're looking at a url here # check to see if we're looking at a url here
# is_url = url_p(text); is_url = self.url_p(text);
# if this is a URL, we want to eat whitespace # if this is a URL, we want to eat whitespace
# if (is_url): if (is_url):
# text.tr!(" ", ""); text = re.sub(" ", "", text);
if (cmp(text, selection_data.data)): if (cmp(text, selection_data.data)):
# print "Setting trimmed text '" + text + "'."; # print "Setting trimmed text '" + text + "'.";