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
141 while not self.exit_flag:
145 # TODO: Find out which exceptions to catch here. Catching
146 # and silencing all exceptions is not a good idea.
151 self.exit_flag = True
154 def update(self,subject=None):
156 Update the current view (e.g., make sure refresh is called). We
157 can't call it directly, since we're in another thread.
164 Called when the current view must be updated. Never call
165 directly. Subclasses should extend this method, not update.
169 def refresh_menu(self):
171 Refresh the menu and its bindings. Calls self.menu_items() to
174 # Two helper functions
175 def shortcut_prefix(key_name):
176 short = key_shortname(key_name)
177 return '[%s]' % short if short else ' '
179 def do_entry((text, callback, key_name)):
180 key = get_key(key_name)
182 self.view.bind(key, callback)
183 self.menu_keys.append(key)
184 title = "%s %s" % (shortcut_prefix(key_name), text)
185 return(title, callback)
187 # Clear the bindings we previously added (we can't just clear
188 # all bindings, since other classes might have added other
190 for key in self.menu_keys:
191 self.view.bind(key, no_action)
194 # Set the menu, and let do_entry add binds at the same time.
195 appuifw.app.menu = [do_entry(item) for item in self.menu_items()]
197 def menu_items(self):
199 Should return a list of menu items. Each menu item is a tuple:
200 (text, callback, shortcut key name).
202 return [(u'Exit', self.exit, None)]
204 class ListView(View):
206 super(ListView, self).__init__()
207 self.current_index = None
208 self.set_view(appuifw.Listbox(self.items(),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).run()
230 def entry_selected(self):
232 This function is called when the user selects an an entry (e.g.,
233 navigates to it and push the ok button).
237 def index_changed(self):
239 This function is called when the index changes. The given index
240 is the new index (don't use self.selected_index() here, since it
241 won't be correct yet!).
246 """ This function should return the list of items to display.
247 See appuifw.ListBox for valid elements for this list. """
250 def set_index(self,index):
251 """ Changes the currently selected item to index. """
252 self.view.set_list(self.items(),index % len(self.items()))
254 def selected_item(self):
255 """ Returns the (title of the) currently selected list item. """
256 if not self.items_cache:
257 return None # No items, so none is selected.
258 return self.items_cache[self.selected_index()]
261 def selected_index(self):
262 """ Returns the currently selected index. """
263 if not self.current_index is None:
264 # We allow the current index to be overridden, so it can be
265 # valid during index_changed events. See arrow_key_pressed.
266 return self.current_index
268 return self.view.current()
270 def clip_index(self, index):
272 Make sure the given index fits within the bounds of this
273 list. If it doesn't, clip it off at the ends of the list (e.g,
276 max_index = len(self.items_cache) - 1
277 return max (0, min(max_index, index))
279 def wrap_index(self, index):
281 Make sure the given index fits within the bounds of this
282 list. If it doesn't, wrap it around (e.g., -1 becomes 5 in a
285 count = len(self.items_cache)
288 class WidgetBasedListView(ListView):
290 self.binding_map = {}
291 self.widgets = self.generate_widgets()
292 super(WidgetBasedListView,self).__init__()
294 def index_changed(self):
296 super(WidgetBasedListView, self).index_changed()
298 def entry_selected(self):
299 self.current_widget().change()
302 def notify(self,object,attribute,new=None,old=None):
306 self.widgets = self.generate_widgets()
307 self.redisplay_widgets()
308 super(WidgetBasedListView,self).refresh()
310 def redisplay_widgets(self):
312 Redisplay the widgets. Should be called if the widgets
313 themselves have changed, does not call generate_widgets again.
315 self.set_index(self.selected_index())
318 # Let ListView show each widget's text.
319 return self.all_widget_texts()
321 def all_widget_texts(self):
323 Return the widget texts as they should be displayed in the
326 return [entry.list_repr() for entry in self.widgets]
328 def current_widget(self):
329 """ Returns the currently selected widget. """
330 return self.widgets[self.selected_index()]
332 def generate_widgets():
333 """ This function should return a list of widgets. """
336 def menu_items(self):
337 # Determine the current menu based on the methods available on
338 # the selected widget and on ourselves.
340 for function in applicable_functions(self.current_widget(),self.binding_map)+\
341 applicable_functions(self,self.binding_map):
342 (key,description) = self.binding_map[function.__name__]
346 menu_items.append((description, do_callback, key))
347 menu_items += super(WidgetBasedListView, self).menu_items()
350 def set_menu(self, binding_map):
352 Set a new map of menu entries with hotkeys. This map maps method names to a
353 tuple of keyname and description.
355 Keyname is a string containing the name of the key (the
356 part after EKey, e.g., "0", "Star", etc.). Keyname can be "", in
357 which case the item has no shortcut.
359 The method name refers to a method on the selected widget, or
362 Example: { 'search_item' : ('0', 'Search item') }
364 self.binding_map = binding_map
366 class SearchableListView(WidgetBasedListView):
368 self.current_entry_filter_index = -1
369 self.entry_filters = []
370 self.filtered_list = lambda:[]
372 super(SearchableListView,self).__init__()
374 def set_filters(self, entry_filters):
376 Set the filters that could be applied to this list. Each filter
377 can be applied in turn by calling switch_entry_filter (for
378 example from a key binding).
380 The entry_filters argument should be a list of filters. The
381 active filter is stored into self.filtered_list and should be
382 processed by generate_widgets in the subclass.
384 self.current_entry_filter_index = 0
385 self.entry_filters = entry_filters
386 self.filtered_list = self.entry_filters[0]
388 def search_item(self):
389 selected_item = appuifw.selection_list(self.all_widget_texts(),search_field=1)
390 if selected_item == None or selected_item == -1:
391 selected_item = self.selected_index()
392 self.view.set_list(self.items(),selected_item)
393 self.set_bindings_for_selection(selected_item)
395 def switch_entry_filter(self):
396 self.current_entry_filter_index += 1
397 self.filtered_list = self.entry_filters[self.current_entry_filter_index % len(self.entry_filters)]
400 #class DisplayableFunction:
401 # def __init__(self,display_name,function):
402 # self.display_name = display_name
403 # self.function = function
404 # def list_repr(self):
405 # return self.display_name
410 __all__= ('SearchableListView','show_config')