1 from config.config import *
2 from model.projects import Projects
6 from log.logging import logger
7 from e32 import Ao_lock, in_emulator
8 from key_codes import *
14 for k, v in cfg.items():
15 v = cfg.format_value(v)
16 if isinstance(v, int) or isinstance(v, long):
19 elif isinstance(v, list) or isinstance(v, tuple):
21 if not isinstance(item, unicode):
22 raise Exception("list can contain only unicode objects, "\
23 "object %r is not supported" % item)
26 elif isinstance(v, unicode):
29 raise Exception("%s has non-supported value" % k)
31 fields.append((unicode(k), tname, v))
34 form = appuifw.Form(fields=fields, flags=appuifw.FFormEditModeOnly | \
35 appuifw.FFormDoubleSpaced)
40 form.save_hook = save_hook
44 # return true if user saved, false otherwise
48 for label, tname, value in form:
50 value = (value[0], int(value[1]))
52 cfg[str(label)] = cfg.parse_value(value)
60 def applicable_functions(obj,allowed_function_names):
61 function_names = [function_name for function_name in dir(obj) if function_name in allowed_function_names]
62 return [eval('obj.%s'%function_name) for function_name in function_names]
64 def get_key(key_name):
68 key=eval('EKey%s'%key_name)
71 def key_shortname(key_name):
72 """ Find the one-character name for a key """
75 elif key_name == 'Backspace':
81 return filter(lambda entry:entry[0:4]=='EKey',dir(key_codes))
100 def save_gui(object):
101 object.old_gui = appuifw.app.body
102 object.old_menu = appuifw.app.menu
103 object.old_exit_key_handler = appuifw.app.exit_key_handler
104 object.old_title=appuifw.app.title
106 def restore_gui(object):
107 appuifw.app.body = object.old_gui
108 appuifw.app.menu = object.old_menu
109 appuifw.app.exit_key_handler = object.old_exit_key_handler
110 appuifw.app.title = object.old_title
114 # Store a list of keys we bound, so we can unbind them
118 self.lock = Ao_lock()
119 self.exit_flag = False
120 super(View, self).__init__()
122 def set_title(self, title):
125 def set_view(self, view):
127 Sets the main view to be displayed (e.g., an appuifw.Listbox
133 self.adjustment = None
135 appuifw.app.screen=COMMON_CONFIG['screen'].encode('utf-8')
136 appuifw.app.title=self.title
137 appuifw.app.body=self.view
138 appuifw.app.exit_key_handler=self.exit
140 while not self.exit_flag:
144 # TODO: Find out which exceptions to catch here. Catching
145 # and silencing all exceptions is not a good idea.
150 self.exit_flag = True
153 def update(self,subject=None):
155 Update the current view (e.g., make sure refresh is called). We
156 can't call it directly, since we're in another thread.
163 Called when the current view must be updated. Never call
164 directly. Subclasses should extend this method, not update.
168 def refresh_menu(self):
170 Refresh the menu and its bindings. Calls self.menu_items() to
173 # Two helper functions
174 def shortcut_prefix(key_name):
175 short = key_shortname(key_name)
177 return '[%s]' % short
181 def do_entry((text, callback, key_name)):
182 key = get_key(key_name)
184 self.view.bind(key, callback)
185 self.menu_keys.append(key)
186 title = "%s %s" % (shortcut_prefix(key_name), text)
187 return(title, callback)
189 # Clear the bindings we previously added (we can't just clear
190 # all bindings, since other classes might have added other
192 for key in self.menu_keys:
193 self.view.bind(key, no_action)
196 # Set the menu, and let do_entry add binds at the same time.
197 appuifw.app.menu = [do_entry(item) for item in self.menu_items()]
199 def menu_items(self):
201 Should return a list of menu items. Each menu item is a tuple:
202 (text, callback, shortcut key name).
204 return [(u'Exit', self.exit, None)]
206 class ListView(View):
208 super(ListView, self).__init__()
209 self.current_index = None
210 self.items_cache = []
211 self.set_view(appuifw.Listbox([], self.entry_selected))
212 self.view.bind(EKeyUpArrow,lambda: self.arrow_key_pressed(-1))
213 self.view.bind(EKeyDownArrow,lambda: self.arrow_key_pressed(1))
215 def arrow_key_pressed(self, dir):
217 This function is called when an arrow key is pressed. Since we
218 don't get any "current list index has changed" events, we'll
219 have to create these ourselves this way.
221 Since the current index is only updated after the key event,
222 we'll have to adjust the index with the direction of the
223 keypress (-1 for up, +1 for down).
225 self.current_index = self.wrap_index(self.selected_index() + dir)
227 self.current_index = None
231 super(ListView, self).refresh()
233 def refresh_list(self):
234 """ Reload the list items. Calls items() again. """
235 # Remember which item was selected
236 selected = self.selected_item()
238 self.items_cache = self.items()
240 # Try to find the selected item in the new list (based on
242 selected_index = self.items_cache.index(selected)
244 # If the selected item is no longer present, just keep the
245 # index the same (but be careful not to fall off the end).
246 selected_index = self.clip_index(self.selected_index())
247 # Update the items in the view
248 self.view.set_list(self.items_cache, selected_index)
252 super(ListView, self).run()
254 def entry_selected(self):
256 This function is called when the user selects an an entry (e.g.,
257 navigates to it and push the ok button).
261 def index_changed(self):
263 This function is called when the index changes. The given index
264 is the new index (don't use self.selected_index() here, since it
265 won't be correct yet!).
270 """ This function should return the list of items to display.
271 See appuifw.ListBox for valid elements for this list. """
274 def set_index(self,index):
275 """ Changes the currently selected item to index. """
276 self.view.set_list(self.items_cache, self.clip_index(index))
278 def selected_item(self):
279 """ Returns the (title of the) currently selected list item. """
280 if not self.items_cache:
281 return None # No items, so none is selected.
282 return self.items_cache[self.selected_index()]
285 def selected_index(self):
286 """ Returns the currently selected index. """
287 if not self.current_index is None:
288 # We allow the current index to be overridden, so it can be
289 # valid during index_changed events. See arrow_key_pressed.
290 return self.current_index
292 return self.view.current()
294 def clip_index(self, index):
296 Make sure the given index fits within the bounds of this
297 list. If it doesn't, clip it off at the ends of the list (e.g,
300 max_index = len(self.items_cache) - 1
301 return max (0, min(max_index, index))
303 def wrap_index(self, index):
305 Make sure the given index fits within the bounds of this
306 list. If it doesn't, wrap it around (e.g., -1 becomes 5 in a
309 count = len(self.items_cache)
312 class WidgetBasedListView(ListView):
314 self.binding_map = {}
315 self.widgets = self.generate_widgets()
316 super(WidgetBasedListView,self).__init__()
318 def index_changed(self):
320 super(WidgetBasedListView, self).index_changed()
322 def entry_selected(self):
323 self.current_widget().change()
326 def notify(self,object,attribute,new=None,old=None):
330 self.refresh_widgets()
331 super(WidgetBasedListView,self).refresh()
333 def refresh_widgets(self):
334 """ Refresh the widget list. Calls generate_widgets(). """
335 self.widgets = self.generate_widgets()
337 def redisplay_widgets(self):
339 Redisplay the widgets. Should be called if the widgets
340 themselves have changed, does not call generate_widgets again.
345 # Let ListView show each widget's text.
346 return self.all_widget_texts()
348 def all_widget_texts(self):
350 Return the widget texts as they should be displayed in the
353 return [entry.list_repr() for entry in self.widgets]
355 def current_widget(self):
356 """ Returns the currently selected widget. """
357 return self.widgets[self.selected_index()]
359 def generate_widgets():
360 """ This function should return a list of widgets. """
363 def menu_items(self):
364 # Determine the current menu based on the methods available on
365 # the selected widget and on ourselves.
367 for function in applicable_functions(self.current_widget(),self.binding_map)+\
368 applicable_functions(self,self.binding_map):
369 (key,description) = self.binding_map[function.__name__]
373 menu_items.append((description, do_callback, key))
374 menu_items += super(WidgetBasedListView, self).menu_items()
377 def set_menu(self, binding_map):
379 Set a new map of menu entries with hotkeys. This map maps method names to a
380 tuple of keyname and description.
382 Keyname is a string containing the name of the key (the
383 part after EKey, e.g., "0", "Star", etc.). Keyname can be "", in
384 which case the item has no shortcut.
386 The method name refers to a method on the selected widget, or
389 Example: { 'search_item' : ('0', 'Search item') }
391 self.binding_map = binding_map
393 class SearchableListView(WidgetBasedListView):
395 self.current_entry_filter_index = -1
396 self.entry_filters = []
397 self.filtered_list = lambda:[]
399 super(SearchableListView,self).__init__()
401 def set_filters(self, entry_filters):
403 Set the filters that could be applied to this list. Each filter
404 can be applied in turn by calling switch_entry_filter (for
405 example from a key binding).
407 The entry_filters argument should be a list of filters. The
408 active filter is stored into self.filtered_list and should be
409 processed by generate_widgets in the subclass.
411 self.current_entry_filter_index = 0
412 self.entry_filters = entry_filters
413 self.filtered_list = self.entry_filters[0]
415 def search_item(self):
416 selected_item = appuifw.selection_list(self.all_widget_texts(),search_field=1)
417 if selected_item == None or selected_item == -1:
418 selected_item = self.selected_index()
419 self.view.set_list(self.items(),selected_item)
420 self.set_bindings_for_selection(selected_item)
422 def switch_entry_filter(self):
423 self.current_entry_filter_index += 1
424 self.filtered_list = self.entry_filters[self.current_entry_filter_index % len(self.entry_filters)]
427 #class DisplayableFunction:
428 # def __init__(self,display_name,function):
429 # self.display_name = display_name
430 # self.function = function
431 # def list_repr(self):
432 # return self.display_name
437 __all__= ('SearchableListView','show_config')