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_index(self):
255 """ Returns the currently selected index. """
256 if not self.current_index is None:
257 # We allow the current index to be overridden, so it can be
258 # valid during index_changed events. See arrow_key_pressed.
259 return self.current_index
261 return self.view.current()
263 def clip_index(self, index):
265 Make sure the given index fits within the bounds of this
266 list. If it doesn't, clip it off at the ends of the list (e.g,
269 max_index = len(self.items_cache) - 1
270 return max (0, min(max_index, index))
272 def wrap_index(self, index):
274 Make sure the given index fits within the bounds of this
275 list. If it doesn't, wrap it around (e.g., -1 becomes 5 in a
278 count = len(self.items_cache)
281 class WidgetBasedListView(ListView):
283 self.binding_map = {}
284 self.widgets = self.generate_widgets()
285 super(WidgetBasedListView,self).__init__()
287 def index_changed(self):
289 super(WidgetBasedListView, self).index_changed()
291 def entry_selected(self):
292 self.current_widget().change()
295 def notify(self,object,attribute,new=None,old=None):
299 self.widgets = self.generate_widgets()
300 self.redisplay_widgets()
301 super(WidgetBasedListView,self).refresh()
303 def redisplay_widgets(self):
305 Redisplay the widgets. Should be called if the widgets
306 themselves have changed, does not call generate_widgets again.
308 self.set_index(self.selected_index())
311 # Let ListView show each widget's text.
312 return self.all_widget_texts()
314 def all_widget_texts(self):
316 Return the widget texts as they should be displayed in the
319 return [entry.list_repr() for entry in self.widgets]
321 def current_widget(self):
322 """ Returns the currently selected widget. """
323 return self.widgets[self.selected_index()]
325 def generate_widgets():
326 """ This function should return a list of widgets. """
329 def menu_items(self):
330 # Determine the current menu based on the methods available on
331 # the selected widget and on ourselves.
333 for function in applicable_functions(self.current_widget(),self.binding_map)+\
334 applicable_functions(self,self.binding_map):
335 (key,description) = self.binding_map[function.__name__]
339 menu_items.append((description, do_callback, key))
340 menu_items += super(WidgetBasedListView, self).menu_items()
343 def set_menu(self, binding_map):
345 Set a new map of menu entries with hotkeys. This map maps method names to a
346 tuple of keyname and description.
348 Keyname is a string containing the name of the key (the
349 part after EKey, e.g., "0", "Star", etc.). Keyname can be "", in
350 which case the item has no shortcut.
352 The method name refers to a method on the selected widget, or
355 Example: { 'search_item' : ('0', 'Search item') }
357 self.binding_map = binding_map
359 class SearchableListView(WidgetBasedListView):
361 self.current_entry_filter_index = -1
362 self.entry_filters = []
363 self.filtered_list = lambda:[]
365 super(SearchableListView,self).__init__()
367 def set_filters(self, entry_filters):
369 Set the filters that could be applied to this list. Each filter
370 can be applied in turn by calling switch_entry_filter (for
371 example from a key binding).
373 The entry_filters argument should be a list of filters. The
374 active filter is stored into self.filtered_list and should be
375 processed by generate_widgets in the subclass.
377 self.current_entry_filter_index = 0
378 self.entry_filters = entry_filters
379 self.filtered_list = self.entry_filters[0]
381 def search_item(self):
382 selected_item = appuifw.selection_list(self.all_widget_texts(),search_field=1)
383 if selected_item == None or selected_item == -1:
384 selected_item = self.selected_index()
385 self.view.set_list(self.items(),selected_item)
386 self.set_bindings_for_selection(selected_item)
388 def switch_entry_filter(self):
389 self.current_entry_filter_index += 1
390 self.filtered_list = self.entry_filters[self.current_entry_filter_index % len(self.entry_filters)]
393 #class DisplayableFunction:
394 # def __init__(self,display_name,function):
395 # self.display_name = display_name
396 # self.function = function
397 # def list_repr(self):
398 # return self.display_name
403 __all__= ('SearchableListView','show_config')