Fix ListView to make sense.
[matthijs/upstream/mobilegtd.git] / src / gui / gui.py
index 1dc34b291ea2d2875aa3405657900e978701188a..20f4d05a34ac3f8aa8294a3525f4e32700909aeb 100644 (file)
@@ -93,7 +93,6 @@ def save_gui(object):
     object.old_menu = appuifw.app.menu
     object.old_exit_key_handler = appuifw.app.exit_key_handler
     object.old_title=appuifw.app.title
-    object.lock = Ao_lock()
 
 def restore_gui(object):
     appuifw.app.body = object.old_gui
@@ -101,21 +100,28 @@ def restore_gui(object):
     appuifw.app.exit_key_handler = object.old_exit_key_handler
     appuifw.app.title = object.old_title
 
-
-class ListView(object):
+class View(object):
     def __init__(self):
-        self.view = appuifw.Listbox(self.items(),self.change_entry)
+        self.title = None
+        self.view = None
+        self.lock = Ao_lock()
+        self.exit_flag = False
+        super(View, self).__init__()
 
     def set_title(self, title):
         self.title = title
-    
-    def change_entry(self):
-        pass
-    
+   
+    def set_view(self, view):
+        """
+        Sets the main view to be displayed (e.g., an appuifw.Listbox
+        instance).
+        """
+        self.view = view
+
     def run(self):
         self.adjustment = None
-        appuifw.app.screen=COMMON_CONFIG['screen'].encode('utf-8')
         save_gui(self)
+        appuifw.app.screen=COMMON_CONFIG['screen'].encode('utf-8')
         appuifw.app.title=self.title
         appuifw.app.body=self.view
         appuifw.app.exit_key_handler=self.exit
@@ -127,52 +133,97 @@ class ListView(object):
         except:
             pass
         restore_gui(self)
+
     def exit(self):
         self.exit_flag = True
         self.lock.signal()
 
     def update(self,subject=None):
-        #logger.log(u'Updated %s'%repr(self))
+        """
+        Update the current view (e.g., make sure refresh is called). We
+        can't call it directly, since we're in another thread.
+        """
         if self.lock:
             self.lock.signal()
-        #pass
-
-    def index_changed(self,adjustment=None):
-        if adjustment:
-            index = self.selected_index() + adjustment
-        else:
-            index = self.selected_index()
-        if index < 0:
-            index = len(self.widgets) - 1
-        if index >= len(self.widgets):
-            index = 0
-        self.set_bindings_for_selection(index)
 
     def refresh(self):
+        """
+        Called when the current view must be updated. Never call
+        directly. Subclasses should extend this method, not update.
+        """
         appuifw.app.menu=self.get_menu_entries()
 
+    def get_menu_entries(self):
+        """ Returns a list of menu entries to display. Will be
+        automatically updated on each refresh.
+
+        Each menu entry is a tuple of a title for the entry and a
+        function to call when the entry is selected.
+        """
+        return []
+
+class ListView(View):
+    def __init__(self):
+        super(ListView, self).__init__()
+        self.set_view(appuifw.Listbox(self.items(),self.entry_selected))
+        self.view.bind(EKeyUpArrow,lambda: self.arrow_key_pressed(-1))
+        self.view.bind(EKeyDownArrow,lambda: self.arrow_key_pressed(1))
+
+    def arrow_key_pressed(self, dir):
+        """
+        This function is called when an arrow key is pressed. Since we
+        don't get any "current list index has changed" events, we'll
+        have to create these ourselves this way.
+
+        Since the current index is only updated after the key event,
+        we'll have to adjust the index with the direction of the
+        keypress (-1 for up, +1 for down).
+        """
+        new_index = (self.selected_index() + dir) % len(self.items())
+        self.index_changed(new_index)
+
+    def entry_selected(self):
+        """
+        This function is called when the user selects an an entry (e.g.,
+        navigates to it and push the ok button).
+        """
+        pass
+
+    def index_changed(self,new_index):
+        """
+        This function is called when the index changes. The given index
+        is the new index (don't use self.selected_index() here, since it
+        won't be correct yet!).
+        """
+        pass
+
+    def items(self):
+        """ This function should return the list of items to display.
+        See appuifw.ListBox for valid elements for this list. """
+        return []
+
     def set_index(self,index):
-        if index > len(self.widgets):
-            index = len(self.widgets)
-        if index < 0:
-            index = 0
-        self.view.set_list(self.items(),index)
+        """ Changes the currently selected item to index. """
+        self.view.set_list(self.items(),index % len(self.items()))
 
     def selected_index(self):
+        """ Returns the currently selected index. """
         return self.view.current()
 
-
 class WidgetBasedListView(ListView):
     def __init__(self):
         self.widgets = self.generate_widgets()
         super(WidgetBasedListView,self).__init__()
-        self.exit_flag = False
 
     def run(self):
         self.refresh()
         self.set_bindings_for_selection(0)
         ListView.run(self)
 
+    def index_changed(self,new_index):
+        self.set_bindings_for_selection(new_index)
+        super(WidgeteBasedListView, self).index_changed(new_index)
+
     def notify(self,object,attribute,new=None,old=None):
         self.refresh()
     def refresh(self):
@@ -192,10 +243,11 @@ class WidgetBasedListView(ListView):
         return self.widgets[self.selected_index()]
         
 
-class KeyBindingView(object):
+class KeyBindingView(View):
     
     def __init__(self):
         self.binding_map = {}
+        super(KeyBindingView,self).__init__()
 
     def set_keybindings(self, binding_map):
         """
@@ -221,28 +273,40 @@ class KeyBindingView(object):
                     description='    '+description
                 menu_entries.append((description,function)) 
         menu_entries.append((u'Exit', self.exit))
-        return menu_entries       
+        return menu_entries + super(KeyBindingView, self).get_menu_entries()
+
     def set_bindings_for_selection(self,selected_index):
         self.remove_all_key_bindings()
         
         for key,key_name,description,function in self.key_and_menu_bindings(selected_index):
             if key:
                 self.view.bind(key,function)
-        self.view.bind(EKeyUpArrow,lambda: self.index_changed(-1))
-        self.view.bind(EKeyDownArrow,lambda: self.index_changed(1))
         
     def remove_all_key_bindings(self):
         for key in all_key_values():
             self.view.bind(key,no_action)
 
 class SearchableListView(WidgetBasedListView):
-    def __init__(self,entry_filters):
-        self.current_entry_filter_index = 0
-        self.entry_filters = entry_filters
-        self.filtered_list = self.entry_filters[0]
+    def __init__(self):
+        self.current_entry_filter_index = -1
+        self.entry_filters = []
+        self.filtered_list = lambda:[]
         self.lock = None
         super(SearchableListView,self).__init__()
 
+    def set_filters(self, entry_filters):
+        """
+        Set the filters that could be applied to this list. Each filter
+        can be applied in turn by calling switch_entry_filter (for
+        example from a key binding).
+
+        The entry_filters argument should be a list of filters. The
+        active filter is stored into self.filtered_list and should be
+        processed by generate_widgets in the subclass.
+        """
+        self.current_entry_filter_index = 0
+        self.entry_filters = entry_filters
+        self.filtered_list = self.entry_filters[0]
 
     def search_item(self):
         selected_item = appuifw.selection_list(self.all_widget_texts(),search_field=1)
@@ -250,6 +314,7 @@ class SearchableListView(WidgetBasedListView):
             selected_item = self.selected_index()
         self.view.set_list(self.items(),selected_item)
         self.set_bindings_for_selection(selected_item)
+
     def switch_entry_filter(self):
         self.current_entry_filter_index += 1
         self.filtered_list = self.entry_filters[self.current_entry_filter_index % len(self.entry_filters)]
@@ -257,9 +322,8 @@ class SearchableListView(WidgetBasedListView):
 
 
 class EditableListView(SearchableListView,KeyBindingView):
-    def __init__(self,entry_filters):
-        KeyBindingView.__init__(self)
-        super(EditableListView, self).__init__(entry_filters)
+    def __init__(self):
+        super(EditableListView, self).__init__()
 
     def key_and_menu_bindings(self,selected_index):
         key_and_menu_bindings=[]
@@ -270,7 +334,7 @@ class EditableListView(SearchableListView,KeyBindingView):
             key_and_menu_bindings.append((get_key(key),key,description,execute_and_update_function))
         return key_and_menu_bindings
 
-    def change_entry(self):
+    def entry_selected(self):
         self.current_widget().change()
         self.refresh()
     def execute_and_update(self,function):