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)
176 return '[%s]' % short if short else ' '
178 def do_entry((text, callback, key_name)):
179 key = get_key(key_name)
181 self.view.bind(key, callback)
182 self.menu_keys.append(key)
183 title = "%s %s" % (shortcut_prefix(key_name), text)
184 return(title, callback)
186 # Clear the bindings we previously added (we can't just clear
187 # all bindings, since other classes might have added other
189 for key in self.menu_keys:
190 self.view.bind(key, no_action)
193 # Set the menu, and let do_entry add binds at the same time.
194 appuifw.app.menu = [do_entry(item) for item in self.menu_items()]
196 def menu_items(self):
198 Should return a list of menu items. Each menu item is a tuple:
199 (text, callback, shortcut key name).
201 return [(u'Exit', self.exit, None)]
203 class ListView(View):
205 super(ListView, self).__init__()
206 self.current_index = None
207 self.items_cache = []
208 self.set_view(appuifw.Listbox([], self.entry_selected))
209 self.view.bind(EKeyUpArrow,lambda: self.arrow_key_pressed(-1))
210 self.view.bind(EKeyDownArrow,lambda: self.arrow_key_pressed(1))
212 def arrow_key_pressed(self, dir):
214 This function is called when an arrow key is pressed. Since we
215 don't get any "current list index has changed" events, we'll
216 have to create these ourselves this way.
218 Since the current index is only updated after the key event,
219 we'll have to adjust the index with the direction of the
220 keypress (-1 for up, +1 for down).
222 self.current_index = self.wrap_index(self.selected_index() + dir)
224 self.current_index = None
228 super(ListView, self).refresh()
230 def refresh_list(self):
231 """ Reload the list items. Calls items() again. """
232 # Remember which item was selected
233 selected = self.selected_item()
235 self.items_cache = self.items()
237 # Try to find the selected item in the new list (based on
239 selected_index = self.items_cache.index(selected)
241 # If the selected item is no longer present, just keep the
242 # index the same (but be careful not to fall off the end).
243 selected_index = self.clip_index(self.selected_index())
244 # Update the items in the view
245 self.view.set_list(self.items_cache, selected_index)
249 super(ListView, self).run()
251 def entry_selected(self):
253 This function is called when the user selects an an entry (e.g.,
254 navigates to it and push the ok button).
258 def index_changed(self):
260 This function is called when the index changes. The given index
261 is the new index (don't use self.selected_index() here, since it
262 won't be correct yet!).
267 """ This function should return the list of items to display.
268 See appuifw.ListBox for valid elements for this list. """
271 def set_index(self,index):
272 """ Changes the currently selected item to index. """
273 self.view.set_list(self.items_cache, self.clip_index(index))
275 def selected_item(self):
276 """ Returns the (title of the) currently selected list item. """
277 if not self.items_cache:
278 return None # No items, so none is selected.
279 return self.items_cache[self.selected_index()]
282 def selected_index(self):
283 """ Returns the currently selected index. """
284 if not self.current_index is None:
285 # We allow the current index to be overridden, so it can be
286 # valid during index_changed events. See arrow_key_pressed.
287 return self.current_index
289 return self.view.current()
291 def clip_index(self, index):
293 Make sure the given index fits within the bounds of this
294 list. If it doesn't, clip it off at the ends of the list (e.g,
297 max_index = len(self.items_cache) - 1
298 return max (0, min(max_index, index))
300 def wrap_index(self, index):
302 Make sure the given index fits within the bounds of this
303 list. If it doesn't, wrap it around (e.g., -1 becomes 5 in a
306 count = len(self.items_cache)
309 class WidgetBasedListView(ListView):
311 self.binding_map = {}
312 self.widgets = self.generate_widgets()
313 super(WidgetBasedListView,self).__init__()
315 def index_changed(self):
317 super(WidgetBasedListView, self).index_changed()
319 def entry_selected(self):
320 self.current_widget().change()
323 def notify(self,object,attribute,new=None,old=None):
327 self.refresh_widgets()
328 super(WidgetBasedListView,self).refresh()
330 def refresh_widgets(self):
331 """ Refresh the widget list. Calls generate_widgets(). """
332 self.widgets = self.generate_widgets()
334 def redisplay_widgets(self):
336 Redisplay the widgets. Should be called if the widgets
337 themselves have changed, does not call generate_widgets again.
342 # Let ListView show each widget's text.
343 return self.all_widget_texts()
345 def all_widget_texts(self):
347 Return the widget texts as they should be displayed in the
350 return [entry.list_repr() for entry in self.widgets]
352 def current_widget(self):
353 """ Returns the currently selected widget. """
354 return self.widgets[self.selected_index()]
356 def generate_widgets():
357 """ This function should return a list of widgets. """
360 def menu_items(self):
361 # Determine the current menu based on the methods available on
362 # the selected widget and on ourselves.
364 for function in applicable_functions(self.current_widget(),self.binding_map)+\
365 applicable_functions(self,self.binding_map):
366 (key,description) = self.binding_map[function.__name__]
370 menu_items.append((description, do_callback, key))
371 menu_items += super(WidgetBasedListView, self).menu_items()
374 def set_menu(self, binding_map):
376 Set a new map of menu entries with hotkeys. This map maps method names to a
377 tuple of keyname and description.
379 Keyname is a string containing the name of the key (the
380 part after EKey, e.g., "0", "Star", etc.). Keyname can be "", in
381 which case the item has no shortcut.
383 The method name refers to a method on the selected widget, or
386 Example: { 'search_item' : ('0', 'Search item') }
388 self.binding_map = binding_map
390 class SearchableListView(WidgetBasedListView):
392 self.current_entry_filter_index = -1
393 self.entry_filters = []
394 self.filtered_list = lambda:[]
396 super(SearchableListView,self).__init__()
398 def set_filters(self, entry_filters):
400 Set the filters that could be applied to this list. Each filter
401 can be applied in turn by calling switch_entry_filter (for
402 example from a key binding).
404 The entry_filters argument should be a list of filters. The
405 active filter is stored into self.filtered_list and should be
406 processed by generate_widgets in the subclass.
408 self.current_entry_filter_index = 0
409 self.entry_filters = entry_filters
410 self.filtered_list = self.entry_filters[0]
412 def search_item(self):
413 selected_item = appuifw.selection_list(self.all_widget_texts(),search_field=1)
414 if selected_item == None or selected_item == -1:
415 selected_item = self.selected_index()
416 self.view.set_list(self.items(),selected_item)
417 self.set_bindings_for_selection(selected_item)
419 def switch_entry_filter(self):
420 self.current_entry_filter_index += 1
421 self.filtered_list = self.entry_filters[self.current_entry_filter_index % len(self.entry_filters)]
424 #class DisplayableFunction:
425 # def __init__(self,display_name,function):
426 # self.display_name = display_name
427 # self.function = function
428 # def list_repr(self):
429 # return self.display_name
434 __all__= ('SearchableListView','show_config')