Add context menu to GAL DRAWING_TOOL, including the zone menu

Includes a tiny class for scoped set-reset of a variable, which is used
here to store the current drawing mode.

Fixes: lp:1218629
 * https://bugs.launchpad.net/kicad/+bug/1518629
This commit is contained in:
John Beard 2017-01-10 01:29:40 +08:00 committed by Maciej Suminski
parent 6baf0edc08
commit f1c5b9a9ae
4 changed files with 256 additions and 10 deletions

View File

@ -0,0 +1,73 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2016 KiCad Developers, see AUTHORS.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 __SCOPED_SET_RESET_H
#define __SCOPED_SET_RESET_H
/**
* @class SCOPED_SET_RESET
* @brief RAII class that sets an value at construction and resets
* it to the original value at destruction.
*
* @note There is no type deduction for template classes until C++17, \
* so you can't do this:
*
* int target = 0;
* SCOPED_SET_RESET( target, 42 );
*
* Instead, you can use a type alias, for example:
*
* using SCOPED_INT_SET_RESET = SCOPED_SET_RESET<int>;
* int target = 0;
* SCOPED_INT_SET_RESET( target , 42 );
*/
template <typename VAL_TYPE>
class SCOPED_SET_RESET
{
public:
SCOPED_SET_RESET( VAL_TYPE& target,
VAL_TYPE value ):
m_target( target )
{
m_original = target;
m_target = value;
}
/**
* Function ~SCOPED_SET_RESET
*
* Destruct the class, and return the target to its original
* value.
*/
~SCOPED_SET_RESET()
{
m_target = m_original;
}
private:
VAL_TYPE m_original;
VAL_TYPE& m_target;
};
#endif // __SCOPED_SET_RESET_H

View File

@ -51,11 +51,23 @@
#include <class_zone.h>
#include <class_module.h>
#include <scoped_set_reset.h>
#include "zoom_menu.h"
#include "grid_menu.h"
#include <tools/selection_tool.h>
using SCOPED_DRAW_MODE = SCOPED_SET_RESET<DRAWING_TOOL::MODE>;
DRAWING_TOOL::DRAWING_TOOL() :
PCB_TOOL( "pcbnew.InteractiveDrawing" ), m_view( NULL ),
m_controls( NULL ), m_board( NULL ), m_frame( NULL ), m_lineWidth( 1 )
PCB_TOOL( "pcbnew.InteractiveDrawing" ),
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)
{
}
@ -65,6 +77,27 @@ 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<PCB_BASE_FRAME>();
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 );
return true;
}
void DRAWING_TOOL::Reset( RESET_REASON aReason )
{
// Init variables used by every drawing tool
@ -75,6 +108,12 @@ void DRAWING_TOOL::Reset( RESET_REASON aReason )
}
DRAWING_TOOL::MODE DRAWING_TOOL::GetDrawingMode() const
{
return m_mode;
}
int DRAWING_TOOL::DrawLine( const TOOL_EVENT& aEvent )
{
BOARD_ITEM_CONTAINER* parent = m_frame->GetModel();
@ -82,6 +121,8 @@ int DRAWING_TOOL::DrawLine( const TOOL_EVENT& aEvent )
boost::optional<VECTOR2D> startingPoint;
BOARD_COMMIT commit( m_frame );
SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::LINE );
m_frame->SetToolID( m_editModules ? ID_MODEDIT_LINE_TOOL : ID_PCB_ADD_LINE_BUTT,
wxCURSOR_PENCIL, _( "Add graphic line" ) );
@ -113,6 +154,8 @@ int DRAWING_TOOL::DrawCircle( const TOOL_EVENT& aEvent )
DRAWSEGMENT* circle = m_editModules ? new EDGE_MODULE( (MODULE*) parent ) : new DRAWSEGMENT;
BOARD_COMMIT commit( m_frame );
SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::CIRCLE );
m_frame->SetToolID( m_editModules ? ID_MODEDIT_CIRCLE_TOOL : ID_PCB_CIRCLE_BUTT,
wxCURSOR_PENCIL, _( "Add graphic circle" ) );
@ -139,6 +182,8 @@ int DRAWING_TOOL::DrawArc( const TOOL_EVENT& aEvent )
DRAWSEGMENT* arc = m_editModules ? new EDGE_MODULE( (MODULE*) parent ) : new DRAWSEGMENT;
BOARD_COMMIT commit( m_frame );
SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::ARC );
m_frame->SetToolID( m_editModules ? ID_MODEDIT_ARC_TOOL : ID_PCB_ARC_BUTT,
wxCURSOR_PENCIL, _( "Add graphic arc" ) );
@ -174,6 +219,8 @@ int DRAWING_TOOL::PlaceText( const TOOL_EVENT& aEvent )
m_controls->SetSnapping( true );
// do not capture or auto-pan until we start placing some text
SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::TEXT );
Activate();
m_frame->SetToolID( m_editModules ? ID_MODEDIT_TEXT_TOOL : ID_PCB_ADD_TEXT_BUTT,
wxCURSOR_PENCIL, _( "Add text" ) );
@ -219,6 +266,11 @@ int DRAWING_TOOL::PlaceText( const TOOL_EVENT& aEvent )
}
}
else if ( evt->IsClick( BUT_RIGHT ) )
{
showContextMenu();
}
else if( evt->IsClick( BUT_LEFT ) )
{
if( !text )
@ -336,6 +388,8 @@ int DRAWING_TOOL::DrawDimension( const TOOL_EVENT& aEvent )
m_controls->ShowCursor( true );
m_controls->SetSnapping( true );
SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::DIMENSION );
Activate();
m_frame->SetToolID( ID_PCB_DIMENSION_BUTT, wxCURSOR_PENCIL, _( "Add dimension" ) );
@ -386,6 +440,11 @@ int DRAWING_TOOL::DrawDimension( const TOOL_EVENT& aEvent )
}
}
else if ( evt->IsClick( BUT_RIGHT ) )
{
showContextMenu();
}
else if( evt->IsClick( BUT_LEFT ) )
{
switch( step )
@ -502,6 +561,7 @@ int DRAWING_TOOL::DrawDimension( const TOOL_EVENT& aEvent )
int DRAWING_TOOL::DrawZone( const TOOL_EVENT& aEvent )
{
SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::ZONE );
m_frame->SetToolID( ID_PCB_ZONES_BUTT, wxCURSOR_PENCIL, _( "Add zones" ) );
return drawZone( false );
@ -510,6 +570,7 @@ int DRAWING_TOOL::DrawZone( const TOOL_EVENT& aEvent )
int DRAWING_TOOL::DrawKeepout( const TOOL_EVENT& aEvent )
{
SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::KEEPOUT );
m_frame->SetToolID( ID_PCB_KEEPOUT_AREA_BUTT, wxCURSOR_PENCIL, _( "Add keepout" ) );
return drawZone( true );
@ -551,6 +612,8 @@ int DRAWING_TOOL::PlaceDXF( const TOOL_EVENT& aEvent )
m_controls->ShowCursor( true );
m_controls->SetSnapping( true );
SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::DXF );
Activate();
// Main loop: keep receiving events
@ -593,6 +656,11 @@ int DRAWING_TOOL::PlaceDXF( const TOOL_EVENT& aEvent )
}
}
else if ( evt->IsClick( BUT_RIGHT ) )
{
showContextMenu();
}
else if( evt->IsClick( BUT_LEFT ) )
{
// Place the drawing
@ -690,6 +758,8 @@ int DRAWING_TOOL::SetAnchor( const TOOL_EVENT& aEvent )
{
assert( m_editModules );
SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::ANCHOR );
Activate();
m_frame->SetToolID( ID_MODEDIT_ANCHOR_TOOL, wxCURSOR_PENCIL,
_( "Place the footprint anchor" ) );
@ -718,7 +788,10 @@ int DRAWING_TOOL::SetAnchor( const TOOL_EVENT& aEvent )
// so deselect the active tool
break;
}
else if ( evt->IsClick( BUT_RIGHT ) )
{
showContextMenu();
}
else if( evt->IsCancel() || evt->IsActivate() )
break;
}
@ -734,6 +807,17 @@ 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<VECTOR2D> aStartingPoint )
{
@ -812,7 +896,10 @@ bool DRAWING_TOOL::drawSegment( int aShape, DRAWSEGMENT*& aGraphic,
aGraphic = NULL;
break;
}
else if( evt->IsClick( BUT_RIGHT ) )
{
showContextMenu();
}
else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
{
if( !started )
@ -958,7 +1045,10 @@ bool DRAWING_TOOL::drawArc( DRAWSEGMENT*& aGraphic )
aGraphic = NULL;
break;
}
else if( evt->IsClick( BUT_RIGHT ) )
{
showContextMenu();
}
else if( evt->IsClick( BUT_LEFT ) )
{
switch( step )
@ -1168,7 +1258,10 @@ int DRAWING_TOOL::drawZone( bool aKeepout )
if( evt->IsActivate() ) // now finish unconditionally
break;
}
else if( evt->IsClick( BUT_RIGHT ) )
{
showContextMenu();
}
else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
{
// Check if it is double click / closing line (so we have to finish the zone)

View File

@ -28,6 +28,8 @@
#include <tools/pcb_tool.h>
#include <boost/optional.hpp>
#include "conditional_menu.h"
namespace KIGFX
{
class VIEW;
@ -36,6 +38,9 @@ namespace KIGFX
class BOARD;
class PCB_BASE_EDIT_FRAME;
class DRAWSEGMENT;
class CONTEXT_MENU;
class GRID_MENU;
class ZOOM_MENU;
/**
* Class DRAWING_TOOL
@ -49,9 +54,41 @@ public:
DRAWING_TOOL();
~DRAWING_TOOL();
/// @copydoc TOOL_INTERACTIVE::Init()
bool Init() override;
/// @copydoc TOOL_INTERACTIVE::Reset()
void Reset( RESET_REASON aReason ) override;
///> Get the DRAWING_TOOL top-level context menu
inline CONDITIONAL_MENU& GetMenu()
{
return m_menu;
}
///> The possible drawing modes of DRAWING_TOOL
enum class MODE
{
NONE,
LINE,
CIRCLE,
ARC,
TEXT,
ANCHOR,
DXF,
DIMENSION,
KEEPOUT,
ZONE,
};
/**
* Function GetDrawingMode
*
* Returns the current drawing mode of the DRAWING_TOOL, or
* MODE::NONE if not currently in any drawing mode
*/
MODE GetDrawingMode() const;
/**
* Function DrawLine()
* Starts interactively drawing a line. After invoking the function it expects the user
@ -125,6 +162,11 @@ public:
void SetTransitions() override;
private:
///> Shows the context menu for the drawing tool
///> This menu consists of normal UI functions (zoom, grid, etc)
///> And any suitable global functions for the active drawing type.
void showContextMenu();
///> Starts drawing a selected shape (i.e. DRAWSEGMENT).
///> @param aShape is the type of created shape (@see STROKE_T).
///> @param aGraphic is an object that is going to be used by the tool for drawing. It has to
@ -161,10 +203,19 @@ private:
KIGFX::VIEW_CONTROLS* m_controls;
BOARD* m_board;
PCB_BASE_EDIT_FRAME* m_frame;
MODE m_mode;
/// 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;
// How does line width change after one -/+ key press.
static const int WIDTH_STEP;
};

View File

@ -28,6 +28,7 @@
#include <tool/tool_manager.h>
#include "selection_tool.h"
#include "drawing_tool.h"
#include "picker_tool.h"
#include <painter.h>
@ -72,6 +73,12 @@ private:
{
SELECTION_TOOL* selTool = getToolManager()->GetTool<SELECTION_TOOL>();
// enable zone actions that ably to a specific set of zones (as opposed to all of them)
bool nonGlobalActionsEnabled = ( SELECTION_CONDITIONS::MoreThan( 0 ) )( selTool->GetSelection() );
Enable( getMenuId( COMMON_ACTIONS::zoneFill ), nonGlobalActionsEnabled );
Enable( getMenuId( COMMON_ACTIONS::zoneUnfill ), nonGlobalActionsEnabled );
// lines like this make me really think about a better name for SELECTION_CONDITIONS class
bool mergeEnabled = ( SELECTION_CONDITIONS::MoreThan( 1 ) &&
/*SELECTION_CONDITIONS::OnlyType( PCB_ZONE_AREA_T ) &&*/
@ -131,21 +138,43 @@ void PCB_EDITOR_CONTROL::Reset( RESET_REASON aReason )
bool PCB_EDITOR_CONTROL::Init()
{
m_zoneMenu = new ZONE_CONTEXT_MENU;
m_zoneMenu->SetTool( this );
m_lockMenu = new LOCK_CONTEXT_MENU;
m_lockMenu->SetTool( this );
// Add the PCB control menus to relevant other tools
SELECTION_TOOL* selTool = m_toolMgr->GetTool<SELECTION_TOOL>();
if( selTool )
{
m_zoneMenu = new ZONE_CONTEXT_MENU;
m_zoneMenu->SetTool( this );
selTool->GetMenu().AddMenu( m_zoneMenu, _( "Zones" ), false,
SELECTION_CONDITIONS::OnlyType( PCB_ZONE_AREA_T ) );
m_lockMenu = new LOCK_CONTEXT_MENU;
m_lockMenu->SetTool( this );
selTool->GetMenu().AddMenu( m_lockMenu, _( "Locking" ), false,
SELECTION_CONDITIONS::OnlyTypes( GENERAL_COLLECTOR::Tracks ) );
}
DRAWING_TOOL* drawingTool = m_toolMgr->GetTool<DRAWING_TOOL>();
if( drawingTool )
{
// Functor to say if the PCB_EDIT_FRAME is in a given mode
// Capture the tool pointer and tool mode by value
auto toolActiveFunctor = [=]( DRAWING_TOOL::MODE aMode )
{
return [=]( const SELECTION& sel )
{
return drawingTool->GetDrawingMode() == aMode;
};
};
drawingTool->GetMenu().AddMenu( m_zoneMenu, _( "Zones" ), false,
toolActiveFunctor( DRAWING_TOOL::MODE::ZONE ) );
}
return true;
}