mirror of
https://gitlab.com/kicad/code/kicad.git
synced 2025-09-15 02:33:15 +02:00
The problem is that wxEVT_CHAR_HOOK doesn’t do the key translation properly. wxEVT_CHAR does, but we only get to that if we skip the event at the end of the tool’s event processing loop, which most tools don’t do. (Selection tools, point editors, pickers, and a couple of others do skip, which is probably why this didn’t get reported earlier.) I played around with a couple of ways to fix wxEVT_CHAR_HOOK. Most of them don’t work, and the few egregious hacks I tried weren't cross- platform. So I’m changing it so that most tools now skip at the end of their event loops. I left out a couple that I felt were high risk (length tuning, for instance). But there’s still enough risk that I’m 100% sure it will break something, I just haven’t a clue what. Fixes: lp:1836903 * https://bugs.launchpad.net/kicad/+bug/1836903
1920 lines
60 KiB
C++
1920 lines
60 KiB
C++
/*
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
*
|
|
* Copyright (C) 2014-2017 CERN
|
|
* Copyright (C) 2018-2019 KiCad Developers, see AUTHORS.txt for contributors.
|
|
* @author Maciej Suminski <maciej.suminski@cern.ch>
|
|
*
|
|
* 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 "drawing_tool.h"
|
|
#include "pcb_actions.h"
|
|
|
|
#include <pcb_edit_frame.h>
|
|
#include <project.h>
|
|
#include <id.h>
|
|
#include <confirm.h>
|
|
#include <import_gfx/dialog_import_gfx.h>
|
|
#include <view/view_controls.h>
|
|
#include <view/view.h>
|
|
#include <gal/graphics_abstraction_layer.h>
|
|
#include <tool/tool_manager.h>
|
|
#include <geometry/geometry_utils.h>
|
|
#include <ratsnest_data.h>
|
|
#include <board_commit.h>
|
|
#include <scoped_set_reset.h>
|
|
#include <bitmaps.h>
|
|
#include <painter.h>
|
|
#include <status_popup.h>
|
|
#include "grid_helper.h"
|
|
#include "point_editor.h"
|
|
#include <dialogs/dialog_text_properties.h>
|
|
#include <preview_items/arc_assistant.h>
|
|
|
|
#include <class_board.h>
|
|
#include <class_edge_mod.h>
|
|
#include <class_pcb_text.h>
|
|
#include <class_dimension.h>
|
|
#include <class_zone.h>
|
|
#include <class_module.h>
|
|
|
|
#include <tools/selection_tool.h>
|
|
#include <tools/tool_event_utils.h>
|
|
#include <tools/zone_create_helper.h>
|
|
|
|
using SCOPED_DRAW_MODE = SCOPED_SET_RESET<DRAWING_TOOL::MODE>;
|
|
|
|
|
|
DRAWING_TOOL::DRAWING_TOOL() :
|
|
PCB_TOOL_BASE( "pcbnew.InteractiveDrawing" ),
|
|
m_view( nullptr ), m_controls( nullptr ),
|
|
m_board( nullptr ), m_frame( nullptr ), m_mode( MODE::NONE ),
|
|
m_lineWidth( 1 )
|
|
{
|
|
}
|
|
|
|
|
|
DRAWING_TOOL::~DRAWING_TOOL()
|
|
{
|
|
}
|
|
|
|
|
|
bool DRAWING_TOOL::Init()
|
|
{
|
|
auto activeToolFunctor = [ this ] ( const SELECTION& aSel ) {
|
|
return m_mode != MODE::NONE;
|
|
};
|
|
|
|
// some interactive drawing tools can undo the last point
|
|
auto canUndoPoint = [ this ] ( const SELECTION& aSel ) {
|
|
return m_mode == MODE::ARC || m_mode == MODE::ZONE;
|
|
};
|
|
|
|
// functor for zone-only actions
|
|
auto zoneActiveFunctor = [this ] ( const SELECTION& aSel ) {
|
|
return m_mode == MODE::ZONE;
|
|
};
|
|
|
|
auto& ctxMenu = m_menu.GetMenu();
|
|
|
|
// cancel current tool goes in main context menu at the top if present
|
|
ctxMenu.AddItem( ACTIONS::cancelInteractive, activeToolFunctor, 1 );
|
|
ctxMenu.AddSeparator( 1 );
|
|
|
|
// tool-specific actions
|
|
ctxMenu.AddItem( PCB_ACTIONS::closeZoneOutline, zoneActiveFunctor, 200 );
|
|
ctxMenu.AddItem( PCB_ACTIONS::deleteLastPoint, canUndoPoint, 200 );
|
|
|
|
ctxMenu.AddSeparator( 500 );
|
|
|
|
// Type-specific sub-menus will be added for us by other tools
|
|
// For example, zone fill/unfill is provided by the PCB control tool
|
|
|
|
// Finally, add the standard zoom/grid items
|
|
getEditFrame<PCB_BASE_FRAME>()->AddStandardSubMenus( m_menu );
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void DRAWING_TOOL::Reset( RESET_REASON aReason )
|
|
{
|
|
// Init variables used by every drawing tool
|
|
m_view = getView();
|
|
m_controls = getViewControls();
|
|
m_board = getModel<BOARD>();
|
|
m_frame = getEditFrame<PCB_BASE_EDIT_FRAME>();
|
|
}
|
|
|
|
|
|
DRAWING_TOOL::MODE DRAWING_TOOL::GetDrawingMode() const
|
|
{
|
|
return m_mode;
|
|
}
|
|
|
|
|
|
int DRAWING_TOOL::DrawLine( const TOOL_EVENT& aEvent )
|
|
{
|
|
if( m_editModules && !m_frame->GetModel() )
|
|
return 0;
|
|
|
|
MODULE* module = dynamic_cast<MODULE*>( m_frame->GetModel() );
|
|
DRAWSEGMENT* line = m_editModules ? new EDGE_MODULE( module ) : new DRAWSEGMENT;
|
|
BOARD_COMMIT commit( m_frame );
|
|
SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::LINE );
|
|
OPT<VECTOR2D> startingPoint = boost::make_optional<VECTOR2D>( false, VECTOR2D( 0, 0 ) );
|
|
|
|
line->SetFlags( IS_NEW );
|
|
|
|
if( aEvent.HasPosition() )
|
|
startingPoint = getViewControls()->GetCursorPosition( !aEvent.Modifier( MD_ALT ) );
|
|
|
|
std::string tool = aEvent.GetCommandStr().get();
|
|
m_frame->PushTool( tool );
|
|
Activate();
|
|
|
|
while( drawSegment( tool, S_SEGMENT, line, startingPoint ) )
|
|
{
|
|
if( line )
|
|
{
|
|
if( m_editModules )
|
|
static_cast<EDGE_MODULE*>( line )->SetLocalCoord();
|
|
|
|
commit.Add( line );
|
|
commit.Push( _( "Draw a line segment" ) );
|
|
startingPoint = VECTOR2D( line->GetEnd() );
|
|
}
|
|
else
|
|
{
|
|
startingPoint = NULLOPT;
|
|
}
|
|
|
|
line = m_editModules ? new EDGE_MODULE( module ) : new DRAWSEGMENT;
|
|
line->SetFlags( IS_NEW );
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int DRAWING_TOOL::DrawCircle( const TOOL_EVENT& aEvent )
|
|
{
|
|
if( m_editModules && !m_frame->GetModel() )
|
|
return 0;
|
|
|
|
MODULE* module = dynamic_cast<MODULE*>( m_frame->GetModel() );
|
|
DRAWSEGMENT* circle = m_editModules ? new EDGE_MODULE( module ) : new DRAWSEGMENT;
|
|
BOARD_COMMIT commit( m_frame );
|
|
SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::CIRCLE );
|
|
OPT<VECTOR2D> startingPoint = boost::make_optional<VECTOR2D>( false, VECTOR2D( 0, 0 ) );
|
|
|
|
circle->SetFlags( IS_NEW );
|
|
|
|
if( aEvent.HasPosition() )
|
|
startingPoint = getViewControls()->GetCursorPosition( !aEvent.Modifier( MD_ALT ) );
|
|
|
|
std::string tool = aEvent.GetCommandStr().get();
|
|
m_frame->PushTool( tool );
|
|
Activate();
|
|
|
|
while( drawSegment( tool, S_CIRCLE, circle, startingPoint ) )
|
|
{
|
|
if( circle )
|
|
{
|
|
if( m_editModules )
|
|
static_cast<EDGE_MODULE*>( circle )->SetLocalCoord();
|
|
|
|
commit.Add( circle );
|
|
commit.Push( _( "Draw a circle" ) );
|
|
|
|
m_toolMgr->RunAction( PCB_ACTIONS::selectItem, true, circle );
|
|
}
|
|
|
|
circle = m_editModules ? new EDGE_MODULE( module ) : new DRAWSEGMENT;
|
|
circle->SetFlags( IS_NEW );
|
|
startingPoint = NULLOPT;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int DRAWING_TOOL::DrawArc( const TOOL_EVENT& aEvent )
|
|
{
|
|
if( m_editModules && !m_frame->GetModel() )
|
|
return 0;
|
|
|
|
MODULE* module = dynamic_cast<MODULE*>( m_frame->GetModel() );
|
|
DRAWSEGMENT* arc = m_editModules ? new EDGE_MODULE( module ) : new DRAWSEGMENT;
|
|
BOARD_COMMIT commit( m_frame );
|
|
SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::ARC );
|
|
bool immediateMode = aEvent.HasPosition();
|
|
|
|
arc->SetFlags( IS_NEW );
|
|
|
|
std::string tool = aEvent.GetCommandStr().get();
|
|
m_frame->PushTool( tool );
|
|
Activate();
|
|
|
|
while( drawArc( tool, arc, immediateMode ) )
|
|
{
|
|
if( arc )
|
|
{
|
|
if( m_editModules )
|
|
static_cast<EDGE_MODULE*>( arc )->SetLocalCoord();
|
|
|
|
commit.Add( arc );
|
|
commit.Push( _( "Draw an arc" ) );
|
|
|
|
m_toolMgr->RunAction( PCB_ACTIONS::selectItem, true, arc );
|
|
}
|
|
|
|
arc = m_editModules ? new EDGE_MODULE( module ) : new DRAWSEGMENT;
|
|
arc->SetFlags( IS_NEW );
|
|
immediateMode = false;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int DRAWING_TOOL::PlaceText( const TOOL_EVENT& aEvent )
|
|
{
|
|
if( m_editModules && !m_frame->GetModel() )
|
|
return 0;
|
|
|
|
BOARD_ITEM* text = NULL;
|
|
const BOARD_DESIGN_SETTINGS& dsnSettings = m_frame->GetDesignSettings();
|
|
BOARD_COMMIT commit( m_frame );
|
|
|
|
m_controls->ShowCursor( true );
|
|
m_controls->SetSnapping( true );
|
|
// do not capture or auto-pan until we start placing some text
|
|
|
|
SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::TEXT );
|
|
|
|
std::string tool = aEvent.GetCommandStr().get();
|
|
m_frame->PushTool( tool );
|
|
Activate();
|
|
|
|
bool reselect = false;
|
|
|
|
// Prime the pump
|
|
if( aEvent.HasPosition() )
|
|
m_toolMgr->RunAction( ACTIONS::cursorClick );
|
|
|
|
// Main loop: keep receiving events
|
|
while( TOOL_EVENT* evt = Wait() )
|
|
{
|
|
m_frame->GetCanvas()->SetCurrentCursor( text ? wxCURSOR_ARROW : wxCURSOR_PENCIL );
|
|
VECTOR2I cursorPos = m_controls->GetCursorPosition();
|
|
|
|
if( reselect && text )
|
|
m_toolMgr->RunAction( PCB_ACTIONS::selectItem, true, text );
|
|
|
|
auto cleanup = [&] () {
|
|
m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
|
|
m_controls->SetAutoPan( false );
|
|
m_controls->CaptureCursor( false );
|
|
m_controls->ShowCursor( true );
|
|
delete text;
|
|
text = NULL;
|
|
};
|
|
|
|
if( evt->IsCancelInteractive() )
|
|
{
|
|
if( text )
|
|
cleanup();
|
|
else
|
|
{
|
|
m_frame->PopTool( tool );
|
|
break;
|
|
}
|
|
}
|
|
else if( evt->IsActivate() )
|
|
{
|
|
if( text )
|
|
cleanup();
|
|
|
|
if( evt->IsMoveTool() )
|
|
{
|
|
// leave ourselves on the stack so we come back after the move
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
m_frame->PopTool( tool );
|
|
break;
|
|
}
|
|
}
|
|
else if( evt->IsClick( BUT_RIGHT ) )
|
|
{
|
|
m_menu.ShowContextMenu( selection() );
|
|
}
|
|
else if( evt->IsClick( BUT_LEFT ) )
|
|
{
|
|
bool placing = text != nullptr;
|
|
|
|
if( !text )
|
|
{
|
|
m_controls->ForceCursorPosition( true, m_controls->GetCursorPosition() );
|
|
PCB_LAYER_ID layer = m_frame->GetActiveLayer();
|
|
|
|
// Init the new item attributes
|
|
if( m_editModules )
|
|
{
|
|
TEXTE_MODULE* textMod = new TEXTE_MODULE( (MODULE*) m_frame->GetModel() );
|
|
|
|
textMod->SetLayer( layer );
|
|
textMod->SetTextSize( dsnSettings.GetTextSize( layer ) );
|
|
textMod->SetThickness( dsnSettings.GetTextThickness( layer ) );
|
|
textMod->SetItalic( dsnSettings.GetTextItalic( layer ) );
|
|
textMod->SetKeepUpright( dsnSettings.GetTextUpright( layer ) );
|
|
textMod->SetTextPos( (wxPoint) cursorPos );
|
|
|
|
text = textMod;
|
|
|
|
DIALOG_TEXT_PROPERTIES textDialog( m_frame, textMod );
|
|
bool cancelled;
|
|
|
|
RunMainStack([&]() {
|
|
cancelled = !textDialog.ShowModal() || textMod->GetText().IsEmpty();
|
|
} );
|
|
|
|
if( cancelled )
|
|
{
|
|
delete text;
|
|
text = nullptr;
|
|
}
|
|
else if( textMod->GetTextPos() != (wxPoint) cursorPos )
|
|
{
|
|
// If the user modified the location then go ahead and place it there.
|
|
// Otherwise we'll drag.
|
|
placing = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TEXTE_PCB* textPcb = new TEXTE_PCB( m_frame->GetModel() );
|
|
// TODO we have to set IS_NEW, otherwise InstallTextPCB.. creates an undo entry :| LEGACY_CLEANUP
|
|
textPcb->SetFlags( IS_NEW );
|
|
|
|
textPcb->SetLayer( layer );
|
|
|
|
// Set the mirrored option for layers on the BACK side of the board
|
|
if( IsBackLayer( layer ) )
|
|
textPcb->SetMirrored( true );
|
|
|
|
textPcb->SetTextSize( dsnSettings.GetTextSize( layer ) );
|
|
textPcb->SetThickness( dsnSettings.GetTextThickness( layer ) );
|
|
textPcb->SetItalic( dsnSettings.GetTextItalic( layer ) );
|
|
textPcb->SetTextPos( (wxPoint) cursorPos );
|
|
|
|
RunMainStack([&]() {
|
|
m_frame->InstallTextOptionsFrame( textPcb );
|
|
} );
|
|
|
|
if( textPcb->GetText().IsEmpty() )
|
|
{
|
|
m_controls->ForceCursorPosition( false );
|
|
delete textPcb;
|
|
}
|
|
else
|
|
text = textPcb;
|
|
}
|
|
|
|
if( text == NULL )
|
|
continue;
|
|
|
|
m_controls->WarpCursor( text->GetPosition(), true );
|
|
m_controls->ForceCursorPosition( false );
|
|
m_controls->CaptureCursor( true );
|
|
m_controls->SetAutoPan( true );
|
|
|
|
m_toolMgr->RunAction( PCB_ACTIONS::selectItem, true, text );
|
|
}
|
|
|
|
if( placing )
|
|
{
|
|
text->ClearFlags();
|
|
m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
|
|
|
|
commit.Add( text );
|
|
commit.Push( _( "Place a text" ) );
|
|
|
|
m_toolMgr->RunAction( PCB_ACTIONS::selectItem, true, text );
|
|
|
|
m_controls->CaptureCursor( false );
|
|
m_controls->SetAutoPan( false );
|
|
m_controls->ShowCursor( true );
|
|
|
|
text = NULL;
|
|
}
|
|
}
|
|
else if( text && evt->IsMotion() )
|
|
{
|
|
text->SetPosition( (wxPoint) cursorPos );
|
|
selection().SetReferencePoint( cursorPos );
|
|
m_view->Update( &selection() );
|
|
frame()->SetMsgPanel( text );
|
|
}
|
|
|
|
else if( text && evt->IsAction( &PCB_ACTIONS::properties ) )
|
|
{
|
|
// Calling 'Properties' action clears the selection, so we need to restore it
|
|
reselect = true;
|
|
}
|
|
|
|
else
|
|
evt->SetPassEvent();
|
|
}
|
|
|
|
frame()->SetMsgPanel( board() );
|
|
return 0;
|
|
}
|
|
|
|
|
|
void DRAWING_TOOL::constrainDimension( DIMENSION* dimension )
|
|
{
|
|
const VECTOR2I lineVector{ dimension->GetEnd() - dimension->GetOrigin() };
|
|
|
|
dimension->SetEnd( wxPoint(
|
|
VECTOR2I( dimension->GetOrigin() ) + GetVectorSnapped45( lineVector ) ) );
|
|
}
|
|
|
|
|
|
int DRAWING_TOOL::DrawDimension( const TOOL_EVENT& aEvent )
|
|
{
|
|
if( m_editModules && !m_frame->GetModel() )
|
|
return 0;
|
|
|
|
POINT_EDITOR* pointEditor = m_toolMgr->GetTool<POINT_EDITOR>();
|
|
DIMENSION* dimension = NULL;
|
|
BOARD_COMMIT commit( m_frame );
|
|
GRID_HELPER grid( m_frame );
|
|
|
|
// Add a VIEW_GROUP that serves as a preview for the new item
|
|
PCBNEW_SELECTION preview;
|
|
|
|
m_view->Add( &preview );
|
|
|
|
m_controls->ShowCursor( true );
|
|
m_controls->SetSnapping( true );
|
|
|
|
SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::DIMENSION );
|
|
|
|
std::string tool = aEvent.GetCommandStr().get();
|
|
m_frame->PushTool( tool );
|
|
Activate();
|
|
|
|
enum DIMENSION_STEPS
|
|
{
|
|
SET_ORIGIN = 0,
|
|
SET_END,
|
|
SET_HEIGHT,
|
|
FINISHED
|
|
};
|
|
int step = SET_ORIGIN;
|
|
|
|
// Prime the pump
|
|
m_toolMgr->RunAction( ACTIONS::refreshPreview );
|
|
|
|
if( aEvent.HasPosition() )
|
|
m_toolMgr->RunAction( ACTIONS::cursorClick );
|
|
|
|
// Main loop: keep receiving events
|
|
while( TOOL_EVENT* evt = Wait() )
|
|
{
|
|
if( !pointEditor->HasPoint() )
|
|
m_frame->GetCanvas()->SetCurrentCursor( wxCURSOR_PENCIL );
|
|
|
|
grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
|
|
grid.SetUseGrid( !evt->Modifier( MD_ALT ) );
|
|
m_controls->SetSnapping( !evt->Modifier( MD_ALT ) );
|
|
VECTOR2I cursorPos = grid.BestSnapAnchor( m_controls->GetMousePosition(), nullptr );
|
|
m_controls->ForceCursorPosition( true, cursorPos );
|
|
|
|
auto cleanup = [&] () {
|
|
preview.Clear();
|
|
delete dimension;
|
|
dimension = nullptr;
|
|
step = SET_ORIGIN;
|
|
};
|
|
|
|
if( evt->IsCancelInteractive() )
|
|
{
|
|
m_controls->SetAutoPan( false );
|
|
|
|
if( step != SET_ORIGIN ) // start from the beginning
|
|
cleanup();
|
|
else
|
|
{
|
|
m_frame->PopTool( tool );
|
|
break;
|
|
}
|
|
}
|
|
else if( evt->IsActivate() )
|
|
{
|
|
if( step != SET_ORIGIN )
|
|
cleanup();
|
|
|
|
if( evt->IsPointEditor() )
|
|
{
|
|
// don't exit (the point editor runs in the background)
|
|
}
|
|
else if( evt->IsMoveTool() )
|
|
{
|
|
// leave ourselves on the stack so we come back after the move
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
m_frame->PopTool( tool );
|
|
break;
|
|
}
|
|
}
|
|
else if( evt->IsAction( &PCB_ACTIONS::incWidth ) && step != SET_ORIGIN )
|
|
{
|
|
m_lineWidth += WIDTH_STEP;
|
|
dimension->SetWidth( m_lineWidth );
|
|
m_view->Update( &preview );
|
|
frame()->SetMsgPanel( dimension );
|
|
}
|
|
else if( evt->IsAction( &PCB_ACTIONS::decWidth ) && step != SET_ORIGIN )
|
|
{
|
|
if( m_lineWidth > WIDTH_STEP )
|
|
{
|
|
m_lineWidth -= WIDTH_STEP;
|
|
dimension->SetWidth( m_lineWidth );
|
|
m_view->Update( &preview );
|
|
frame()->SetMsgPanel( dimension );
|
|
}
|
|
}
|
|
else if( evt->IsClick( BUT_RIGHT ) )
|
|
{
|
|
m_menu.ShowContextMenu( selection() );
|
|
}
|
|
else if( evt->IsClick( BUT_LEFT ) )
|
|
{
|
|
switch( step )
|
|
{
|
|
case SET_ORIGIN:
|
|
{
|
|
m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
|
|
|
|
PCB_LAYER_ID layer = getDrawingLayer();
|
|
const BOARD_DESIGN_SETTINGS& boardSettings = m_board->GetDesignSettings();
|
|
|
|
if( layer == Edge_Cuts ) // dimensions are not allowed on EdgeCuts
|
|
layer = Dwgs_User;
|
|
|
|
// Init the new item attributes
|
|
dimension = new DIMENSION( m_board );
|
|
dimension->SetLayer( layer );
|
|
dimension->SetOrigin( (wxPoint) cursorPos );
|
|
dimension->SetEnd( (wxPoint) cursorPos );
|
|
dimension->Text().SetTextSize( boardSettings.GetTextSize( layer ) );
|
|
dimension->Text().SetThickness( boardSettings.GetTextThickness( layer ) );
|
|
dimension->Text().SetItalic( boardSettings.GetTextItalic( layer ) );
|
|
dimension->SetWidth( boardSettings.GetLineThickness( layer ) );
|
|
dimension->SetUnits( m_frame->GetUserUnits(), false );
|
|
dimension->AdjustDimensionDetails();
|
|
|
|
preview.Add( dimension );
|
|
frame()->SetMsgPanel( dimension );
|
|
|
|
m_controls->SetAutoPan( true );
|
|
m_controls->CaptureCursor( true );
|
|
}
|
|
break;
|
|
|
|
case SET_END:
|
|
dimension->SetEnd( (wxPoint) cursorPos );
|
|
|
|
if( !!evt->Modifier( MD_CTRL ) )
|
|
constrainDimension( dimension );
|
|
|
|
// Dimensions that have origin and end in the same spot are not valid
|
|
if( dimension->GetOrigin() == dimension->GetEnd() )
|
|
--step;
|
|
|
|
break;
|
|
|
|
case SET_HEIGHT:
|
|
{
|
|
if( (wxPoint) cursorPos != dimension->GetPosition() )
|
|
{
|
|
assert( dimension->GetOrigin() != dimension->GetEnd() );
|
|
assert( dimension->GetWidth() > 0 );
|
|
|
|
preview.Remove( dimension );
|
|
|
|
commit.Add( dimension );
|
|
commit.Push( _( "Draw a dimension" ) );
|
|
|
|
m_toolMgr->RunAction( PCB_ACTIONS::selectItem, true, dimension );
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
if( ++step == FINISHED )
|
|
{
|
|
step = SET_ORIGIN;
|
|
m_controls->SetAutoPan( false );
|
|
m_controls->CaptureCursor( false );
|
|
}
|
|
}
|
|
else if( evt->IsMotion() )
|
|
{
|
|
switch( step )
|
|
{
|
|
case SET_END:
|
|
dimension->SetEnd( (wxPoint) cursorPos );
|
|
|
|
if( !!evt->Modifier( MD_CTRL ) )
|
|
constrainDimension( dimension );
|
|
|
|
break;
|
|
|
|
case SET_HEIGHT:
|
|
{
|
|
// Calculating the direction of travel perpendicular to the selected axis
|
|
double angle = dimension->GetAngle() + ( M_PI / 2 );
|
|
|
|
wxPoint delta( (wxPoint) cursorPos - dimension->m_featureLineDO );
|
|
double height = ( delta.x * cos( angle ) ) + ( delta.y * sin( angle ) );
|
|
dimension->SetHeight( height );
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Show a preview of the item
|
|
m_view->Update( &preview );
|
|
if( step )
|
|
frame()->SetMsgPanel( dimension );
|
|
else
|
|
frame()->SetMsgPanel( board() );
|
|
}
|
|
else
|
|
evt->SetPassEvent();
|
|
}
|
|
|
|
if( step != SET_ORIGIN )
|
|
delete dimension;
|
|
|
|
m_controls->SetAutoPan( false );
|
|
m_controls->ForceCursorPosition( false );
|
|
|
|
m_view->Remove( &preview );
|
|
frame()->SetMsgPanel( board() );
|
|
return 0;
|
|
}
|
|
|
|
|
|
int DRAWING_TOOL::PlaceImportedGraphics( const TOOL_EVENT& aEvent )
|
|
{
|
|
if( !m_frame->GetModel() )
|
|
return 0;
|
|
|
|
// Note: PlaceImportedGraphics() will convert PCB_LINE_T and PCB_TEXT_T to module graphic
|
|
// items if needed
|
|
DIALOG_IMPORT_GFX dlg( m_frame, m_editModules );
|
|
int dlgResult = dlg.ShowModal();
|
|
|
|
auto& list = dlg.GetImportedItems();
|
|
|
|
if( dlgResult != wxID_OK )
|
|
return 0;
|
|
|
|
// Ensure the list is not empty:
|
|
if( list.empty() )
|
|
{
|
|
wxMessageBox( _( "No graphic items found in file to import") );
|
|
return 0;
|
|
}
|
|
|
|
m_toolMgr->RunAction( ACTIONS::cancelInteractive, true );
|
|
|
|
// Add a VIEW_GROUP that serves as a preview for the new item
|
|
PCBNEW_SELECTION preview;
|
|
BOARD_COMMIT commit( m_frame );
|
|
|
|
// Build the undo list & add items to the current view
|
|
for( auto& ptr : list)
|
|
{
|
|
EDA_ITEM* item = ptr.get();
|
|
|
|
if( m_editModules )
|
|
wxASSERT( item->Type() == PCB_MODULE_EDGE_T || item->Type() == PCB_MODULE_TEXT_T );
|
|
else
|
|
wxASSERT( item->Type() == PCB_LINE_T || item->Type() == PCB_TEXT_T );
|
|
|
|
if( dlg.IsPlacementInteractive() )
|
|
preview.Add( item );
|
|
else
|
|
commit.Add( item );
|
|
|
|
ptr.release();
|
|
}
|
|
|
|
if( !dlg.IsPlacementInteractive() )
|
|
{
|
|
commit.Push( _( "Place a DXF_SVG drawing" ) );
|
|
return 0;
|
|
}
|
|
|
|
BOARD_ITEM* firstItem = static_cast<BOARD_ITEM*>( preview.Front() );
|
|
m_view->Add( &preview );
|
|
|
|
m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
|
|
m_controls->ShowCursor( true );
|
|
m_controls->SetSnapping( true );
|
|
m_controls->ForceCursorPosition( false );
|
|
|
|
SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::DXF );
|
|
|
|
// Now move the new items to the current cursor position:
|
|
VECTOR2I cursorPos = m_controls->GetCursorPosition();
|
|
VECTOR2I delta = cursorPos - firstItem->GetPosition();
|
|
|
|
for( EDA_ITEM* item : preview )
|
|
static_cast<BOARD_ITEM*>( item )->Move( (wxPoint) delta );
|
|
|
|
m_view->Update( &preview );
|
|
|
|
std::string tool = aEvent.GetCommandStr().get();
|
|
m_frame->PushTool( tool );
|
|
Activate();
|
|
|
|
// Main loop: keep receiving events
|
|
while( TOOL_EVENT* evt = Wait() )
|
|
{
|
|
m_frame->GetCanvas()->SetCurrentCursor( wxCURSOR_ARROW );
|
|
cursorPos = m_controls->GetCursorPosition();
|
|
|
|
if( evt->IsCancelInteractive() || evt->IsActivate() )
|
|
{
|
|
preview.FreeItems();
|
|
|
|
m_frame->PopTool( tool );
|
|
break;
|
|
}
|
|
else if( evt->IsMotion() )
|
|
{
|
|
delta = cursorPos - firstItem->GetPosition();
|
|
|
|
for( auto item : preview )
|
|
static_cast<BOARD_ITEM*>( item )->Move( (wxPoint) delta );
|
|
|
|
m_view->Update( &preview );
|
|
}
|
|
else if( evt->Category() == TC_COMMAND )
|
|
{
|
|
// TODO it should be handled by EDIT_TOOL, so add items and select?
|
|
if( TOOL_EVT_UTILS::IsRotateToolEvt( *evt ) )
|
|
{
|
|
const auto rotationPoint = (wxPoint) cursorPos;
|
|
const auto rotationAngle = TOOL_EVT_UTILS::GetEventRotationAngle( *m_frame, *evt );
|
|
|
|
for( auto item : preview )
|
|
static_cast<BOARD_ITEM*>( item )->Rotate( rotationPoint, rotationAngle );
|
|
|
|
m_view->Update( &preview );
|
|
}
|
|
else if( evt->IsAction( &PCB_ACTIONS::flip ) )
|
|
{
|
|
bool leftRight = m_frame->Settings().m_FlipLeftRight;
|
|
|
|
for( auto item : preview )
|
|
static_cast<BOARD_ITEM*>( item )->Flip( (wxPoint) cursorPos, leftRight);
|
|
|
|
m_view->Update( &preview );
|
|
}
|
|
}
|
|
else if( evt->IsClick( BUT_RIGHT ) )
|
|
{
|
|
m_menu.ShowContextMenu( selection() );
|
|
}
|
|
else if( evt->IsClick( BUT_LEFT ) )
|
|
{
|
|
// Place the imported drawings
|
|
for( auto item : preview )
|
|
commit.Add( item );
|
|
|
|
commit.Push( _( "Place a DXF_SVG drawing" ) );
|
|
break;
|
|
}
|
|
else
|
|
evt->SetPassEvent();
|
|
}
|
|
|
|
preview.Clear();
|
|
m_view->Remove( &preview );
|
|
return 0;
|
|
}
|
|
|
|
|
|
int DRAWING_TOOL::SetAnchor( const TOOL_EVENT& aEvent )
|
|
{
|
|
assert( m_editModules );
|
|
|
|
if( !m_frame->GetModel() )
|
|
return 0;
|
|
|
|
SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::ANCHOR );
|
|
|
|
std::string tool = aEvent.GetCommandStr().get();
|
|
m_frame->PushTool( tool );
|
|
Activate();
|
|
|
|
m_controls->ShowCursor( true );
|
|
m_controls->SetSnapping( true );
|
|
m_controls->SetAutoPan( true );
|
|
m_controls->CaptureCursor( false );
|
|
|
|
while( TOOL_EVENT* evt = Wait() )
|
|
{
|
|
m_frame->GetCanvas()->SetCurrentCursor( wxCURSOR_BULLSEYE );
|
|
|
|
if( evt->IsClick( BUT_LEFT ) )
|
|
{
|
|
MODULE* module = (MODULE*) m_frame->GetModel();
|
|
BOARD_COMMIT commit( m_frame );
|
|
commit.Modify( module );
|
|
|
|
// set the new relative internal local coordinates of footprint items
|
|
VECTOR2I cursorPos = m_controls->GetCursorPosition();
|
|
wxPoint moveVector = module->GetPosition() - (wxPoint) cursorPos;
|
|
module->MoveAnchorPosition( moveVector );
|
|
|
|
commit.Push( _( "Move the footprint reference anchor" ) );
|
|
|
|
// Usually, we do not need to change twice the anchor position,
|
|
// so deselect the active tool
|
|
m_frame->PopTool( tool );
|
|
break;
|
|
}
|
|
else if( evt->IsClick( BUT_RIGHT ) )
|
|
{
|
|
m_menu.ShowContextMenu( selection() );
|
|
}
|
|
else if( evt->IsCancelInteractive() || evt->IsActivate() )
|
|
{
|
|
m_frame->PopTool( tool );
|
|
break;
|
|
}
|
|
else
|
|
evt->SetPassEvent();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
bool DRAWING_TOOL::drawSegment( const std::string& aTool, int aShape, DRAWSEGMENT*& aGraphic,
|
|
OPT<VECTOR2D> aStartingPoint )
|
|
{
|
|
// Only two shapes are currently supported
|
|
assert( aShape == S_SEGMENT || aShape == S_CIRCLE );
|
|
GRID_HELPER grid( m_frame );
|
|
POINT_EDITOR* pointEditor = m_toolMgr->GetTool<POINT_EDITOR>();
|
|
|
|
m_lineWidth = getSegmentWidth( getDrawingLayer() );
|
|
m_frame->SetActiveLayer( getDrawingLayer() );
|
|
|
|
// Add a VIEW_GROUP that serves as a preview for the new item
|
|
PCBNEW_SELECTION preview;
|
|
m_view->Add( &preview );
|
|
|
|
m_controls->ShowCursor( true );
|
|
|
|
bool direction45 = false; // 45 degrees only mode
|
|
bool started = false;
|
|
bool cancelled = false;
|
|
bool isLocalOriginSet = ( m_frame->GetScreen()->m_LocalOrigin != VECTOR2D( 0, 0 ) );
|
|
VECTOR2I cursorPos = m_controls->GetMousePosition();
|
|
|
|
// Prime the pump
|
|
m_toolMgr->RunAction( ACTIONS::refreshPreview );
|
|
|
|
if( aStartingPoint )
|
|
m_toolMgr->RunAction( ACTIONS::cursorClick );
|
|
|
|
// Main loop: keep receiving events
|
|
while( TOOL_EVENT* evt = Wait() )
|
|
{
|
|
if( !pointEditor->HasPoint() )
|
|
m_frame->GetCanvas()->SetCurrentCursor( wxCURSOR_PENCIL );
|
|
|
|
grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
|
|
grid.SetUseGrid( !evt->Modifier( MD_ALT ) );
|
|
m_controls->SetSnapping( !evt->Modifier( MD_ALT ) );
|
|
cursorPos = grid.BestSnapAnchor( m_controls->GetMousePosition(), getDrawingLayer() );
|
|
m_controls->ForceCursorPosition( true, cursorPos );
|
|
|
|
// 45 degree angle constraint enabled with an option and toggled with Ctrl
|
|
bool limit45 = frame()->Settings().m_Use45DegreeGraphicSegments;
|
|
|
|
if( evt->Modifier( MD_CTRL ) )
|
|
limit45 = !limit45;
|
|
|
|
if( direction45 != limit45 && started && aShape == S_SEGMENT )
|
|
{
|
|
direction45 = limit45;
|
|
|
|
if( direction45 )
|
|
{
|
|
const VECTOR2I lineVector( cursorPos - VECTOR2I( aGraphic->GetStart() ) );
|
|
|
|
// get a restricted 45/H/V line from the last fixed point to the cursor
|
|
auto newEnd = GetVectorSnapped45( lineVector );
|
|
aGraphic->SetEnd( aGraphic->GetStart() + (wxPoint) newEnd );
|
|
m_controls->ForceCursorPosition( true, VECTOR2I( aGraphic->GetEnd() ) );
|
|
}
|
|
else
|
|
{
|
|
aGraphic->SetEnd( (wxPoint) cursorPos );
|
|
}
|
|
|
|
m_view->Update( &preview );
|
|
frame()->SetMsgPanel( aGraphic );
|
|
}
|
|
|
|
auto cleanup = [&] () {
|
|
preview.Clear();
|
|
m_view->Update( &preview );
|
|
delete aGraphic;
|
|
aGraphic = nullptr;
|
|
|
|
if( !isLocalOriginSet )
|
|
m_frame->GetScreen()->m_LocalOrigin = VECTOR2D( 0, 0 );
|
|
};
|
|
|
|
if( evt->IsCancelInteractive() )
|
|
{
|
|
if( started )
|
|
cleanup();
|
|
else
|
|
{
|
|
m_frame->PopTool( aTool );
|
|
cancelled = true;
|
|
}
|
|
|
|
break;
|
|
}
|
|
else if( evt->IsActivate() )
|
|
{
|
|
if( evt->IsPointEditor() )
|
|
{
|
|
// don't exit (the point editor runs in the background)
|
|
}
|
|
else if( evt->IsMoveTool() )
|
|
{
|
|
if( started )
|
|
cleanup();
|
|
|
|
// leave ourselves on the stack so we come back after the move
|
|
cancelled = true;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
if( started )
|
|
cleanup();
|
|
|
|
m_frame->PopTool( aTool );
|
|
cancelled = true;
|
|
break;
|
|
}
|
|
}
|
|
else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
|
|
{
|
|
m_lineWidth = getSegmentWidth( getDrawingLayer() );
|
|
aGraphic->SetLayer( getDrawingLayer() );
|
|
aGraphic->SetWidth( m_lineWidth );
|
|
m_view->Update( &preview );
|
|
frame()->SetMsgPanel( aGraphic );
|
|
}
|
|
else if( evt->IsClick( BUT_RIGHT ) )
|
|
{
|
|
m_menu.ShowContextMenu( selection() );
|
|
}
|
|
else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
|
|
{
|
|
if( !started )
|
|
{
|
|
m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
|
|
|
|
if( aStartingPoint )
|
|
{
|
|
cursorPos = aStartingPoint.get();
|
|
aStartingPoint = NULLOPT;
|
|
}
|
|
|
|
m_lineWidth = getSegmentWidth( getDrawingLayer() );
|
|
|
|
// Init the new item attributes
|
|
aGraphic->SetShape( (STROKE_T) aShape );
|
|
aGraphic->SetWidth( m_lineWidth );
|
|
aGraphic->SetStart( (wxPoint) cursorPos );
|
|
aGraphic->SetEnd( (wxPoint) cursorPos );
|
|
aGraphic->SetLayer( getDrawingLayer() );
|
|
|
|
if( !isLocalOriginSet )
|
|
m_frame->GetScreen()->m_LocalOrigin = cursorPos;
|
|
|
|
preview.Add( aGraphic );
|
|
frame()->SetMsgPanel( aGraphic );
|
|
m_controls->SetAutoPan( true );
|
|
m_controls->CaptureCursor( true );
|
|
|
|
started = true;
|
|
}
|
|
else
|
|
{
|
|
auto snapItem = dyn_cast<DRAWSEGMENT*>( grid.GetSnapped() );
|
|
|
|
if( aGraphic->GetEnd() == aGraphic->GetStart()
|
|
|| ( evt->IsDblClick( BUT_LEFT ) && aShape == S_SEGMENT )
|
|
|| snapItem )
|
|
// User has clicked twice in the same spot
|
|
// or clicked on the end of an existing segment (closing a path)
|
|
{
|
|
BOARD_COMMIT commit( m_frame );
|
|
|
|
// If the user clicks on an existing snap point from a drawsegment
|
|
// we finish the segment as they are likely closing a path
|
|
if( snapItem && aGraphic->GetLength() > 0.0 )
|
|
{
|
|
commit.Add( aGraphic );
|
|
commit.Push( _( "Draw a line segment" ) );
|
|
m_toolMgr->RunAction( PCB_ACTIONS::selectItem, true, aGraphic );
|
|
}
|
|
else
|
|
{
|
|
delete aGraphic;
|
|
}
|
|
|
|
aGraphic = nullptr;
|
|
}
|
|
|
|
preview.Clear();
|
|
break;
|
|
}
|
|
}
|
|
else if( evt->IsMotion() )
|
|
{
|
|
// 45 degree lines
|
|
if( direction45 && aShape == S_SEGMENT )
|
|
{
|
|
const VECTOR2I lineVector( cursorPos - VECTOR2I( aGraphic->GetStart() ) );
|
|
|
|
// get a restricted 45/H/V line from the last fixed point to the cursor
|
|
auto newEnd = GetVectorSnapped45( lineVector );
|
|
aGraphic->SetEnd( aGraphic->GetStart() + (wxPoint) newEnd );
|
|
m_controls->ForceCursorPosition( true, VECTOR2I( aGraphic->GetEnd() ) );
|
|
}
|
|
else
|
|
aGraphic->SetEnd( (wxPoint) cursorPos );
|
|
|
|
m_view->Update( &preview );
|
|
|
|
if( started )
|
|
frame()->SetMsgPanel( aGraphic );
|
|
else
|
|
frame()->SetMsgPanel( board() );
|
|
}
|
|
else if( evt->IsAction( &PCB_ACTIONS::incWidth ) )
|
|
{
|
|
m_lineWidth += WIDTH_STEP;
|
|
aGraphic->SetWidth( m_lineWidth );
|
|
m_view->Update( &preview );
|
|
frame()->SetMsgPanel( aGraphic );
|
|
}
|
|
else if( evt->IsAction( &PCB_ACTIONS::decWidth ) && ( m_lineWidth > WIDTH_STEP ) )
|
|
{
|
|
m_lineWidth -= WIDTH_STEP;
|
|
aGraphic->SetWidth( m_lineWidth );
|
|
m_view->Update( &preview );
|
|
frame()->SetMsgPanel( aGraphic );
|
|
}
|
|
else if( evt->IsAction( &ACTIONS::resetLocalCoords ) )
|
|
{
|
|
isLocalOriginSet = true;
|
|
}
|
|
else
|
|
evt->SetPassEvent();
|
|
}
|
|
|
|
if( !isLocalOriginSet ) // reset the relative coordinte if it was not set before
|
|
m_frame->GetScreen()->m_LocalOrigin = VECTOR2D( 0, 0 );
|
|
|
|
m_view->Remove( &preview );
|
|
frame()->SetMsgPanel( board() );
|
|
m_controls->SetAutoPan( false );
|
|
m_controls->CaptureCursor( false );
|
|
m_controls->ForceCursorPosition( false );
|
|
|
|
return !cancelled;
|
|
}
|
|
|
|
|
|
/**
|
|
* Update an arc DRAWSEGMENT from the current state
|
|
* of an Arc Geometry Manager
|
|
*/
|
|
static void updateArcFromConstructionMgr( const KIGFX::PREVIEW::ARC_GEOM_MANAGER& aMgr,
|
|
DRAWSEGMENT& aArc )
|
|
{
|
|
auto vec = aMgr.GetOrigin();
|
|
|
|
aArc.SetCenter( { vec.x, vec.y } );
|
|
|
|
vec = aMgr.GetStartRadiusEnd();
|
|
aArc.SetArcStart( { vec.x, vec.y } );
|
|
|
|
aArc.SetAngle( RAD2DECIDEG( -aMgr.GetSubtended() ) );
|
|
}
|
|
|
|
|
|
bool DRAWING_TOOL::drawArc( const std::string& aTool, DRAWSEGMENT*& aGraphic, bool aImmediateMode )
|
|
{
|
|
m_lineWidth = getSegmentWidth( getDrawingLayer() );
|
|
|
|
// Arc geometric construction manager
|
|
KIGFX::PREVIEW::ARC_GEOM_MANAGER arcManager;
|
|
|
|
// Arc drawing assistant overlay
|
|
KIGFX::PREVIEW::ARC_ASSISTANT arcAsst( arcManager, m_frame->GetUserUnits() );
|
|
|
|
// Add a VIEW_GROUP that serves as a preview for the new item
|
|
PCBNEW_SELECTION preview;
|
|
m_view->Add( &preview );
|
|
m_view->Add( &arcAsst );
|
|
GRID_HELPER grid( m_frame );
|
|
|
|
m_controls->ShowCursor( true );
|
|
m_controls->SetSnapping( true );
|
|
|
|
bool firstPoint = false;
|
|
bool cancelled = false;
|
|
|
|
// Prime the pump
|
|
m_toolMgr->RunAction( ACTIONS::refreshPreview );
|
|
|
|
if( aImmediateMode )
|
|
m_toolMgr->RunAction( ACTIONS::cursorClick );
|
|
|
|
// Main loop: keep receiving events
|
|
while( TOOL_EVENT* evt = Wait() )
|
|
{
|
|
PCB_LAYER_ID layer = getDrawingLayer();
|
|
aGraphic->SetLayer( layer );
|
|
|
|
m_frame->GetCanvas()->SetCurrentCursor( wxCURSOR_PENCIL );
|
|
grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
|
|
grid.SetUseGrid( !evt->Modifier( MD_ALT ) );
|
|
m_controls->SetSnapping( !evt->Modifier( MD_ALT ) );
|
|
VECTOR2I cursorPos = grid.BestSnapAnchor( m_controls->GetMousePosition(), aGraphic );
|
|
m_controls->ForceCursorPosition( true, cursorPos );
|
|
|
|
auto cleanup = [&] () {
|
|
preview.Clear();
|
|
delete aGraphic;
|
|
aGraphic = nullptr;
|
|
};
|
|
|
|
if( evt->IsCancelInteractive() )
|
|
{
|
|
if( firstPoint )
|
|
cleanup();
|
|
else
|
|
{
|
|
m_frame->PopTool( aTool );
|
|
cancelled = true;
|
|
}
|
|
|
|
break;
|
|
}
|
|
else if( evt->IsActivate() )
|
|
{
|
|
if( evt->IsPointEditor() )
|
|
{
|
|
// don't exit (the point editor runs in the background)
|
|
}
|
|
else if( evt->IsMoveTool() )
|
|
{
|
|
if( firstPoint )
|
|
cleanup();
|
|
|
|
// leave ourselves on the stack so we come back after the move
|
|
cancelled = true;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
if( firstPoint )
|
|
cleanup();
|
|
|
|
m_frame->PopTool( aTool );
|
|
cancelled = true;
|
|
break;
|
|
}
|
|
}
|
|
else if( evt->IsClick( BUT_LEFT ) )
|
|
{
|
|
if( !firstPoint )
|
|
{
|
|
m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
|
|
|
|
m_controls->SetAutoPan( true );
|
|
m_controls->CaptureCursor( true );
|
|
|
|
m_lineWidth = getSegmentWidth( getDrawingLayer() );
|
|
|
|
// Init the new item attributes
|
|
// (non-geometric, those are handled by the manager)
|
|
aGraphic->SetShape( S_ARC );
|
|
aGraphic->SetWidth( m_lineWidth );
|
|
|
|
preview.Add( aGraphic );
|
|
firstPoint = true;
|
|
}
|
|
|
|
arcManager.AddPoint( cursorPos, true );
|
|
}
|
|
else if( evt->IsAction( &PCB_ACTIONS::deleteLastPoint ) )
|
|
{
|
|
arcManager.RemoveLastPoint();
|
|
}
|
|
else if( evt->IsMotion() )
|
|
{
|
|
// set angle snap
|
|
arcManager.SetAngleSnap( evt->Modifier( MD_CTRL ) );
|
|
|
|
// update, but don't step the manager state
|
|
arcManager.AddPoint( cursorPos, false );
|
|
}
|
|
else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
|
|
{
|
|
m_lineWidth = getSegmentWidth( getDrawingLayer() );
|
|
aGraphic->SetLayer( getDrawingLayer() );
|
|
aGraphic->SetWidth( m_lineWidth );
|
|
m_view->Update( &preview );
|
|
frame()->SetMsgPanel( aGraphic );
|
|
}
|
|
else if( evt->IsClick( BUT_RIGHT ) )
|
|
{
|
|
m_menu.ShowContextMenu( selection() );
|
|
}
|
|
else if( evt->IsAction( &PCB_ACTIONS::incWidth ) )
|
|
{
|
|
m_lineWidth += WIDTH_STEP;
|
|
aGraphic->SetWidth( m_lineWidth );
|
|
m_view->Update( &preview );
|
|
frame()->SetMsgPanel( aGraphic );
|
|
}
|
|
else if( evt->IsAction( &PCB_ACTIONS::decWidth ) && m_lineWidth > WIDTH_STEP )
|
|
{
|
|
m_lineWidth -= WIDTH_STEP;
|
|
aGraphic->SetWidth( m_lineWidth );
|
|
m_view->Update( &preview );
|
|
frame()->SetMsgPanel( aGraphic );
|
|
}
|
|
else if( evt->IsAction( &PCB_ACTIONS::arcPosture ) )
|
|
{
|
|
arcManager.ToggleClockwise();
|
|
}
|
|
else
|
|
evt->SetPassEvent();
|
|
|
|
if( arcManager.IsComplete() )
|
|
{
|
|
break;
|
|
}
|
|
else if( arcManager.HasGeometryChanged() )
|
|
{
|
|
updateArcFromConstructionMgr( arcManager, *aGraphic );
|
|
m_view->Update( &preview );
|
|
m_view->Update( &arcAsst );
|
|
|
|
if( firstPoint )
|
|
frame()->SetMsgPanel( aGraphic );
|
|
else
|
|
frame()->SetMsgPanel( board() );
|
|
}
|
|
}
|
|
|
|
preview.Remove( aGraphic );
|
|
m_view->Remove( &arcAsst );
|
|
m_view->Remove( &preview );
|
|
frame()->SetMsgPanel( board() );
|
|
m_controls->SetAutoPan( false );
|
|
m_controls->CaptureCursor( false );
|
|
m_controls->ForceCursorPosition( false );
|
|
|
|
return !cancelled;
|
|
}
|
|
|
|
|
|
bool DRAWING_TOOL::getSourceZoneForAction( ZONE_MODE aMode, ZONE_CONTAINER*& aZone )
|
|
{
|
|
bool clearSelection = false;
|
|
aZone = nullptr;
|
|
|
|
// not an action that needs a source zone
|
|
if( aMode == ZONE_MODE::ADD || aMode == ZONE_MODE::GRAPHIC_POLYGON )
|
|
return true;
|
|
|
|
SELECTION_TOOL* selTool = m_toolMgr->GetTool<SELECTION_TOOL>();
|
|
const PCBNEW_SELECTION& selection = selTool->GetSelection();
|
|
|
|
if( selection.Empty() )
|
|
{
|
|
clearSelection = true;
|
|
m_toolMgr->RunAction( PCB_ACTIONS::selectionCursor, true );
|
|
}
|
|
|
|
// we want a single zone
|
|
if( selection.Size() == 1 )
|
|
aZone = dyn_cast<ZONE_CONTAINER*>( selection[0] );
|
|
|
|
// expected a zone, but didn't get one
|
|
if( !aZone )
|
|
{
|
|
if( clearSelection )
|
|
m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
int DRAWING_TOOL::DrawZone( const TOOL_EVENT& aEvent )
|
|
{
|
|
if( m_editModules && !m_frame->GetModel() )
|
|
return 0;
|
|
|
|
ZONE_MODE zoneMode = aEvent.Parameter<ZONE_MODE>();
|
|
MODE drawMode = MODE::ZONE;
|
|
|
|
if( aEvent.IsAction( &PCB_ACTIONS::drawZoneKeepout ) )
|
|
drawMode = MODE::KEEPOUT;
|
|
|
|
if( aEvent.IsAction( &PCB_ACTIONS::drawPolygon ) )
|
|
drawMode = MODE::GRAPHIC_POLYGON;
|
|
|
|
SCOPED_DRAW_MODE scopedDrawMode( m_mode, drawMode );
|
|
|
|
// get a source zone, if we need one. We need it for:
|
|
// ZONE_MODE::CUTOUT (adding a hole to the source zone)
|
|
// ZONE_MODE::SIMILAR (creating a new zone using settings of source zone
|
|
ZONE_CONTAINER* sourceZone = nullptr;
|
|
|
|
if( !getSourceZoneForAction( zoneMode, sourceZone ) )
|
|
return 0;
|
|
|
|
ZONE_CREATE_HELPER::PARAMS params;
|
|
|
|
params.m_keepout = drawMode == MODE::KEEPOUT;
|
|
params.m_mode = zoneMode;
|
|
params.m_sourceZone = sourceZone;
|
|
|
|
if( zoneMode == ZONE_MODE::GRAPHIC_POLYGON )
|
|
params.m_layer = getDrawingLayer();
|
|
else if( zoneMode == ZONE_MODE::SIMILAR )
|
|
params.m_layer = sourceZone->GetLayer();
|
|
else
|
|
params.m_layer = m_frame->GetActiveLayer();
|
|
|
|
ZONE_CREATE_HELPER zoneTool( *this, params );
|
|
|
|
// the geometry manager which handles the zone geometry, and
|
|
// hands the calculated points over to the zone creator tool
|
|
POLYGON_GEOM_MANAGER polyGeomMgr( zoneTool );
|
|
|
|
std::string tool = aEvent.GetCommandStr().get();
|
|
m_frame->PushTool( tool );
|
|
Activate(); // register for events
|
|
|
|
m_controls->ShowCursor( true );
|
|
m_controls->SetSnapping( true );
|
|
|
|
bool started = false;
|
|
GRID_HELPER grid( m_frame );
|
|
STATUS_TEXT_POPUP status( m_frame );
|
|
status.SetTextColor( wxColour( 255, 0, 0 ) );
|
|
status.SetText( _( "Self-intersecting polygons are not allowed" ) );
|
|
|
|
// Prime the pump
|
|
if( aEvent.HasPosition() )
|
|
m_toolMgr->RunAction( ACTIONS::cursorClick );
|
|
|
|
// Main loop: keep receiving events
|
|
while( TOOL_EVENT* evt = Wait() )
|
|
{
|
|
m_frame->GetCanvas()->SetCurrentCursor( wxCURSOR_PENCIL );
|
|
LSET layers( m_frame->GetActiveLayer() );
|
|
grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
|
|
grid.SetUseGrid( !evt->Modifier( MD_ALT ) );
|
|
m_controls->SetSnapping( !evt->Modifier( MD_ALT ) );
|
|
VECTOR2I cursorPos = grid.BestSnapAnchor( m_controls->GetMousePosition(), layers );
|
|
m_controls->ForceCursorPosition( true, cursorPos );
|
|
|
|
auto cleanup = [&] () {
|
|
polyGeomMgr.Reset();
|
|
started = false;
|
|
m_controls->SetAutoPan( false );
|
|
m_controls->CaptureCursor( false );
|
|
};
|
|
|
|
if( evt->IsCancelInteractive())
|
|
{
|
|
if( polyGeomMgr.IsPolygonInProgress() )
|
|
cleanup();
|
|
else
|
|
{
|
|
m_frame->PopTool( tool );
|
|
break;
|
|
}
|
|
}
|
|
else if( evt->IsActivate() )
|
|
{
|
|
if( polyGeomMgr.IsPolygonInProgress() )
|
|
cleanup();
|
|
|
|
if( evt->IsPointEditor() )
|
|
{
|
|
// don't exit (the point editor runs in the background)
|
|
}
|
|
else if( evt->IsMoveTool() )
|
|
{
|
|
// leave ourselves on the stack so we come back after the move
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
m_frame->PopTool( tool );
|
|
break;
|
|
}
|
|
}
|
|
else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
|
|
{
|
|
if( zoneMode == ZONE_MODE::GRAPHIC_POLYGON )
|
|
params.m_layer = getDrawingLayer();
|
|
else if( zoneMode == ZONE_MODE::ADD || zoneMode == ZONE_MODE::CUTOUT )
|
|
params.m_layer = frame()->GetActiveLayer();
|
|
}
|
|
else if( evt->IsClick( BUT_RIGHT ) )
|
|
{
|
|
m_menu.ShowContextMenu( selection() );
|
|
}
|
|
// events that lock in nodes
|
|
else if( evt->IsClick( BUT_LEFT )
|
|
|| evt->IsDblClick( BUT_LEFT )
|
|
|| evt->IsAction( &PCB_ACTIONS::closeZoneOutline ) )
|
|
{
|
|
// Check if it is double click / closing line (so we have to finish the zone)
|
|
const bool endPolygon = evt->IsDblClick( BUT_LEFT )
|
|
|| evt->IsAction( &PCB_ACTIONS::closeZoneOutline )
|
|
|| polyGeomMgr.NewPointClosesOutline( cursorPos );
|
|
|
|
if( endPolygon )
|
|
{
|
|
polyGeomMgr.SetFinished();
|
|
polyGeomMgr.Reset();
|
|
|
|
started = false;
|
|
m_controls->SetAutoPan( false );
|
|
m_controls->CaptureCursor( false );
|
|
}
|
|
|
|
// adding a corner
|
|
else if( polyGeomMgr.AddPoint( cursorPos ) )
|
|
{
|
|
if( !started )
|
|
{
|
|
started = true;
|
|
m_controls->SetAutoPan( true );
|
|
m_controls->CaptureCursor( true );
|
|
}
|
|
}
|
|
}
|
|
else if( evt->IsAction( &PCB_ACTIONS::deleteLastPoint ) )
|
|
{
|
|
polyGeomMgr.DeleteLastCorner();
|
|
|
|
if( !polyGeomMgr.IsPolygonInProgress() )
|
|
{
|
|
// report finished as an empty shape
|
|
polyGeomMgr.SetFinished();
|
|
|
|
// start again
|
|
started = false;
|
|
m_controls->SetAutoPan( false );
|
|
m_controls->CaptureCursor( false );
|
|
}
|
|
}
|
|
else if( polyGeomMgr.IsPolygonInProgress()
|
|
&& ( evt->IsMotion() || evt->IsDrag( BUT_LEFT ) ) )
|
|
{
|
|
polyGeomMgr.SetCursorPosition( cursorPos, evt->Modifier( MD_CTRL )
|
|
? POLYGON_GEOM_MANAGER::LEADER_MODE::DEG45
|
|
: POLYGON_GEOM_MANAGER::LEADER_MODE::DIRECT );
|
|
|
|
if( polyGeomMgr.IsSelfIntersecting( true ) )
|
|
{
|
|
wxPoint p = wxGetMousePosition() + wxPoint( 20, 20 );
|
|
status.Move( p );
|
|
status.PopupFor( 1500 );
|
|
}
|
|
else
|
|
{
|
|
status.Hide();
|
|
}
|
|
}
|
|
else
|
|
evt->SetPassEvent();
|
|
|
|
} // end while
|
|
|
|
m_controls->ForceCursorPosition( false );
|
|
return 0;
|
|
}
|
|
|
|
|
|
int DRAWING_TOOL::DrawVia( const TOOL_EVENT& aEvent )
|
|
{
|
|
struct VIA_PLACER : public INTERACTIVE_PLACER_BASE
|
|
{
|
|
GRID_HELPER m_gridHelper;
|
|
|
|
VIA_PLACER( PCB_BASE_EDIT_FRAME* aFrame ) : m_gridHelper( aFrame )
|
|
{}
|
|
|
|
TRACK* findTrack( VIA* aVia )
|
|
{
|
|
const LSET lset = aVia->GetLayerSet();
|
|
wxPoint position = aVia->GetPosition();
|
|
BOX2I bbox = aVia->GetBoundingBox();
|
|
|
|
std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> items;
|
|
auto view = m_frame->GetCanvas()->GetView();
|
|
std::vector<TRACK*> possible_tracks;
|
|
|
|
view->Query( bbox, items );
|
|
|
|
for( auto it : items )
|
|
{
|
|
BOARD_ITEM* item = static_cast<BOARD_ITEM*>( it.first );
|
|
|
|
if( !(item->GetLayerSet() & lset ).any() )
|
|
continue;
|
|
|
|
if( auto track = dyn_cast<TRACK*>( item ) )
|
|
{
|
|
if( TestSegmentHit( position, track->GetStart(), track->GetEnd(),
|
|
( track->GetWidth() + aVia->GetWidth() ) / 2 ) )
|
|
possible_tracks.push_back( track );
|
|
}
|
|
}
|
|
|
|
TRACK* return_track = nullptr;
|
|
int min_d = std::numeric_limits<int>::max();
|
|
for( auto track : possible_tracks )
|
|
{
|
|
SEG test( track->GetStart(), track->GetEnd() );
|
|
auto dist = ( test.NearestPoint( position ) - position ).EuclideanNorm();
|
|
|
|
if( dist < min_d )
|
|
{
|
|
min_d = dist;
|
|
return_track = track;
|
|
}
|
|
}
|
|
|
|
return return_track;
|
|
}
|
|
|
|
|
|
bool hasDRCViolation( VIA* aVia )
|
|
{
|
|
const LSET lset = aVia->GetLayerSet();
|
|
wxPoint position = aVia->GetPosition();
|
|
int drillRadius = aVia->GetDrillValue() / 2;
|
|
BOX2I bbox = aVia->GetBoundingBox();
|
|
|
|
std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> items;
|
|
int net = 0;
|
|
int clearance = 0;
|
|
auto view = m_frame->GetCanvas()->GetView();
|
|
int holeToHoleMin = m_frame->GetBoard()->GetDesignSettings().m_HoleToHoleMin;
|
|
|
|
view->Query( bbox, items );
|
|
|
|
for( auto it : items )
|
|
{
|
|
BOARD_ITEM* item = static_cast<BOARD_ITEM*>( it.first );
|
|
|
|
if( !(item->GetLayerSet() & lset ).any() )
|
|
continue;
|
|
|
|
if( auto track = dyn_cast<TRACK*>( item ) )
|
|
{
|
|
int max_clearance = std::max( clearance, track->GetClearance() );
|
|
|
|
if( TestSegmentHit( position, track->GetStart(), track->GetEnd(),
|
|
( track->GetWidth() + aVia->GetWidth() ) / 2 + max_clearance ) )
|
|
{
|
|
if( net && track->GetNetCode() != net )
|
|
return true;
|
|
|
|
net = track->GetNetCode();
|
|
clearance = track->GetClearance();
|
|
}
|
|
}
|
|
|
|
if( auto via = dyn_cast<VIA*>( item ) )
|
|
{
|
|
int dist = KiROUND( GetLineLength( position, via->GetPosition() ) );
|
|
|
|
if( dist < drillRadius + via->GetDrillValue() / 2 + holeToHoleMin )
|
|
return true;
|
|
}
|
|
|
|
if( auto mod = dyn_cast<MODULE*>( item ) )
|
|
{
|
|
for( D_PAD* pad : mod->Pads() )
|
|
{
|
|
int max_clearance = std::max( clearance, pad->GetClearance() );
|
|
|
|
if( pad->HitTest( aVia->GetBoundingBox(), false, max_clearance ) )
|
|
{
|
|
if( net && pad->GetNetCode() != net )
|
|
return true;
|
|
|
|
net = pad->GetNetCode();
|
|
clearance = pad->GetClearance();
|
|
}
|
|
|
|
if( pad->GetDrillSize().x && pad->GetDrillShape() == PAD_DRILL_SHAPE_CIRCLE )
|
|
{
|
|
int dist = KiROUND( GetLineLength( position, pad->GetPosition() ) );
|
|
|
|
if( dist < drillRadius + pad->GetDrillSize().x / 2 + holeToHoleMin )
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
int findStitchedZoneNet( VIA* aVia )
|
|
{
|
|
const wxPoint position = aVia->GetPosition();
|
|
const LSET lset = aVia->GetLayerSet();
|
|
|
|
for( auto mod : m_board->Modules() )
|
|
{
|
|
for( D_PAD* pad : mod->Pads() )
|
|
{
|
|
if( pad->HitTest( position ) && ( pad->GetLayerSet() & lset ).any() )
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
std::vector<ZONE_CONTAINER*> foundZones;
|
|
|
|
for( ZONE_CONTAINER* zone : m_board->Zones() )
|
|
{
|
|
if( zone->HitTestFilledArea( position ) )
|
|
foundZones.push_back( zone );
|
|
}
|
|
|
|
std::sort( foundZones.begin(), foundZones.end(),
|
|
[] ( const ZONE_CONTAINER* a, const ZONE_CONTAINER* b )
|
|
{
|
|
return a->GetLayer() < b->GetLayer();
|
|
} );
|
|
|
|
// first take the net of the active layer
|
|
for( ZONE_CONTAINER* z : foundZones )
|
|
{
|
|
if( m_frame->GetActiveLayer() == z->GetLayer() )
|
|
return z->GetNetCode();
|
|
}
|
|
|
|
// none? take the topmost visible layer
|
|
for( ZONE_CONTAINER* z : foundZones )
|
|
{
|
|
if( m_board->IsLayerVisible( z->GetLayer() ) )
|
|
return z->GetNetCode();
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
void SnapItem( BOARD_ITEM *aItem ) override
|
|
{
|
|
// If you place a Via on a track but not on its centerline, the current
|
|
// connectivity algorithm will require us to put a kink in the track when
|
|
// we break it (so that each of the two segments ends on the via center).
|
|
// That's not ideal, and is in fact probably worse than forcing snap in
|
|
// this situation.
|
|
|
|
m_gridHelper.SetSnap( !( m_modifiers & MD_SHIFT ) );
|
|
auto via = static_cast<VIA*>( aItem );
|
|
wxPoint position = via->GetPosition();
|
|
TRACK* track = findTrack( via );
|
|
|
|
if( track )
|
|
{
|
|
SEG trackSeg( track->GetStart(), track->GetEnd() );
|
|
VECTOR2I snap = m_gridHelper.AlignToSegment( position, trackSeg );
|
|
|
|
aItem->SetPosition( (wxPoint) snap );
|
|
}
|
|
}
|
|
|
|
bool PlaceItem( BOARD_ITEM* aItem, BOARD_COMMIT& aCommit ) override
|
|
{
|
|
VIA* via = static_cast<VIA*>( aItem );
|
|
wxPoint viaPos = via->GetPosition();
|
|
int newNet;
|
|
TRACK* track = findTrack( via );
|
|
|
|
if( hasDRCViolation( via ) )
|
|
return false;
|
|
|
|
if( track )
|
|
{
|
|
if( viaPos != track->GetStart() && viaPos != track->GetEnd() )
|
|
{
|
|
aCommit.Modify( track );
|
|
TRACK* newTrack = dynamic_cast<TRACK*>( track->Clone() );
|
|
track->SetEnd( viaPos );
|
|
newTrack->SetStart( viaPos );
|
|
aCommit.Add( newTrack );
|
|
}
|
|
|
|
newNet = track->GetNetCode();
|
|
}
|
|
else
|
|
newNet = findStitchedZoneNet( via );
|
|
|
|
if( newNet > 0 )
|
|
via->SetNetCode( newNet );
|
|
|
|
aCommit.Add( aItem );
|
|
return true;
|
|
}
|
|
|
|
std::unique_ptr<BOARD_ITEM> CreateItem() override
|
|
{
|
|
auto& ds = m_board->GetDesignSettings();
|
|
VIA* via = new VIA( m_board );
|
|
|
|
via->SetNetCode( 0 );
|
|
via->SetViaType( ds.m_CurrentViaType );
|
|
|
|
// for microvias, the size and hole will be changed later.
|
|
via->SetWidth( ds.GetCurrentViaSize() );
|
|
via->SetDrill( ds.GetCurrentViaDrill() );
|
|
|
|
// Usual via is from copper to component.
|
|
// layer pair is B_Cu and F_Cu.
|
|
via->SetLayerPair( B_Cu, F_Cu );
|
|
|
|
PCB_LAYER_ID first_layer = m_frame->GetActiveLayer();
|
|
PCB_LAYER_ID last_layer;
|
|
|
|
// prepare switch to new active layer:
|
|
if( first_layer != m_frame->GetScreen()->m_Route_Layer_TOP )
|
|
last_layer = m_frame->GetScreen()->m_Route_Layer_TOP;
|
|
else
|
|
last_layer = m_frame->GetScreen()->m_Route_Layer_BOTTOM;
|
|
|
|
// Adjust the actual via layer pair
|
|
switch( via->GetViaType() )
|
|
{
|
|
case VIA_BLIND_BURIED:
|
|
via->SetLayerPair( first_layer, last_layer );
|
|
break;
|
|
|
|
case VIA_MICROVIA: // from external to the near neighbor inner layer
|
|
{
|
|
PCB_LAYER_ID last_inner_layer =
|
|
ToLAYER_ID( ( m_board->GetCopperLayerCount() - 2 ) );
|
|
|
|
if( first_layer == B_Cu )
|
|
last_layer = last_inner_layer;
|
|
else if( first_layer == F_Cu )
|
|
last_layer = In1_Cu;
|
|
else if( first_layer == last_inner_layer )
|
|
last_layer = B_Cu;
|
|
else if( first_layer == In1_Cu )
|
|
last_layer = F_Cu;
|
|
|
|
// else error: will be removed later
|
|
via->SetLayerPair( first_layer, last_layer );
|
|
|
|
// Update diameter and hole size, which where set previously
|
|
// for normal vias
|
|
NETINFO_ITEM* net = via->GetNet();
|
|
|
|
if( net )
|
|
{
|
|
via->SetWidth( net->GetMicroViaSize() );
|
|
via->SetDrill( net->GetMicroViaDrillSize() );
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return std::unique_ptr<BOARD_ITEM>( via );
|
|
}
|
|
};
|
|
|
|
VIA_PLACER placer( frame() );
|
|
|
|
SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::VIA );
|
|
|
|
doInteractiveItemPlacement( aEvent.GetCommandStr().get(), &placer, _( "Place via" ),
|
|
IPO_REPEAT | IPO_SINGLE_CLICK );
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int DRAWING_TOOL::getSegmentWidth( PCB_LAYER_ID aLayer ) const
|
|
{
|
|
assert( m_board );
|
|
return m_board->GetDesignSettings().GetLineThickness( aLayer );
|
|
}
|
|
|
|
|
|
PCB_LAYER_ID DRAWING_TOOL::getDrawingLayer() const
|
|
{
|
|
PCB_LAYER_ID layer = m_frame->GetActiveLayer();
|
|
|
|
if( ( GetDrawingMode() == MODE::DIMENSION || GetDrawingMode() == MODE::GRAPHIC_POLYGON )
|
|
&& IsCopperLayer( layer ) )
|
|
{
|
|
if( layer == F_Cu )
|
|
layer = F_SilkS;
|
|
else if( layer == B_Cu )
|
|
layer = B_SilkS;
|
|
else
|
|
layer = Dwgs_User;
|
|
|
|
m_frame->SetActiveLayer( layer );
|
|
}
|
|
|
|
return layer;
|
|
}
|
|
|
|
|
|
const unsigned int DRAWING_TOOL::WIDTH_STEP = Millimeter2iu( 0.1 );
|
|
|
|
|
|
void DRAWING_TOOL::setTransitions()
|
|
{
|
|
Go( &DRAWING_TOOL::DrawLine, PCB_ACTIONS::drawLine.MakeEvent() );
|
|
Go( &DRAWING_TOOL::DrawZone, PCB_ACTIONS::drawPolygon.MakeEvent() );
|
|
Go( &DRAWING_TOOL::DrawCircle, PCB_ACTIONS::drawCircle.MakeEvent() );
|
|
Go( &DRAWING_TOOL::DrawArc, PCB_ACTIONS::drawArc.MakeEvent() );
|
|
Go( &DRAWING_TOOL::DrawDimension, PCB_ACTIONS::drawDimension.MakeEvent() );
|
|
Go( &DRAWING_TOOL::DrawZone, PCB_ACTIONS::drawZone.MakeEvent() );
|
|
Go( &DRAWING_TOOL::DrawZone, PCB_ACTIONS::drawZoneKeepout.MakeEvent() );
|
|
Go( &DRAWING_TOOL::DrawZone, PCB_ACTIONS::drawZoneCutout.MakeEvent() );
|
|
Go( &DRAWING_TOOL::DrawZone, PCB_ACTIONS::drawSimilarZone.MakeEvent() );
|
|
Go( &DRAWING_TOOL::DrawVia, PCB_ACTIONS::drawVia.MakeEvent() );
|
|
Go( &DRAWING_TOOL::PlaceText, PCB_ACTIONS::placeText.MakeEvent() );
|
|
Go( &DRAWING_TOOL::PlaceImportedGraphics, PCB_ACTIONS::placeImportedGraphics.MakeEvent() );
|
|
Go( &DRAWING_TOOL::SetAnchor, PCB_ACTIONS::setAnchor.MakeEvent() );
|
|
}
|