Ejemplo n.º 1
0
class CdDrivesMenu(Menu):
    """ File browser navigator menu """
    def __init__(self, util, bounding_box, listener):
        """ Initializer
        
        :param util: utility object
        :param bounding_box: bounding box
        :param listeners: buttons listeners
        """
        self.factory = Factory(util)
        m = self.create_cd_drive_menu_button
        label_area = (bounding_box.h / 100) * (100 - ICON_AREA)
        font_size = int((label_area / 100) * FONT_HEIGHT)
        r = Rect(bounding_box.x, bounding_box.y, bounding_box.w,
                 bounding_box.h + 1)
        Menu.__init__(self,
                      util,
                      None,
                      r,
                      None,
                      None,
                      create_item_method=m,
                      font_size=font_size)
        self.name = "cd.drives.menu"
        self.content = bounding_box
        self.content_x = bounding_box.x
        self.content_y = bounding_box.y
        self.menu_buttons = []
        id = str(util.config[CD_PLAYBACK][CD_DRIVE_ID])
        if len(id) == 0:
            id = "0"
        self.select_by_index(int(id))

    def create_cd_drive_menu_button(self, s, constr, action, scale, font_size):
        """ Create CD drive button

        :param s: button state
        :param constr: scaling constraints
        :param action: button event listener
        :param scale: True - scale images, False - don't scale images

        :return: file menu button
        """
        s.image_area_percent = ICON_AREA

        return self.factory.create_menu_button(s,
                                               constr,
                                               action,
                                               scale,
                                               font_size=font_size)

    def add_observers(self, update_observer, redraw_observer):
        """ Add screen observers
        
        :param update_observer: observer for updating the screen
        :param redraw_observer: observer to redraw the whole screen
        """
        for b in self.menu_buttons:
            self.add_button_observers(b, update_observer, redraw_observer)
Ejemplo n.º 2
0
class HomeMenu(Menu):
    """ Home Menu class. Extends base Menu class """
    def __init__(self, util, bgr=None, bounding_box=None, font_size=None):
        """ Initializer

        :param util: utility object
        :param bgr: menu background
        :param bounding_box: bounding box
        """
        self.util = util
        self.factory = Factory(util)
        self.config = util.config
        self.cdutil = CdUtil(util)

        self.bb = bounding_box
        self.horizontal_layout = True
        self.rows = None
        self.cols = None
        items = self.get_menu_items()
        cell_bb = items[2]

        m = self.create_home_menu_button
        label_area = (cell_bb.h / 100) * (100 - ICON_AREA)
        font_size = int((label_area / 100) * FONT_HEIGHT)
        Menu.__init__(self,
                      util,
                      bgr,
                      bounding_box,
                      None,
                      None,
                      create_item_method=m,
                      font_size=font_size)
        self.set_modes(*items)

    def get_menu_items(self):
        """ Prepare menu items

        :return: array containing menu items, disabled items, cell bounding box snf icon box
        """
        items = []
        disabled_items = []
        player = self.config[AUDIO][PLAYER_NAME]

        if self.config[HOME_MENU][RADIO]:
            items.append(RADIO)
            if not self.util.is_radio_enabled(
            ) or not self.util.connected_to_internet:
                disabled_items.append(RADIO)

        if self.config[HOME_MENU][AUDIO_FILES]:
            items.append(AUDIO_FILES)

        if self.config[HOME_MENU][AUDIOBOOKS]:
            items.append(AUDIOBOOKS)
            if not self.util.is_audiobooks_enabled(
            ) or not self.util.connected_to_internet:
                disabled_items.append(AUDIOBOOKS)

        if self.config[HOME_MENU][STREAM]:
            items.append(STREAM)
            if not self.util.connected_to_internet:
                disabled_items.append(STREAM)

        if self.config[HOME_MENU][CD_PLAYER]:
            cd_drives_info = self.cdutil.get_cd_drives_info()
            if len(cd_drives_info) == 0 or player == MPV_NAME:
                disabled_items.append(CD_PLAYER)
            items.append(CD_PLAYER)

        if self.config[HOME_MENU][PODCASTS]:
            podcasts_util = self.util.get_podcasts_util()
            podcasts = podcasts_util.get_podcasts_links()
            downloads = podcasts_util.are_there_any_downloads()
            connected = self.util.connected_to_internet
            valid_players = [VLC_NAME, MPV_NAME]
            if (connected and len(podcasts) == 0 and not downloads) or (
                    not connected
                    and not downloads) or player not in valid_players:
                disabled_items.append(PODCASTS)
            items.append(PODCASTS)

        if self.config[HOME_MENU][AIRPLAY]:
            items.append(AIRPLAY)
            if not self.util.config[LINUX_PLATFORM]:
                disabled_items.append(AIRPLAY)

        if self.config[HOME_MENU][SPOTIFY_CONNECT]:
            items.append(SPOTIFY_CONNECT)
            if not self.util.config[LINUX_PLATFORM]:
                disabled_items.append(SPOTIFY_CONNECT)

        if self.config[HOME_MENU][COLLECTION]:
            items.append(COLLECTION)
            db_util = self.util.get_db_util()
            if db_util.conn == None:
                disabled_items.append(COLLECTION)

        l = self.get_layout(items)
        bounding_box = l.get_next_constraints()
        box = self.factory.get_icon_bounding_box(bounding_box, ICON_LOCATION,
                                                 ICON_AREA, ICON_SIZE,
                                                 BUTTON_PADDING)

        return (items, disabled_items, bounding_box, box)

    def set_current_modes(self):
        """ Set current player modes """

        items = self.get_menu_items()
        self.set_modes(*items)

    def set_modes(self, items, disabled_items, bounding_box, box):
        """ Set menu items

        :param items: menu items
        :param disabled_items: disabled menu items
        :param bounding_box: cell bounding box
        :param box: image boundng box
        """
        self.modes = self.util.load_menu(items,
                                         NAME,
                                         disabled_items,
                                         V_ALIGN_TOP,
                                         bb=box)
        va_commands = self.util.get_voice_commands()

        if self.config[USAGE][USE_VOICE_ASSISTANT]:
            self.add_voice_command(RADIO, ["VA_RADIO", "VA_GO_RADIO"],
                                   va_commands)
            self.add_voice_command(AUDIO_FILES, ["VA_FILES", "VA_GO_FILES"],
                                   va_commands)
            self.add_voice_command(
                AUDIOBOOKS, ["VA_AUDIOBOOKS", "VA_BOOKS", "VA_GO_BOOKS"],
                va_commands)
            self.add_voice_command(STREAM, ["VA_STREAM", "VA_GO_STREAM"],
                                   va_commands)
            self.add_voice_command(CD_PLAYER, ["VA_CD_PLAYER"], va_commands)
            self.add_voice_command(PODCASTS, ["VA_PODCAST", "VA_PODCASTS"],
                                   va_commands)

        if not items:
            return

        if not self.config[CURRENT][MODE]:
            for i in items:
                if i not in disabled_items:
                    mode = i
                    break
        else:
            mode = self.config[CURRENT][MODE]

        self.set_items(self.modes, 0, self.change_mode, False)
        self.current_mode = self.modes[mode.lower()]
        self.item_selected(self.current_mode)

    def create_home_menu_button(self, s, constr, action, scale, font_size):
        """ Create Home Menu button

        :param s: button state
        :param constr: scaling constraints
        :param action: button event listener
        :param scale: True - scale images, False - don't scale images
        :param font_size: label font height in pixels

        :return: home menu button
        """
        s.padding = BUTTON_PADDING
        s.image_area_percent = ICON_AREA

        return self.factory.create_menu_button(s,
                                               constr,
                                               action,
                                               scale,
                                               font_size=font_size)

    def add_voice_command(self, name, commands, va_commands):
        """ Add voice command

        :param name: item name
        :param commands: item commands
        :param va_commands: voice commands
        """
        if not self.config[HOME_MENU][name]:
            return
        c = []
        for m in commands:
            c.append(va_commands[m].strip())
        self.modes[name].voice_commands = c

    def change_mode(self, state):
        """ Change mode event listener

        :param state: button state
        """
        if not self.visible:
            return
        state.previous_mode = self.current_mode.name
        self.current_mode = state
        self.config[CURRENT][MODE] = state.name
        self.notify_listeners(state)
Ejemplo n.º 3
0
class SaverDelayMenu(Menu):
    """ Screensaver Delay Menu class. Extends base Menu class """
    def __init__(self, util, bgr=None, bounding_box=None):
        """ Initializer
        
        :param util: utility object
        :param bgr: menu background
        :param bounding_box: bounding box
        """
        self.factory = Factory(util)
        m = self.create_saver_delay_menu_button
        font_size = int((bounding_box.h / 100) * FONT_HEIGHT)
        Menu.__init__(self,
                      util,
                      bgr,
                      bounding_box,
                      ROWS,
                      COLUMNS,
                      create_item_method=m,
                      font_size=font_size)
        self.config = util.config
        current_delay_name = self.config[SCREENSAVER][DELAY]
        self.delays = util.get_screensaver_delays()

        if self.config[USAGE][USE_VOICE_ASSISTANT]:
            voice_commands = util.get_voice_commands()
            self.delays[KEY_SCREENSAVER_DELAY_1].voice_commands = [
                voice_commands["VA_ONE_MINUTE"].strip()
            ]
            self.delays[KEY_SCREENSAVER_DELAY_3].voice_commands = [
                voice_commands["VA_THREE_MINUTES"].strip()
            ]
            self.delays[KEY_SCREENSAVER_DELAY_OFF].voice_commands = [
                voice_commands["VA_OFF"].strip()
            ]

        self.set_items(self.delays, 0, self.change_delay, False)
        current_delay = self.delays[current_delay_name]
        self.item_selected(current_delay)

    def create_saver_delay_menu_button(self, s, constr, action, scale,
                                       font_size):
        """ Create Screensaver Delay Menu button

        :param s: button state
        :param constr: scaling constraints
        :param action: button event listener
        :param scale: True - scale images, False - don't scale images

        :return: screensaver delay menu button
        """
        s.show_img = False

        return self.factory.create_menu_button(s,
                                               constr,
                                               action,
                                               scale,
                                               font_size=font_size)

    def change_delay(self, state):
        """ Change delay event listener
        
        :param state: button state
        """
        self.config[SCREENSAVER][DELAY] = state.name
        self.notify_listeners(state)
Ejemplo n.º 4
0
class RadioGroupScreen(MenuScreen):
    """ Radio group screen """
    def __init__(self, util, listeners, voice_assistant):
        self.util = util
        self.config = util.config
        self.groups_list = self.util.get_stations_folders()
        self.factory = Factory(util)
        d = [MENU_ROWS, MENU_COLUMNS]
        MenuScreen.__init__(self,
                            util,
                            listeners,
                            MENU_ROWS,
                            MENU_COLUMNS,
                            voice_assistant,
                            d,
                            self.turn_page,
                            page_in_title=False)
        self.total_pages = math.ceil(len(self.groups_list) / PAGE_SIZE)
        self.title = util.get_stations_top_folder()
        m = self.create_genre_menu_button
        label_area = (
            (self.menu_layout.h / MENU_ROWS) / 100) * (100 - ICON_AREA)
        font_size = int((label_area / 100) * FONT_HEIGHT)
        self.groups_menu = MultiPageMenu(util,
                                         self.next_page,
                                         self.previous_page,
                                         self.set_title,
                                         self.reset_title,
                                         self.go_to_page,
                                         m,
                                         MENU_ROWS,
                                         MENU_COLUMNS,
                                         None, (0, 0, 0, 0),
                                         self.menu_layout,
                                         align=ALIGN_CENTER,
                                         font_size=font_size)
        self.groups_menu.add_listener(listeners[KEY_GENRE])
        self.set_menu(self.groups_menu)

        color_dark_light = self.config[COLORS][COLOR_DARK_LIGHT]
        self.navigator = RadioGroupNavigator(self.util, self.layout.BOTTOM,
                                             listeners, color_dark_light,
                                             self.total_pages)
        self.add_component(self.navigator)

        current_name = self.get_current_group_name()

        if current_name == None:
            self.current_page = 1
        else:
            try:
                current_group_index = self.groups_list.index(current_name)
                self.current_page = int(current_group_index / PAGE_SIZE) + 1
            except:
                current_group_index = 0

        self.turn_page()

    def create_genre_menu_button(self, s, constr, action, scale, font_size):
        """ Create Genre Menu button

        :param s: button state
        :param constr: scaling constraints
        :param action: button event listener
        :param scale: True - scale images, False - don't scale images

        :return: genre menu button
        """
        s.padding = BUTTON_PADDING
        s.image_area_percent = ICON_AREA
        s.fixed_height = font_size
        s.v_align = CENTER

        return self.factory.create_menu_button(s,
                                               constr,
                                               action,
                                               scale,
                                               font_size=font_size)

    def get_current_group_name(self):
        key = STATIONS + "." + self.config[CURRENT][LANGUAGE]
        name = None
        try:
            name = self.config[key][CURRENT_STATIONS]
        except:
            pass
        return name

    def get_page(self):
        start = (self.current_page - 1) * PAGE_SIZE
        end = self.current_page * PAGE_SIZE
        tmp_layout = self.groups_menu.get_layout(self.groups_list)
        button_rect = tmp_layout.constraints[0]
        image_box = self.factory.get_icon_bounding_box(button_rect,
                                                       ICON_LOCATION,
                                                       ICON_AREA, ICON_SIZE,
                                                       BUTTON_PADDING)
        groups_dict = self.util.load_stations_folders(image_box)

        return self.util.get_radio_group_slice(groups_dict, start, end)

    def turn_page(self):
        """ Turn book genre page """

        group_page = self.get_page()
        self.groups_menu.set_items(group_page, 0, self.change_group, False)
        current_name = self.get_current_group_name()

        try:
            self.current_genre = group_page[current_name]
            self.groups_menu.item_selected(self.current_genre)
        except:
            keys = list(group_page.keys())
            self.groups_menu.item_selected(group_page[keys[0]])

        if self.navigator and self.total_pages > 1:
            self.navigator.left_button.change_label(str(self.current_page - 1))
            self.navigator.right_button.change_label(
                str(self.total_pages - self.current_page))

        for b in self.groups_menu.buttons.values():
            b.parent_screen = self

        self.set_title(self.current_page)
        self.groups_menu.clean_draw_update()

    def change_group(self, state):
        """ Change group event listener
         
        :param state: button state
        """
        if not self.visible:
            return
        self.current_genre = state

        key = STATIONS + "." + self.config[CURRENT][LANGUAGE]
        self.config[key][CURRENT_STATIONS] = state.genre
        state.source = GENRE
        self.groups_menu.notify_listeners(state)

    def add_screen_observers(self, update_observer, redraw_observer):
        """ Add screen observers
        
        :param update_observer: observer for updating the screen
        :param redraw_observer: observer to redraw the whole screen
        """
        self.navigator.add_observers(update_observer, redraw_observer)
        self.groups_menu.add_menu_observers(update_observer,
                                            redraw_observer,
                                            release=False)
Ejemplo n.º 5
0
class Popup(Container):
    """ Popup Menu class """
    def __init__(self,
                 items,
                 util,
                 bounding_box,
                 update_parent,
                 callback,
                 default_selection=None):
        """ Initializer

        :param items: list of item names
        :param util: utility object
        :param bounding_box: bounding box
        :param update_parent: redraw parent function
        :param callback: menu selection callback
        """
        Container.__init__(self, util, bounding_box, (0, 0, 0))
        self.util = util
        self.factory = Factory(util)
        self.config = util.config
        self.update_parent = update_parent
        self.callback = callback
        self.popup = True

        c = Component(self.util)
        w = self.config[SCREEN_INFO][WIDTH]
        h = self.config[SCREEN_INFO][HEIGHT]
        c.content = pygame.Rect(0, 0, w, h)
        c.content_x = 0
        c.content_y = 0
        c.bounding_box = c.content
        c.bgr = (0, 0, 0, 0)
        c.name = "popup.overlay.bgr"
        c.handle_event = self.handle_outside_event
        self.add_component(c)

        c = Component(self.util)
        c.content = pygame.Rect(bounding_box.x, bounding_box.y, bounding_box.w,
                                bounding_box.h - 1)
        c.content_x = 0
        c.content_y = 0
        c.bounding_box = c.content
        c.bgr = self.config[COLORS][COLOR_BRIGHT]
        c.name = "popup.bgr"
        self.add_component(c)

        self.cols = 1
        self.rows = len(items)

        m = self.create_popup_menu_button
        b = pygame.Rect(bounding_box.x, bounding_box.y, bounding_box.w,
                        bounding_box.h - 2)
        self.menu = Menu(util,
                         None,
                         b,
                         self.rows,
                         self.cols,
                         create_item_method=m)

        layout = GridLayout(self.menu.bb)
        layout.set_pixel_constraints(self.rows, self.cols, 1, 1)
        bounding_box = layout.get_next_constraints()
        self.modes = self.util.load_menu(items,
                                         NAME, [],
                                         V_ALIGN_TOP,
                                         bb=bounding_box,
                                         scale=IMAGE_SCALE)

        if not default_selection:
            selection = self.modes[items[0]]
        else:
            selection = self.modes[default_selection]

        self.menu.set_items(self.modes, 0, self.select_item, False)
        self.menu.visible = False
        self.menu.item_selected(selection)
        self.add_component(self.menu)

        self.redraw_observer = None
        self.clicked = False
        self.visible = False

    def create_popup_menu_button(self, s, constr, action, scale, font_size=0):
        """ Create Popup Menu button

        :param s: button state
        :param constr: scaling constraints
        :param action: button event listener
        :param scale: True - scale images, False - don't scale images

        :return: home menu button
        """
        return self.factory.create_menu_button(s,
                                               constr,
                                               action,
                                               scale=True,
                                               show_label=False,
                                               ignore_bgr_opacity=True)

    def handle_outside_event(self, event):
        """ Handle popup event
        
        :param event: the event to handle
        """
        if not self.visible: return

        mouse_events = [pygame.MOUSEBUTTONUP, pygame.MOUSEBUTTONDOWN]

        if event.type in mouse_events and event.button == 1 and not self.menu.bb.collidepoint(
                event.pos):
            if event.type == pygame.MOUSEBUTTONDOWN:
                self.clicked = True
            elif event.type == pygame.MOUSEBUTTONUP and self.clicked:
                self.clicked = False
                self.set_visible(False)
                self.update_parent()
                if self.redraw_observer:
                    self.redraw_observer()
        elif event.type == USER_EVENT_TYPE:
            valid_keys = [
                pygame.K_UP, pygame.K_DOWN, pygame.K_LEFT, pygame.K_RIGHT
            ]
            if event.sub_type == SUB_TYPE_KEYBOARD and event.keyboard_key not in valid_keys and event.action == pygame.KEYUP:
                self.set_visible(False)
                self.update_parent()

    def select_item(self, state):
        """ Select menu item

        :param state: button state
        """
        self.set_visible(False)
        self.update_parent()
        self.callback(state)

    def update_popup(self, state):
        if not self.visible: return

        self.clean_draw_update()

    def add_menu_observers(self, update_observer, redraw_observer):
        """ Add menu observer
        
        :param update_observer: observer for updating menu
        :param redraw_observer: observer to redraw the whole screen
        """
        for b in self.menu.buttons.values():
            b.add_press_listener(update_observer)
            b.add_release_listener(redraw_observer)

        self.menu.add_move_listener(redraw_observer)
        self.menu.add_listener(redraw_observer)

        self.redraw_observer = redraw_observer
Ejemplo n.º 6
0
class SaverMenu(Menu):
    """ Screensaver Menu class. Extends base Menu class """
    
    def __init__(self, util, bgr=None, bounding_box=None):
        """ Initializer
        
        :param util: utility object
        :param bgr: menu background
        :param bounding_box: bounding box
        """
        self.factory = Factory(util)
        self.config = util.config
        
        items = []
        if self.config[SCREENSAVER_MENU][CLOCK]: items.append(CLOCK)
        if self.config[SCREENSAVER_MENU][LOGO]: items.append(LOGO)
        if self.config[SCREENSAVER_MENU][SLIDESHOW]: items.append(SLIDESHOW)
        if self.config[SCREENSAVER_MENU][VUMETER]: items.append(VUMETER)
        if self.config[SCREENSAVER_MENU][WEATHER]: items.append(WEATHER)
        if self.config[SCREENSAVER_MENU][SPECTRUM]: items.append(SPECTRUM)
        if self.config[SCREENSAVER_MENU][LYRICS]: items.append(LYRICS)
        if self.config[SCREENSAVER_MENU][RANDOM]: items.append(RANDOM)
        
        rows_num = 2
        cols_num = 4
        length = len(items)
        
        if length == 6 or length == 5:
            rows_num = 2
            cols_num = 3
        elif length == 4:
            rows_num = 2
            cols_num = 2
        elif length == 3:
            rows_num = 1
            cols_num = 3
        elif length == 2:
            rows_num = 1
            cols_num = 2
        elif length == 1:
            rows_num = 1
            cols_num = 1
        
        m = self.create_saver_menu_button
        label_area = (bounding_box.h / rows_num / 100) * (100 - ICON_AREA)
        font_size = int((label_area / 100) * FONT_HEIGHT)
        Menu.__init__(self, util, bgr, bounding_box, rows=rows_num, cols=cols_num, create_item_method=m, font_size=font_size)
                
        current_saver_name = items[0]
        for s in items:
            if s == self.config[SCREENSAVER][NAME]:
                current_saver_name = s
                break
        
        l = self.get_layout(items)
        bounding_box = l.get_next_constraints()
        box = self.factory.get_icon_bounding_box(bounding_box, ICON_LOCATION, ICON_AREA, ICON_SIZE, BUTTON_PADDING)
        box.w = box.w / 2
        self.savers = util.load_menu(items, GENRE, v_align=V_ALIGN_TOP, bb=box)
        
        if self.config[USAGE][USE_VOICE_ASSISTANT]:
            voice_commands = util.get_voice_commands()
            self.savers[CLOCK].voice_commands = [voice_commands["VA_CLOCK"].strip()]
            self.savers[LOGO].voice_commands = [voice_commands["VA_LOGO"].strip()]
            self.savers[SLIDESHOW].voice_commands = [voice_commands["VA_SLIDESHOW"].strip()]
            self.savers[VUMETER].voice_commands = [voice_commands["VA_INDICATOR"].strip()]
            self.savers[WEATHER].voice_commands = [voice_commands["VA_WEATHER"].strip()]
        
        self.set_items(self.savers, 0, self.change_saver, False)
        self.current_saver = self.savers[current_saver_name]
        self.item_selected(self.current_saver)

    def create_saver_menu_button(self, s, constr, action, scale, font_size):
        """ Create Screensaver Menu button

        :param s: button state
        :param constr: scaling constraints
        :param action: button event listener
        :param scale: True - scale images, False - don't scale images
        :param font_size: label font height in pixels

        :return: screensaver menu button
        """
        s.padding = BUTTON_PADDING
        s.image_area_percent = ICON_AREA
        s.v_align = CENTER

        return self.factory.create_menu_button(s, constr, action, scale, font_size=font_size)

    def get_saver_by_index(self, index):
        """ Return screensaver specified by its index
        
        :param index: screensaver index in the map of screensavers
        
        :return: screensaver
        """
        return self.savers[index]

    def change_saver(self, state):
        """ Change screensaver event listener
        
        :param state: button state
        """
        if not self.visible:
            return
        
        self.config[SCREENSAVER][NAME] = state.name        
        self.notify_listeners(state)
        
Ejemplo n.º 7
0
class LanguageMenu(Menu):
    """ Language Menu class. Extends base Menu class """
    
    def __init__(self, util, bgr=None, bounding_box=None):
        """ Initializer
        
        :param util: utility object
        :param bgr: menu background
        :param bounding_box: bounding box
        """
        self.util = util
        self.factory = Factory(util)

        m = self.create_language_menu_button
        Menu.__init__(self, util, bgr, bounding_box, None, None, create_item_method=m)
        self.config = self.util.config
        language = self.config[CURRENT][LANGUAGE] 
        
        languages = self.config[KEY_LANGUAGES]
        layout = self.get_layout(languages)        
        button_rect = layout.constraints[0]
        image_box = self.factory.get_icon_bounding_box(button_rect, ICON_LOCATION, ICON_AREA, ICON_SIZE, BUTTON_PADDING)
        self.languages = self.util.load_languages_menu(image_box)

        label_area = (button_rect.h / 100) * (100 - ICON_AREA)
        self.font_size = int((label_area / 100) * FONT_HEIGHT)

        self.set_items(self.languages, 0, self.change_language, False)
        self.current_language = self.languages[language]
        self.item_selected(self.current_language)

    def create_language_menu_button(self, s, constr, action, scale, font_size):
        """ Create Language Menu button

        :param s: button state
        :param constr: scaling constraints
        :param action: button event listener
        :param scale: True - scale images, False - don't scale images

        :return: language menu button
        """
        s.padding = BUTTON_PADDING
        s.image_area_percent = ICON_AREA

        return self.factory.create_menu_button(s, constr, action, scale, font_size=font_size)

    def set_voice_commands(self, language):
        """ Set menu voice commands

        :param language: new language
        """
        if not self.config[USAGE][USE_VOICE_ASSISTANT]:
            return

        va_commands = self.util.get_va_language_commands()
        for k, v in self.languages.items():
            v.voice_commands = va_commands[k]
    
    def change_language(self, state):
        """ Change language event listener
        
        :param state: button state
        """
        if not self.visible:
            return
        self.set_voice_commands(state.name)      
        self.notify_listeners(state)
Ejemplo n.º 8
0
class HomeMenu(Menu):
    """ Home Menu class. Extends base Menu class """
    def __init__(self, util, bgr=None, bounding_box=None, font_size=None):
        """ Initializer

        :param util: utility object
        :param bgr: menu background
        :param bounding_box: bounding box
        """
        self.util = util
        self.factory = Factory(util)
        self.config = util.config
        self.cdutil = CdUtil(util)

        self.bb = bounding_box
        self.horizontal_layout = True
        self.rows = None
        self.cols = None
        items = self.get_menu_items()
        cell_bb = items[2]

        m = self.create_home_menu_button
        label_area = (cell_bb.h / 100) * (100 - ICON_AREA)
        font_size = int((label_area / 100) * FONT_HEIGHT)
        Menu.__init__(self,
                      util,
                      bgr,
                      bounding_box,
                      None,
                      None,
                      create_item_method=m,
                      font_size=font_size)
        self.set_modes(*items)

    def get_menu_items(self):
        """ Prepare menu items

        :return: array containing menu items, disabled items, cell bounding box and icon box
        """
        items = []
        disabled_items = []
        disabled_modes = self.util.get_disabled_modes()
        modes = [
            RADIO, AUDIO_FILES, AUDIOBOOKS, STREAM, CD_PLAYER, PODCASTS,
            AIRPLAY, SPOTIFY_CONNECT, COLLECTION
        ]
        for mode in modes:
            self.add_mode(mode, items, disabled_items, disabled_modes)

        l = self.get_layout(items)
        bounding_box = l.get_next_constraints()
        box = self.factory.get_icon_bounding_box(bounding_box, ICON_LOCATION,
                                                 ICON_AREA, ICON_SIZE,
                                                 BUTTON_PADDING)

        return (items, disabled_items, bounding_box, box)

    def add_mode(self, mode, enabled, disabled, disabled_modes):
        """ Add mode

        :param mode: the mode to add
        :param enabled: list of enabled items
        :param disabled: list of disabled items
        :param disabled_modes: list of disabled modes
        """
        if not self.config[HOME_MENU][mode]:
            return

        enabled.append(mode)
        if mode in disabled_modes:
            disabled.append(mode)

    def set_current_modes(self):
        """ Set current player modes """

        items = self.get_menu_items()
        self.set_modes(*items)

    def set_modes(self, items, disabled_items, bounding_box, box):
        """ Set menu items

        :param items: menu items
        :param disabled_items: disabled menu items
        :param bounding_box: cell bounding box
        :param box: image boundng box
        """
        self.modes = self.util.load_menu(items,
                                         NAME,
                                         disabled_items,
                                         V_ALIGN_TOP,
                                         bb=box)
        va_commands = self.util.get_voice_commands()

        if self.config[USAGE][USE_VOICE_ASSISTANT]:
            self.add_voice_command(RADIO, ["VA_RADIO", "VA_GO_RADIO"],
                                   va_commands)
            self.add_voice_command(AUDIO_FILES, ["VA_FILES", "VA_GO_FILES"],
                                   va_commands)
            self.add_voice_command(
                AUDIOBOOKS, ["VA_AUDIOBOOKS", "VA_BOOKS", "VA_GO_BOOKS"],
                va_commands)
            self.add_voice_command(STREAM, ["VA_STREAM", "VA_GO_STREAM"],
                                   va_commands)
            self.add_voice_command(CD_PLAYER, ["VA_CD_PLAYER"], va_commands)
            self.add_voice_command(PODCASTS, ["VA_PODCAST", "VA_PODCASTS"],
                                   va_commands)

        if not items:
            return

        if not self.config[CURRENT][MODE]:
            for i in items:
                if i not in disabled_items:
                    mode = i
                    break
        else:
            mode = self.config[CURRENT][MODE]

        self.set_items(self.modes, 0, self.change_mode, False)
        try:
            self.current_mode = self.modes[mode.lower()]
            self.item_selected(self.current_mode)
        except:
            pass

    def create_home_menu_button(self, s, constr, action, scale, font_size):
        """ Create Home Menu button

        :param s: button state
        :param constr: scaling constraints
        :param action: button event listener
        :param scale: True - scale images, False - don't scale images
        :param font_size: label font height in pixels

        :return: home menu button
        """
        s.padding = BUTTON_PADDING
        s.image_area_percent = ICON_AREA

        return self.factory.create_menu_button(s,
                                               constr,
                                               action,
                                               scale,
                                               font_size=font_size)

    def add_voice_command(self, name, commands, va_commands):
        """ Add voice command

        :param name: item name
        :param commands: item commands
        :param va_commands: voice commands
        """
        if not self.config[HOME_MENU][name]:
            return
        c = []
        for m in commands:
            c.append(va_commands[m].strip())
        self.modes[name].voice_commands = c

    def change_mode(self, state):
        """ Change mode event listener

        :param state: button state
        """
        if not self.visible:
            return
        state.previous_mode = self.current_mode.name
        self.current_mode = state
        self.config[CURRENT][MODE] = state.name
        self.notify_listeners(state)
Ejemplo n.º 9
0
class RadioBrowserScreen(MenuScreen):
    """ Radio Browser Screen """
    def __init__(self, util, listeners, voice_assistant):
        """ Initializer
        
        :param util: utility object
        :param listeners: screen event listeners
        :param voice_assistant: the voice assistant
        """
        self.util = util
        self.config = util.config
        self.groups_list = self.util.get_stations_folders()
        self.factory = Factory(util)
        self.favorites_util = FavoritesUtil(self.util)
        rows = self.config[FILE_BROWSER_ROWS]
        columns = self.config[FILE_BROWSER_COLUMNS]
        d = [rows, columns]
        self.page_size = rows * columns

        MenuScreen.__init__(self,
                            util,
                            listeners,
                            rows,
                            columns,
                            voice_assistant,
                            d,
                            self.turn_page,
                            page_in_title=False)
        self.total_pages = 0
        self.title = ""
        m = self.create_radio_browser_menu_button
        button_height = (self.menu_layout.h / rows) - (self.config[PADDING] *
                                                       2)
        bgr = self.config[BACKGROUND][MENU_BGR_COLOR]

        if self.config[ALIGN_BUTTON_CONTENT_X] == 'center':
            font_size = int(((100 - STATION_IMAGE_AREA) / 100) *
                            self.config[FONT_HEIGHT_PERCENT])
        else:
            font_size = int(
                (button_height / 100) * self.config[FONT_HEIGHT_PERCENT])

        self.navigator = RadioNavigator(self.util, self.layout.BOTTOM,
                                        listeners)
        self.add_navigator(self.navigator)
        self.left_button = self.navigator.get_button_by_name(KEY_PAGE_DOWN)
        self.right_button = self.navigator.get_button_by_name(KEY_PAGE_UP)
        self.player_button = self.navigator.get_button_by_name(KEY_PLAYER)

        h = self.config[HORIZONTAL_LAYOUT]
        self.stations_menu = Menu(util,
                                  bgr,
                                  self.menu_layout,
                                  rows,
                                  columns,
                                  create_item_method=m,
                                  align=ALIGN_CENTER,
                                  horizontal_layout=h,
                                  font_size=font_size)
        self.set_menu(self.stations_menu)

        self.current_page = None
        self.current_language = self.config[CURRENT][LANGUAGE]
        self.current_genre = self.util.get_current_genre()
        self.turn_page()

        self.animated_title = True

    def create_radio_browser_menu_button(self, state, constr, action, scale,
                                         font_size):
        """ Factory function for menu button

        :param state: button state
        :param constr: bounding box
        :param action: action listener
        :param scale: True - sacle, False - don't scale
        :param font_size: the label font size

        :return: menu button
        """
        s = copy(state)
        s.bounding_box = constr
        s.padding = self.config[PADDING]
        s.image_area_percent = STATION_IMAGE_AREA
        label_area_percent = 100 - s.image_area_percent
        if self.config[ALIGN_BUTTON_CONTENT_X] == 'left':
            s.image_location = LEFT
            s.label_location = LEFT
            s.h_align = H_ALIGN_LEFT
        elif self.config[ALIGN_BUTTON_CONTENT_X] == 'right':
            s.image_location = RIGHT
            s.label_location = RIGHT
            s.h_align = H_ALIGN_RIGHT
        elif self.config[ALIGN_BUTTON_CONTENT_X] == 'center':
            s.image_location = TOP
            s.label_location = BOTTOM
            s.h_align = H_ALIGN_CENTER
        s.v_align = CENTER
        s.wrap_labels = self.config[WRAP_LABELS]
        s.fixed_height = font_size
        s.scaled = True
        self.util.add_icon(s, self.get_scale_factor(s))

        scale = True
        if hasattr(s, "show_label"):
            b = self.factory.create_menu_button(
                s,
                constr,
                action,
                scale,
                label_area_percent=label_area_percent,
                show_label=s.show_label,
                font_size=font_size)
        else:
            b = self.factory.create_menu_button(
                s,
                constr,
                action,
                scale,
                label_area_percent=label_area_percent,
                font_size=font_size)

        b.state.icon_selected_scaled = b.state.icon_base_scaled
        b.state.icon_selected = s.icon_base
        return b

    def get_scale_factor(self, s):
        """ Calculate scale factor

        :param s: button state object

        :return: scale width and height tuple
        """
        bb = s.bounding_box
        if self.config[ALIGN_BUTTON_CONTENT_X] == 'center':
            location = TOP
        else:
            location = self.config[ALIGN_BUTTON_CONTENT_X]
        icon_box = self.factory.get_icon_bounding_box(bb, location,
                                                      self.config[IMAGE_AREA],
                                                      self.config[IMAGE_SIZE],
                                                      self.config[PADDING])
        icon_box_without_label = self.factory.get_icon_bounding_box(
            bb, location, 100, 100, self.config[PADDING], False)
        if self.config[HIDE_FOLDER_NAME]:
            s.show_label = False
            w = icon_box_without_label.w
            h = icon_box_without_label.h
        else:
            s.show_label = True
            w = icon_box.w
            h = icon_box.h

        return (w, h)

    def get_playlist(self, genre=None):
        """ Get playlist

        :param genre: the genre

        :return: the playlist
        """
        if self.current_genre.name == KEY_FAVORITES:
            return self.favorites_util.get_favorites_playlist()
        else:
            return self.util.get_radio_browser_playlist(genre)

    def get_page(self):
        """ Get the current page from the playlist

        :return: the page
        """
        language = self.config[CURRENT][LANGUAGE]
        genre = self.util.get_current_genre()
        playlist = self.get_playlist(genre.l_name)
        playlist_length = len(playlist)
        self.total_pages = math.ceil(playlist_length / self.page_size)

        if self.current_page == None or language != self.current_language or self.current_genre != genre:
            self.current_language = language
            self.current_genre = genre
            playlist = self.get_playlist(genre.l_name)
            playlist_length = len(playlist)
            self.total_pages = math.ceil(playlist_length / self.page_size)
            if self.total_pages == 0:
                self.left_button.change_label("0")
                self.right_button.change_label("0")
                self.set_title()
                return []
            self.current_page = self.get_page_by_index()

        self.set_title()

        start = (self.current_page - 1) * self.page_size
        end = self.current_page * self.page_size
        return playlist[start:end]

    def get_page_by_index(self):
        """ Get the page by index

        :return: the page
        """
        page = None
        index = self.util.get_current_radio_station_index()
        if index < self.page_size:
            page = 1
        else:
            page = math.ceil(index / self.page_size)
        return page

    def set_title(self):
        """ Set the screen title """

        genre = self.util.get_current_genre()
        if genre.name == KEY_FAVORITES:
            station = self.favorites_util.get_current_favorites_station()
        else:
            station = self.util.get_current_radio_station()

        if station:
            title = station.comparator_item
        else:
            title = ""
        d = {"current_title": title}
        self.screen_title.set_text(d)

    def turn_page(self):
        """ Turn page """

        page = self.get_page()
        d = self.stations_menu.make_dict(page)
        self.stations_menu.set_items(d, 0, self.change_station, False)
        self.favorites_util.mark_favorites(self.stations_menu.buttons)
        index = self.util.get_current_radio_station_index()
        menu_selected = self.stations_menu.select_by_index(index)

        if self.navigator and self.total_pages > 1:
            self.left_button.change_label(str(self.current_page - 1))
            self.right_button.change_label(
                str(self.total_pages - self.current_page))
        else:
            self.left_button.change_label("0")
            self.right_button.change_label("0")

        for b in self.stations_menu.buttons.values():
            b.parent_screen = self
            b.release_listeners.insert(0, self.handle_favorite)

        self.stations_menu.clean_draw_update()
        if menu_selected:
            self.navigator.unselect()

        self.link_borders()
        navigator_selected = self.navigator.is_selected()

        if (len(page) == 0 or
            (not menu_selected and not navigator_selected)) and self.navigator:
            self.navigator.unselect()
            self.player_button.set_selected(True)
            self.player_button.clean_draw_update()

    def change_station(self, state):
        """ Change station

        :param state: state object
        """
        found = False
        for b in self.stations_menu.buttons.values():
            if b.state == state:
                found = True
                break

        if not found:  # after deleting favorite
            playlist = self.get_playlist(self.current_genre.l_name)
            index = self.util.get_current_radio_station_index()
            if playlist and len(playlist) > 0:
                state = playlist[index]
            else:
                state = None

        if state:
            state.source = KEY_RADIO_BROWSER
            state.name = self.util.get_current_genre().name
            self.util.set_radio_station_index(state.index)
        else:
            self.util.set_radio_station_index(None)
        self.go_player(state)
        self.set_title()

    def add_screen_observers(self, update_observer, redraw_observer):
        """ Add screen observers
        
        :param update_observer: observer for updating the screen
        :param redraw_observer: observer to redraw the whole screen
        """
        self.navigator.add_observers(update_observer, redraw_observer)
        self.stations_menu.add_menu_observers(update_observer,
                                              redraw_observer,
                                              release=False)

    def set_current(self, state=None):
        """ Set current screen

        :param state: the source button state object
        """
        self.turn_page()

    def handle_event(self, event):
        """ Handle screen event

        :param event: the event to handle
        """
        self.handle_event_common(event)

    def handle_favorite(self, state):
        """ Add/Remove station to/from the favorites
        
        :param state: button state
        """
        if state == None or not getattr(state, "long_press", False):
            return

        favorites, lang_dict = self.favorites_util.get_favorites_from_config()

        if self.favorites_util.is_favorite(favorites, state):
            self.favorites_util.remove_favorite(favorites, state)
            if self.current_genre.name == KEY_FAVORITES:
                current_index = state.index
                if len(favorites) == 0:
                    self.util.set_radio_station_index(None)
                else:
                    if current_index == 0:
                        self.util.set_radio_station_index(0)
                    else:
                        self.util.set_radio_station_index(current_index - 1)
                self.turn_page()
            else:
                selected_button = self.stations_menu.get_selected_item()
                if selected_button and len(selected_button.components) == 4:
                    del selected_button.components[3]
                    selected_button.clean_draw_update()
        else:
            self.favorites_util.add_favorite(favorites, state)
            self.favorites_util.mark_favorites(self.stations_menu.buttons)
Ejemplo n.º 10
0
class Keyboard(Container):
    """ Keyboard class. """

    def __init__(self, util, bb, callback, screen):
        """ Initializer

        :param util: utility object
        :param bb: bounding box
        :param callback: function to call on Enter
        :param screen: parent screen
        """
        Container.__init__(self, util, bb, (0, 0, 0))
        self.content = None
        self.screen = screen
        self.bb = bb
        self.util = util
        self.config = util.config
        self.callback = callback
        self.move_listeners = []
        self.text_listeners = []
        self.buttons = {}
        self.factory = Factory(util)
        self.caps = False
        self.text = ""

        self.controls = ["Caps", "Del", "abc", "ABC", "123", "#+=", "Enter"]
        self.keyboards = {}
        self.current_keyboard_type = None
        self.create_keyboard(KEYBOARD_abc, LAYOUT_1, TRANSITION_MAP_1)

    def get_layout(self, span):
        """ Create layout

        :param span:
        :return: layout
        """
        h = int(self.bb.h / 4)
        u = int((self.bb.w) / 20)
        g = self.bb.h - (h * 4)

        layout = []
        for k, n in enumerate(span):
            z = 0
            for i, m in enumerate(n):
                x = (z * u) + 1
                y = self.bb.y + (k * h) + 1
                w = (u * m) - 1
                d = h - 1
                if k == 3 and (g - 1) > 0:
                    d += (g - 1)
                if (k == 0 and i == 9) or (k == 1 and i == 10) or (k == 2 and i == 8) or (k == 3 and i == 2):
                    w -= 1
                layout.append(pygame.Rect(x, y, w, d))
                z += m

        return layout

    def create_keyboard(self, keyboard_type, span, transition_map):
        """ Create keyboard

        :param keyboard_type: type
        :param span: span
        :param transition_map: transition map
        """
        layout = self.get_layout(span)
        buttons = []
        keys = None
        self.current_keyboard_type = keyboard_type

        try:
            buttons = self.keyboards[keyboard_type]
            self.components = buttons
            return
        except:
            pass

        if keyboard_type == KEYBOARD_abc:
            keys = KEYBOARD_1
        elif keyboard_type == KEYBOARD_ABC:
            keys = KEYBOARD_2
        elif keyboard_type == KEYBOARD_123:
            keys = KEYBOARD_3
        elif keyboard_type == KEYBOARD_symbol:
            keys = KEYBOARD_4

        for i, k in enumerate(keys):
            if not k:
                c = Component(self.util, layout[i], bgr=self.config[BACKGROUND][MENU_BGR_COLOR])
                c.parent_screen = self.screen
                c.name = "gap" + str(i)
                buttons.append(c)
                continue
            s = State()
            s.index = i
            s.name = k
            s.l_name = k
            s.comparator_item = s.index
            s.bgr = self.config[COLORS][COLOR_DARK]
            s.show_bgr = True
            s.bounding_box = layout[i]
            s.key_map = transition_map[i]
            button = self.factory.create_menu_button(s, layout[i], self.press_key, False, 50, 100, False, True)
            buttons.append(button)
        buttons[0].set_selected(True)
        self.keyboards[keyboard_type] = buttons
        self.components = buttons

        if keyboard_type != KEYBOARD_abc:
            self.set_observers()

        self.buttons = {i : item for i, item in enumerate(buttons)}

    def press_key(self, state):
        """ Key press handler

        :param state: button state
        """
        if state.name not in self.controls and len(self.text) == 64:
            return

        self.unselect()
        state.event_origin.set_selected(True)

        if state.name == "Caps" or state.name == "ABC":
            if state.name == "Caps":
                self.caps = not self.caps
            if self.caps:
                self.create_keyboard(KEYBOARD_ABC, LAYOUT_1, TRANSITION_MAP_1)
            else:
                self.create_keyboard(KEYBOARD_abc, LAYOUT_1, TRANSITION_MAP_1)
        elif state.name == "123":
            self.create_keyboard(KEYBOARD_123, LAYOUT_2, TRANSITION_MAP_2)
        elif state.name == "#+=":
            self.create_keyboard(KEYBOARD_symbol, LAYOUT_3, TRANSITION_MAP_3)
        elif state.name == "Space":
            self.text += " "
            self.notify_text_listeners(self.text)
        elif state.name == "Del":
            if len(self.text) > 0:
                self.text = self.text[0: -1]
                self.notify_text_listeners(self.text)
        elif state.name == "Enter":
            if len(self.text) == 0:
                return
            s = State()
            s.source = "search"
            s.callback_var = self.text
            self.callback(s)
        else:
            self.text += state.name
            self.notify_text_listeners(self.text)

        self.clean_draw_update()

    def delete(self, state):
        """ Delete character

        :param state: button state
        """
        self.text = ""
        self.notify_text_listeners(self.text)

    def add_move_listener(self, listener):
        """ Add arrow button event listener

        :param listener: event listener
        """
        if listener not in self.move_listeners:
            self.move_listeners.append(listener)

    def notify_move_listeners(self):
        """ Notify arrow button event listeners """

        for listener in self.move_listeners:
            listener(None)

    def add_text_listener(self, listener):
        """ Add text event listener

        :param listener: event listener
        """
        if listener not in self.text_listeners:
            self.text_listeners.append(listener)

    def notify_text_listeners(self, text):
        """ Notify all text listeners

        :param state: button state
        """
        for listener in self.text_listeners:
            listener(text)

    def unselect(self):
        """ Unselect currently selected button """

        for c in self.components:
            if isinstance(c, Button) and c.selected:
                c.set_selected(False)
                return

    def is_selected(self):
        """ Check if keyboard has selected key

        :return: True - has selected key, False - doesn't have
        """
        selected = False
        for c in self.components:
            if isinstance(c, Button) and c.selected:
                selected = True
                break
        return selected

    def get_current_key(self):
        """ Get currently selected key

        :return: selected component
        """
        for c in self.components:
            if isinstance(c, Button) and c.selected:
                return c
        return self.components[0]

    def select_key_by_index(self, index):
        """ Slecte key by index

        :param index:
        :return:
        """
        for c in self.components:
            if isinstance(c, Button) and c.state.index == index:
                c.set_selected(True)
                return

    def handle_event(self, event):
        """ Menu event handler

        :param event: menu event
        """
        if not self.visible: return

        if event.type == USER_EVENT_TYPE and event.sub_type == SUB_TYPE_KEYBOARD and event.action == pygame.KEYUP:
            key_events = [kbd_keys[KEY_LEFT], kbd_keys[KEY_RIGHT], kbd_keys[KEY_UP], kbd_keys[KEY_DOWN], kbd_keys[KEY_SELECT]]

            if event.keyboard_key not in key_events or not self.is_selected():
                Container.handle_event(self, event)
                return

            current_key = self.get_current_key()

            if event.keyboard_key == kbd_keys[KEY_SELECT]:
                self.press_key(current_key.state)
                self.notify_move_listeners()
                return

            transition_map = current_key.state.key_map
            next_key_index = 0
            self.unselect()

            if event.keyboard_key == kbd_keys[KEY_LEFT]:
                index = transition_map[0]
                if index == -3:
                    b = self.get_button_by_index(9)
                    self.exit_keyboard(b)
                    return
                next_key_index = index
            elif event.keyboard_key == kbd_keys[KEY_RIGHT]:
                index = transition_map[1]
                if index == -4:
                    b = self.get_button_by_index(current_key.state.index - 2)
                    self.exit_keyboard(b)
                    return
                next_key_index = index
            elif event.keyboard_key == kbd_keys[KEY_UP]:
                index = transition_map[2]
                if index == -1:
                    self.exit_keyboard(current_key)
                    return    
                else:
                    next_key_index = index
            elif event.keyboard_key == kbd_keys[KEY_DOWN]:
                index = transition_map[3]
                if index == -2 or index == -5:
                    self.exit_keyboard(current_key)
                    return    
                else:
                    next_key_index = index

            self.select_key_by_index(next_key_index)
            self.clean_draw_update()
            self.notify_move_listeners()
        else:
            Container.handle_event(self, event)
            self.notify_move_listeners()

    def get_button_by_index(self, index):
        for b in self.components:
            if isinstance(b, Button):
                if b.state.index == index:
                    return b
        return None

    def exit_keyboard(self, key):
        """ Exit keyboard

        :param key: current key
        """
        x = int(key.bounding_box.x + (key.bounding_box.w / 2))
        y = self.bb.y + self.bb.h + 10
        self.util.post_exit_event(x, y, self)
        self.clean_draw_update()

    def set_observers(self):
        """ Set observers """

        for b in self.components:
            if not isinstance(b, Button):
                continue
            if self.update_observer and self.press:
                b.add_press_listener(self.update_observer)
            if self.update_observer and self.release:
                b.add_release_listener(self.update_observer)
            if self.redraw_observer:
                b.add_release_listener(self.redraw_observer)
        if self.redraw_observer:
            self.add_move_listener(self.redraw_observer)

    def add_menu_observers(self, update_observer, redraw_observer=None, press=True, release=True):
        """ Add menu observer

        :param update_observer: observer for updating menu
        :param redraw_observer: observer to redraw the whole screen
        :param press: True - add observer as press listener (default)
        :param release: True - add observer as release listener (default)
        """
        self.update_observer = update_observer
        self.redraw_observer = redraw_observer
        self.press = press
        self.release = release

        self.set_observers()
Ejemplo n.º 11
0
class FileMenu(Menu):
    """ File Menu class. Extends base Menu class """
    
    def __init__(self, filelist, util, playlist_provider, bounding_box=None, align=ALIGN_CENTER, icon_box=None, icon_box_without_label=None):
        """ Initializer
        
        :param filelist: file list
        :param util: utility object
        :param bgr: menu background
        :param bounding_box: bounding box
        """
        self.factory = Factory(util)
        self.util = util
        self.cdutil = CdUtil(self.util)
        self.playlist_provider = playlist_provider
        self.config = self.util.config
        self.filelist = filelist
        self.icon_box = icon_box
        self.icon_box_without_label = icon_box_without_label

        m = self.create_file_menu_button
        self.bounding_box = bounding_box
        bgr = util.config[BACKGROUND][MENU_BGR_COLOR]
        
        r = c = 3
        if filelist:
            r = filelist.rows
            c = filelist.columns

        h = self.config[HORIZONTAL_LAYOUT]
        button_height = (self.bounding_box.h / r) - (self.config[PADDING] * 2)

        if self.config[ALIGN_BUTTON_CONTENT_X] == 'center':
            font_size = int(((100 - self.config[IMAGE_AREA]) / 100) * self.config[FONT_HEIGHT_PERCENT])
        else:
            font_size = int((button_height / 100) * self.config[FONT_HEIGHT_PERCENT])

        Menu.__init__(self, util, bgr, self.bounding_box, r, c, create_item_method=m, align=align, horizontal_layout=h, font_size=font_size)

        self.browsing_history = {}        
        self.left_number_listeners = []
        self.right_number_listeners = []
        self.change_folder_listeners = []
        self.play_file_listeners = []
        self.playlist_size_listeners = []
        self.menu_navigation_listeners = []
        self.page_turned = False
        self.separator = os.sep
        self.empty_state = State()
        url = selection = None
        self.current_folder = self.config[FILE_PLAYBACK][CURRENT_FOLDER]
        folder = self.current_folder

        playback_mode = self.config[FILE_PLAYBACK][CURRENT_FILE_PLAYBACK_MODE]
        if playback_mode == FILE_AUDIO or playback_mode == FILE_RECURSIVE:
            if not self.current_folder.endswith(os.sep):
                self.current_folder += os.sep                
                        
            if self.config[CURRENT][MODE] == CD_PLAYER:
                cd_drive_name = self.config[CD_PLAYBACK][CD_DRIVE_NAME]
                track = self.config[CD_PLAYBACK][CD_TRACK]
                url = self.cdutil.get_cd_track_url(cd_drive_name, track)
            else:
                url = self.current_folder + self.config[FILE_PLAYBACK][CURRENT_FILE]                            
        elif playback_mode == FILE_PLAYLIST:
            url = self.config[FILE_PLAYBACK][CURRENT_FILE]
            self.browsing_history[self.current_folder] = 0
            p = self.current_folder + self.separator + self.config[FILE_PLAYBACK][CURRENT_FILE_PLAYLIST]
            self.browsing_history[p] = 0
            folder = p
            self.current_folder = folder
        selection = url
        
        if url and self.filelist:        
            self.filelist.set_current_item_by_url(url)
        
        p_index = self.filelist.current_page_index
        pl = self.filelist.items
        self.change_folder(folder, page_index=p_index, playlist=pl, selected=selection)            
    
    def create_file_menu_button(self, s, constr, action, scale, font_size):
        """ Create File Menu button

        :param s: button state
        :param constr: scaling constraints
        :param action: button event listener
        :param scale: True - scale images, False - don't scale images

        :return: file menu button
        """
        scale = False
        s.padding = self.config[PADDING]
        s.image_area_percent = self.config[IMAGE_AREA]
        label_area_percent = 100 - s.image_area_percent
        if self.config[ALIGN_BUTTON_CONTENT_X] == 'left':
            s.image_location = LEFT
            s.label_location = LEFT
            s.h_align = H_ALIGN_LEFT
        elif self.config[ALIGN_BUTTON_CONTENT_X] == 'right':
            s.image_location = RIGHT
            s.label_location = RIGHT
            s.h_align = H_ALIGN_RIGHT
        elif self.config[ALIGN_BUTTON_CONTENT_X] == 'center':
            s.image_location = TOP
            s.label_location = BOTTOM
            s.h_align = H_ALIGN_CENTER
        s.v_align = CENTER
        s.wrap_labels = self.config[WRAP_LABELS]
        s.fixed_height = font_size

        if s.file_type == FOLDER_WITH_ICON or (s.file_type == FILE_AUDIO and getattr(s, "has_embedded_image", None)):
            scale = True
        if hasattr(s, "show_label"):
            return self.factory.create_menu_button(s, constr, action, scale, label_area_percent=label_area_percent, show_label=s.show_label, font_size=font_size)
        else:
            return self.factory.create_menu_button(s, constr, action, scale, label_area_percent=label_area_percent, font_size=font_size)

    def recursive_change_folder(self, state):
        """ Change recursive folder
        
        :param state: state object
        """
        f = self.util.file_util.get_first_folder_with_audio_files(state.url)
        if f == None:
            self.change_folder(state.url)
            return
        
        self.config[FILE_PLAYBACK][CURRENT_FILE_PLAYBACK_MODE] = FILE_RECURSIVE
        
        self.config[FILE_PLAYBACK][CURRENT_FOLDER] = f[0]
        state.folder = f[0]
        state.file_name = f[1]
        state.file_type = FILE_AUDIO
        state.url = os.path.join(f[0], f[1])
        self.change_folder(f[0])
        self.handle_file(state)
        
    def select_item(self, state):
        """ Select menu item
        
        :param state: state object defining selected object
        """ 
        if self.visible:
            if state.file_type == FILE_AUDIO or state.file_type == FILE_PLAYLIST:
                self.config[FILE_PLAYBACK][CURRENT_FOLDER] = self.util.file_util.current_folder
                if self.config[FILE_PLAYBACK][CURRENT_FILE_PLAYBACK_MODE] == FILE_PLAYLIST:
                    if hasattr(state, "file_name") and hasattr(state, "name"):
                        file_name = state.file_name
                        name = state.name
                        index = file_name.rfind(name)
                        if index != -1:
                            folder = file_name[0 : index]
                            if len(folder.strip()) > 0:
                                self.config[FILE_PLAYBACK][CURRENT_FOLDER] = folder
        
        if state.file_type == FOLDER or state.file_type == FOLDER_WITH_ICON:
            if getattr(state, "long_press", False):
                if self.config[FILE_PLAYBACK][CURRENT_FILE_PLAYBACK_MODE] == FILE_RECURSIVE:
                    self.config[FILE_PLAYBACK][CURRENT_FILE_PLAYBACK_MODE] = FILE_AUDIO
                    self.change_folder(state.url)
                else:
                    self.config[FILE_PLAYBACK][CURRENT_FILE_PLAYLIST] = state.url
                    self.recursive_change_folder(state)
            else:
                self.config[FILE_PLAYBACK][CURRENT_FILE_PLAYBACK_MODE] = FILE_AUDIO  
                self.change_folder(state.url)
        elif state.file_type == FILE_AUDIO:
            if getattr(state, "long_press", False) and self.config[FILE_PLAYBACK][CURRENT_FILE_PLAYBACK_MODE] == FILE_RECURSIVE:
                self.config[FILE_PLAYBACK][CURRENT_FILE_PLAYBACK_MODE] = FILE_AUDIO
            
            m = getattr(state, "playback_mode", FILE_AUDIO)
            mode = self.config[FILE_PLAYBACK][CURRENT_FILE_PLAYBACK_MODE] or FILE_AUDIO
            if m == FILE_AUDIO and mode == FILE_AUDIO:
                self.handle_file(state)
            else:
                self.config[FILE_PLAYBACK][CURRENT_FILE] = state.file_name
                self.config[FILE_PLAYBACK][CURRENT_TRACK_TIME] = None
                if not getattr(state, "dont_notify", None):            
                    self.notify_play_file_listeners(state)
                else:
                    n = self.config[AUDIO][CLIENT_NAME]
                    if n == VLC or n == MPV:
                        self.handle_file(state)
            self.handle_border_links()
        elif state.file_type == FILE_PLAYLIST:
            self.config[FILE_PLAYBACK][CURRENT_FILE_PLAYBACK_MODE] = FILE_PLAYLIST
            i = state.url.rfind(self.separator)
            url = state.url[i + 1:]
            self.config[FILE_PLAYBACK][CURRENT_FILE_PLAYLIST] = url
            state.music_folder = self.config[AUDIO][MUSIC_FOLDER]
            pl = self.util.load_playlist(state, self.playlist_provider, self.filelist.rows, self.filelist.columns, (self.icon_box.w, self.icon_box.h))
            url = state.url
            if pl:
                self.notify_playlist_size_listener(len(pl))
            else:
                url = state.url[0 : i]
            u = getattr(state, "url", None)            
            if u == None:
                state.url = state.folder + os.sep + state.file_name
                url = state.url
                                
            self.change_folder(url, playlist=pl)
            
        if self.visible:
            self.draw()
   
    def page_up(self, state=None):
        """ Called by page up button
        
        :param state: not used
        """        
        if self.filelist.length <= self.filelist.items_per_page:
            return
        
        self.switch_to_next_page(None)
        
    def page_down(self, state=None):
        """ Called by page down button
        
        :param state: not used
        """        
        if self.filelist.length <= self.filelist.items_per_page:
            return
        
        self.switch_to_previous_page(None)
   
    def handle_file(self, state):
        """ Handle audio file
        
        :param state: state object defining audio file
        """
        
        state.track_time = '0'
        
        if not state.file_name.startswith("cdda:"):
            self.config[FILE_PLAYBACK][CURRENT_FILE] = state.file_name
            self.config[FILE_PLAYBACK][CURRENT_TRACK_TIME] = state.track_time
        
        if self.is_in_filelist(state.file_name):
            b = self.get_button_by_filename(state.file_name)
            if not b:
                self.switch_to_next_page(state)
            else:
                state.comparator_item = b.state.comparator_item
        
        if not self.config[FILE_PLAYBACK][CURRENT_FILE].startswith("cdda:"):
            if not self.config[FILE_PLAYBACK][CURRENT_FOLDER].endswith(os.sep):
                url = self.config[FILE_PLAYBACK][CURRENT_FOLDER] + os.sep + self.config[FILE_PLAYBACK][CURRENT_FILE]
            else:
                url = self.config[FILE_PLAYBACK][CURRENT_FOLDER] + self.config[FILE_PLAYBACK][CURRENT_FILE]
            self.filelist.set_current_item_by_url(url)
        
        mode = getattr(state, "playback_mode", None)
        if mode == None:
            if state.file_type == FILE_AUDIO:
                state.playback_mode = FILE_AUDIO
            else:
                state.playback_mode = FILE_PLAYLIST

        self.item_selected(state)
        if not getattr(state, "dont_notify", None):
            self.notify_play_file_listeners(state)
    
    def update_playlist_menu(self, state):
        """ Update playlist menu
        This is initiated by player
        
        :param state: state object from player defining current playlist file
        """
        if self.config[FILE_PLAYBACK][CURRENT_FILE_PLAYBACK_MODE] != FILE_PLAYLIST:
            return 
        
        s = State()
        s.dont_notify = True
        
        i = self.util.get_dictionary_value(state, "current_track_id")
        s.track_time = self.util.get_dictionary_value(state, "seek_time", "0")
        self.config[FILE_PLAYBACK][CURRENT_TRACK_TIME] = s.track_time
        
        if i != None:            
            b = self.get_button_by_index_in_page(int(i) - 1)
            if not b:
                self.switch_to_next_page(s)
            s.comparator_item = int(i) 
            
        name = self.util.get_dictionary_value(state, "Track")
        
        if not name:
            name = self.util.get_dictionary_value(state, "file_name")
        
        if name != None:
            self.config[FILE_PLAYBACK][CURRENT_FILE] = name
        
        self.item_selected(state)
    
    def get_comparator(self, state):
        """ Return comparator object from state
        
        :param state: state object containing comparator item
        """
        try:
            p = state["Pos"]
            return int(p)
        except:
            pass
        
        try:
            s_comp = getattr(state, "comparator_item", None)
            if s_comp != None:
                return s_comp
        except:
            pass
        
        s_name = None
        
        if isinstance(state, dict):
            try:
                s_name = state["file_name"]
            except:
                pass
        elif isinstance(state, State):
            s_name = getattr(state, "file_name", None)
        
        for button in self.buttons.values():            
            b_name = getattr(button.state, "file_name", None)
            if s_name and b_name and s_name == button.state.file_name:
                return button.state.comparator_item
        return None
    
    def set_current_folder(self, folder):
        """ Current folder setter
        
        :param folder: new current folder
        """
        self.current_folder = folder
    
    def set_filelist(self, filelist):
        """ Set filelist
        
        :param filelist: the filelist to set
        """
        self.filelist = filelist        
    
    def init_page(self, index):
        """ Page initializer
        
        :param index: new current page index
        """
        url = self.config[FILE_PLAYBACK][CURRENT_FILE]
        if self.config[FILE_PLAYBACK][CURRENT_FILE_PLAYBACK_MODE] == FILE_AUDIO:  
            url = self.config[FILE_PLAYBACK][CURRENT_FOLDER] + os.sep + self.config[FILE_PLAYBACK][CURRENT_FILE]
            
        if url:
            self.filelist.set_current_item_by_url(url)
            
        self.filelist.current_page_index = index        
        page = self.filelist.get_current_page()
        self.set_page(self.filelist.current_item_index_in_page, page)
        
        if not self.filelist.current_item:
            self.filelist.current_page_index = index

    def set_page(self, index_on_page, page, align=ALIGN_CENTER):
        """ Page setter
        
        :param index_on_page: current item index on page
        :param page: current page content
        """
        if page == None: return
        
        self.set_items(self.make_dict(page), index_on_page, self.select_item)

        for b in self.buttons.values():
            b.parent_screen = self.parent_screen

        self.draw() 

    def switch_to_next_page(self, state):
        """ Switch to the next page
        
        :param state: button state
        """
        if len(self.filelist.items) == 0:
            return
        self.turn_page(state, self.filelist.next_page())
            
    def switch_to_previous_page(self, state):
        """ Switch to the previous page
        
        :param state: button state
        """
        if len(self.filelist.items) == 0:
            return        
        self.turn_page(state, self.filelist.previous_page())
        
    def turn_page(self, state, page):
        """ Change current page
        
        :param state: state object defining current item on page
        :param page: new page content
        """
        self.set_page(self.filelist.current_item_index, page)
        if getattr(state, "url", None):
            self.filelist.set_current_item_by_url(state.url)
        if self.visible:
            self.item_selected(self.filelist.current_item)        
        self.update_buttons()
        if not getattr(state, "dont_notify", None):
            self.draw()            
        self.page_turned = True
        
        f = self.current_folder
        
        if f[-1] == self.separator:
            f = f[:-1]

        self.browsing_history[f] = self.filelist.current_page_index
        self.handle_border_links()
    
    def handle_border_links(self):
        """ Handle border links and menu/navigator selection """

        if hasattr(self, "link_borders"):
            self.link_borders()

        if hasattr(self, "navigator"):
            if self.get_selected_item() != None:
                self.navigator.unselect()
            else:
                if not self.navigator.is_selected():
                    b = self.navigator.get_button_by_name(KEY_HOME)
                    b.set_selected(True)
                    b.clean_draw_update()

    def switch_to_root(self, state):
        """ Switch to the root folder
        
        :param state: not used state object
        """
        self.switch_folder(self.util.file_util.ROOT)
            
    def switch_to_user_home(self, state):
        """ Switch to the user home folder
        
        :param state: not used state object
        """
        self.switch_folder(self.util.file_util.USER_HOME)  
    
    def switch_folder(self, folder):
        """ Switch to folder
        
        :param folder: folder to switch to
        """
        self.switch_file_playback_mode()
        self.change_folder(folder)
    
    def switch_file_playback_mode(self):
        if self.config[FILE_PLAYBACK][CURRENT_FILE_PLAYBACK_MODE] == FILE_PLAYLIST:
            self.config[FILE_PLAYBACK][CURRENT_FILE_PLAYBACK_MODE] = FILE_AUDIO
    
    def switch_to_parent_folder(self, state):
        """ Switch to the parent folder
        
        :param state: not used state object
        """
        if self.current_folder == self.separator:
            return
        
        self.switch_file_playback_mode()
        
        if self.current_folder and self.current_folder[-1] == self.separator:
            self.current_folder = self.current_folder[:-1]

        tmp = self.current_folder    
        
        sep_index = self.current_folder.rfind(self.separator)
        self.browsing_history.pop(self.current_folder, None)
        
        folder = self.current_folder
        page_index = 0 
        selected_item = tmp
        
        if sep_index == -1: return
        
        if sep_index != 0:
            folder = self.current_folder[0:sep_index]
            
            if len(self.browsing_history) == 0:
                page_index = -1
            else:
                page_index = self.browsing_history.get(folder, 0)
        elif sep_index == 0:
            folder = self.separator            
            try:
                page_index = self.browsing_history[folder]
            except:
                pass
            
        try:
            self.change_folder(folder, page_index, selected=selected_item)
        except:
            pass
                    
        if self.filelist.length != 0 and tmp != None:
            self.filelist.set_current_item_by_url(tmp)
            self.unselect()
            i = self.filelist.current_item_index
            self.select_by_index(i)
            if hasattr(self, "navigator") and self.get_selected_item() != None:
                self.navigator.unselect()
    
    def change_folder(self, folder, page_index=0, playlist=None, selected=None):
        """ Change folder
        
        :param folder: new folder name
        :param page_index: new page index
        :param playlist: playlist content
        """
        if not folder:
            if self.config[AUDIO][MUSIC_FOLDER]:
                folder = self.config[AUDIO][MUSIC_FOLDER]
            else:
                folder = self.util.file_util.current_folder        
        
        self.current_folder = folder
        self.selected_index = None
        
        folder_content = playlist
        if not folder_content and self.config[CURRENT][MODE] != CD_PLAYER:
            folder_content = self.util.load_folder_content(folder, self.filelist.rows, self.filelist.columns, self.icon_box, self.icon_box_without_label)
            
        if not folder_content:
            self.buttons = {}
            self.components = []            
        self.filelist = Page(folder_content, self.filelist.rows, self.filelist.columns)

        if selected:
            self.filelist.set_current_item_by_url(selected)
            page_index = self.filelist.current_item_page_index

        self.browsing_history[folder] = page_index 
        self.init_page(page_index)
        self.notify_change_folder_listeners(folder)
        self.update_buttons()
        self.page_turned = True

        self.handle_border_links()               
    
    def update_buttons(self):
        """ Update left/right buttons """
        
        left = str(self.filelist.get_left_items_number())
        right = str(self.filelist.get_right_items_number())        
        self.notify_left_number_listeners(left)
        self.notify_right_number_listeners(right)    
            
    def get_button_by_index_in_page(self, index):
        """ Return the button by its index on page
        
        :param index: button index
        :return: the button
        """
        for button in self.buttons.values():
            if button.state.comparator_item == index:
                return button
        return None
    
    def get_button_by_filename(self, file_name):
        """ Return the button by its file name
        
        :param file_name: file name
        :return: the button
        """
        for button in self.buttons.values():
            if button.state.file_name == file_name:
                return button
        return None
    
    def is_in_filelist(self, file_name):
        """ Check that file is in the current file list
        
        :param file_name: file name
        :return: True - in file list, False- not in file list
        """
        if not self.filelist.items:
            return False

        for item in self.filelist.items:
            if item.file_name == file_name:
                return True
        return False
    
    def get_button_by_index(self, index):
        """ Return the button by its index on page
        
        :param index: button index
        :return: the button
        """
        for button in self.buttons.values():
            if button.state.index == index:
                return button
        return None
    
    def draw(self):
        """ Draw menu """
        
        self.clean()
        super(FileMenu, self).draw()        
        self.update()
     
    def add_left_number_listener(self, listener):
        """ Add left button event listener
        
        :param listener: event listener
        """
        if listener not in self.left_number_listeners:
            self.left_number_listeners.append(listener)     
 
    def notify_left_number_listeners(self, index):
        """ Notify left number button listeners
        
        :param index: file index
        """
        for listener in self.left_number_listeners:
            listener(index)
            
    def add_right_number_listener(self, listener):
        """ Add right button event listener
        
        :param listener: event listener
        """
        if listener not in self.right_number_listeners:
            self.right_number_listeners.append(listener)     
 
    def notify_right_number_listeners(self, index):
        """ Notify right number button listeners
        
        :param index: file index
        """
        for listener in self.right_number_listeners:
            listener(index)
            
    def add_change_folder_listener(self, listener):
        """ Add change folder event listener
        
        :param listener: event listener
        """
        if listener not in self.change_folder_listeners:
            self.change_folder_listeners.append(listener)     
 
    def notify_change_folder_listeners(self, state):
        """ Notify change folder listeners
        
        :param state: state object defining new folder
        """
        for listener in self.change_folder_listeners:
            listener(state)
            
    def add_play_file_listener(self, listener):
        """ Add play file event listener
        
        :param listener: event listener
        """
        if listener not in self.play_file_listeners:
            self.play_file_listeners.append(listener)     
 
    def notify_play_file_listeners(self, state):
        """ Notify play file listeners
        
        :param state: state object defining file
        """
        for listener in self.play_file_listeners:
            listener(state)
            
    def add_playlist_size_listener(self, listener):
        """ Add playlist size event listener
        
        :param listener: event listener
        """
        if listener not in self.playlist_size_listeners:
            self.playlist_size_listeners.append(listener)     
 
    def notify_playlist_size_listener(self, size):
        """ Notify playlist size listeners
        
        :param size: playlist size
        """
        for listener in self.playlist_size_listeners:
            listener(size)
            
    def add_menu_navigation_listeners(self, listener):
        """ Add menu navigation event listener
        
        :param listener: event listener
        """
        if listener not in self.menu_navigation_listeners:
            self.menu_navigation_listeners.append(listener)     
 
    def notify_menu_navigation_listeners(self, state):
        """ Notify menu navigation listeners """
        
        for listener in self.menu_navigation_listeners:
            listener(state)
            
Ejemplo n.º 12
0
class CollectionMenu(Menu):
    """ Collection Menu class """
    def __init__(self, util, bgr=None, bounding_box=None, font_size=None):
        """ Initializer

        :param util: utility object
        :param bgr: menu background
        :param bounding_box: bounding box
        :param font_size: labels font size
        """
        self.util = util
        self.factory = Factory(util)
        self.config = util.config
        dbutil = util.get_db_util()
        self.stats = dbutil.get_collection_summary()
        suffix = []
        items = []
        item_list = [
            GENRE, ARTIST, COMPOSER, ALBUM, TITLE, DATE, FOLDER, FILENAME, TYPE
        ]
        if self.config[USAGE][USE_VOICE_ASSISTANT]:
            command_list = [
                "VA_GENRE", "VA_ARTIST", "VA_COMPOSER", "VA_ALBUM", "VA_TITLE",
                "VA_DATE", "VA_FOLDER", "VA_FILENAME", "VA_TYPE"
            ]
            va_commands = self.util.get_voice_commands()

        for n, i in enumerate(item_list):
            if self.stats and self.config[COLLECTION][SHOW_NUMBERS]:
                suffix.append(self.stats[i])

            if i == FOLDER and self.config[COLLECTION_MENU][FOLDER]:
                items.append(KEY_AUDIO_FOLDER)
            elif i == FILENAME and self.config[COLLECTION_MENU][FILENAME]:
                items.append(KEY_FILE)
            elif self.config[COLLECTION_MENU][i]:
                items.append(i)

            if self.config[USAGE][USE_VOICE_ASSISTANT] and i in items:
                self.add_voice_command(i, command_list[n], va_commands)

        m = self.create_collection_main_menu_button
        label_area = ((bounding_box.h / 3) / 100) * (100 - ICON_AREA)
        font_size = int((label_area / 100) * FONT_HEIGHT)
        Menu.__init__(self,
                      util,
                      bgr,
                      bounding_box,
                      None,
                      None,
                      create_item_method=m,
                      font_size=font_size)

        if not items:
            return

        l = self.get_layout(items)
        bounding_box = l.get_next_constraints()
        image_box = self.factory.get_icon_bounding_box(bounding_box,
                                                       ICON_LOCATION,
                                                       ICON_AREA, ICON_SIZE,
                                                       BUTTON_PADDING)

        self.topics = self.util.load_menu(items,
                                          NAME, [],
                                          V_ALIGN_TOP,
                                          bb=image_box,
                                          suffix=suffix)
        self.set_items(self.topics, 0, self.change_topic, False)

        topic = self.config[COLLECTION_PLAYBACK][COLLECTION_TOPIC]
        if topic:
            for k in self.topics.keys():
                if k == topic:
                    self.current_topic = self.topics[k]
                    break
        else:
            self.current_topic = self.topics[items[0]]

        self.item_selected(self.current_topic)

    def create_collection_main_menu_button(self, s, constr, action, scale,
                                           font_size):
        """ Create Collection Main Menu button

        :param s: button state
        :param constr: scaling constraints
        :param action: button event listener
        :param scale: True - scale images, False - don't scale images

        :return: home menu button
        """
        s.padding = BUTTON_PADDING
        s.image_area_percent = ICON_AREA
        s.fixed_height = font_size
        s.v_align = CENTER

        return self.factory.create_menu_button(s,
                                               constr,
                                               action,
                                               scale,
                                               font_size=font_size)

    def add_voice_command(self, name, commands, va_commands):
        """ Add voice command

        :param name: item name
        :param commands: item commands
        :param va_commands: voice commands
        """
        c = []
        for m in commands:
            c.append(va_commands[m].strip())
        self.topics[name].voice_commands = c

    def change_topic(self, state):
        """ Change topic event listener

        :param state: button state
        """
        if not self.visible:
            return
        state.previous_mode = self.current_topic.name
        state.source = "menu"
        self.current_topic = state
        self.notify_listeners(state)