From d7db84e282a0755594e368a516037243f29e1120 Mon Sep 17 00:00:00 2001 From: John Beard Date: Thu, 12 Jan 2017 01:22:53 +0800 Subject: [PATCH] Break out tools' common menu functions and ownership Add a class TOOL_MENU, which provides a management class for a CONDITIONAL_MENU and a set of CONTEXT_MENUs. The aim of this is to provide a central place where all TOOL_INTERACTIVEs can get a "basic" context menu that either they or other tools can register new items and sub-menus against. This means that "top-level" tools no longer need to manage the lifetimes of any CONTEXT_MENUs that they add, and can also delegate simple menu display functions. --- pcbnew/CMakeLists.txt | 1 + pcbnew/tools/drawing_tool.cpp | 48 ++------- pcbnew/tools/drawing_tool.h | 16 +-- pcbnew/tools/selection_tool.cpp | 46 ++------- pcbnew/tools/selection_tool.h | 17 +--- pcbnew/tools/tool_menu.cpp | 104 +++++++++++++++++++ pcbnew/tools/tool_menu.h | 173 ++++++++++++++++++++++++++++++++ 7 files changed, 307 insertions(+), 98 deletions(-) create mode 100644 pcbnew/tools/tool_menu.cpp create mode 100644 pcbnew/tools/tool_menu.h diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt index 040f8e94a1..be46007f9b 100644 --- a/pcbnew/CMakeLists.txt +++ b/pcbnew/CMakeLists.txt @@ -294,6 +294,7 @@ set( PCBNEW_CLASS_SRCS tools/picker_tool.cpp tools/zoom_tool.cpp tools/tools_common.cpp + tools/tool_menu.cpp tools/grid_menu.cpp tools/zoom_menu.cpp diff --git a/pcbnew/tools/drawing_tool.cpp b/pcbnew/tools/drawing_tool.cpp index 052dc15bc9..bb72490b27 100644 --- a/pcbnew/tools/drawing_tool.cpp +++ b/pcbnew/tools/drawing_tool.cpp @@ -43,6 +43,7 @@ #include #include #include +#include #include #include @@ -51,11 +52,6 @@ #include #include -#include - -#include "zoom_menu.h" -#include "grid_menu.h" - #include @@ -66,8 +62,7 @@ DRAWING_TOOL::DRAWING_TOOL() : m_view( nullptr ), m_controls( nullptr ), m_board( nullptr ), m_frame( nullptr ), m_mode( MODE::NONE ), m_lineWidth( 1 ), - m_menu( this ), m_contextMenu( nullptr ), - m_gridMenu( nullptr), m_zoomMenu( nullptr) + m_menu( *this ) { } @@ -80,19 +75,7 @@ DRAWING_TOOL::~DRAWING_TOOL() bool DRAWING_TOOL::Init() { // Drawing type-specific options will be added by the PCB control tool - - m_menu.AddItem( COMMON_ACTIONS::zoomCenter, SELECTION_CONDITIONS::ShowAlways, 1000 ); - m_menu.AddItem( COMMON_ACTIONS::zoomIn, SELECTION_CONDITIONS::ShowAlways, 1000 ); - m_menu.AddItem( COMMON_ACTIONS::zoomOut , SELECTION_CONDITIONS::ShowAlways, 1000 ); - m_menu.AddItem( COMMON_ACTIONS::zoomFitScreen , SELECTION_CONDITIONS::ShowAlways, 1000 ); - - PCB_BASE_FRAME* frame = getEditFrame(); - - m_zoomMenu = new ZOOM_MENU( frame ); - m_menu.AddMenu( m_zoomMenu, _( "Zoom" ), false, SELECTION_CONDITIONS::ShowAlways, 1000 ); - - m_gridMenu = new GRID_MENU( frame ); - m_menu.AddMenu( m_gridMenu, _( "Grid" ), false, SELECTION_CONDITIONS::ShowAlways, 1000 ); + m_menu.AddStandardSubMenus( *getEditFrame() ); return true; } @@ -268,7 +251,7 @@ int DRAWING_TOOL::PlaceText( const TOOL_EVENT& aEvent ) else if ( evt->IsClick( BUT_RIGHT ) ) { - showContextMenu(); + m_menu.ShowContextMenu(); } else if( evt->IsClick( BUT_LEFT ) ) @@ -442,7 +425,7 @@ int DRAWING_TOOL::DrawDimension( const TOOL_EVENT& aEvent ) else if ( evt->IsClick( BUT_RIGHT ) ) { - showContextMenu(); + m_menu.ShowContextMenu(); } else if( evt->IsClick( BUT_LEFT ) ) @@ -658,7 +641,7 @@ int DRAWING_TOOL::PlaceDXF( const TOOL_EVENT& aEvent ) else if ( evt->IsClick( BUT_RIGHT ) ) { - showContextMenu(); + m_menu.ShowContextMenu(); } else if( evt->IsClick( BUT_LEFT ) ) @@ -790,7 +773,7 @@ int DRAWING_TOOL::SetAnchor( const TOOL_EVENT& aEvent ) } else if ( evt->IsClick( BUT_RIGHT ) ) { - showContextMenu(); + m_menu.ShowContextMenu(); } else if( evt->IsCancel() || evt->IsActivate() ) break; @@ -807,17 +790,6 @@ int DRAWING_TOOL::SetAnchor( const TOOL_EVENT& aEvent ) } -void DRAWING_TOOL::showContextMenu() -{ - // Dummy selection - the drawing tool doesn't depend on a selection - SELECTION aSelection; - m_contextMenu = m_menu.Generate( aSelection ); - - if( m_contextMenu->GetMenuItemCount() > 0 ) - SetContextMenu( m_contextMenu, CMENU_NOW ); -} - - bool DRAWING_TOOL::drawSegment( int aShape, DRAWSEGMENT*& aGraphic, boost::optional aStartingPoint ) { @@ -898,7 +870,7 @@ bool DRAWING_TOOL::drawSegment( int aShape, DRAWSEGMENT*& aGraphic, } else if( evt->IsClick( BUT_RIGHT ) ) { - showContextMenu(); + m_menu.ShowContextMenu(); } else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) ) { @@ -1047,7 +1019,7 @@ bool DRAWING_TOOL::drawArc( DRAWSEGMENT*& aGraphic ) } else if( evt->IsClick( BUT_RIGHT ) ) { - showContextMenu(); + m_menu.ShowContextMenu(); } else if( evt->IsClick( BUT_LEFT ) ) { @@ -1260,7 +1232,7 @@ int DRAWING_TOOL::drawZone( bool aKeepout ) } else if( evt->IsClick( BUT_RIGHT ) ) { - showContextMenu(); + m_menu.ShowContextMenu(); } else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) ) { diff --git a/pcbnew/tools/drawing_tool.h b/pcbnew/tools/drawing_tool.h index cc9ccf8b7d..da0012eb4f 100644 --- a/pcbnew/tools/drawing_tool.h +++ b/pcbnew/tools/drawing_tool.h @@ -28,7 +28,7 @@ #include #include -#include "conditional_menu.h" +#include "tool_menu.h" namespace KIGFX { @@ -38,9 +38,6 @@ namespace KIGFX class BOARD; class PCB_BASE_EDIT_FRAME; class DRAWSEGMENT; -class CONTEXT_MENU; -class GRID_MENU; -class ZOOM_MENU; /** * Class DRAWING_TOOL @@ -63,7 +60,7 @@ public: ///> Get the DRAWING_TOOL top-level context menu inline CONDITIONAL_MENU& GetMenu() { - return m_menu; + return m_menu.GetMenu(); } ///> The possible drawing modes of DRAWING_TOOL @@ -208,13 +205,8 @@ private: /// Stores the current line width for multisegment drawing. unsigned int m_lineWidth; - /// Menu displayed by the tool. - CONDITIONAL_MENU m_menu; - - /// Pointers to context menus - CONTEXT_MENU* m_contextMenu; - GRID_MENU* m_gridMenu; - ZOOM_MENU* m_zoomMenu; + /// Menu model displayed by the tool. + TOOL_MENU m_menu; // How does line width change after one -/+ key press. static const int WIDTH_STEP; diff --git a/pcbnew/tools/selection_tool.cpp b/pcbnew/tools/selection_tool.cpp index 56462d2be7..dde12c6e80 100644 --- a/pcbnew/tools/selection_tool.cpp +++ b/pcbnew/tools/selection_tool.cpp @@ -50,8 +50,6 @@ using namespace std::placeholders; #include "selection_tool.h" #include "selection_area.h" -#include "zoom_menu.h" -#include "grid_menu.h" #include "bright_box.h" #include "common_actions.h" @@ -70,8 +68,7 @@ public: SELECTION_TOOL::SELECTION_TOOL() : PCB_TOOL( "pcbnew.InteractiveSelection" ), m_frame( NULL ), m_additive( false ), m_multiple( false ), - m_locked( true ), m_menu( this ), m_contextMenu( NULL ), m_selectMenu( NULL ), - m_zoomMenu( NULL ), m_gridMenu( NULL ) + m_locked( true ), m_menu( *this ) { // Do not leave uninitialized members: m_preliminary = false; @@ -81,11 +78,6 @@ SELECTION_TOOL::SELECTION_TOOL() : SELECTION_TOOL::~SELECTION_TOOL() { getView()->Remove( &m_selection ); - - delete m_contextMenu; - delete m_selectMenu; - delete m_zoomMenu; - delete m_gridMenu; } @@ -96,25 +88,17 @@ bool SELECTION_TOOL::Init() auto showSelectMenuFunctor = ( S_C::OnlyType( PCB_VIA_T ) || S_C::OnlyType( PCB_TRACE_T ) ) && S_C::Count( 1 ); - m_selectMenu = new SELECT_MENU; - m_selectMenu->SetTool( this ); + auto selectMenu = std::make_shared(); + selectMenu->SetTool( this ); + m_menu.AddSubMenu( selectMenu ); - m_menu.AddMenu( m_selectMenu, _( "Select..." ), false, showSelectMenuFunctor ); + auto& menu = m_menu.GetMenu(); + + menu.AddMenu( selectMenu.get(), _( "Select..." ), false, showSelectMenuFunctor ); // only show separator if there is a Select menu to show above it - GetMenu().AddSeparator( showSelectMenuFunctor, 1000 ); + menu.AddSeparator( showSelectMenuFunctor, 1000 ); - m_menu.AddItem( COMMON_ACTIONS::zoomCenter, S_C::ShowAlways, 1000 ); - m_menu.AddItem( COMMON_ACTIONS::zoomIn, S_C::ShowAlways, 1000 ); - m_menu.AddItem( COMMON_ACTIONS::zoomOut , S_C::ShowAlways, 1000 ); - m_menu.AddItem( COMMON_ACTIONS::zoomFitScreen , S_C::ShowAlways, 1000 ); - - PCB_BASE_FRAME* frame = getEditFrame(); - - m_zoomMenu = new ZOOM_MENU( frame ); - m_menu.AddMenu( m_zoomMenu, _( "Zoom" ), false, S_C::ShowAlways, 1000 ); - - m_gridMenu = new GRID_MENU( frame ); - m_menu.AddMenu( m_gridMenu, _( "Grid" ), false, S_C::ShowAlways, 1000 ); + m_menu.AddStandardSubMenus( *getEditFrame() ); return true; } @@ -177,11 +161,7 @@ int SELECTION_TOOL::Main( const TOOL_EVENT& aEvent ) if( emptySelection ) selectPoint( evt->Position() ); - delete m_contextMenu; - m_contextMenu = m_menu.Generate( m_selection ); - - if( m_contextMenu->GetMenuItemCount() > 0 ) - SetContextMenu( m_contextMenu, CMENU_NOW ); + m_menu.ShowContextMenu( m_selection ); m_preliminary = emptySelection; } @@ -288,11 +268,7 @@ int SELECTION_TOOL::Main( const TOOL_EVENT& aEvent ) if( m_preliminary ) clearSelection(); - if( evt->Parameter() == m_contextMenu ) - { - delete m_contextMenu; - m_contextMenu = NULL; - } + m_menu.CloseContextMenu( evt ); } } diff --git a/pcbnew/tools/selection_tool.h b/pcbnew/tools/selection_tool.h index 054b994414..eaebaa0734 100644 --- a/pcbnew/tools/selection_tool.h +++ b/pcbnew/tools/selection_tool.h @@ -33,15 +33,12 @@ #include #include "selection_conditions.h" -#include "conditional_menu.h" +#include "tool_menu.h" class PCB_BASE_FRAME; class SELECTION_AREA; class BOARD_ITEM; class GENERAL_COLLECTOR; -class SELECT_MENU; -class ZOOM_MENU; -class GRID_MENU; namespace KIGFX { @@ -198,7 +195,7 @@ public: inline CONDITIONAL_MENU& GetMenu() { - return m_menu; + return m_menu.GetMenu(); } ///> Checks if the user has agreed to modify locked items for the given selection. @@ -392,14 +389,8 @@ private: /// Determines if the selection is preliminary or final. bool m_preliminary; - /// Menu displayed by the tool. - CONDITIONAL_MENU m_menu; - - /// Pointers to context menus - CONTEXT_MENU* m_contextMenu; - SELECT_MENU* m_selectMenu; - ZOOM_MENU* m_zoomMenu; - GRID_MENU* m_gridMenu; + /// Menu model displayed by the tool. + TOOL_MENU m_menu; }; #endif diff --git a/pcbnew/tools/tool_menu.cpp b/pcbnew/tools/tool_menu.cpp new file mode 100644 index 0000000000..9af7fff719 --- /dev/null +++ b/pcbnew/tools/tool_menu.cpp @@ -0,0 +1,104 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2017 KiCad Developers, see CHANGELOG.txt for contributors. + * + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "tool_menu.h" + +#include + +#include "common_actions.h" +#include "zoom_menu.h" +#include "grid_menu.h" +#include "selection_tool.h" // For SELECTION + + +TOOL_MENU::TOOL_MENU( TOOL_INTERACTIVE& aTool ) : + m_menu( &aTool ), + m_tool( aTool ) +{ +} + + +TOOL_MENU::~TOOL_MENU() +{ +} + + +CONDITIONAL_MENU& TOOL_MENU::GetMenu() +{ + return m_menu; +} + + +void TOOL_MENU::AddSubMenu( std::shared_ptr aSubMenu ) +{ + // store a copy of the menu (keeps a reference) + m_subMenus.push_back( std::move( aSubMenu ) ); +} + + +void TOOL_MENU::ShowContextMenu( SELECTION& aSelection ) +{ + m_contextMenu = std::unique_ptr( + m_menu.Generate( aSelection ) ); + + if( m_contextMenu->GetMenuItemCount() > 0 ) + { + m_tool.SetContextMenu( m_contextMenu.get(), CMENU_NOW ); + } +} + + +void TOOL_MENU::ShowContextMenu() +{ + SELECTION dummySelection; + + ShowContextMenu( dummySelection ); +} + + +void TOOL_MENU::CloseContextMenu( OPT_TOOL_EVENT& evt ) +{ + // m_contextMenu can be null here, that's OK + if( evt->Parameter() == m_contextMenu.get() ) + { + m_contextMenu = nullptr; + } +} + + +// This makes the factory functions a bit less verbose +using S_C = SELECTION_CONDITIONS; + +void TOOL_MENU::AddStandardSubMenus( EDA_DRAW_FRAME& aFrame ) +{ + m_menu.AddItem( COMMON_ACTIONS::zoomCenter, S_C::ShowAlways, 1000 ); + m_menu.AddItem( COMMON_ACTIONS::zoomIn, S_C::ShowAlways, 1000 ); + m_menu.AddItem( COMMON_ACTIONS::zoomOut, S_C::ShowAlways, 1000 ); + m_menu.AddItem( COMMON_ACTIONS::zoomFitScreen, S_C::ShowAlways, 1000 ); + + m_menu.AddMenu( createOwnSubMenu( &aFrame ).get(), + _( "Zoom" ), false, S_C::ShowAlways, 1000 ); + + m_menu.AddMenu( createOwnSubMenu( &aFrame ).get(), + _( "Grid" ), false, S_C::ShowAlways, 1000 ); +} diff --git a/pcbnew/tools/tool_menu.h b/pcbnew/tools/tool_menu.h new file mode 100644 index 0000000000..e9774a54d7 --- /dev/null +++ b/pcbnew/tools/tool_menu.h @@ -0,0 +1,173 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2017 KiCad Developers, see CHANGELOG.txt for contributors. + * + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef TOOLS_TOOL_MENU__H_ +#define TOOLS_TOOL_MENU__H_ + +#include "conditional_menu.h" +#include "pcb_tool.h" + +#include +#include + +class CONTEXT_MENU; + +/** + * Class TOOL_MENU + * + * Manages a CONDITIONAL_MENU and some number of + * CONTEXT_MENUs as sub-menus + * + * Each "top-level" interactive tool can have one of these, + * and other tools can contribute CONTEXT_MENUS to it. + * + * There are also helper functions for adding common sets of + * menu items, for example zoom and grid controls. + */ +class TOOL_MENU +{ +public: + + /** + * Function TOOL_MENU + * + * Construct a new TOOL_MENU for a specific tool. This menu + * will be empty - it's up to the caller to add the relevant + * items. This can be done directy, using the reference returned + * by TOOL_MENU::GetMenu(), or the helpers for common command sets + * can be used, or a combination of the two. + */ + TOOL_MENU( TOOL_INTERACTIVE& aTool ); + + /** + * Destructor, also destructs any submenus created with + * TOOL_MENU::CreateSubMenu(). + */ + ~TOOL_MENU(); + + /** + * Function GetMenu + * + * @return reference to the CONDITIONAL_MENU model, which can be + * used tby tools to add their own commands to the menu. + */ + CONDITIONAL_MENU& GetMenu(); + + /** + * Function CreateSubMenu + * + * Store a submenu of this menu model. This can be shared with + * other menu models. + * + * It is the callers responsibility to add the submenu to + * m_menu (via GetMenu() ) in the right way, as well + * as to set the tool with SetTool(), since it's not a given + * that the menu's tool is the tool that directly owns this + * TOOL_MENU + * + * @param aSubMenu: a sub menu to add + */ + void AddSubMenu( std::shared_ptr aSubMenu ); + + /** + * Function ShowContextMenu + * + * Helper function to set and immediately show a CONTEXT_MENU + * based on the internal CONDITIONAL_MENU in concert with + * the given SELECTION + * + * You don't have to use this function, if the caller has a + * different way to show the menu, it can create one from + * the reference returned by TOOL_MENU::GetMenu(), but it will + * have to be managed externally to this class. + */ + void ShowContextMenu( SELECTION& aSelection ); + + /** + * Function ShowContextMenu + * + * Helper function to show a context menu without any selection + * for tools that can't make selections. + */ + void ShowContextMenu(); + + /** + * Function CloseContextMenu + * + * Helper function to close a menu previously opened with + * ShowContextMenu(), if a suitable event is received + */ + void CloseContextMenu( OPT_TOOL_EVENT& evt ); + + /** + * Function CreateBasicMenu + * + * Construct a "basic" menu for a tool, containing only items + * that apply to all tools (e.g. zoom and grid) + */ + void AddStandardSubMenus( EDA_DRAW_FRAME& aFrame ); + +private: + + /*! + * Helper function for factories to abe able to easily add + * their own new sub menus. This sets the tool to the TOOL_MENUs + * owner and adds to the store. + * + * Note, this won't share the menu between multiple invocations + * of the factory. But if different top-level tools are using the + * same factory, which one would be used for SetTool()? + */ + template + std::shared_ptr createOwnSubMenu( Args&& ... args ) + { + auto subMenuPtr = std::make_shared( args ... ); + + subMenuPtr->SetTool( &m_tool ); + AddSubMenu( subMenuPtr ); + + return subMenuPtr; + } + + /** + * The conditional model of the menu displayed by the tool + */ + CONDITIONAL_MENU m_menu; + + /** + * The actual menu displayed by the tool + */ + std::unique_ptr m_contextMenu; + + /** + * The tool that owns this menu + */ + TOOL_INTERACTIVE& m_tool; + + /** + * Lifetime-managing container of submenus + */ + std::vector > m_subMenus; +}; + +#endif // TOOLS_TOOL_MENU__H_