def migrate(force=False): if not force and not migration_needed(): logger.debug("Will not migrate and overwrite data.") return logger.info("Migrating data from 0.2.14....") # allow force to overwrite the new db newdbpath = os.path.join(xdg.get_data_dirs()[0], 'music.db') if os.path.exists(newdbpath): os.remove(newdbpath) oldsettings = SafeConfigParser() oldsettings.read(os.path.expanduser('~/.exaile/settings.ini')) if not olddb.SQLITE_AVAIL: raise MigrationException("Sqlite is not available. " "Unable to migrate 0.2.14 settings") # old database db = olddb.DBManager(os.path.expanduser('~/.exaile/music.db'), False) # new database newdb = collection.Collection('tdb', os.path.join(xdg.get_data_dirs()[0], 'music.db')) _migrate_old_tracks(oldsettings, db, newdb) _migrate_old_settings(oldsettings) settings.MANAGER.save() playlists = PlaylistManager() _migrate_playlists(db, newdb, playlists) logger.info("Migration complete!")
def __init__(self, manager_name, player, hotkey): SmartNotebook.__init__(self) self.tab_manager = PlaylistManager(manager_name) self.manager_name = manager_name self.player = player # For saving closed tab history self._moving_tab = False self.tab_history = [] self.history_counter = 90000 # to get unique (reverse-ordered) item names # Build static menu entries item = menu.simple_separator('clear-sep', []) item.register('playlist-closed-tab-menu', self) item = menu.simple_menu_item( 'clear-history', ['clear-sep'], _("_Clear Tab History"), 'edit-clear-all', self.clear_closed_tabs, ) item.register('playlist-closed-tab-menu', self) # Simple factory for 'Recently Closed Tabs' MenuItem submenu = menu.ProviderMenu('playlist-closed-tab-menu', self) def factory(menu_, parent, context): if self.page_num(parent) == -1: return None item = Gtk.MenuItem.new_with_mnemonic(_("Recently Closed _Tabs")) if len(self.tab_history) > 0: item.set_submenu(submenu) else: item.set_sensitive(False) return item # Add menu to tab context menu item = menu.MenuItem('%s-tab-history' % manager_name, factory, ['tab-close']) item.register('playlist-tab-context-menu') # Add menu to View menu # item = menu.MenuItem('tab-history', factory, ['clear-playlist']) # providers.register('menubar-view-menu', item) # setup notebook actions self.actions = NotebookActionService(self, 'playlist-notebook-actions') # Add hotkey self.accelerator = Accelerator( hotkey, _('Restore closed tab'), lambda *x: self.restore_closed_tab(0) ) providers.register('mainwindow-accelerators', self.accelerator) # Load saved tabs self.load_saved_tabs() self.tab_placement_map = { 'left': Gtk.PositionType.LEFT, 'right': Gtk.PositionType.RIGHT, 'top': Gtk.PositionType.TOP, 'bottom': Gtk.PositionType.BOTTOM, } self.connect('page-added', self.on_page_added) self.connect('page-removed', self.on_page_removed) self.on_option_set('gui_option_set', settings, 'gui/show_tabbar') self.on_option_set('gui_option_set', settings, 'gui/tab_placement') event.add_ui_callback(self.on_option_set, 'gui_option_set')
class PlaylistNotebook(SmartNotebook): def __init__(self, manager_name, player, hotkey): SmartNotebook.__init__(self) self.tab_manager = PlaylistManager(manager_name) self.manager_name = manager_name self.player = player # For saving closed tab history self._moving_tab = False self.tab_history = [] self.history_counter = 90000 # to get unique (reverse-ordered) item names # Build static menu entries item = menu.simple_separator('clear-sep', []) item.register('playlist-closed-tab-menu', self) item = menu.simple_menu_item( 'clear-history', ['clear-sep'], _("_Clear Tab History"), 'edit-clear-all', self.clear_closed_tabs, ) item.register('playlist-closed-tab-menu', self) # Simple factory for 'Recently Closed Tabs' MenuItem submenu = menu.ProviderMenu('playlist-closed-tab-menu', self) def factory(menu_, parent, context): if self.page_num(parent) == -1: return None item = Gtk.MenuItem.new_with_mnemonic(_("Recently Closed _Tabs")) if len(self.tab_history) > 0: item.set_submenu(submenu) else: item.set_sensitive(False) return item # Add menu to tab context menu item = menu.MenuItem('%s-tab-history' % manager_name, factory, ['tab-close']) item.register('playlist-tab-context-menu') # Add menu to View menu # item = menu.MenuItem('tab-history', factory, ['clear-playlist']) # providers.register('menubar-view-menu', item) # setup notebook actions self.actions = NotebookActionService(self, 'playlist-notebook-actions') # Add hotkey self.accelerator = Accelerator( hotkey, _('Restore closed tab'), lambda *x: self.restore_closed_tab(0) ) providers.register('mainwindow-accelerators', self.accelerator) # Load saved tabs self.load_saved_tabs() self.tab_placement_map = { 'left': Gtk.PositionType.LEFT, 'right': Gtk.PositionType.RIGHT, 'top': Gtk.PositionType.TOP, 'bottom': Gtk.PositionType.BOTTOM, } self.connect('page-added', self.on_page_added) self.connect('page-removed', self.on_page_removed) self.on_option_set('gui_option_set', settings, 'gui/show_tabbar') self.on_option_set('gui_option_set', settings, 'gui/tab_placement') event.add_ui_callback(self.on_option_set, 'gui_option_set') def create_tab_from_playlist(self, playlist): """ Create a tab that will contain the passed-in playlist :param playlist: The playlist to create tab from :type playlist: :class:`xl.playlist.Playlist` """ page = PlaylistPage(playlist, self.player) tab = NotebookTab(self, page) self.add_tab(tab, page) return tab def create_new_playlist(self): """ Create a new tab containing a blank playlist. The tab will be automatically given a unique name. """ seen = [] default_playlist_name = _('Playlist %d') # Split into 'Playlist ' and '' default_name_parts = default_playlist_name.split('%d') for n in range(self.get_n_pages()): page = self.get_nth_page(n) name = page.get_page_name() name_parts = [ # 'Playlist 99' => 'Playlist ' name[0 : len(default_name_parts[0])], # 'Playlist 99' => '' name[len(name) - len(default_name_parts[1]) :], ] # Playlist name matches our format if name_parts == default_name_parts: # Extract possible number between name parts number = name[len(name_parts[0]) : len(name) - len(name_parts[1])] try: number = int(number) except ValueError: pass else: seen += [number] seen.sort() n = 1 while True: if n not in seen: break n += 1 playlist = Playlist(default_playlist_name % n) return self.create_tab_from_playlist(playlist) def add_default_tab(self): return self.create_new_playlist() def load_saved_tabs(self): names = self.tab_manager.list_playlists() if not names: return count = -1 count2 = 0 names.sort() # holds the order#'s of the already added tabs added_tabs = {} name_re = re.compile(r'^order(?P<tab>\d+)\.(?P<tag>[^.]*)\.(?P<name>.*)$') for i, name in enumerate(names): match = name_re.match(name) if not match or not match.group('tab') or not match.group('name'): logger.error("`%r` did not match valid playlist file", name) continue logger.debug("Adding playlist %d: %s", i, name) logger.debug( "Tab:%s; Tag:%s; Name:%s", match.group('tab'), match.group('tag'), match.group('name'), ) pl = self.tab_manager.get_playlist(name) pl.name = match.group('name') if match.group('tab') not in added_tabs: self.create_tab_from_playlist(pl) added_tabs[match.group('tab')] = pl pl = added_tabs[match.group('tab')] if match.group('tag') == 'current': count = i if self.player.queue.current_playlist is None: self.player.queue.set_current_playlist(pl) elif match.group('tag') == 'playing': count2 = i self.player.queue.set_current_playlist(pl) # If there's no selected playlist saved, use the currently # playing if count == -1: count = count2 self.set_current_page(count) def save_current_tabs(self): """ Saves the open tabs """ # first, delete the current tabs names = self.tab_manager.list_playlists() for name in names: logger.debug("Removing tab %s", name) self.tab_manager.remove_playlist(name) # TODO: make this generic enough to save other kinds of tabs for n, page in enumerate(self): if not isinstance(page, PlaylistPage): continue tag = '' if page.playlist is self.player.queue.current_playlist: tag = 'playing' elif n == self.get_current_page(): tag = 'current' page.playlist.name = 'order%d.%s.%s' % (n, tag, page.playlist.name) logger.debug('Saving tab %r', page.playlist.name) try: self.tab_manager.save_playlist(page.playlist, True) except Exception: # an exception here could cause exaile to be unable to quit. # Catch all exceptions. logger.exception("Error saving tab %r", page.playlist.name) def show_current_track(self): """ Tries to find the currently playing track and selects it and its containing tab page """ for n, page in enumerate(self): if not isinstance(page, PlaylistPage): continue if page.playlist is not self.player.queue.current_playlist: continue self.set_current_page(n) page.view.scroll_to_cell(page.playlist.current_position) page.view.set_cursor(page.playlist.current_position) return True def on_page_added(self, notebook, child, page_number): """ Updates appearance on page add """ if self.get_n_pages() > 1: # Enforce tabbar visibility self.set_show_tabs(True) def on_page_removed(self, notebook, child, page_number): """ Updates appearance on page removal """ if self.get_n_pages() == 1: self.set_show_tabs(settings.get_option('gui/show_tabbar', True)) # closed tab history if not self._moving_tab: if settings.get_option('gui/save_closed_tabs', True) and isinstance( child, PlaylistPage ): self.save_closed_tab(child.playlist) # Destroy it unless it's the queue page if not isinstance(child, QueuePage): child.destroy() def restore_closed_tab(self, pos=None, playlist=None, item_name=None): ret = self.remove_closed_tab(pos, playlist, item_name) if ret is not None: self.create_tab_from_playlist(ret[0]) def save_closed_tab(self, playlist): # don't let the list grow indefinitely if len(self.tab_history) > settings.get_option('gui/max_closed_tabs', 10): self.remove_closed_tab(-1) # remove last item item_name = 'playlist%05d' % self.history_counter close_time = datetime.now() # define a MenuItem factory that supports dynamic labels def factory(menu_, parent, context): item = None dt = datetime.now() - close_time if dt.seconds > 60: display_name = _( '{playlist_name} ({track_count} tracks, closed {minutes} min ago)' ).format( playlist_name=playlist.name, track_count=len(playlist), minutes=dt.seconds // 60, ) else: display_name = _( '{playlist_name} ({track_count} tracks, closed {seconds} sec ago)' ).format( playlist_name=playlist.name, track_count=len(playlist), seconds=dt.seconds, ) item = Gtk.ImageMenuItem.new_with_mnemonic(display_name) item.set_image( Gtk.Image.new_from_icon_name('music-library', Gtk.IconSize.MENU) ) # Add accelerator to top item if self.tab_history[0][1].name == item_name: key, mods = Gtk.accelerator_parse(self.accelerator.keys) item.add_accelerator( 'activate', menu.FAKEACCELGROUP, key, mods, Gtk.AccelFlags.VISIBLE ) item.connect( 'activate', lambda w: self.restore_closed_tab(item_name=item_name) ) return item # create menuitem item = menu.MenuItem(item_name, factory, []) providers.register('playlist-closed-tab-menu', item, self) self.history_counter -= 1 # add self.tab_history.insert(0, (playlist, item)) def get_closed_tab(self, pos=None, playlist=None, item_name=None): if pos is not None: try: return self.tab_history[pos] except IndexError: return None elif playlist is not None: for (pl, item) in self.tab_history: if pl == playlist: return (pl, item) elif item_name is not None: for (pl, item) in self.tab_history: if item.name == item_name: return (pl, item) return None # remove from menus def remove_closed_tab(self, pos=None, playlist=None, item_name=None): ret = self.get_closed_tab(pos, playlist, item_name) if ret is not None: self.tab_history.remove(ret) providers.unregister('playlist-closed-tab-menu', ret[1], self) return ret def clear_closed_tabs(self, widget, name, parent, context): for i in range(len(self.tab_history)): self.remove_closed_tab(0) def focus_tab(self, tab_nr): """ Selects the playlist notebook tab tab_nr, and gives it the keyboard focus. """ if tab_nr < self.get_n_pages(): self.set_current_page(tab_nr) self.get_current_tab().focus() def select_next_tab(self): """ Selects the previous playlist notebook tab, warping around if the first page is currently displayed. """ tab_nr = self.get_current_page() tab_nr += 1 tab_nr %= self.get_n_pages() self.set_current_page(tab_nr) def select_prev_tab(self): """ Selects the next playlist notebook tab, warping around if the last page is currently displayed. """ tab_nr = self.get_current_page() tab_nr -= 1 tab_nr %= self.get_n_pages() self.set_current_page(tab_nr) def on_option_set(self, event, settings, option): """ Updates appearance on setting change """ if option == 'gui/show_tabbar': show_tabbar = settings.get_option(option, True) if not show_tabbar and self.get_n_pages() > 1: show_tabbar = True self.set_show_tabs(show_tabbar) if option == 'gui/tab_placement': tab_placement = settings.get_option(option, 'top') self.set_tab_pos(self.tab_placement_map[tab_placement])
def __init__(self, manager_name, player, hotkey): SmartNotebook.__init__(self) self.tab_manager = PlaylistManager(manager_name) self.manager_name = manager_name self.player = player # For saving closed tab history self._moving_tab = False self.tab_history = [] self.history_counter = 90000 # to get unique (reverse-ordered) item names # Build static menu entries item = menu.simple_separator('clear-sep',[]) item.register('playlist-closed-tab-menu', self) item = menu.simple_menu_item('clear-history', ['clear-sep'], None, 'gtk-clear', self.clear_closed_tabs) item.register('playlist-closed-tab-menu', self) # Simple factory for 'Recently Closed Tabs' MenuItem submenu = menu.ProviderMenu('playlist-closed-tab-menu',self) def factory(menu_, parent, context): if self.page_num(parent) == -1: return None item = gtk.MenuItem(_("Recently Closed Tabs")) if len(self.tab_history) > 0: item.set_submenu(submenu) else: item.set_sensitive(False) return item # Add menu to tab context menu item = menu.MenuItem('%s-tab-history' % manager_name, factory, ['tab-close']) item.register('playlist-tab-context-menu') # Add menu to View menu #item = menu.MenuItem('tab-history', factory, ['clear-playlist']) #providers.register('menubar-view-menu', item) # setup notebook actions self.actions = NotebookActionService(self, 'playlist-notebook-actions') # Add hotkey self.accelerator = Accelerator(hotkey, lambda *x: self.restore_closed_tab(0)) providers.register('mainwindow-accelerators',self.accelerator) # Load saved tabs self.load_saved_tabs() self.tab_placement_map = { 'left': gtk.POS_LEFT, 'right': gtk.POS_RIGHT, 'top': gtk.POS_TOP, 'bottom': gtk.POS_BOTTOM } self.connect('page-added', self.on_page_added) self.connect('page-removed', self.on_page_removed) self.on_option_set('gui_option_set', settings, 'gui/show_tabbar') self.on_option_set('gui_option_set', settings, 'gui/tab_placement') event.add_callback(self.on_option_set, 'gui_option_set')
class PlaylistNotebook(SmartNotebook): def __init__(self, manager_name, player, hotkey): SmartNotebook.__init__(self) self.tab_manager = PlaylistManager(manager_name) self.manager_name = manager_name self.player = player # For saving closed tab history self._moving_tab = False self.tab_history = [] self.history_counter = 90000 # to get unique (reverse-ordered) item names # Build static menu entries item = menu.simple_separator('clear-sep',[]) item.register('playlist-closed-tab-menu', self) item = menu.simple_menu_item('clear-history', ['clear-sep'], None, 'gtk-clear', self.clear_closed_tabs) item.register('playlist-closed-tab-menu', self) # Simple factory for 'Recently Closed Tabs' MenuItem submenu = menu.ProviderMenu('playlist-closed-tab-menu',self) def factory(menu_, parent, context): if self.page_num(parent) == -1: return None item = gtk.MenuItem(_("Recently Closed Tabs")) if len(self.tab_history) > 0: item.set_submenu(submenu) else: item.set_sensitive(False) return item # Add menu to tab context menu item = menu.MenuItem('%s-tab-history' % manager_name, factory, ['tab-close']) item.register('playlist-tab-context-menu') # Add menu to View menu #item = menu.MenuItem('tab-history', factory, ['clear-playlist']) #providers.register('menubar-view-menu', item) # setup notebook actions self.actions = NotebookActionService(self, 'playlist-notebook-actions') # Add hotkey self.accelerator = Accelerator(hotkey, lambda *x: self.restore_closed_tab(0)) providers.register('mainwindow-accelerators',self.accelerator) # Load saved tabs self.load_saved_tabs() self.tab_placement_map = { 'left': gtk.POS_LEFT, 'right': gtk.POS_RIGHT, 'top': gtk.POS_TOP, 'bottom': gtk.POS_BOTTOM } self.connect('page-added', self.on_page_added) self.connect('page-removed', self.on_page_removed) self.on_option_set('gui_option_set', settings, 'gui/show_tabbar') self.on_option_set('gui_option_set', settings, 'gui/tab_placement') event.add_callback(self.on_option_set, 'gui_option_set') def create_tab_from_playlist(self, playlist): """ Create a tab that will contain the passed-in playlist :param playlist: The playlist to create tab from :type playlist: :class:`xl.playlist.Playlist` """ page = PlaylistPage(playlist, self.player) tab = NotebookTab(self, page) self.add_tab(tab, page) return tab def create_new_playlist(self): """ Create a new tab containing a blank playlist. The tab will be automatically given a unique name. """ seen = [] default_playlist_name = _('Playlist %d') # Split into 'Playlist ' and '' default_name_parts = default_playlist_name.split('%d') for n in range(self.get_n_pages()): page = self.get_nth_page(n) name = page.get_page_name() name_parts = [ # 'Playlist 99' => 'Playlist ' name[0:len(default_name_parts[0])], # 'Playlist 99' => '' name[len(name) - len(default_name_parts[1]):] ] # Playlist name matches our format if name_parts == default_name_parts: # Extract possible number between name parts number = name[len(name_parts[0]):len(name) - len(name_parts[1])] try: number = int(number) except ValueError: pass else: seen += [number] seen.sort() n = 1 while True: if n not in seen: break n += 1 playlist = Playlist(default_playlist_name % n) return self.create_tab_from_playlist(playlist) def add_default_tab(self): return self.create_new_playlist() def load_saved_tabs(self): names = self.tab_manager.list_playlists() if not names: return count = -1 count2 = 0 names.sort() # holds the order#'s of the already added tabs added_tabs = {} name_re = re.compile( r'^order(?P<tab>\d+)\.(?P<tag>[^.]*)\.(?P<name>.*)$') for i, name in enumerate(names): match = name_re.match(name) if not match or not match.group('tab') or not match.group('name'): logger.error("%s did not match valid playlist file" % repr(name)) continue logger.debug("Adding playlist %d: %s" % (i, name)) logger.debug("Tab:%s; Tag:%s; Name:%s" % (match.group('tab'), match.group('tag'), match.group('name'), )) pl = self.tab_manager.get_playlist(name) pl.name = match.group('name') if match.group('tab') not in added_tabs: self.create_tab_from_playlist(pl) added_tabs[match.group('tab')] = pl pl = added_tabs[match.group('tab')] if match.group('tag') == 'current': count = i if self.player.queue.current_playlist is None: self.player.queue.set_current_playlist(pl) elif match.group('tag') == 'playing': count2 = i self.player.queue.set_current_playlist(pl) # If there's no selected playlist saved, use the currently # playing if count == -1: count = count2 self.set_current_page(count) def save_current_tabs(self): """ Saves the open tabs """ # first, delete the current tabs names = self.tab_manager.list_playlists() for name in names: logger.debug("Removing tab %s" % name) self.tab_manager.remove_playlist(name) # TODO: make this generic enough to save other kinds of tabs for n, page in enumerate(self): if not isinstance(page, PlaylistPage): continue tag = '' if page.playlist is self.player.queue.current_playlist: tag = 'playing' elif n == self.get_current_page(): tag = 'current' page.playlist.name = 'order%d.%s.%s' % (n, tag, page.playlist.name) logger.debug('Saving tab %r', page.playlist.name) try: self.tab_manager.save_playlist(page.playlist, True) except Exception: # an exception here could cause exaile to be unable to quit. # Catch all exceptions. logger.exception("Error saving tab %r", page.playlist.name) def show_current_track(self): """ Tries to find the currently playing track and selects it and its containing tab page """ for n, page in enumerate(self): if not isinstance(page, PlaylistPage): continue if page.playlist is not self.player.queue.current_playlist: continue self.set_current_page(n) page.view.scroll_to_cell(page.playlist.current_position) page.view.set_cursor(page.playlist.current_position) return True def on_page_added(self, notebook, child, page_number): """ Updates appearance on page add """ if self.get_n_pages() > 1: # Enforce tabbar visibility self.set_show_tabs(True) def on_page_removed(self, notebook, child, page_number): """ Updates appearance on page removal """ if self.get_n_pages() == 1: self.set_show_tabs(settings.get_option('gui/show_tabbar', True)) # closed tab history if not self._moving_tab and \ settings.get_option('gui/save_closed_tabs', True) and \ isinstance(child, PlaylistPage): self.save_closed_tab(child.playlist) def restore_closed_tab(self, pos=None, playlist=None, item_name=None): ret = self.remove_closed_tab(pos, playlist, item_name) if ret is not None: self.create_tab_from_playlist(ret[0]) def save_closed_tab(self, playlist): # don't let the list grow indefinitely items = providers.get('playlist-closed-tab-menu', self) if len(self.tab_history) > settings.get_option('gui/max_closed_tabs', 10): self.remove_closed_tab(-1) # remove last item item_name = 'playlist%05d'%self.history_counter close_time = datetime.now() # define a MenuItem factory that supports dynamic labels def factory(menu_, parent, context): item = None dt = (datetime.now() - close_time) if dt.seconds > 60: display_name = _('{playlist_name} ({track_count} tracks, closed {minutes} min ago)').format( playlist_name=playlist.name, track_count=len(playlist), minutes=dt.seconds // 60 ) else: display_name = _('{playlist_name} ({track_count} tracks, closed {seconds} sec ago)').format( playlist_name=playlist.name, track_count=len(playlist), seconds=dt.seconds ) item = gtk.ImageMenuItem(display_name) item.set_image(gtk.image_new_from_icon_name('music-library', gtk.ICON_SIZE_MENU)) # Add accelerator to top item if self.tab_history[0][1].name == item_name: key, mods = gtk.accelerator_parse(self.accelerator.keys) item.add_accelerator('activate', menu.FAKEACCELGROUP, key, mods, gtk.ACCEL_VISIBLE) item.connect('activate', lambda w: self.restore_closed_tab(item_name=item_name)) return item # create menuitem item = menu.MenuItem(item_name, factory, []) providers.register('playlist-closed-tab-menu', item, self) self.history_counter -= 1 # add self.tab_history.insert(0, (playlist,item)) def get_closed_tab(self, pos=None, playlist=None, item_name=None): if pos is not None: try: return self.tab_history[pos] except IndexError: return None elif playlist is not None: for (pl, item) in self.tab_history: if pl == playlist: return (pl, item) elif item_name is not None: for (pl, item) in self.tab_history: if item.name == item_name: return (pl, item) return None # remove from menus def remove_closed_tab(self, pos=None, playlist=None, item_name=None): ret = self.get_closed_tab(pos, playlist, item_name) if ret is not None: self.tab_history.remove(ret) providers.unregister('playlist-closed-tab-menu', ret[1], self) return ret def clear_closed_tabs(self, widget, name, parent, context): for i in xrange(len(self.tab_history)): self.remove_closed_tab(0) def focus_tab(self, tab_nr): """ Selects the playlist notebook tab tab_nr, and gives it the keyboard focus. """ if tab_nr < self.get_n_pages(): self.set_current_page(tab_nr) self.get_current_tab().focus() def select_next_tab(self): """ Selects the previous playlist notebook tab, warping around if the first page is currently displayed. """ tab_nr = self.get_current_page() tab_nr += 1 tab_nr %= self.get_n_pages() self.set_current_page(tab_nr) def select_prev_tab(self): """ Selects the next playlist notebook tab, warping around if the last page is currently displayed. """ tab_nr = self.get_current_page() tab_nr -= 1 tab_nr %= self.get_n_pages() self.set_current_page(tab_nr) def on_option_set(self, event, settings, option): """ Updates appearance on setting change """ if option == 'gui/show_tabbar': show_tabbar = settings.get_option(option, True) if not show_tabbar and self.get_n_pages() > 1: show_tabbar = True glib.idle_add(self.set_show_tabs, show_tabbar) if option == 'gui/tab_placement': tab_placement = settings.get_option(option, 'top') glib.idle_add(self.set_tab_pos, self.tab_placement_map[tab_placement])