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>
* 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
create the user interface. Oh, the joy of being rid of C.
+1 -1
View File
@@ -67,6 +67,7 @@
<signal>
<name>selection_received</name>
<handler>on_query_selection_received</handler>
<after>True</after>
<last_modification_time>Sat, 16 Mar 2002 21:46:37 GMT</last_modification_time>
</signal>
<editable>True</editable>
@@ -83,7 +84,6 @@
<widget>
<class>GtkButton</class>
<name>prefs</name>
<can_focus>True</can_focus>
<signal>
<name>clicked</name>
<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
# Copyright (C) 2001 Michael Bayne
@@ -27,6 +27,7 @@ import string
import re
import bindings
import history
import keyval_util
import properties
@@ -37,6 +38,11 @@ class Lookuplet:
SELECTION_PRIMARY = 1;
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
bindings = None;
@@ -49,9 +55,17 @@ class Lookuplet:
# whether or not we're running as a Gnome applet
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):
window = xmlui.get_widget("lookuplet");
query = xmlui.get_widget("query");
self.about = xmlui.get_widget("about");
self.string_atom = None;
self.bindings = bindings;
@@ -64,6 +78,9 @@ class Lookuplet:
nameFuncMap[key] = getattr(self, key);
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
# applet widget
if (appletMode):
@@ -93,6 +110,7 @@ class Lookuplet:
# request the selection
if (self.string_atom == None):
self.string_atom = gtk.atom_intern("STRING", gtk.FALSE);
query = xmlui.get_widget("query");
query.selection_convert(self.SELECTION_PRIMARY,
self.string_atom,
self.CURRENT_TIME);
@@ -109,24 +127,100 @@ class Lookuplet:
def on_prefs_clicked (self, button):
self.props.editProperties();
def on_query_key_press_event (self, query, event):
# handle special keystrokes such as history and auto-complete
# if (handle_special_keys(widget, ek)):
# return true;
def handle_special_keys (self, query, event):
# we'll need this later
count = len(self.history.history);
# ignore plain or shifted keystrokes
if (event.state <= 1):
# handle special keystrokes such as history and auto-complete
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;
# convert the key press into a string
keystr = keyval_util.convert_keyval_state_to_string(
event.keyval, event.state);
# if we found something scrolling or autocompleting, show it
if (self.hisidx > -1 and self.hisidx < count):
# 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
binding = self.bindings.get_match(keystr);
# otherwise go back to whatever we were editing
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 we found one, invoke it
binding.invoke(query.get_text());
text = 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):
query.set_text("");
else:
@@ -137,6 +231,12 @@ class Lookuplet:
# otherwise, let GTK know that we didn't handle the key press
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):
if (selection_data.length < 0):
# print "Selection retrieval failed.";
@@ -156,11 +256,11 @@ class Lookuplet:
# make sure some selection was provided
if (len(text) > 0):
# 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 (is_url):
# text.tr!(" ", "");
if (is_url):
text = re.sub(" ", "", text);
if (cmp(text, selection_data.data)):
# print "Setting trimmed text '" + text + "'.";