Provide search-as-you-type in the mapping dialogues.
authorAndrew Flegg <andrew@bleb.org>
Thu, 10 Jun 2010 21:56:15 +0000 (22:56 +0100)
committerAndrew Flegg <andrew@bleb.org>
Thu, 10 Jun 2010 21:56:15 +0000 (22:56 +0100)
package/debian/changelog
package/src/org/maemo/hermes/gui/contactview.py
package/src/org/maemo/hermes/gui/gtkui.py
package/src/org/maemo/hermes/gui/mapcontact.py
package/src/org/maemo/hermes/gui/searchablelist.py [new file with mode: 0644]

index 3bd745a..d37c57b 100644 (file)
@@ -3,7 +3,7 @@ hermes (0.8.2) unstable; urgency=low
   * Tweak dbus command to launch browser in attempt to fix MB#8369.
     Assistance by Marcin Juszkiewicz.
   * Add about dialogue box.
-  * ...
+  * Allow dialogues to be 'find-as-you-type'.
 
  -- Andrew Flegg <andrew@bleb.org>  Thu, 10 Jun 2010 14:57:35 +0100
 
index 98f8ef1..87db9ac 100644 (file)
@@ -4,8 +4,9 @@ import gobject
 import evolution
 from ctypes import *
 from pygobject import *
+from org.maemo.hermes.gui.searchablelist import SearchableList
 
-class ContactView(hildon.PannableArea):
+class ContactView(SearchableList):
     """Widget which shows a list of contacts in a pannable area.
          
        Copyright (c) Andrew Flegg <andrew@bleb.org> 2009.
@@ -16,7 +17,7 @@ class ContactView(hildon.PannableArea):
     def __init__(self, contacts):
         """Constructor. Passed a list of Contacts."""
         
-        hildon.PannableArea.__init__(self)
+        SearchableList.__init__(self, 1)
         self.contacts = contacts
         
         columns = [gtk.gdk.Pixbuf,         # 0. Photo
@@ -86,7 +87,6 @@ class ContactView(hildon.PannableArea):
         
         self.treeview.connect('row-activated', self._activated)
         self.add(self.treeview)
-        self.set_size_request_policy(hildon.SIZE_REQUEST_CHILDREN)
 
 
     # -----------------------------------------------------------------------
@@ -114,5 +114,3 @@ class ContactView(hildon.PannableArea):
 
 _contact_activated = gobject.signal_new('contact-activated', ContactView, gobject.SIGNAL_ACTION, gobject.TYPE_NONE, [gobject.TYPE_PYOBJECT])
 
-
-
index a639e72..5008925 100644 (file)
@@ -189,6 +189,7 @@ class HermesGUI(WimpWorks):
         view.connect('contact-activated', self.map_contact, fb2c)
         dialog.vbox.add(view)
         dialog.show_all()
+        dialog.vbox.add(view.get_search())
       
         dialog.run()
         dialog.hide()
@@ -202,6 +203,7 @@ class HermesGUI(WimpWorks):
         dialog.add_button(_('Update'), gtk.RESPONSE_OK)
         dialog.vbox.add(view)
         dialog.show_all()
+        dialog.vbox.add(view.get_search())
       
         result = dialog.run()
         dialog.hide()
index aa60ea9..a603066 100644 (file)
@@ -2,8 +2,9 @@ import gtk
 import hildon
 from ctypes import *
 from pygobject import *
+from org.maemo.hermes.gui.searchablelist import SearchableList
 
-class MapContact(hildon.PannableArea):
+class MapContact(SearchableList):
     """Widget which shows a list of friends from various feeds and allows
        the mapping to a particular contact.
        
@@ -15,7 +16,7 @@ class MapContact(hildon.PannableArea):
     def __init__(self, friends, contact):
         """Constructor. Passed a list of `friends' and the contact we're mapping."""
         
-        hildon.PannableArea.__init__(self)
+        SearchableList.__init__(self, 1)
         self.friends = friends
         self.contact = contact
         self.treestore = gtk.ListStore(gtk.gdk.Pixbuf, str, gtk.gdk.Pixbuf, gobject.TYPE_PYOBJECT)
@@ -73,7 +74,6 @@ class MapContact(hildon.PannableArea):
             self.treeview.get_selection().unselect_all()
           
         self.add(self.treeview)
-        self.set_size_request_policy(hildon.SIZE_REQUEST_CHILDREN)
 
       
     # -----------------------------------------------------------------------
diff --git a/package/src/org/maemo/hermes/gui/searchablelist.py b/package/src/org/maemo/hermes/gui/searchablelist.py
new file mode 100644 (file)
index 0000000..c2c203f
--- /dev/null
@@ -0,0 +1,97 @@
+import gtk, hildon
+
+class SearchableList(hildon.PannableArea):
+    """Widget which exposes a TreeView with find-as-you-type features.
+         
+       Copyright (c) Andrew Flegg <andrew@bleb.org> 2010.
+       Released under the Artistic Licence."""
+    
+    
+    # -----------------------------------------------------------------------
+    def __init__(self, search_column):
+        """Constructor. Passed a search column - expected to be exposed from
+           a subclass.
+
+           Subclasses must also initialise self.treestore, self.treeview and
+           then do self.add(self.treeview)."""
+        
+        hildon.PannableArea.__init__(self)
+        self._search_column = search_column
+        self.set_size_request_policy(hildon.SIZE_REQUEST_CHILDREN)
+
+        self.hbox_entry_filter = None
+
+
+    # -----------------------------------------------------------------------
+    def get_search(self):
+        """Return a search box. If added to the tree will handle filtering this
+           treeview automatically."""
+
+        self.hbox_entry_filter = gtk.VBox()
+        real_hbox = gtk.HBox()
+        self.hbox_entry_filter.pack_end(real_hbox, False, False)
+
+        self.entry_filter = hildon.Entry(gtk.HILDON_SIZE_FINGER_HEIGHT)
+        self.entry_filter.connect('changed', lambda w: self.filter.refilter())
+        real_hbox.pack_start(self.entry_filter, True, True)
+
+        #close = hildon.Button(gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL, title = _('Show all'))
+        #close.connect('clicked', self._hide_search)
+        #real_hbox.pack_end(close, False, False, 0)
+
+        self.filter = self.treeview.get_model().filter_new()
+        self.filter.set_visible_func(self._visible_func, self._search_column)
+        self.treeview.connect('key-press-event', self._on_key_press)
+        self.treeview.set_model(self.filter)
+        real_hbox.show_all()
+
+        return self.hbox_entry_filter
+
+
+
+    # -----------------------------------------------------------------------
+    def _on_key_press(self, treeview, event):
+        if event.keyval == gtk.keysyms.Escape or event.keyval == gtk.keysyms.BackSpace:
+            self._hide_search()
+        elif event.state & gtk.gdk.CONTROL_MASK:
+            # Don't handle type-ahead when control is pressed (so shortcuts
+            # with the Ctrl key still work, e.g. Ctrl+A, ...)
+            return True
+        else:
+            unicode_char_id = gtk.gdk.keyval_to_unicode(event.keyval)
+            if unicode_char_id == 0:
+                return False
+            input_char = unichr(unicode_char_id)
+            self._show_search(input_char)
+        return True
+
+
+
+    # -----------------------------------------------------------------------
+    def _visible_func(self, model, iter, column):
+        text = self.entry_filter.get_text().lower()
+        if not self.hbox_entry_filter.get_property('visible'):
+            return True
+        elif text == '':
+            self._hide_search()
+            return True
+        else:
+            value = model.get_value(iter, column).lower()
+            return text in value
+
+    
+    # -----------------------------------------------------------------------
+    def _hide_search(self, *args):
+        self.hbox_entry_filter.hide()
+        self.entry_filter.set_text('')
+        self.filter.refilter()
+        self.treeview.grab_focus()
+
+
+    # -----------------------------------------------------------------------
+    def _show_search(self, input_char):
+        self.hbox_entry_filter.show_all()
+        self.entry_filter.insert_text(input_char, -1)
+        self.entry_filter.grab_focus()
+        self.entry_filter.set_position(-1)
+