diff options
author | Jesús Eduardo <heckyel@hyperbola.info> | 2017-09-11 17:47:17 -0500 |
---|---|---|
committer | Jesús Eduardo <heckyel@hyperbola.info> | 2017-09-11 17:47:17 -0500 |
commit | 14738704ede6dfa6ac79f362a9c1f7f40f470cdc (patch) | |
tree | 31c83bdd188ae7b64d7169974d6f066ccfe95367 /lvc/widgets/gtk/gtkmenus.py | |
parent | eb1896583afbbb622cadcde1a24e17173f61904f (diff) | |
download | librevideoconverter-14738704ede6dfa6ac79f362a9c1f7f40f470cdc.tar.lz librevideoconverter-14738704ede6dfa6ac79f362a9c1f7f40f470cdc.tar.xz librevideoconverter-14738704ede6dfa6ac79f362a9c1f7f40f470cdc.zip |
rename mvc at lvc
Diffstat (limited to 'lvc/widgets/gtk/gtkmenus.py')
-rw-r--r-- | lvc/widgets/gtk/gtkmenus.py | 404 |
1 files changed, 404 insertions, 0 deletions
diff --git a/lvc/widgets/gtk/gtkmenus.py b/lvc/widgets/gtk/gtkmenus.py new file mode 100644 index 0000000..0e89fa8 --- /dev/null +++ b/lvc/widgets/gtk/gtkmenus.py @@ -0,0 +1,404 @@ +# @Base: Miro - an RSS based video player application +# Copyright (C) 2011 +# Participatory Culture Foundation +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# +# In addition, as a special exception, the copyright holders give +# permission to link the code of portions of this program with the OpenSSL +# library. +# +# You must obey the GNU General Public License in all respects for all of +# the code used other than OpenSSL. If you modify file(s) with this +# exception, you may extend this exception to your version of the file(s), +# but you are not obligated to do so. If you do not wish to do so, delete +# this exception statement from your version. If you delete this exception +# statement from all source files in the program, then also delete it here. + +"""gtkmenus.py -- Manage menu layout.""" + +import gtk + +from lvc.widgets import app + +import base +import keymap +import wrappermap + +def _setup_accel(widget, name, shortcut=None): + """Setup accelerators for a menu item. + + This method sets an accel path for the widget and optionally connects a + shortcut to that accel path. + """ + # The GTK docs say that we should set the path using this form: + # <Window-Name>/Menu/Submenu/MenuItem + # ...but this is hard to do because we don't yet know what window/menu + # this menu item is going to be added to. gtk.Action and gtk.ActionGroup + # don't follow the above suggestion, so we don't need to either. + path = "<MiroActions>/MenuBar/%s" % name + widget.set_accel_path(path) + if shortcut is not None: + accel_string = keymap.get_accel_string(shortcut) + key, mods = gtk.accelerator_parse(accel_string) + if gtk.accel_map_lookup_entry(path) is None: + gtk.accel_map_add_entry(path, key, mods) + else: + gtk.accel_map_change_entry(path, key, mods, True) + +# map menu names to GTK stock ids. +_STOCK_IDS = { + "SaveItem": gtk.STOCK_SAVE, + "CopyItemURL": gtk.STOCK_COPY, + "RemoveItems": gtk.STOCK_REMOVE, + "StopItem": gtk.STOCK_MEDIA_STOP, + "NextItem": gtk.STOCK_MEDIA_NEXT, + "PreviousItem": gtk.STOCK_MEDIA_PREVIOUS, + "PlayPauseItem": gtk.STOCK_MEDIA_PLAY, + "Open": gtk.STOCK_OPEN, + "EditPreferences": gtk.STOCK_PREFERENCES, + "Quit": gtk.STOCK_QUIT, + "Help": gtk.STOCK_HELP, + "About": gtk.STOCK_ABOUT, + "Translate": gtk.STOCK_EDIT +} +try: + _STOCK_IDS['Fullscreen'] = gtk.STOCK_FULLSCREEN +except AttributeError: + # fullscreen not available on all GTK versions + pass + +class MenuItemBase(base.Widget): + """Base class for MenuItem and Separator.""" + + def show(self): + """Show this menu item.""" + self._widget.show() + + def hide(self): + """Hide and disable this menu item.""" + self._widget.hide() + + def remove_from_parent(self): + """Remove this menu item from it's parent Menu.""" + parent_menu = self._widget.get_parent() + if parent_menu is None: + return + parent_menu_item = parent_menu.get_attach_widget() + if parent_menu_item is None: + return + parent_menu_item.remove(self._widget) + + def _set_accel_group(self, accel_group): + # menu items don't care about the accel group, their parent Menu + # handles it for them + pass + +class MenuItem(MenuItemBase): + """Single item in the menu that can be clicked + + :param label: The label it has (must be internationalized) + :param name: String identifier for this item + :param shortcut: Shortcut object to use + + Signals: + - activate: menu item was clicked + + Example: + + >>> MenuItem(_("Preferences"), "EditPreferences") + >>> MenuItem(_("Cu_t"), "ClipboardCut", Shortcut("x", MOD)) + >>> MenuItem(_("_Update Podcasts and Library"), "UpdatePodcasts", + ... (Shortcut("r", MOD), Shortcut(F5))) + >>> MenuItem(_("_Play"), "PlayPauseItem", + ... play=_("_Play"), pause=_("_Pause")) + """ + + def __init__(self, label, name, shortcut=None): + MenuItemBase.__init__(self) + self.name = name + self.set_widget(self.make_widget(label)) + self.activate_id = self.wrapped_widget_connect('activate', + self._on_activate) + self._widget.show() + self.create_signal('activate') + _setup_accel(self._widget, self.name, shortcut) + + def _on_activate(self, menu_item): + self.emit('activate') + gtk_menubar = self._find_menubar() + if gtk_menubar is not None: + try: + menubar = wrappermap.wrapper(gtk_menubar) + except KeyError: + logging.exception('menubar activate: ' + 'no wrapper for gtbbk.MenuBar') + else: + menubar.emit('activate', self.name) + + def _find_menubar(self): + """Find the MenuBar that this menu item is attached to.""" + menu_item = self._widget + while True: + parent_menu = menu_item.get_parent() + if isinstance(parent_menu, gtk.MenuBar): + return parent_menu + elif parent_menu is None: + return None + menu_item = parent_menu.get_attach_widget() + if menu_item is None: + return None + + def make_widget(self, label): + """Create the menu item to use for this widget. + + Subclasses will probably want to override this. + """ + if self.name in _STOCK_IDS: + mi = gtk.ImageMenuItem(stock_id=_STOCK_IDS[self.name]) + mi.set_label(label) + return mi + else: + return gtk.MenuItem(label) + + def set_label(self, new_label): + self._widget.set_label(new_label) + + def get_label(self): + self._widget.get_label() + +class CheckMenuItem(MenuItem): + """MenuItem that toggles on/off""" + + def make_widget(self, label): + return gtk.CheckMenuItem(label) + + def set_state(self, active): + # prevent the activate signal from fireing in response to us manually + # changing a value + self._widget.handler_block(self.activate_id) + if active is not None: + self._widget.set_inconsistent(False) + self._widget.set_active(active) + else: + self._widget.set_inconsistent(True) + self._widget.set_active(False) + self._widget.handler_unblock(self.activate_id) + + def get_state(self): + return self._widget.get_active() + +class RadioMenuItem(CheckMenuItem): + """MenuItem that toggles on/off and is grouped with other RadioMenuItems. + """ + + def make_widget(self, label): + widget = gtk.RadioMenuItem() + widget.set_label(label) + return widget + + def set_group(self, group_item): + self._widget.set_group(group_item._widget) + + def remove_from_group(self): + """Remove this RadioMenuItem from its current group.""" + self._widget.set_group(None) + + def _on_activate(self, menu_item): + # GTK sends the activate signal for both the radio button that's + # toggled on and the one that gets turned off. Just emit our signal + # for the active radio button. + if self.get_state(): + MenuItem._on_activate(self, menu_item) + +class Separator(MenuItemBase): + """Separator item for menus""" + + def __init__(self): + MenuItemBase.__init__(self) + self.set_widget(gtk.SeparatorMenuItem()) + self._widget.show() + # Set name to be None just so that it has a similar API to other menu + # items. + self.name = None + +class MenuShell(base.Widget): + """Common code shared between Menu and MenuBar. + + Subclasses must define a _menu attribute that's a gtk.MenuShell subclass. + """ + + def __init__(self): + base.Widget.__init__(self) + self._accel_group = None + self.children = [] + + def append(self, menu_item): + """Add a menu item to the end of this menu.""" + self.children.append(menu_item) + menu_item._set_accel_group(self._accel_group) + self._menu.append(menu_item._widget) + + def insert(self, index, menu_item): + """Insert a menu item in the middle of this menu.""" + self.children.insert(index, menu_item) + menu_item._set_accel_group(self._accel_group) + self._menu.insert(menu_item._widget, index) + + def remove(self, menu_item): + """Remove a child menu item. + + :raises ValueError: menu_item is not a child of this menu + """ + self.children.remove(menu_item) + self._menu.remove(menu_item._widget) + menu_item._set_accel_group(None) + + def index(self, name): + """Get the position of a menu item in this list. + + :param name: name of the menu + :returns: index of the menu item, or -1 if not found. + """ + for i, menu_item in enumerate(self.children): + if menu_item.name == name: + return i + return -1 + + def get_children(self): + """Get the child menu items in order.""" + return list(self.children) + + def find(self, name): + """Search for a menu or menu item + + This method recursively searches the entire menu structure for a Menu + or MenuItem object with a given name. + + :raises KeyError: name not found + """ + found = self._find(name) + if found is None: + raise KeyError(name) + else: + return found + + def _find(self, name): + """Low-level helper-method for find(). + + :returns: found menu item or None. + """ + for menu_item in self.get_children(): + if menu_item.name == name: + return menu_item + if isinstance(menu_item, MenuShell): + submenu_find = menu_item._find(name) + if submenu_find is not None: + return submenu_find + return None + +class Menu(MenuShell): + """A Menu holds a list of MenuItems and Menus. + + Example: + >>> Menu(_("P_layback"), "Playback", [ + ... MenuItem(_("_Foo"), "Foo"), + ... MenuItem(_("_Bar"), "Bar") + ... ]) + >>> Menu("", "toplevel", [ + ... Menu(_("_File"), "File", [ ... ]) + ... ]) + """ + + def __init__(self, label, name, child_items): + MenuShell.__init__(self) + self.set_widget(gtk.MenuItem(label)) + self._widget.show() + self.name = name + # set up _menu for the MenuShell code + self._menu = gtk.Menu() + _setup_accel(self._menu, self.name) + self._widget.set_submenu(self._menu) + for item in child_items: + self.append(item) + + def show(self): + """Show this menu.""" + self._widget.show() + + def hide(self): + """Hide this menu.""" + self._widget.hide() + + def _set_accel_group(self, accel_group): + """Set the accel group for this widget. + + Accel groups get created by the MenuBar. Whenever a menu or menu item + is added to that menu bar, the parent calls _set_accel_group() to give + the accel group to the child. + """ + if accel_group == self._accel_group: + return + self._menu.set_accel_group(accel_group) + self._accel_group = accel_group + for child in self.children: + child._set_accel_group(accel_group) + +class MenuBar(MenuShell): + """Displays a list of Menu items. + + Signals: + + - activate(menu_bar, name): a menu item was activated + """ + + def __init__(self): + """Create a new MenuBar + + :param name: string id to use for our action group + """ + MenuShell.__init__(self) + self.create_signal('activate') + self.set_widget(gtk.MenuBar()) + self._widget.show() + self._accel_group = gtk.AccelGroup() + # set up _menu for the MenuShell code + self._menu = self._widget + + def get_accel_group(self): + return self._accel_group + +class MainWindowMenuBar(MenuBar): + """MenuBar for the main window. + + This gets installed into app.widgetapp.menubar on GTK. + """ + def add_initial_menus(self, menus): + """Add the initial set of menus. + + We modify the menu structure slightly for GTK. + """ + for menu in menus: + self.append(menu) + self._modify_initial_menus() + + def _modify_initial_menus(self): + """Update the portable root menu with GTK-specific stuff.""" + # on linux, we don't have a CheckVersion option because + # we update with the package system. + #this_platform = app.config.get(prefs.APP_PLATFORM) + #if this_platform == 'linux': + # self.find("CheckVersion").remove_from_parent() + #app.video_renderer.setup_subtitle_encoding_menu() |