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:
149 self.exit_flag = True
152 def update(self,subject=None):
154 Update the current view (e.g., make sure refresh is called). We
155 can't call it directly, since we're in another thread.
162 Called when the current view must be updated. Never call
163 directly. Subclasses should extend this method, not update.
167 def refresh_menu(self):
169 Refresh the menu and its bindings. Calls self.menu_items() to
172 # Two helper functions
173 def shortcut_prefix(key_name):
174 short = key_shortname(key_name)
175 return '[%s]' % short if short else ' '
177 def do_entry((text, callback, key_name)):
178 key = get_key(key_name)
180 self.view.bind(key, callback)
181 self.menu_keys.append(key)
182 title = "%s %s" % (shortcut_prefix(key_name), text)
183 return(title, callback)
185 # Clear the bindings we previously added (we can't just clear
186 # all bindings, since other classes might have added other
188 for key in self.menu_keys:
189 self.view.bind(key, no_action)
192 # Set the menu, and let do_entry add binds at the same time.
193 appuifw.app.menu = [do_entry(item) for item in self.menu_items()]
195 def menu_items(self):
197 Should return a list of menu items. Each menu item is a tuple:
198 (text, callback, shortcut key name).
200 return [(u'Exit', self.exit, None)]
202 class ListView(View):
204 super(ListView, self).__init__()
205 self.current_index = None
206 self.set_view(appuifw.Listbox(self.items(),self.entry_selected))
207 self.view.bind(EKeyUpArrow,lambda: self.arrow_key_pressed(-1))
208 self.view.bind(EKeyDownArrow,lambda: self.arrow_key_pressed(1))
210 def arrow_key_pressed(self, dir):
212 This function is called when an arrow key is pressed. Since we
213 don't get any "current list index has changed" events, we'll
214 have to create these ourselves this way.
216 Since the current index is only updated after the key event,
217 we'll have to adjust the index with the direction of the
218 keypress (-1 for up, +1 for down).
220 self.current_index = self.wrap_index(self.selected_index() + dir)
222 self.current_index = None
226 super(ListView, self).run()
228 def entry_selected(self):
230 This function is called when the user selects an an entry (e.g.,
231 navigates to it and push the ok button).
235 def index_changed(self):
237 This function is called when the index changes. The given index
238 is the new index (don't use self.selected_index() here, since it
239 won't be correct yet!).
244 """ This function should return the list of items to display.
245 See appuifw.ListBox for valid elements for this list. """
248 def set_index(self,index):
249 """ Changes the currently selected item to index. """
250 self.view.set_list(self.items(),index % len(self.items()))
252 def selected_index(self):
253 """ Returns the currently selected index. """
254 if not self.current_index is None:
255 # We allow the current index to be overridden, so it can be
256 # valid during index_changed events. See arrow_key_pressed.
257 return self.current_index
259 return self.view.current()
261 def clip_index(self, index):
263 Make sure the given index fits within the bounds of this
264 list. If it doesn't, clip it off at the ends of the list (e.g,
267 max_index = len(self.items_cache) - 1
268 return max (0, min(max_index, index))
270 def wrap_index(self, index):
272 Make sure the given index fits within the bounds of this
273 list. If it doesn't, wrap it around (e.g., -1 becomes 5 in a
276 count = len(self.items_cache)
279 class WidgetBasedListView(ListView):
281 self.binding_map = {}
282 self.widgets = self.generate_widgets()
283 super(WidgetBasedListView,self).__init__()
285 def index_changed(self):
287 super(WidgetBasedListView, self).index_changed()
289 def entry_selected(self):
290 self.current_widget().change()
293 def notify(self,object,attribute,new=None,old=None):
297 self.widgets = self.generate_widgets()
298 self.redisplay_widgets()
299 super(WidgetBasedListView,self).refresh()
301 def redisplay_widgets(self):
303 Redisplay the widgets. Should be called if the widgets
304 themselves have changed, does not call generate_widgets again.
306 self.set_index(self.selected_index())
309 # Let ListView show each widget's text.
310 return self.all_widget_texts()
312 def all_widget_texts(self):
314 Return the widget texts as they should be displayed in the
317 return [entry.list_repr() for entry in self.widgets]
319 def current_widget(self):
320 """ Returns the currently selected widget. """
321 return self.widgets[self.selected_index()]
323 def generate_widgets():
324 """ This function should return a list of widgets. """
327 def menu_items(self):
328 # Determine the current menu based on the methods available on
329 # the selected widget and on ourselves.
331 for function in applicable_functions(self.current_widget(),self.binding_map)+\
332 applicable_functions(self,self.binding_map):
333 (key,description) = self.binding_map[function.__name__]
337 menu_items.append((description, do_callback, key))
338 menu_items += super(WidgetBasedListView, self).menu_items()
341 def set_menu(self, binding_map):
343 Set a new map of menu entries with hotkeys. This map maps method names to a
344 tuple of keyname and description.
346 Keyname is a string containing the name of the key (the
347 part after EKey, e.g., "0", "Star", etc.). Keyname can be "", in
348 which case the item has no shortcut.
350 The method name refers to a method on the selected widget, or
353 Example: { 'search_item' : ('0', 'Search item') }
355 self.binding_map = binding_map
357 class SearchableListView(WidgetBasedListView):
359 self.current_entry_filter_index = -1
360 self.entry_filters = []
361 self.filtered_list = lambda:[]
363 super(SearchableListView,self).__init__()
365 def set_filters(self, entry_filters):
367 Set the filters that could be applied to this list. Each filter
368 can be applied in turn by calling switch_entry_filter (for
369 example from a key binding).
371 The entry_filters argument should be a list of filters. The
372 active filter is stored into self.filtered_list and should be
373 processed by generate_widgets in the subclass.
375 self.current_entry_filter_index = 0
376 self.entry_filters = entry_filters
377 self.filtered_list = self.entry_filters[0]
379 def search_item(self):
380 selected_item = appuifw.selection_list(self.all_widget_texts(),search_field=1)
381 if selected_item == None or selected_item == -1:
382 selected_item = self.selected_index()
383 self.view.set_list(self.items(),selected_item)
384 self.set_bindings_for_selection(selected_item)
386 def switch_entry_filter(self):
387 self.current_entry_filter_index += 1
388 self.filtered_list = self.entry_filters[self.current_entry_filter_index % len(self.entry_filters)]
391 #class DisplayableFunction:
392 # def __init__(self,display_name,function):
393 # self.display_name = display_name
394 # self.function = function
395 # def list_repr(self):
396 # return self.display_name
401 __all__= ('SearchableListView','show_config')