def back_one_menu(self, arg=None, menuw=None): if len(self.menustack) > 1: try: count = -self.menustack[-1].back_one_menu except: count = -1 self.menustack = self.menustack[:count] menu = self.menustack[-1] if not isinstance(menu, Menu): menu.refresh() return True if skin.get_display_style(menu) != menu.display_style: self.rebuild_page() if menu.reload_func: reload = menu.reload_func() if reload: self.menustack[-1] = reload self.init_page() else: self.init_page() if arg == 'reload': self.refresh(reload=1) else: self.refresh()
def back_one_menu(self, arg=None, menuw=None): if len(self.menustack) > 1: try: count = -self.menustack[-1].back_one_menu except: count = -1 self.menustack = self.menustack[:count] menu = self.menustack[-1] self.set_event_context() if not isinstance(menu, Menu): menu.refresh() return True if skin.get_display_style(menu) != menu.display_style: self.rebuild_page() if menu.reload_func: reload = menu.reload_func() if reload: self.menustack[-1] = reload self.init_page() else: self.init_page() self.screen_transition = skin.TRANSITION_OUT if arg == 'reload': self.refresh(reload=1) else: self.refresh()
def __init__(self, heading, choices, fxd_file=None, umount_all=0, reload_func=None, item_types=None, force_skin_layout=-1): self.heading = heading self.choices = choices # List of MenuItems if len(self.choices): self.selected = self.choices[0] else: self.selected = None self.page_start = 0 self.previous_page_start = [] self.previous_page_start.append(0) self.umount_all = umount_all # umount all ROM drives on display? self.skin_settings = None if fxd_file: self.skin_settings = skin.load(fxd_file) # special items for the new skin to use in the view or info # area. If None, menu.selected will be taken self.infoitem = None self.viewitem = None # Called when a child menu returns. This function returns a new menu # or None and the old menu will be reused self.reload_func = reload_func self.item_types = item_types self.force_skin_layout = force_skin_layout self.display_style = skin.get_display_style(self) # How many menus to go back when 'BACK_ONE_MENU' is called self.back_one_menu = 1
def rebuild_page(self): menu = self.menustack[-1] if not menu: return # recalc everything! current = menu.selected pos = menu.choices.index(current) menu.previous_page_start = [] menu.previous_page_start.append(0) menu.page_start = 0 rows, cols = menu.items_per_page() items_per_page = rows * cols while pos >= menu.page_start + items_per_page: self.goto_next_page(arg='no_refresh') menu.selected = current self.init_page() menu.display_style = skin.get_display_style(menu)
class MenuWidget(GUIObject): """ The MenuWidget handles a stack of Menus """ def __init__(self): GUIObject.__init__(self) self.menustack = [] self.rows = 0 self.cols = 0 self.visible = 1 self.eventhandler_plugins = None self.event_context = 'menu' self.show_callbacks = [] self.force_page_rebuild = False self.screen_transition = skin.TRANSITION_NONE def __str__(self): """ return the class as string """ s = '%s' % (self.label, ) #s += ', rect=%s' % (self.rect,) return s def __repr__(self): if self.label: return '<%-.16s: %r>' % (self.label, self.__class__) return '%r' % (self.__class__) def set_event_context(self): """ Set the event context """ context = self.event_context if self.menustack and hasattr(self.menustack[-1], 'event_context'): context = self.menustack[-1].event_context rc.set_app_context(self, context) def show(self): if not self.visible: self.visible = 1 self.refresh(reload=1) for callback in copy.copy(self.show_callbacks): callback() self.set_event_context() def hide(self, clear=True): if self.visible: self.visible = 0 if clear: skin.clear(osd_update=clear) def delete_menu(self, arg=None, menuw=None, allow_reload=True): if len(self.menustack) > 1: self.menustack = self.menustack[:-1] menu = self.menustack[-1] self.set_event_context() if not isinstance(menu, Menu): return True if menu.reload_func and allow_reload: reload = menu.reload_func() if reload: self.menustack[-1] = reload self.init_page() self.screen_transition = skin.TRANSITION_OUT def delete_submenu(self, refresh=True, reload=False, osd_message=''): """ Delete the last menu if it is a submenu. Also refresh or reload the new menu if the attributes are set to True. If osd_message is set, this message will be send if the current menu is no submenu """ if len(self.menustack) > 1 and hasattr(self.menustack[-1], 'is_submenu') and \ self.menustack[-1].is_submenu: if refresh and reload: self.back_one_menu(arg='reload') elif refresh: self.back_one_menu() else: self.delete_menu() elif len(self.menustack) > 1 and osd_message: rc.post_event(Event(OSD_MESSAGE, arg=osd_message)) def back_one_menu(self, arg=None, menuw=None): if len(self.menustack) > 1: try: count = -self.menustack[-1].back_one_menu except: count = -1 self.menustack = self.menustack[:count] menu = self.menustack[-1] self.set_event_context() if not isinstance(menu, Menu): menu.refresh() return True if skin.get_display_style(menu) != menu.display_style: self.rebuild_page() if menu.reload_func: reload = menu.reload_func() if reload: self.menustack[-1] = reload self.init_page() else: self.init_page() self.screen_transition = skin.TRANSITION_OUT if arg == 'reload': self.refresh(reload=1) else: self.refresh() def goto_main_menu(self, arg=None, menuw=None): self.menustack = [self.menustack[0]] self.set_event_context() self.init_page() self.refresh() def goto_media_menu(self, media='audio'): """ Go to a main menu item media = 'tv' or 'audio' or 'video' or 'image' or 'games' used for events: - MENU_GOTO_TVMENU - MENU_GOTO_TVGUIDEMENU #doesn't yet work - MENU_GOTO_VIDEOMENU - MENU_GOTO_AUDIOMENU - MENU_GOTO_IMAGEMENU - MENU_GOTO_GAMESMENU - MENU_GOTO_RADIOMENU - MENU_GOTO_SHUTDOWN """ self.menustack = [self.menustack[0]] menu = self.menustack[0] self.set_event_context() self.init_page() if media == 'shutdown': for menuitem in self.menustack[0].choices: if self.all_items.index(menuitem) >= self.rows - 1: self.goto_next_page() if string.find(str(menuitem), 'shutdown.') > 0: menu.selected = menuitem self.eventhandler(MENU_SELECT) return elif media == 'tv.guide': menu.selected = self.all_items[len(self.menustack[0].choices) - 1] for menuitem in self.menustack[0].choices: try: if menuitem.arg == ('tv', 0): menu.selected = menuitem self.refresh() self.eventhandler(MENU_SELECT) self.refresh() self.eventhandler(MENU_SELECT) return except: return level = 0 for mediaitem in media.split('.'): for menuitem in self.menustack[level].choices: try: if menuitem.arg[0] == mediaitem: if config.OSD_SOUNDS: try: key = "menu." + media if config.OSD_SOUNDS[key]: sounds.play_sound(sounds.load_sound(key)) except KeyError: pass osd.busyicon.wait(config.OSD_BUSYICON_TIMER[0]) menuitem.select(menuw=self) osd.busyicon.stop() break except AttributeError: # may have no .arg (no media menu) pass except TypeError: # .arg may be not indexable pass level += 1 def goto_prev_page(self, arg=None, menuw=None): menu = self.menustack[-1] if self.cols == 1: if menu.page_start != 0: menu.page_start = menu.previous_page_start.pop() self.init_page() menu.selected = self.all_items[0] else: if menu.page_start - self.cols >= 0: menu.page_start -= self.cols self.init_page() if arg != 'no_refresh': self.refresh() def goto_next_page(self, arg=None, menuw=None): menu = self.menustack[-1] self.rows, self.cols = menu.items_per_page() items_per_page = self.rows * self.cols if self.cols == 1: down_items = items_per_page - 1 if menu.page_start + down_items < len(menu.choices): menu.previous_page_start.append(menu.page_start) menu.page_start += down_items self.init_page() menu.selected = self.menu_items[-1] else: if menu.page_start + self.cols * self.rows < len(menu.choices): if self.rows == 1: menu.page_start += self.cols else: menu.page_start += self.cols * (self.rows - 1) self.init_page() if arg != 'no_refresh': self.refresh() def pushmenu(self, menu): self.menustack.append(menu) self.set_event_context() if isinstance(menu, Menu): menu.page_start = 0 self.init_page() menu.selected = self.all_items[0] self.screen_transition = skin.TRANSITION_IN self.refresh() else: menu.refresh() def refresh(self, reload=0): menu = self.menustack[-1] if not isinstance(menu, Menu): # Do not draw if there are any children if self.children: return False return menu.refresh() # We need to unmount devices but only at the top menu if len(self.menustack) == 1: if self.menustack[-1].umount_all == 1: util.umount_all() if reload: if menu.reload_func: reload = menu.reload_func() if reload: self.menustack[-1] = reload if self.force_page_rebuild: self.force_page_rebuild = False self.rebuild_page() self.init_page() skin.draw('menu', self, self.menustack[-1], self.screen_transition) self.screen_transition = skin.TRANSITION_NONE def make_submenu(self, menu_name, actions, item): #print 'make_submenu(menu_name=%r, actions=%r, item=%r)' % (menu_name, actions, item) items = [] for a in actions: if isinstance(a, Item): items.append(a) else: items.append(MenuItem(a[1], a[0])) fxd_file = None if item.skin_fxd: fxd_file = item.skin_fxd for i in items: if not hasattr(item, 'is_mainmenu_item'): i.image = item.image if hasattr(item, 'display_type'): i.display_type = item.display_type elif hasattr(item, 'type'): i.display_type = item.type s = Menu(menu_name, items, fxd_file=fxd_file) s.is_submenu = True self.pushmenu(s) def _handle_up(self, menu, event): curr_selected = self.all_items.index(menu.selected) sounds.play_sound(sounds.MENU_NAVIGATE) if curr_selected-self.cols < 0 and \ menu.selected != menu.choices[0]: self.goto_prev_page(arg='no_refresh') try: if self.cols == 1: curr_selected = self.rows - 1 elif self.rows != 1: curr_selected = self.all_items.index(menu.selected) else: curr_selected += self.cols except ValueError: curr_selected += self.cols curr_selected = max(curr_selected - self.cols, 0) menu.selected = self.all_items[curr_selected] self.refresh() def _handle_down(self, menu, event): curr_selected = self.all_items.index(menu.selected) sounds.play_sound(sounds.MENU_NAVIGATE) if curr_selected+self.cols > len(self.all_items)-1 and \ menu.page_start + len(self.all_items) < len(menu.choices): self.goto_next_page(arg='no_refresh') try: if self.cols == 1: curr_selected = 0 elif self.rows != 1: curr_selected = self.all_items.index(menu.selected) else: curr_selected -= self.cols except ValueError: curr_selected -= self.cols curr_selected = min(curr_selected + self.cols, len(self.all_items) - 1) menu.selected = self.all_items[curr_selected] self.refresh() def _handle_pageup(self, menu, event): # Do nothing for an empty file list if not len(self.menu_items): return curr_selected = self.all_items.index(menu.selected) # Move to the previous page if the current position is at the # top of the list, otherwise move to the top of the list. if curr_selected == 0: self.goto_prev_page() else: curr_selected = 0 menu.selected = self.all_items[curr_selected] self.refresh() return def _handle_pagedown(self, menu, event): # Do nothing for an empty file list if not len(self.menu_items): return if menu.selected == menu.choices[-1]: return curr_selected = self.all_items.index(menu.selected) bottom_index = self.menu_items.index(self.menu_items[-1]) # Move to the next page if the current position is at the # bottom of the list, otherwise move to the bottom of the list. if curr_selected >= bottom_index: self.goto_next_page() else: curr_selected = bottom_index menu.selected = self.all_items[curr_selected] self.refresh() return def _handle_left(self, menu, event): # Do nothing for an empty file list if not len(self.menu_items): return sounds.play_sound(sounds.MENU_NAVIGATE) curr_selected = self.all_items.index(menu.selected) if curr_selected == 0: self.goto_prev_page(arg='no_refresh') try: curr_selected = self.all_items.index(menu.selected) if self.rows == 1: curr_selected = len(self.all_items) except ValueError: curr_selected += self.cols curr_selected = max(curr_selected - 1, 0) menu.selected = self.all_items[curr_selected] self.refresh() return def _handle_right(self, menu, event): # Do nothing for an empty file list if not len(self.menu_items): return sounds.play_sound(sounds.MENU_NAVIGATE) curr_selected = self.all_items.index(menu.selected) if curr_selected == len(self.all_items) - 1: self.goto_next_page(arg='no_refresh') try: curr_selected = self.all_items.index(menu.selected) if self.rows == 1: curr_selected -= 1 except ValueError: curr_selected -= self.cols curr_selected = min(curr_selected + 1, len(self.all_items) - 1) menu.selected = self.all_items[curr_selected] self.refresh() return def _handle_play_item(self, menu, event): action = None arg = None sounds.play_sound(sounds.MENU_SELECT) try: action = menu.selected.action except AttributeError: actions = menu.selected.actions() or [] # Add the actions of the plugins to the list of actions. This is needed when a # Item class has no actions but plugins provides them. This case happens with an # empty disc. # # FIXME The event MENU_SELECT is called when selecting a submenu entry too. The # item passed to the plugin is then the submenu entry instead its parent item. So # if we are in a submenu we don't want to call the actions of the plugins. # because we'll break some (or all) plugins behavior. Does that sound correct? if config.OSD_SOUNDS: if hasattr(menu.selected, 'arg'): try: key = "menu." + menu.selected.arg[0] if config.OSD_SOUNDS[key]: sounds.play_sound(sounds.load_sound(key)) except: pass else: try: key = "menu." + menu.selected.__class__.__name__ if config.OSD_SOUNDS[key]: sounds.play_sound(sounds.load_sound(key)) except: pass if not hasattr(menu, 'is_submenu'): plugins = plugin.get('item') + plugin.get( 'item_%s' % menu.selected.type) if hasattr(menu.selected, 'display_type'): plugins += plugin.get('item_%s' % menu.selected.display_type) plugins.sort(lambda l, o: cmp(l._level, o._level)) for p in plugins: for a in p.actions(menu.selected): if isinstance(a, MenuItem): actions.append(a) else: actions.append(a[:2]) if actions: action = actions[0] if isinstance(action, MenuItem): action = action.function arg = action.arg else: action = action[0] if not action: AlertBox(text=_('No action defined for this choice!')).show() return action(arg=arg, menuw=self) def _handle_submenu(self, menu, event): action = None arg = None #try: # action = menu.selected.action #except AttributeError: # pass actions = menu.selected.actions() force = False if not actions: actions = [] force = True if not hasattr(menu, 'is_submenu'): plugins = plugin.get('item') + plugin.get( 'item_%s' % menu.selected.type) if hasattr(menu.selected, 'display_type'): plugins += plugin.get('item_%s' % menu.selected.display_type) plugins.sort(lambda l, o: cmp(l._level, o._level)) for p in plugins: for a in p.actions(menu.selected): if isinstance(a, MenuItem): actions.append(a) else: actions.append(a[:2]) if len(a) == 3 and a[2] == 'MENU_SUBMENU': a[0](menuw=self) return if actions: if len(actions) > 1 or force: self.make_submenu(menu.selected.name, actions, menu.selected) elif len(actions) == 1: # if there is only one action, call it! action = actions[0] if isinstance(action, MenuItem): action = action.function arg = action.arg else: action = action[0] action(arg=arg, menuw=self) def _handle_call_item_action(self, menu, event): logger.debug('calling action %s', event.arg) for a in menu.selected.actions(): if not isinstance(a, Item) and len(a) > 2 and a[2] == event.arg: a[0](arg=None, menuw=self) return plugins = plugin.get('item') + plugin.get( 'item_%s' % menu.selected.type) if hasattr(menu.selected, 'display_type'): plugins += plugin.get('item_%s' % menu.selected.display_type) for p in plugins: for a in p.actions(menu.selected): if not isinstance( a, MenuItem) and len(a) > 2 and a[2] == event.arg: a[0](arg=None, menuw=self) return logger.debug('action %s not found', event.arg) def __call_action(self, action, arg): result = action(arg=arg, menuw=self) if isinstance(result, kaa.InProgress): result.wait() def eventhandler(self, event): menu = self.menustack[-1] if self.cols == 1 and isinstance(menu, Menu): if config.MENU_ARROW_NAVIGATION: if event == MENU_LEFT: event = MENU_BACK_ONE_MENU elif event == MENU_RIGHT: event = MENU_SELECT else: if event == MENU_LEFT: event = MENU_PAGEUP elif event == MENU_RIGHT: event = MENU_PAGEDOWN if self.eventhandler_plugins == None: self.eventhandler_plugins = plugin.get('daemon_eventhandler') if event == MENU_GOTO_MAINMENU: self.goto_main_menu() return True if event == MENU_GOTO_TV: self.goto_media_menu("tv") return True if event == MENU_GOTO_TVGUIDE: self.goto_media_menu("tv.guide") return True if event == MENU_GOTO_VIDEOS: self.goto_media_menu("video") return True if event == MENU_GOTO_MUSIC: self.goto_media_menu("audio") return True if event == MENU_GOTO_IMAGES: self.goto_media_menu("image") return True if event == MENU_GOTO_GAMES: self.goto_media_menu("games") return True if event == MENU_GOTO_RADIO: self.goto_media_menu("audio.radio") return True if event == MENU_GOTO_SHUTDOWN: self.goto_media_menu("shutdown") return True if event == MENU_BACK_ONE_MENU or \ (event == MOUSE_BTN_PRESS and event.button == 3): sounds.play_sound(sounds.MENU_BACK_ONE) self.back_one_menu() return True if not isinstance(menu, Menu) and menu.eventhandler(event): return True if event == 'MENU_RELOAD': self.refresh(True) return True if event == 'MENU_REFRESH': self.refresh() return True if event == 'MENU_REBUILD': self.init_page() self.refresh() return True if not self.menu_items: if event in (MENU_SELECT, MENU_SUBMENU, MENU_PLAY_ITEM): self.back_one_menu() return True menu = self.menustack[-2] if hasattr(menu, 'selected') and hasattr( menu.selected, 'eventhandler') and menu.selected.eventhandler: if menu.selected.eventhandler(event=event, menuw=self): return True for p in self.eventhandler_plugins: if p.eventhandler(event=event, menuw=self): return True return False if not isinstance(menu, Menu): if self.eventhandler_plugins == None: self.eventhandler_plugins = plugin.get('daemon_eventhandler') for p in self.eventhandler_plugins: if p.eventhandler(event=event, menuw=self): return True logger.log(9, 'no eventhandler for event %s', event) return False if event == MENU_UP: self._handle_up(menu, event) return True if event == MENU_DOWN: self._handle_down(menu, event) return True if event == MENU_PAGEUP: self._handle_pageup(menu, event) return True if event == MENU_PAGEDOWN: self._handle_pagedown(menu, event) return True if event == MENU_LEFT: self._handle_left(menu, event) return True if event == MENU_RIGHT: self._handle_right(menu, event) return True if event == MENU_PLAY_ITEM and hasattr(menu.selected, 'play'): menu.selected.play(menuw=self) return True if event == MENU_PLAY_ITEM or event == MENU_SELECT: self._handle_play_item(menu, event) return True if event == MENU_SUBMENU: self._handle_submenu(menu, event) return True if event == MENU_CALL_ITEM_ACTION: self._handle_call_item_action(menu, event) return True if event == MENU_CHANGE_STYLE and len(self.menustack) > 1: # did the menu change? if skin.toggle_display_style(menu): self.rebuild_page() self.refresh() return True if event == MOUSE_MOTION: for menuitem in menu.choices: if menuitem.rect.collidepoint(event.pos): self.highlight_menuitem(menuitem) return True if event == MOUSE_BTN_PRESS: # Left click if event.button == 1: for menuitem in menu.choices: if menuitem.rect.collidepoint(event.pos): self.highlight_menuitem(menuitem) self.select_menuitem(menuitem) # Middle click elif event.button == 2: self.submenu_menuitem() # Wheel up elif event.button == 4: self.up_menuitem() # Wheel down elif event.button == 5: self.down_menuitem() return True if hasattr(menu.selected, 'eventhandler') and menu.selected.eventhandler: if menu.selected.eventhandler(event=event, menuw=self): return True for p in self.eventhandler_plugins: if p.eventhandler(event=event, menuw=self): return True logger.log(9, 'no eventhandler for event %s', str(event)) return False def highlight_menuitem(self, clicked_menu): i = 0 menu = self.menustack[-1] logger.debug('clicked_menu=%s, self.all_item=%s', clicked_menu, self.all_items[0]) for menuitem in self.all_items: if clicked_menu == menuitem: sounds.play_sound(sounds.MENU_NAVIGATE) curr_selected = i menu.selected = self.all_items[curr_selected] self.refresh() return i += 1 def select_menuitem(self, clicked_menu): self.eventhandler(MENU_SELECT) def submenu_menuitem(self): self.eventhandler(MENU_SUBMENU) def up_menuitem(self): self.eventhandler(MENU_UP) def down_menuitem(self): self.eventhandler(MENU_DOWN) def rebuild_page(self): menu = self.menustack[-1] if not menu: return # recalc everything! current = menu.selected try: pos = menu.choices.index(current) except ValueError, e: print 'menu.choices.index(current) failed: %s' % (e) menu.previous_page_start = [] menu.previous_page_start.append(0) menu.page_start = 0 rows, cols = menu.items_per_page() items_per_page = rows * cols while pos >= menu.page_start + items_per_page: self.goto_next_page(arg='no_refresh') menu.selected = current self.init_page() menu.display_style = skin.get_display_style(menu)