""" Copyright 2007, 2008, 2009, 2015, 2016 Free Software Foundation, Inc. This file is part of GNU Radio GNU Radio Companion 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. GNU Radio Companion 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 Street, Fifth Floor, Boston, MA 02110-1301, USA """ from __future__ import absolute_import import logging from gi.repository import Gtk, GObject, Gio, GLib from . import Actions log = logging.getLogger(__name__) ''' # Menu/Toolbar Lists: # # Sub items can be 1 of 3 types # - List Creates a section within the current menu # - Tuple Creates a submenu using a string or action as the parent. The child # can be another menu list or an identifier used to call a helper function. # - Action Appends a new menu item to the current menu # LIST_NAME = [ [Action1, Action2], # New section (Action3, [Action4, Action5]), # Submenu with action as parent ("Label", [Action6, Action7]), # Submenu with string as parent ("Label2", "helper") # Submenu with helper function. Calls 'create_helper()' ] ''' # The list of actions for the toolbar. TOOLBAR_LIST = [ [(Actions.FLOW_GRAPH_NEW, 'flow_graph_new_type'), (Actions.FLOW_GRAPH_OPEN, 'flow_graph_recent'), Actions.FLOW_GRAPH_SAVE, Actions.FLOW_GRAPH_CLOSE], [Actions.TOGGLE_FLOW_GRAPH_VAR_EDITOR, Actions.FLOW_GRAPH_SCREEN_CAPTURE], [Actions.BLOCK_CUT, Actions.BLOCK_COPY, Actions.BLOCK_PASTE, Actions.ELEMENT_DELETE], [Actions.FLOW_GRAPH_UNDO, Actions.FLOW_GRAPH_REDO], [Actions.ERRORS_WINDOW_DISPLAY, Actions.FLOW_GRAPH_GEN, Actions.FLOW_GRAPH_EXEC, Actions.FLOW_GRAPH_KILL], [Actions.BLOCK_ROTATE_CCW, Actions.BLOCK_ROTATE_CW], [Actions.BLOCK_ENABLE, Actions.BLOCK_DISABLE, Actions.BLOCK_BYPASS, Actions.TOGGLE_HIDE_DISABLED_BLOCKS], [Actions.FIND_BLOCKS, Actions.RELOAD_BLOCKS, Actions.OPEN_HIER] ] # The list of actions and categories for the menu bar. MENU_BAR_LIST = [ ('_File', [ [(Actions.FLOW_GRAPH_NEW, 'flow_graph_new_type'), Actions.FLOW_GRAPH_DUPLICATE, Actions.FLOW_GRAPH_OPEN, (Actions.FLOW_GRAPH_OPEN_RECENT, 'flow_graph_recent')], [Actions.FLOW_GRAPH_SAVE, Actions.FLOW_GRAPH_SAVE_AS, Actions.FLOW_GRAPH_SAVE_COPY], [Actions.FLOW_GRAPH_SCREEN_CAPTURE], [Actions.FLOW_GRAPH_CLOSE, Actions.APPLICATION_QUIT] ]), ('_Edit', [ [Actions.FLOW_GRAPH_UNDO, Actions.FLOW_GRAPH_REDO], [Actions.BLOCK_CUT, Actions.BLOCK_COPY, Actions.BLOCK_PASTE, Actions.ELEMENT_DELETE, Actions.SELECT_ALL], [Actions.BLOCK_ROTATE_CCW, Actions.BLOCK_ROTATE_CW, ('_Align', Actions.BLOCK_ALIGNMENTS)], [Actions.BLOCK_ENABLE, Actions.BLOCK_DISABLE, Actions.BLOCK_BYPASS], [Actions.BLOCK_PARAM_MODIFY] ]), ('_View', [ [Actions.TOGGLE_BLOCKS_WINDOW], [Actions.TOGGLE_CONSOLE_WINDOW, Actions.TOGGLE_SCROLL_LOCK, Actions.SAVE_CONSOLE, Actions.CLEAR_CONSOLE], [Actions.TOGGLE_HIDE_VARIABLES, Actions.TOGGLE_FLOW_GRAPH_VAR_EDITOR, Actions.TOGGLE_FLOW_GRAPH_VAR_EDITOR_SIDEBAR], [Actions.TOGGLE_HIDE_DISABLED_BLOCKS, Actions.TOGGLE_AUTO_HIDE_PORT_LABELS, Actions.TOGGLE_SNAP_TO_GRID, Actions.TOGGLE_SHOW_BLOCK_COMMENTS], [Actions.TOGGLE_SHOW_CODE_PREVIEW_TAB], [Actions.ERRORS_WINDOW_DISPLAY, Actions.FIND_BLOCKS], ]), ('_Run', [ Actions.FLOW_GRAPH_GEN, Actions.FLOW_GRAPH_EXEC, Actions.FLOW_GRAPH_KILL ]), ('_Tools', [ [Actions.TOOLS_RUN_FDESIGN, Actions.FLOW_GRAPH_OPEN_QSS_THEME], [Actions.TOGGLE_SHOW_FLOWGRAPH_COMPLEXITY] ]), ('_Help', [ [Actions.HELP_WINDOW_DISPLAY, Actions.TYPES_WINDOW_DISPLAY, Actions.XML_PARSER_ERRORS_DISPLAY], [Actions.ABOUT_WINDOW_DISPLAY] ])] # The list of actions for the context menu. CONTEXT_MENU_LIST = [ [Actions.BLOCK_CUT, Actions.BLOCK_COPY, Actions.BLOCK_PASTE, Actions.ELEMENT_DELETE], [Actions.BLOCK_ROTATE_CCW, Actions.BLOCK_ROTATE_CW, Actions.BLOCK_ENABLE, Actions.BLOCK_DISABLE, Actions.BLOCK_BYPASS], [("_More", [ [Actions.BLOCK_CREATE_HIER, Actions.OPEN_HIER], [Actions.BUSSIFY_SOURCES, Actions.BUSSIFY_SINKS]] )], [Actions.BLOCK_PARAM_MODIFY], ] class SubMenuHelper(object): ''' Generates custom submenus for the main menu or toolbar. ''' def __init__(self): self.submenus = {} def build_submenu(self, name, obj, set_func): # Get the correct helper function create_func = getattr(self, "create_{}".format(name)) # Save the helper functions for rebuilding the menu later self.submenus[name] = (create_func, obj, set_func) # Actually build the menu set_func(obj, create_func()) def refresh_submenus(self): for name in self.submenus: create_func, obj, set_func = self.submenus[name] set_func(obj, create_func()) def create_flow_graph_new_type(self): """ Different flowgraph types """ menu = Gio.Menu() platform = Gtk.Application.get_default().platform generate_modes = platform.get_generate_options() for key, name, default in generate_modes: target = "app.flowgraph.new_type::{}".format(key) menu.append(name, target) return menu def create_flow_graph_recent(self): """ Recent flow graphs """ config = Gtk.Application.get_default().config recent_files = config.get_recent_files() menu = Gio.Menu() if len(recent_files) > 0: files = Gio.Menu() for i, file_name in enumerate(recent_files): target = "app.flowgraph.open_recent::{}".format(file_name) files.append(file_name.replace("_", "__"), target) menu.append_section(None, files) #clear = Gio.Menu() #clear.append("Clear recent files", "app.flowgraph.clear_recent") #menu.append_section(None, clear) else: # Show an empty menu menuitem = Gio.MenuItem.new("No items found", "app.none") menu.append_item(menuitem) return menu class MenuHelper(SubMenuHelper): """ Recursively builds a menu from a given list of actions. Args: - actions: List of actions to build the menu - menu: Current menu being built Notes: - Tuple: Create a new submenu from the parent (1st) and child (2nd) elements - Action: Append to current menu - List: Start a new section """ def __init__(self): SubMenuHelper.__init__(self) def build_menu(self, actions, menu): for item in actions: if isinstance(item, tuple): # Create a new submenu parent, child = (item[0], item[1]) # Create the parent label, target = (parent, None) if isinstance(parent, Actions.Action): label = parent.label target = "{}.{}".format(parent.prefix, parent.name) menuitem = Gio.MenuItem.new(label, None) if hasattr(parent, "icon_name"): menuitem.set_icon(Gio.Icon.new_for_string(parent.icon_name)) # Create the new submenu if isinstance(child, list): submenu = Gio.Menu() self.build_menu(child, submenu) menuitem.set_submenu(submenu) elif isinstance(child, str): # Child is the name of the submenu to create def set_func(obj, menu): obj.set_submenu(menu) self.build_submenu(child, menuitem, set_func) menu.append_item(menuitem) elif isinstance(item, list): # Create a new section section = Gio.Menu() self.build_menu(item, section) menu.append_section(None, section) elif isinstance(item, Actions.Action): # Append a new menuitem target = "{}.{}".format(item.prefix, item.name) menuitem = Gio.MenuItem.new(item.label, target) if item.icon_name: menuitem.set_icon(Gio.Icon.new_for_string(item.icon_name)) menu.append_item(menuitem) class ToolbarHelper(SubMenuHelper): """ Builds a toolbar from a given list of actions. Args: - actions: List of actions to build the menu - item: Current menu being built Notes: - Tuple: Create a new submenu from the parent (1st) and child (2nd) elements - Action: Append to current menu - List: Start a new section """ def __init__(self): SubMenuHelper.__init__(self) def build_toolbar(self, actions, current): for item in actions: if isinstance(item, list): # Toolbar's don't have sections like menus, so call this function # recursively with the "section" and just append a separator. self.build_toolbar(item, self) current.insert(Gtk.SeparatorToolItem.new(), -1) elif isinstance(item, tuple): parent, child = (item[0], item[1]) # Create an item with a submenu # Generate the submenu and add to the item. # Add the item to the toolbar button = Gtk.MenuToolButton.new() # The tuple should be made up of an Action and something. button.set_label(parent.label) button.set_tooltip_text(parent.tooltip) button.set_icon_name(parent.icon_name) target = "{}.{}".format(parent.prefix, parent.name) button.set_action_name(target) def set_func(obj, menu): obj.set_menu(Gtk.Menu.new_from_model(menu)) self.build_submenu(child, button, set_func) current.insert(button, -1) elif isinstance(item, Actions.Action): button = Gtk.ToolButton.new() button.set_label(item.label) button.set_tooltip_text(item.tooltip) button.set_icon_name(item.icon_name) target = "{}.{}".format(item.prefix, item.name) button.set_action_name(target) current.insert(button, -1) class Menu(Gio.Menu, MenuHelper): """ Main Menu """ def __init__(self): GObject.GObject.__init__(self) MenuHelper.__init__(self) log.debug("Building the main menu") self.build_menu(MENU_BAR_LIST, self) class ContextMenu(Gio.Menu, MenuHelper): """ Context menu for the drawing area """ def __init__(self): GObject.GObject.__init__(self) log.debug("Building the context menu") self.build_menu(CONTEXT_MENU_LIST, self) class Toolbar(Gtk.Toolbar, ToolbarHelper): """ The gtk toolbar with actions added from the toolbar list. """ def __init__(self): """ Parse the list of action names in the toolbar list. Look up the action for each name in the action list and add it to the toolbar. """ GObject.GObject.__init__(self) ToolbarHelper.__init__(self) self.set_style(Gtk.ToolbarStyle.ICONS) #self.get_style_context().add_class(Gtk.STYLE_CLASS_PRIMARY_TOOLBAR) #SubMenuCreator.__init__(self) self.build_toolbar(TOOLBAR_LIST, self)