summaryrefslogtreecommitdiff
path: root/pcbnew/tools
diff options
context:
space:
mode:
authorsaurabhb172020-02-26 15:57:49 +0530
committersaurabhb172020-02-26 15:57:49 +0530
commitaa35045840b78d3f48212db45da59a2e5c69b223 (patch)
tree6acee185a4dc19113fcbf0f9a3d6941085dedaf7 /pcbnew/tools
parent0db48f6533517ecebfd9f0693f89deca28408b76 (diff)
downloadKiCad-eSim-aa35045840b78d3f48212db45da59a2e5c69b223.tar.gz
KiCad-eSim-aa35045840b78d3f48212db45da59a2e5c69b223.tar.bz2
KiCad-eSim-aa35045840b78d3f48212db45da59a2e5c69b223.zip
Added main execs
Diffstat (limited to 'pcbnew/tools')
-rw-r--r--pcbnew/tools/bright_box.cpp61
-rw-r--r--pcbnew/tools/bright_box.h79
-rw-r--r--pcbnew/tools/common_actions.cpp726
-rw-r--r--pcbnew/tools/common_actions.h337
-rw-r--r--pcbnew/tools/conditional_menu.cpp109
-rw-r--r--pcbnew/tools/conditional_menu.h221
-rw-r--r--pcbnew/tools/drawing_tool.cpp1541
-rw-r--r--pcbnew/tools/drawing_tool.h200
-rw-r--r--pcbnew/tools/edit_constraints.cpp191
-rw-r--r--pcbnew/tools/edit_constraints.h267
-rw-r--r--pcbnew/tools/edit_points.cpp227
-rw-r--r--pcbnew/tools/edit_points.h527
-rw-r--r--pcbnew/tools/edit_tool.cpp1099
-rw-r--r--pcbnew/tools/edit_tool.h238
-rw-r--r--pcbnew/tools/grid_helper.cpp393
-rw-r--r--pcbnew/tools/grid_helper.h108
-rw-r--r--pcbnew/tools/grid_menu.cpp68
-rw-r--r--pcbnew/tools/grid_menu.h44
-rw-r--r--pcbnew/tools/module_tools.cpp608
-rw-r--r--pcbnew/tools/module_tools.h117
-rw-r--r--pcbnew/tools/pcb_editor_control.cpp792
-rw-r--r--pcbnew/tools/pcb_editor_control.h116
-rw-r--r--pcbnew/tools/pcbnew_control.cpp1014
-rw-r--r--pcbnew/tools/pcbnew_control.h109
-rw-r--r--pcbnew/tools/picker_tool.cpp115
-rw-r--r--pcbnew/tools/picker_tool.h127
-rw-r--r--pcbnew/tools/placement_tool.cpp354
-rw-r--r--pcbnew/tools/placement_tool.h89
-rw-r--r--pcbnew/tools/point_editor.cpp890
-rw-r--r--pcbnew/tools/point_editor.h120
-rw-r--r--pcbnew/tools/selection_area.cpp63
-rw-r--r--pcbnew/tools/selection_area.h84
-rw-r--r--pcbnew/tools/selection_conditions.cpp256
-rw-r--r--pcbnew/tools/selection_conditions.h193
-rw-r--r--pcbnew/tools/selection_tool.cpp1459
-rw-r--r--pcbnew/tools/selection_tool.h361
-rw-r--r--pcbnew/tools/size_menu.cpp85
-rw-r--r--pcbnew/tools/size_menu.h55
-rw-r--r--pcbnew/tools/tools_common.cpp54
-rw-r--r--pcbnew/tools/zoom_menu.cpp71
-rw-r--r--pcbnew/tools/zoom_menu.h44
41 files changed, 13612 insertions, 0 deletions
diff --git a/pcbnew/tools/bright_box.cpp b/pcbnew/tools/bright_box.cpp
new file mode 100644
index 0000000..7901de2
--- /dev/null
+++ b/pcbnew/tools/bright_box.cpp
@@ -0,0 +1,61 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2013 CERN
+ * @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 "bright_box.h"
+#include <gal/graphics_abstraction_layer.h>
+#include <class_track.h>
+
+using namespace KIGFX;
+
+const double BRIGHT_BOX::LINE_WIDTH = 100000.0;
+const COLOR4D BRIGHT_BOX::BOX_COLOR = KIGFX::COLOR4D( 0.0, 1.0, 0.0, 1.0 );
+
+BRIGHT_BOX::BRIGHT_BOX( BOARD_ITEM* aItem ) :
+ EDA_ITEM( NOT_USED ), // this item is never added to a BOARD so it needs no type
+ m_item( aItem )
+{
+}
+
+
+void BRIGHT_BOX::ViewDraw( int aLayer, GAL* aGal ) const
+{
+ aGal->SetIsStroke( true );
+ aGal->SetIsFill( false );
+ aGal->SetLineWidth( LINE_WIDTH );
+ aGal->SetStrokeColor( BOX_COLOR );
+
+ if( m_item->Type() == PCB_TRACE_T )
+ {
+ const TRACK* track = static_cast<const TRACK*>( m_item );
+
+ aGal->DrawSegment( track->GetStart(), track->GetEnd(), track->GetWidth() );
+ }
+ else
+ {
+ BOX2I box = m_item->ViewBBox();
+
+ aGal->DrawRectangle( box.GetOrigin(), box.GetOrigin() + box.GetSize() );
+ }
+}
+
diff --git a/pcbnew/tools/bright_box.h b/pcbnew/tools/bright_box.h
new file mode 100644
index 0000000..fc9aeee
--- /dev/null
+++ b/pcbnew/tools/bright_box.h
@@ -0,0 +1,79 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2013 CERN
+ * @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
+ */
+
+#ifndef __BRIGHT_BOX_H
+#define __BRIGHT_BOX_H
+
+#include <math/box2.h>
+#include <view/view.h>
+#include <class_board_item.h>
+#include <layers_id_colors_and_visibility.h>
+#include <gal/color4d.h>
+
+/**
+ * Class BRIGHT_BOX
+ *
+ * Draws a decoration to indicate a brightened item.
+ */
+class BRIGHT_BOX : public EDA_ITEM
+{
+public:
+ BRIGHT_BOX( BOARD_ITEM* aItem );
+ ~BRIGHT_BOX() {};
+
+ virtual const BOX2I ViewBBox() const
+ {
+ return m_item->ViewBBox();
+ }
+
+ void ViewDraw( int aLayer, KIGFX::GAL* aGal ) const;
+
+ void ViewGetLayers( int aLayers[], int& aCount ) const
+ {
+ aLayers[0] = ITEM_GAL_LAYER( GP_OVERLAY );
+ aCount = 1;
+ }
+
+#if defined(DEBUG)
+ void Show( int x, std::ostream& st ) const
+ {
+ }
+#endif
+
+ /** Get class name
+ * @return string "BRIGHT_BOX"
+ */
+ virtual wxString GetClass() const
+ {
+ return wxT( "BRIGHT_BOX" );
+ }
+
+private:
+ static const KIGFX::COLOR4D BOX_COLOR;
+ static const double LINE_WIDTH;
+
+ BOARD_ITEM* m_item;
+};
+
+#endif
diff --git a/pcbnew/tools/common_actions.cpp b/pcbnew/tools/common_actions.cpp
new file mode 100644
index 0000000..f452719
--- /dev/null
+++ b/pcbnew/tools/common_actions.cpp
@@ -0,0 +1,726 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2013-2016 CERN
+ * @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 "common_actions.h"
+#include <tool/action_manager.h>
+#include <pcbnew_id.h>
+#include <layers_id_colors_and_visibility.h>
+#include <bitmaps.h>
+#include <wx/defs.h>
+#include <hotkeys.h>
+
+// These members are static in class COMMON_ACTIONS: Build them here:
+
+// Selection tool actions
+TOOL_ACTION COMMON_ACTIONS::selectionActivate( "pcbnew.InteractiveSelection",
+ AS_GLOBAL, 0,
+ "", "", NULL, AF_ACTIVATE ); // No description, it is not supposed to be shown anywhere
+
+TOOL_ACTION COMMON_ACTIONS::selectionCursor( "pcbnew.InteractiveSelection.Cursor",
+ AS_GLOBAL, 0,
+ "", "" ); // No description, it is not supposed to be shown anywhere
+
+TOOL_ACTION COMMON_ACTIONS::selectItem( "pcbnew.InteractiveSelection.SelectItem",
+ AS_GLOBAL, 0,
+ "", "" ); // No description, it is not supposed to be shown anywhere
+
+TOOL_ACTION COMMON_ACTIONS::unselectItem( "pcbnew.InteractiveSelection.UnselectItem",
+ AS_GLOBAL, 0,
+ "", "" ); // No description, it is not supposed to be shown anywhere
+
+TOOL_ACTION COMMON_ACTIONS::selectionClear( "pcbnew.InteractiveSelection.Clear",
+ AS_GLOBAL, 0,
+ "", "" ); // No description, it is not supposed to be shown anywhere
+
+TOOL_ACTION COMMON_ACTIONS::selectConnection( "pcbnew.InteractiveSelection.SelectConnection",
+ AS_GLOBAL, 'U',
+ _( "trivial connection" ), _( "Selects a connection between two junctions." ) );
+
+TOOL_ACTION COMMON_ACTIONS::selectCopper( "pcbnew.InteractiveSelection.SelectCopper",
+ AS_GLOBAL, 'I',
+ _( "copper connection" ), _( "Selects whole copper connection." ) );
+
+TOOL_ACTION COMMON_ACTIONS::selectNet( "pcbnew.InteractiveSelection.SelectNet",
+ AS_GLOBAL, 0,
+ _( "whole net" ), _( "Selects all tracks & vias belonging to the same net." ) );
+
+TOOL_ACTION COMMON_ACTIONS::find( "pcbnew.InteractiveSelection.Find",
+ AS_GLOBAL, 0, //TOOL_ACTION::LegacyHotKey( HK_FIND_ITEM ), // handled by wxWidgets
+ _( "Find an item" ), _( "Searches the document for an item" ), find_xpm );
+
+TOOL_ACTION COMMON_ACTIONS::findMove( "pcbnew.InteractiveSelection.FindMove",
+ AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_GET_AND_MOVE_FOOTPRINT ) );
+
+
+// Edit tool actions
+TOOL_ACTION COMMON_ACTIONS::editFootprintInFpEditor( "pcbnew.InteractiveEdit.editFootprintInFpEditor",
+ AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_EDIT_MODULE_WITH_MODEDIT ),
+ _( "Open in Footprint Editor" ),
+ _( "Opens the selected footprint in the Footprint Editor" ),
+ module_editor_xpm );
+
+TOOL_ACTION COMMON_ACTIONS::copyPadToSettings( "pcbnew.InteractiveEdit.copyPadToSettings",
+ AS_GLOBAL, 0,
+ _( "Copy pad settings to Current Settings" ),
+ _( "Copies the properties of selected pad to the current template pad settings." ) );
+
+TOOL_ACTION COMMON_ACTIONS::copySettingsToPads( "pcbnew.InteractiveEdit.copySettingsToPads",
+ AS_GLOBAL, 0,
+ _( "Copy Current Settings to pads" ),
+ _( "Copies the current template pad settings to the selected pad(s)." ) );
+
+TOOL_ACTION COMMON_ACTIONS::globalEditPads( "pcbnew.InteractiveEdit.globalPadEdit",
+ AS_GLOBAL, 0,
+ _( "Global Pad Edition" ),
+ _( "Changes pad properties globally." ), global_options_pad_xpm );
+
+TOOL_ACTION COMMON_ACTIONS::editActivate( "pcbnew.InteractiveEdit",
+ AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_MOVE_ITEM ),
+ _( "Move" ), _( "Moves the selected item(s)" ), move_xpm, AF_ACTIVATE );
+
+TOOL_ACTION COMMON_ACTIONS::duplicate( "pcbnew.InteractiveEdit.duplicate",
+ AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_DUPLICATE_ITEM ),
+ _( "Duplicate" ), _( "Duplicates the selected item(s)" ), duplicate_module_xpm );
+
+TOOL_ACTION COMMON_ACTIONS::duplicateIncrement( "pcbnew.InteractiveEdit.duplicateIncrementPads",
+ AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_DUPLICATE_ITEM_AND_INCREMENT ),
+ _( "Duplicate" ), _( "Duplicates the selected item(s), incrementing pad numbers" ) );
+
+TOOL_ACTION COMMON_ACTIONS::moveExact( "pcbnew.InteractiveEdit.moveExact",
+ AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_MOVE_ITEM_EXACT ),
+ _( "Move Exactly..." ), _( "Moves the selected item(s) by an exact amount" ),
+ move_module_xpm );
+
+TOOL_ACTION COMMON_ACTIONS::createArray( "pcbnew.InteractiveEdit.createArray",
+ AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_CREATE_ARRAY ),
+ _( "Create array" ), _( "Create array" ), array_module_xpm, AF_ACTIVATE );
+
+TOOL_ACTION COMMON_ACTIONS::rotate( "pcbnew.InteractiveEdit.rotate",
+ AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_ROTATE_ITEM ),
+ _( "Rotate" ), _( "Rotates selected item(s)" ), rotate_cw_xpm );
+
+TOOL_ACTION COMMON_ACTIONS::flip( "pcbnew.InteractiveEdit.flip",
+ AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_FLIP_ITEM ),
+ _( "Flip" ), _( "Flips selected item(s)" ), swap_layer_xpm );
+
+TOOL_ACTION COMMON_ACTIONS::remove( "pcbnew.InteractiveEdit.remove",
+ AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_DELETE ),
+ _( "Remove" ), _( "Deletes selected item(s)" ), delete_xpm );
+
+TOOL_ACTION COMMON_ACTIONS::properties( "pcbnew.InteractiveEdit.properties",
+ AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_EDIT_ITEM ),
+ _( "Properties..." ), _( "Displays item properties dialog" ), editor_xpm );
+
+
+// Drawing tool actions
+TOOL_ACTION COMMON_ACTIONS::drawLine( "pcbnew.InteractiveDrawing.line",
+ AS_GLOBAL, 0,
+ _( "Draw a line" ), _( "Draw a line" ), NULL, AF_ACTIVATE );
+
+TOOL_ACTION COMMON_ACTIONS::drawCircle( "pcbnew.InteractiveDrawing.circle",
+ AS_GLOBAL, 0,
+ _( "Draw a circle" ), _( "Draw a circle" ), NULL, AF_ACTIVATE );
+
+TOOL_ACTION COMMON_ACTIONS::drawArc( "pcbnew.InteractiveDrawing.arc",
+ AS_GLOBAL, 0,
+ _( "Draw an arc" ), _( "Draw an arc" ), NULL, AF_ACTIVATE );
+
+TOOL_ACTION COMMON_ACTIONS::placeText( "pcbnew.InteractiveDrawing.text",
+ AS_GLOBAL, 0,
+ _( "Add a text" ), _( "Add a text" ), NULL, AF_ACTIVATE );
+
+TOOL_ACTION COMMON_ACTIONS::drawDimension( "pcbnew.InteractiveDrawing.dimension",
+ AS_GLOBAL, 0,
+ _( "Add a dimension" ), _( "Add a dimension" ), NULL, AF_ACTIVATE );
+
+TOOL_ACTION COMMON_ACTIONS::drawZone( "pcbnew.InteractiveDrawing.zone",
+ AS_GLOBAL, 0,
+ _( "Add a filled zone" ), _( "Add a filled zone" ), NULL, AF_ACTIVATE );
+
+TOOL_ACTION COMMON_ACTIONS::drawKeepout( "pcbnew.InteractiveDrawing.keepout",
+ AS_GLOBAL, 0,
+ _( "Add a keepout area" ), _( "Add a keepout area" ), NULL, AF_ACTIVATE );
+
+TOOL_ACTION COMMON_ACTIONS::placeDXF( "pcbnew.InteractiveDrawing.placeDXF",
+ AS_GLOBAL, 0,
+ "", "", NULL, AF_ACTIVATE );
+
+TOOL_ACTION COMMON_ACTIONS::setAnchor( "pcbnew.InteractiveDrawing.setAnchor",
+ AS_GLOBAL, 0,
+ _( "Place the footprint anchor" ), _( "Place the footprint anchor" ),
+ NULL, AF_ACTIVATE );
+
+TOOL_ACTION COMMON_ACTIONS::incWidth( "pcbnew.InteractiveDrawing.incWidth",
+ AS_CONTEXT, '+',
+ _( "Increase the line width" ), _( "Increase the line width" ) );
+
+TOOL_ACTION COMMON_ACTIONS::decWidth( "pcbnew.InteractiveDrawing.decWidth",
+ AS_CONTEXT, '-',
+ _( "Decrease the line width" ), _( "Decrease the line width" ) );
+
+TOOL_ACTION COMMON_ACTIONS::arcPosture( "pcbnew.InteractiveDrawing.arcPosture",
+ AS_CONTEXT, TOOL_ACTION::LegacyHotKey( HK_SWITCH_TRACK_POSTURE ),
+ _( "Switch the arc posture" ), _( "Switch the arc posture" ) );
+
+
+// View Controls
+TOOL_ACTION COMMON_ACTIONS::zoomIn( "common.Control.zoomIn",
+ AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_ZOOM_IN ),
+ _( "Zoom In" ), "", zoom_in_xpm );
+
+TOOL_ACTION COMMON_ACTIONS::zoomOut( "common.Control.zoomOut",
+ AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_ZOOM_OUT ),
+ _( "Zoom Out" ), "", zoom_out_xpm );
+
+TOOL_ACTION COMMON_ACTIONS::zoomInCenter( "common.Control.zoomInCenter",
+ AS_GLOBAL, 0,
+ "", "" );
+
+TOOL_ACTION COMMON_ACTIONS::zoomOutCenter( "common.Control.zoomOutCenter",
+ AS_GLOBAL, 0,
+ "", "" );
+
+TOOL_ACTION COMMON_ACTIONS::zoomCenter( "common.Control.zoomCenter",
+ AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_ZOOM_CENTER ),
+ _( "Center" ), "", zoom_center_on_screen_xpm );
+
+TOOL_ACTION COMMON_ACTIONS::zoomFitScreen( "common.Control.zoomFitScreen",
+ AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_ZOOM_AUTO ),
+ _( "Zoom Auto" ), "", zoom_fit_in_page_xpm );
+
+TOOL_ACTION COMMON_ACTIONS::zoomPreset( "common.Control.zoomPreset",
+ AS_GLOBAL, 0,
+ "", "" );
+
+
+// Display modes
+TOOL_ACTION COMMON_ACTIONS::trackDisplayMode( "pcbnew.Control.trackDisplayMode",
+ AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_SWITCH_TRACK_DISPLAY_MODE ),
+ "", "" );
+
+TOOL_ACTION COMMON_ACTIONS::padDisplayMode( "pcbnew.Control.padDisplayMode",
+ AS_GLOBAL, 0,
+ "", "" );
+
+TOOL_ACTION COMMON_ACTIONS::viaDisplayMode( "pcbnew.Control.viaDisplayMode",
+ AS_GLOBAL, 0,
+ "", "" );
+
+TOOL_ACTION COMMON_ACTIONS::zoneDisplayEnable( "pcbnew.Control.zoneDisplayEnable",
+ AS_GLOBAL, 0,
+ "", "" );
+
+TOOL_ACTION COMMON_ACTIONS::zoneDisplayDisable( "pcbnew.Control.zoneDisplayDisable",
+ AS_GLOBAL, 0,
+ "", "" );
+
+TOOL_ACTION COMMON_ACTIONS::zoneDisplayOutlines( "pcbnew.Control.zoneDisplayOutlines",
+ AS_GLOBAL, 0,
+ "", "" );
+
+TOOL_ACTION COMMON_ACTIONS::highContrastMode( "pcbnew.Control.highContrastMode",
+ AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_SWITCH_HIGHCONTRAST_MODE ),
+ "", "" );
+
+TOOL_ACTION COMMON_ACTIONS::highContrastInc( "pcbnew.Control.highContrastInc",
+ AS_GLOBAL, '>',
+ "", "" );
+
+TOOL_ACTION COMMON_ACTIONS::highContrastDec( "pcbnew.Control.highContrastDec",
+ AS_GLOBAL, '<',
+ "", "" );
+
+
+// Layer control
+TOOL_ACTION COMMON_ACTIONS::layerTop( "pcbnew.Control.layerTop",
+ AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_SWITCH_LAYER_TO_COMPONENT ),
+ "", "", NULL, AF_NONE, (void*) F_Cu );
+
+TOOL_ACTION COMMON_ACTIONS::layerInner1( "pcbnew.Control.layerInner1",
+ AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_SWITCH_LAYER_TO_INNER1 ),
+ "", "", NULL, AF_NONE, (void*) In1_Cu );
+
+TOOL_ACTION COMMON_ACTIONS::layerInner2( "pcbnew.Control.layerInner2",
+ AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_SWITCH_LAYER_TO_INNER2 ),
+ "", "", NULL, AF_NONE, (void*) In2_Cu );
+
+TOOL_ACTION COMMON_ACTIONS::layerInner3( "pcbnew.Control.layerInner3",
+ AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_SWITCH_LAYER_TO_INNER3 ),
+ "", "", NULL, AF_NONE, (void*) In3_Cu );
+
+TOOL_ACTION COMMON_ACTIONS::layerInner4( "pcbnew.Control.layerInner4",
+ AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_SWITCH_LAYER_TO_INNER4 ),
+ "", "", NULL, AF_NONE, (void*) In4_Cu );
+
+TOOL_ACTION COMMON_ACTIONS::layerInner5( "pcbnew.Control.layerInner5",
+ AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_SWITCH_LAYER_TO_INNER5 ),
+ "", "", NULL, AF_NONE, (void*) In5_Cu );
+
+TOOL_ACTION COMMON_ACTIONS::layerInner6( "pcbnew.Control.layerInner6",
+ AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_SWITCH_LAYER_TO_INNER6 ),
+ "", "", NULL, AF_NONE, (void*) In6_Cu );
+
+TOOL_ACTION COMMON_ACTIONS::layerBottom( "pcbnew.Control.layerBottom",
+ AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_SWITCH_LAYER_TO_COPPER ),
+ "", "", NULL, AF_NONE, (void*) B_Cu );
+
+TOOL_ACTION COMMON_ACTIONS::layerNext( "pcbnew.Control.layerNext",
+ AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_SWITCH_LAYER_TO_NEXT ),
+ "", "" );
+
+TOOL_ACTION COMMON_ACTIONS::layerPrev( "pcbnew.Control.layerPrev",
+ AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_SWITCH_LAYER_TO_PREVIOUS ),
+ "", "" );
+
+TOOL_ACTION COMMON_ACTIONS::layerToggle( "pcbnew.Control.layerToggle",
+ AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_ADD_THROUGH_VIA ),
+ "", "" );
+
+TOOL_ACTION COMMON_ACTIONS::layerAlphaInc( "pcbnew.Control.layerAlphaInc",
+ AS_GLOBAL, '}',
+ "", "" );
+
+TOOL_ACTION COMMON_ACTIONS::layerAlphaDec( "pcbnew.Control.layerAlphaDec",
+ AS_GLOBAL, '{',
+ "", "" );
+
+TOOL_ACTION COMMON_ACTIONS::layerChanged( "pcbnew.Control.layerChanged",
+ AS_GLOBAL, 0,
+ "", "", NULL, AF_NOTIFY );
+
+
+// Grid control
+TOOL_ACTION COMMON_ACTIONS::gridFast1( "common.Control.gridFast1",
+ AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_SWITCH_GRID_TO_FASTGRID1 ),
+ "", "" );
+
+TOOL_ACTION COMMON_ACTIONS::gridFast2( "common.Control.gridFast2",
+ AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_SWITCH_GRID_TO_FASTGRID2 ),
+ "", "" );
+
+TOOL_ACTION COMMON_ACTIONS::gridNext( "common.Control.gridNext",
+ AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_SWITCH_GRID_TO_NEXT ),
+ "", "" );
+
+TOOL_ACTION COMMON_ACTIONS::gridPrev( "common.Control.gridPrev",
+ AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_SWITCH_GRID_TO_PREVIOUS ),
+ "", "" );
+
+TOOL_ACTION COMMON_ACTIONS::gridSetOrigin( "common.Control.gridSetOrigin",
+ AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_SET_GRID_ORIGIN ),
+ "", "" );
+
+TOOL_ACTION COMMON_ACTIONS::gridResetOrigin( "common.Control.gridResetOrigin",
+ AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_RESET_GRID_ORIGIN ),
+ "", "" );
+
+TOOL_ACTION COMMON_ACTIONS::gridPreset( "common.Control.gridPreset",
+ AS_GLOBAL, 0,
+ "", "" );
+
+// Track & via size control
+TOOL_ACTION COMMON_ACTIONS::trackWidthInc( "pcbnew.EditorControl.trackWidthInc",
+ AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_SWITCH_TRACK_WIDTH_TO_NEXT ),
+ "", "" );
+
+TOOL_ACTION COMMON_ACTIONS::trackWidthDec( "pcbnew.EditorControl.trackWidthDec",
+ AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_SWITCH_TRACK_WIDTH_TO_PREVIOUS ),
+ "", "" );
+
+TOOL_ACTION COMMON_ACTIONS::viaSizeInc( "pcbnew.EditorControl.viaSizeInc",
+ AS_GLOBAL, '\'',
+ "", "" );
+
+TOOL_ACTION COMMON_ACTIONS::viaSizeDec( "pcbnew.EditorControl.viaSizeDec",
+ AS_GLOBAL, '\\',
+ "", "" );
+
+TOOL_ACTION COMMON_ACTIONS::trackViaSizeChanged( "pcbnew.EditorControl.trackViaSizeChanged",
+ AS_GLOBAL, 0,
+ "", "", NULL, AF_NOTIFY );
+
+
+// Zone actions
+TOOL_ACTION COMMON_ACTIONS::zoneFill( "pcbnew.EditorControl.zoneFill",
+ AS_GLOBAL, 0,
+ _( "Fill" ), _( "Fill zone(s)" ), fill_zone_xpm );
+
+TOOL_ACTION COMMON_ACTIONS::zoneFillAll( "pcbnew.EditorControl.zoneFillAll",
+ AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_ZONE_FILL_OR_REFILL ),
+ _( "Fill all" ), _( "Fill all zones" ) );
+
+TOOL_ACTION COMMON_ACTIONS::zoneUnfill( "pcbnew.EditorControl.zoneUnfill",
+ AS_GLOBAL, 0,
+ _( "Unfill" ), _( "Unfill zone(s)" ), zone_unfill_xpm );
+
+TOOL_ACTION COMMON_ACTIONS::zoneUnfillAll( "pcbnew.EditorControl.zoneUnfillAll",
+ AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_ZONE_REMOVE_FILLED ),
+ _( "Unfill all" ), _( "Unfill all zones" ) );
+
+TOOL_ACTION COMMON_ACTIONS::zoneMerge( "pcbnew.EditorControl.zoneMerge",
+ AS_GLOBAL, 0,
+ _( "Merge zones" ), _( "Merge zones" ) );
+
+
+TOOL_ACTION COMMON_ACTIONS::placeTarget( "pcbnew.EditorControl.placeTarget",
+ AS_GLOBAL, 0,
+ _( "Add layer alignment target" ), _( "Add layer alignment target" ), NULL, AF_ACTIVATE );
+
+TOOL_ACTION COMMON_ACTIONS::placeModule( "pcbnew.EditorControl.placeModule",
+ AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_ADD_MODULE ),
+ _( "Add footprints" ), _( "Add footprints" ), NULL, AF_ACTIVATE );
+
+TOOL_ACTION COMMON_ACTIONS::drillOrigin( "pcbnew.EditorControl.drillOrigin",
+ AS_GLOBAL, 0,
+ "", "" );
+
+TOOL_ACTION COMMON_ACTIONS::crossProbeSchToPcb( "pcbnew.EditorControl.crossProbSchToPcb",
+ AS_GLOBAL, 0,
+ "", "" );
+
+TOOL_ACTION COMMON_ACTIONS::toggleLockModule( "pcbnew.EditorControl.toggleLockModule",
+ AS_GLOBAL, 'L',
+ "", "" );
+
+TOOL_ACTION COMMON_ACTIONS::appendBoard( "pcbnew.EditorControl.appendBoard",
+ AS_GLOBAL, 0,
+ "", "" );
+
+TOOL_ACTION COMMON_ACTIONS::highlightNet( "pcbnew.EditorControl.highlightNet",
+ AS_GLOBAL, 0,
+ "", "" );
+
+TOOL_ACTION COMMON_ACTIONS::highlightNetCursor( "pcbnew.EditorControl.highlightNetCursor",
+ AS_GLOBAL, 0,
+ "", "" );
+
+
+// Module editor tools
+TOOL_ACTION COMMON_ACTIONS::placePad( "pcbnew.ModuleEditor.placePad",
+ AS_GLOBAL, 0,
+ _( "Add pads" ), _( "Add pads" ), NULL, AF_ACTIVATE );
+
+TOOL_ACTION COMMON_ACTIONS::enumeratePads( "pcbnew.ModuleEditor.enumeratePads",
+ AS_GLOBAL, 0,
+ _( "Enumerate pads" ), _( "Enumerate pads" ), pad_enumerate_xpm, AF_ACTIVATE );
+
+TOOL_ACTION COMMON_ACTIONS::copyItems( "pcbnew.ModuleEditor.copyItems",
+ AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_COPY_ITEM ),
+ _( "Copy items" ), _( "Copy items" ), NULL, AF_ACTIVATE );
+
+TOOL_ACTION COMMON_ACTIONS::pasteItems( "pcbnew.ModuleEditor.pasteItems",
+ AS_GLOBAL, MD_CTRL + int( 'V' ),
+ _( "Paste items" ), _( "Paste items" ), NULL, AF_ACTIVATE );
+
+TOOL_ACTION COMMON_ACTIONS::moduleEdgeOutlines( "pcbnew.ModuleEditor.graphicOutlines",
+ AS_GLOBAL, 0,
+ "", "" );
+
+TOOL_ACTION COMMON_ACTIONS::moduleTextOutlines( "pcbnew.ModuleEditor.textOutlines",
+ AS_GLOBAL, 0,
+ "", "" );
+
+
+// Cursor control
+TOOL_ACTION COMMON_ACTIONS::cursorUp( "pcbnew.Control.cursorUp",
+ AS_GLOBAL, WXK_UP, "", "", NULL, AF_NONE, (void*) CURSOR_UP );
+TOOL_ACTION COMMON_ACTIONS::cursorDown( "pcbnew.Control.cursorDown",
+ AS_GLOBAL, WXK_DOWN, "", "" , NULL, AF_NONE, (void*) CURSOR_DOWN );
+TOOL_ACTION COMMON_ACTIONS::cursorLeft( "pcbnew.Control.cursorLeft",
+ AS_GLOBAL, WXK_LEFT, "", "" , NULL, AF_NONE, (void*) CURSOR_LEFT );
+TOOL_ACTION COMMON_ACTIONS::cursorRight( "pcbnew.Control.cursorRight",
+ AS_GLOBAL, WXK_RIGHT, "", "" , NULL, AF_NONE, (void*) CURSOR_RIGHT );
+
+TOOL_ACTION COMMON_ACTIONS::cursorUpFast( "pcbnew.Control.cursorUpFast",
+ AS_GLOBAL, MD_CTRL + WXK_UP, "", "", NULL, AF_NONE, (void*) ( CURSOR_UP | CURSOR_FAST_MOVE ) );
+TOOL_ACTION COMMON_ACTIONS::cursorDownFast( "pcbnew.Control.cursorDownFast",
+ AS_GLOBAL, MD_CTRL + WXK_DOWN, "", "" , NULL, AF_NONE, (void*) ( CURSOR_DOWN | CURSOR_FAST_MOVE ) );
+TOOL_ACTION COMMON_ACTIONS::cursorLeftFast( "pcbnew.Control.cursorLeftFast",
+ AS_GLOBAL, MD_CTRL + WXK_LEFT, "", "" , NULL, AF_NONE, (void*) ( CURSOR_LEFT | CURSOR_FAST_MOVE ) );
+TOOL_ACTION COMMON_ACTIONS::cursorRightFast( "pcbnew.Control.cursorRightFast",
+ AS_GLOBAL, MD_CTRL + WXK_RIGHT, "", "" , NULL, AF_NONE, (void*) ( CURSOR_RIGHT | CURSOR_FAST_MOVE ) );
+
+TOOL_ACTION COMMON_ACTIONS::cursorClick( "pcbnew.Control.cursorClick",
+ AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_LEFT_CLICK ),
+ "", "", NULL, AF_NONE, (void*) CURSOR_CLICK );
+TOOL_ACTION COMMON_ACTIONS::cursorDblClick( "pcbnew.Control.cursorDblClick",
+ AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_LEFT_DCLICK ),
+ "", "", NULL, AF_NONE, (void*) CURSOR_DBL_CLICK );
+
+TOOL_ACTION COMMON_ACTIONS::panUp( "pcbnew.Control.panUp",
+ AS_GLOBAL, MD_SHIFT + WXK_UP, "", "", NULL, AF_NONE, (void*) CURSOR_UP );
+TOOL_ACTION COMMON_ACTIONS::panDown( "pcbnew.Control.panDown",
+ AS_GLOBAL, MD_SHIFT + WXK_DOWN, "", "" , NULL, AF_NONE, (void*) CURSOR_DOWN );
+TOOL_ACTION COMMON_ACTIONS::panLeft( "pcbnew.Control.panLeft",
+ AS_GLOBAL, MD_SHIFT + WXK_LEFT, "", "" , NULL, AF_NONE, (void*) CURSOR_LEFT );
+TOOL_ACTION COMMON_ACTIONS::panRight( "pcbnew.Control.panRight",
+ AS_GLOBAL, MD_SHIFT + WXK_RIGHT, "", "" , NULL, AF_NONE, (void*) CURSOR_RIGHT );
+
+// Miscellaneous
+TOOL_ACTION COMMON_ACTIONS::selectionTool( "pcbnew.Control.selectionTool",
+ AS_GLOBAL, 0,
+ "", "", NULL, AF_ACTIVATE );
+
+TOOL_ACTION COMMON_ACTIONS::pickerTool( "pcbnew.Picker", AS_GLOBAL, 0, "", "", NULL, AF_ACTIVATE );
+
+TOOL_ACTION COMMON_ACTIONS::resetCoords( "pcbnew.Control.resetCoords",
+ AS_GLOBAL, ' ',
+ "", "" );
+
+TOOL_ACTION COMMON_ACTIONS::switchCursor( "pcbnew.Control.switchCursor",
+ AS_GLOBAL, 0,
+ "", "" );
+
+TOOL_ACTION COMMON_ACTIONS::switchUnits( "pcbnew.Control.switchUnits",
+ AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_SWITCH_UNITS ),
+ "", "" );
+
+TOOL_ACTION COMMON_ACTIONS::deleteItemCursor( "pcbnew.Control.deleteItemCursor",
+ AS_GLOBAL, 0,
+ "", "" );
+
+TOOL_ACTION COMMON_ACTIONS::showHelp( "pcbnew.Control.showHelp",
+ AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_HELP ),
+ "", "" );
+
+TOOL_ACTION COMMON_ACTIONS::toBeDone( "pcbnew.Control.toBeDone",
+ AS_GLOBAL, 0, // dialog saying it is not implemented yet
+ "", "" ); // so users are aware of that
+
+
+TOOL_ACTION COMMON_ACTIONS::routerActivateSingle( "pcbnew.InteractiveRouter.SingleTrack",
+ AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_ADD_NEW_TRACK ),
+ _( "Run push & shove router (single tracks)" ),
+ _( "Run push & shove router (single tracks)" ), ps_router_xpm, AF_ACTIVATE );
+
+TOOL_ACTION COMMON_ACTIONS::routerActivateDiffPair( "pcbnew.InteractiveRouter.DiffPair",
+ AS_GLOBAL, '6',
+ _( "Run push & shove router (differential pairs)" ),
+ _( "Run push & shove router (differential pairs)" ), ps_diff_pair_xpm, AF_ACTIVATE );
+
+TOOL_ACTION COMMON_ACTIONS::routerActivateSettingsDialog( "pcbnew.InteractiveRouter.SettingsDialog",
+ AS_GLOBAL, 0,
+ _( "Open Interactive Router settings" ),
+ _( "Open Interactive Router settings" ), NULL, AF_ACTIVATE );
+
+TOOL_ACTION COMMON_ACTIONS::routerActivateDpDimensionsDialog( "pcbnew.InteractiveRouter.DpDimensionsDialog",
+ AS_GLOBAL, 0,
+ _( "Open Differential Pair Dimension settings" ),
+ _( "Open Differential Pair Dimension settings" ), ps_diff_pair_gap_xpm, AF_ACTIVATE );
+
+TOOL_ACTION COMMON_ACTIONS::routerActivateTuneSingleTrace( "pcbnew.LengthTuner.TuneSingleTrack",
+ AS_GLOBAL, '7',
+ _( "Tune length of a single track" ), "", ps_tune_length_xpm, AF_ACTIVATE );
+
+TOOL_ACTION COMMON_ACTIONS::routerActivateTuneDiffPair( "pcbnew.LengthTuner.TuneDiffPair",
+ AS_GLOBAL, '8',
+ _( "Tune length of a differential pair" ), "", NULL, AF_ACTIVATE );
+
+TOOL_ACTION COMMON_ACTIONS::routerActivateTuneDiffPairSkew( "pcbnew.LengthTuner.TuneDiffPairSkew",
+ AS_GLOBAL, '9',
+ _( "Tune skew of a differential pair" ), "", NULL, AF_ACTIVATE );
+
+TOOL_ACTION COMMON_ACTIONS::routerInlineDrag( "pcbnew.InteractiveRouter.InlineDrag",
+ AS_GLOBAL, 0,
+ "", "" );
+
+// Point editor
+TOOL_ACTION COMMON_ACTIONS::pointEditorUpdate( "pcbnew.PointEditor.update",
+ AS_GLOBAL, 0,
+ "", "" ); // No description, it is not supposed to be shown anywhere
+
+TOOL_ACTION COMMON_ACTIONS::pointEditorAddCorner( "pcbnew.PointEditor.addCorner",
+ AS_GLOBAL, 0,
+ _( "Create corner" ), _( "Create corner" ), add_corner_xpm );
+
+TOOL_ACTION COMMON_ACTIONS::pointEditorRemoveCorner( "pcbnew.PointEditor.removeCorner",
+ AS_GLOBAL, 0,
+ _( "Remove corner" ), _( "Remove corner" ), delete_xpm );
+
+// Placement tool
+TOOL_ACTION COMMON_ACTIONS::alignTop( "pcbnew.Place.alignTop",
+ AS_GLOBAL, 0,
+ _( "Align items to the top" ),
+ _( "Aligns selected items to the top edge" ) );
+
+TOOL_ACTION COMMON_ACTIONS::alignBottom( "pcbnew.Place.alignBottom",
+ AS_GLOBAL, 0,
+ _( "Align items to the bottom" ),
+ _( "Aligns selected items to the bottom edge" ) );
+
+TOOL_ACTION COMMON_ACTIONS::alignLeft( "pcbnew.Place.alignLeft",
+ AS_GLOBAL, 0,
+ _( "Align items to the left" ),
+ _( "Aligns selected items to the left edge" ) );
+
+TOOL_ACTION COMMON_ACTIONS::alignRight( "pcbnew.Place.alignRight",
+ AS_GLOBAL, 0,
+ _( "Align items to the right" ),
+ _( "Aligns selected items to the right edge" ) );
+
+TOOL_ACTION COMMON_ACTIONS::distributeHorizontally( "pcbnew.Place.distributeHorizontally",
+ AS_GLOBAL, 0,
+ _( "Distribute horizontally" ),
+ _( "Distributes selected items along the horizontal axis" ) );
+
+TOOL_ACTION COMMON_ACTIONS::distributeVertically( "pcbnew.Place.distributeVertically",
+ AS_GLOBAL, 0,
+ _( "Distribute vertically" ),
+ _( "Distributes selected items along the vertical axis" ) );
+
+
+boost::optional<TOOL_EVENT> COMMON_ACTIONS::TranslateLegacyId( int aId )
+{
+ switch( aId )
+ {
+ case ID_PCB_MODULE_BUTT:
+ return COMMON_ACTIONS::placeModule.MakeEvent();
+
+ case ID_TRACK_BUTT:
+ return COMMON_ACTIONS::routerActivateSingle.MakeEvent();
+
+ case ID_DIFF_PAIR_BUTT:
+ return COMMON_ACTIONS::routerActivateDiffPair.MakeEvent();
+
+ case ID_TUNE_SINGLE_TRACK_LEN_BUTT:
+ return COMMON_ACTIONS::routerActivateTuneSingleTrace.MakeEvent();
+
+ case ID_TUNE_DIFF_PAIR_LEN_BUTT:
+ return COMMON_ACTIONS::routerActivateTuneDiffPair.MakeEvent();
+
+ case ID_TUNE_DIFF_PAIR_SKEW_BUTT:
+ return COMMON_ACTIONS::routerActivateTuneDiffPairSkew.MakeEvent();
+
+ case ID_MENU_INTERACTIVE_ROUTER_SETTINGS:
+ return COMMON_ACTIONS::routerActivateSettingsDialog.MakeEvent();
+
+ case ID_MENU_DIFF_PAIR_DIMENSIONS:
+ return COMMON_ACTIONS::routerActivateDpDimensionsDialog.MakeEvent();
+
+ case ID_PCB_ZONES_BUTT:
+ return COMMON_ACTIONS::drawZone.MakeEvent();
+
+ case ID_PCB_KEEPOUT_AREA_BUTT:
+ return COMMON_ACTIONS::drawKeepout.MakeEvent();
+
+ case ID_PCB_ADD_LINE_BUTT:
+ case ID_MODEDIT_LINE_TOOL:
+ return COMMON_ACTIONS::drawLine.MakeEvent();
+
+ case ID_PCB_CIRCLE_BUTT:
+ case ID_MODEDIT_CIRCLE_TOOL:
+ return COMMON_ACTIONS::drawCircle.MakeEvent();
+
+ case ID_PCB_ARC_BUTT:
+ case ID_MODEDIT_ARC_TOOL:
+ return COMMON_ACTIONS::drawArc.MakeEvent();
+
+ case ID_PCB_ADD_TEXT_BUTT:
+ case ID_MODEDIT_TEXT_TOOL:
+ return COMMON_ACTIONS::placeText.MakeEvent();
+
+ case ID_PCB_DIMENSION_BUTT:
+ return COMMON_ACTIONS::drawDimension.MakeEvent();
+
+ case ID_PCB_MIRE_BUTT:
+ return COMMON_ACTIONS::placeTarget.MakeEvent();
+
+ case ID_MODEDIT_PAD_TOOL:
+ return COMMON_ACTIONS::placePad.MakeEvent();
+
+ case ID_GEN_IMPORT_DXF_FILE:
+ return COMMON_ACTIONS::placeDXF.MakeEvent();
+
+ case ID_MODEDIT_ANCHOR_TOOL:
+ return COMMON_ACTIONS::setAnchor.MakeEvent();
+
+ case ID_PCB_PLACE_GRID_COORD_BUTT:
+ case ID_MODEDIT_PLACE_GRID_COORD:
+ return COMMON_ACTIONS::gridSetOrigin.MakeEvent();
+
+ case ID_ZOOM_IN: // toolbar button "Zoom In"
+ return COMMON_ACTIONS::zoomInCenter.MakeEvent();
+
+ case ID_ZOOM_OUT: // toolbar button "Zoom In"
+ return COMMON_ACTIONS::zoomOutCenter.MakeEvent();
+
+ case ID_ZOOM_PAGE: // toolbar button "Fit on Screen"
+ return COMMON_ACTIONS::zoomFitScreen.MakeEvent();
+
+ case ID_TB_OPTIONS_SHOW_TRACKS_SKETCH:
+ return COMMON_ACTIONS::trackDisplayMode.MakeEvent();
+
+ case ID_TB_OPTIONS_SHOW_PADS_SKETCH:
+ return COMMON_ACTIONS::padDisplayMode.MakeEvent();
+
+ case ID_TB_OPTIONS_SHOW_VIAS_SKETCH:
+ return COMMON_ACTIONS::viaDisplayMode.MakeEvent();
+
+ case ID_TB_OPTIONS_SHOW_ZONES:
+ return COMMON_ACTIONS::zoneDisplayEnable.MakeEvent();
+
+ case ID_TB_OPTIONS_SHOW_ZONES_DISABLE:
+ return COMMON_ACTIONS::zoneDisplayDisable.MakeEvent();
+
+ case ID_TB_OPTIONS_SHOW_ZONES_OUTLINES_ONLY:
+ return COMMON_ACTIONS::zoneDisplayOutlines.MakeEvent();
+
+ case ID_TB_OPTIONS_SHOW_MODULE_EDGE_SKETCH:
+ return COMMON_ACTIONS::moduleEdgeOutlines.MakeEvent();
+
+ case ID_TB_OPTIONS_SHOW_MODULE_TEXT_SKETCH:
+ return COMMON_ACTIONS::moduleTextOutlines.MakeEvent();
+
+ case ID_TB_OPTIONS_SHOW_HIGH_CONTRAST_MODE:
+ return COMMON_ACTIONS::highContrastMode.MakeEvent();
+
+ case ID_FIND_ITEMS:
+ return COMMON_ACTIONS::find.MakeEvent();
+
+ case ID_POPUP_PCB_GET_AND_MOVE_MODULE_REQUEST:
+ return COMMON_ACTIONS::findMove.MakeEvent();
+
+ case ID_NO_TOOL_SELECTED:
+ return COMMON_ACTIONS::selectionTool.MakeEvent();
+
+ case ID_PCB_DELETE_ITEM_BUTT:
+ case ID_MODEDIT_DELETE_TOOL:
+ return COMMON_ACTIONS::deleteItemCursor.MakeEvent();
+
+ case ID_PCB_PLACE_OFFSET_COORD_BUTT:
+ return COMMON_ACTIONS::drillOrigin.MakeEvent();
+
+ case ID_PCB_HIGHLIGHT_BUTT:
+ return COMMON_ACTIONS::highlightNetCursor.MakeEvent();
+
+ case ID_APPEND_FILE:
+ return COMMON_ACTIONS::appendBoard.MakeEvent();
+
+ case ID_PCB_SHOW_1_RATSNEST_BUTT:
+ case ID_TB_OPTIONS_SHOW_MODULE_RATSNEST:
+ return COMMON_ACTIONS::toBeDone.MakeEvent();
+ }
+
+ return boost::optional<TOOL_EVENT>();
+}
diff --git a/pcbnew/tools/common_actions.h b/pcbnew/tools/common_actions.h
new file mode 100644
index 0000000..fbedcdc
--- /dev/null
+++ b/pcbnew/tools/common_actions.h
@@ -0,0 +1,337 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2013-2016 CERN
+ * @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
+ */
+
+#ifndef __COMMON_ACTIONS_H
+#define __COMMON_ACTIONS_H
+
+#include <tool/tool_action.h>
+#include <boost/optional.hpp>
+
+class TOOL_EVENT;
+class TOOL_MANAGER;
+
+/**
+ * Class COMMON_ACTIONS
+ *
+ * Gathers all the actions that are shared by tools. The instance of COMMON_ACTION is created
+ * inside of ACTION_MANAGER object that registers the actions.
+ */
+class COMMON_ACTIONS
+{
+public:
+ // Selection Tool
+ /// Activation of the selection tool
+ static TOOL_ACTION selectionActivate;
+
+ /// Select a single item under the cursor position
+ static TOOL_ACTION selectionCursor;
+
+ /// Clears the current selection
+ static TOOL_ACTION selectionClear;
+
+ /// Selects an item (specified as the event parameter).
+ static TOOL_ACTION selectItem;
+
+ /// Unselects an item (specified as the event parameter).
+ static TOOL_ACTION unselectItem;
+
+ /// Selects a connection between junctions.
+ static TOOL_ACTION selectConnection;
+
+ /// Selects whole copper connection.
+ static TOOL_ACTION selectCopper;
+
+ /// Selects all connections belonging to a single net.
+ static TOOL_ACTION selectNet;
+
+ // Edit Tool
+ /// Activation of the edit tool
+ static TOOL_ACTION editActivate;
+
+ /// Rotation of selected objects
+ static TOOL_ACTION rotate;
+
+ /// Flipping of selected objects
+ static TOOL_ACTION flip;
+
+ /// Activation of the edit tool
+ static TOOL_ACTION properties;
+
+ /// Activation of the exact move tool
+ static TOOL_ACTION moveExact;
+
+ /// Activation of the duplication tool
+ static TOOL_ACTION duplicate;
+
+ /// Activation of the duplication tool with incrementing (e.g. pad number)
+ static TOOL_ACTION duplicateIncrement;
+
+ /// Deleting a BOARD_ITEM
+ static TOOL_ACTION remove;
+
+ // Drawing Tool
+ /// Activation of the drawing tool (line)
+ static TOOL_ACTION drawLine;
+
+ /// Activation of the drawing tool (circle)
+ static TOOL_ACTION drawCircle;
+
+ /// Activation of the drawing tool (arc)
+ static TOOL_ACTION drawArc;
+
+ /// Activation of the drawing tool (text)
+ static TOOL_ACTION placeText;
+
+ /// Activation of the drawing tool (dimension)
+ static TOOL_ACTION drawDimension;
+
+ /// Activation of the drawing tool (drawing a ZONE)
+ static TOOL_ACTION drawZone;
+
+ /// Activation of the drawing tool (drawing a keepout area)
+ static TOOL_ACTION drawKeepout;
+
+ /// Activation of the drawing tool (placing a TARGET)
+ static TOOL_ACTION placeTarget;
+
+ /// Activation of the drawing tool (placing a MODULE)
+ static TOOL_ACTION placeModule;
+
+ /// Activation of the drawing tool (placing a drawing from DXF file)
+ static TOOL_ACTION placeDXF;
+
+ /// Activation of the drawing tool (placing the footprint anchor)
+ static TOOL_ACTION setAnchor;
+
+ /// Increase width of currently drawn line
+ static TOOL_ACTION incWidth;
+
+ /// Decrease width of currently drawn line
+ static TOOL_ACTION decWidth;
+
+ /// Switch posture when drawing arc
+ static TOOL_ACTION arcPosture;
+
+ // Push and Shove Router Tool
+
+ /// Activation of the Push and Shove router
+ static TOOL_ACTION routerActivateSingle;
+
+ /// Activation of the Push and Shove router (differential pair mode)
+ static TOOL_ACTION routerActivateDiffPair;
+
+ /// Activation of the Push and Shove router (tune single line mode)
+ static TOOL_ACTION routerActivateTuneSingleTrace;
+
+ /// Activation of the Push and Shove router (diff pair tuning mode)
+ static TOOL_ACTION routerActivateTuneDiffPair;
+
+ /// Activation of the Push and Shove router (skew tuning mode)
+ static TOOL_ACTION routerActivateTuneDiffPairSkew;
+
+ /// Activation of the Push and Shove settings dialogs
+ static TOOL_ACTION routerActivateSettingsDialog;
+ static TOOL_ACTION routerActivateDpDimensionsDialog;
+
+
+ /// Activation of the Push and Shove router (inline dragging mode)
+ static TOOL_ACTION routerInlineDrag;
+
+ // Point Editor
+ /// Update edit points
+ static TOOL_ACTION pointEditorUpdate;
+
+ /// Break outline (insert additional points to an edge)
+ static TOOL_ACTION pointEditorAddCorner;
+
+ /// Removes a corner
+ static TOOL_ACTION pointEditorRemoveCorner;
+
+ // Placement tool
+ /// Align items to the top edge of selection bounding box
+ static TOOL_ACTION alignTop;
+
+ /// Align items to the bottom edge of selection bounding box
+ static TOOL_ACTION alignBottom;
+
+ /// Align items to the left edge of selection bounding box
+ static TOOL_ACTION alignLeft;
+
+ /// Align items to the right edge of selection bounding box
+ static TOOL_ACTION alignRight;
+
+ /// Distributes items evenly along the horizontal axis
+ static TOOL_ACTION distributeHorizontally;
+
+ /// Distributes items evenly along the vertical axis
+ static TOOL_ACTION distributeVertically;
+
+ // View controls
+ static TOOL_ACTION zoomIn;
+ static TOOL_ACTION zoomOut;
+ static TOOL_ACTION zoomInCenter;
+ static TOOL_ACTION zoomOutCenter;
+ static TOOL_ACTION zoomCenter;
+ static TOOL_ACTION zoomFitScreen;
+ static TOOL_ACTION zoomPreset;
+
+ // Display modes
+ static TOOL_ACTION trackDisplayMode;
+ static TOOL_ACTION padDisplayMode;
+ static TOOL_ACTION viaDisplayMode;
+ static TOOL_ACTION zoneDisplayEnable;
+ static TOOL_ACTION zoneDisplayDisable;
+ static TOOL_ACTION zoneDisplayOutlines;
+ static TOOL_ACTION highContrastMode;
+ static TOOL_ACTION highContrastInc;
+ static TOOL_ACTION highContrastDec;
+
+ // Layer control
+ static TOOL_ACTION layerTop;
+ static TOOL_ACTION layerInner1;
+ static TOOL_ACTION layerInner2;
+ static TOOL_ACTION layerInner3;
+ static TOOL_ACTION layerInner4;
+ static TOOL_ACTION layerInner5;
+ static TOOL_ACTION layerInner6;
+ static TOOL_ACTION layerBottom;
+ static TOOL_ACTION layerNext;
+ static TOOL_ACTION layerPrev;
+ static TOOL_ACTION layerAlphaInc;
+ static TOOL_ACTION layerAlphaDec;
+ static TOOL_ACTION layerToggle;
+
+ static TOOL_ACTION layerChanged; // notification
+
+ // Grid control
+ static TOOL_ACTION gridFast1;
+ static TOOL_ACTION gridFast2;
+ static TOOL_ACTION gridNext;
+ static TOOL_ACTION gridPrev;
+ static TOOL_ACTION gridSetOrigin;
+ static TOOL_ACTION gridResetOrigin;
+ static TOOL_ACTION gridPreset;
+
+ // Track & via size control
+ static TOOL_ACTION trackWidthInc;
+ static TOOL_ACTION trackWidthDec;
+ static TOOL_ACTION viaSizeInc;
+ static TOOL_ACTION viaSizeDec;
+
+ static TOOL_ACTION trackViaSizeChanged; // notification
+
+ // Zone actions
+ static TOOL_ACTION zoneFill;
+ static TOOL_ACTION zoneFillAll;
+ static TOOL_ACTION zoneUnfill;
+ static TOOL_ACTION zoneUnfillAll;
+ static TOOL_ACTION zoneMerge;
+
+ // Module editor tools
+ /// Activation of the drawing tool (placing a PAD)
+ static TOOL_ACTION placePad;
+
+ /// Tool for quick pad enumeration
+ static TOOL_ACTION enumeratePads;
+
+ /// Tool for creating an array of objects
+ static TOOL_ACTION createArray;
+
+ /// Copying module items to clipboard
+ static TOOL_ACTION copyItems;
+
+ /// Pasting module items from clipboard
+ static TOOL_ACTION pasteItems;
+
+ /// Display module edges as outlines
+ static TOOL_ACTION moduleEdgeOutlines;
+
+ /// Display module texts as outlines
+ static TOOL_ACTION moduleTextOutlines;
+
+ /// Cursor control with keyboard
+ static TOOL_ACTION cursorUp;
+ static TOOL_ACTION cursorDown;
+ static TOOL_ACTION cursorLeft;
+ static TOOL_ACTION cursorRight;
+
+ static TOOL_ACTION cursorUpFast;
+ static TOOL_ACTION cursorDownFast;
+ static TOOL_ACTION cursorLeftFast;
+ static TOOL_ACTION cursorRightFast;
+
+ static TOOL_ACTION cursorClick;
+ static TOOL_ACTION cursorDblClick;
+
+ // Panning with keyboard
+ static TOOL_ACTION panUp;
+ static TOOL_ACTION panDown;
+ static TOOL_ACTION panLeft;
+ static TOOL_ACTION panRight;
+
+ // Miscellaneous
+ static TOOL_ACTION selectionTool;
+ static TOOL_ACTION pickerTool;
+ static TOOL_ACTION resetCoords;
+ static TOOL_ACTION switchCursor;
+ static TOOL_ACTION switchUnits;
+ static TOOL_ACTION deleteItemCursor;
+ static TOOL_ACTION highlightNet;
+ static TOOL_ACTION highlightNetCursor;
+ static TOOL_ACTION drillOrigin;
+ static TOOL_ACTION crossProbeSchToPcb;
+ static TOOL_ACTION toggleLockModule;
+ static TOOL_ACTION appendBoard;
+ static TOOL_ACTION showHelp;
+ static TOOL_ACTION toBeDone;
+
+ /// Find an item
+ static TOOL_ACTION find;
+
+ /// Find an item and start moving
+ static TOOL_ACTION findMove;
+
+ static TOOL_ACTION editFootprintInFpEditor;
+ static TOOL_ACTION copyPadToSettings;
+ static TOOL_ACTION copySettingsToPads;
+ static TOOL_ACTION globalEditPads;
+
+
+ /**
+ * Function TranslateLegacyId()
+ * Translates legacy tool ids to the corresponding TOOL_ACTION name.
+ * @param aId is legacy tool id to be translated.
+ * @return std::string is name of the corresponding TOOL_ACTION. It may be empty, if there is
+ * no corresponding TOOL_ACTION.
+ */
+ static boost::optional<TOOL_EVENT> TranslateLegacyId( int aId );
+
+ ///> Cursor control event types
+ enum CURSOR_EVENT_TYPE { CURSOR_UP, CURSOR_DOWN, CURSOR_LEFT, CURSOR_RIGHT,
+ CURSOR_CLICK, CURSOR_DBL_CLICK, CURSOR_FAST_MOVE = 0x8000 };
+};
+
+void registerAllTools( TOOL_MANAGER* aToolManager );
+
+#endif
diff --git a/pcbnew/tools/conditional_menu.cpp b/pcbnew/tools/conditional_menu.cpp
new file mode 100644
index 0000000..a0e0937
--- /dev/null
+++ b/pcbnew/tools/conditional_menu.cpp
@@ -0,0 +1,109 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2015 CERN
+ * @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 "conditional_menu.h"
+#include <tool/context_menu.h>
+
+void CONDITIONAL_MENU::AddItem( const TOOL_ACTION& aAction, const SELECTION_CONDITION& aCondition,
+ int aOrder )
+{
+ assert( aAction.GetId() > 0 ); // Check if action was previously registered in ACTION_MANAGER
+ addEntry( ENTRY( &aAction, aCondition, aOrder ) );
+}
+
+
+void CONDITIONAL_MENU::AddMenu( CONTEXT_MENU* aMenu, const wxString& aLabel, bool aExpand,
+ const SELECTION_CONDITION& aCondition, int aOrder )
+{
+ addEntry( ENTRY( aMenu, aLabel, aExpand, aCondition, aOrder ) );
+}
+
+
+void CONDITIONAL_MENU::AddSeparator( const SELECTION_CONDITION& aCondition, int aOrder )
+{
+ addEntry( ENTRY( aCondition, aOrder ) );
+}
+
+
+CONTEXT_MENU* CONDITIONAL_MENU::Generate( SELECTION& aSelection )
+{
+ CONTEXT_MENU* m_menu = new CONTEXT_MENU;
+ m_menu->SetTool( m_tool );
+
+ for( std::list<ENTRY>::iterator it = m_entries.begin(); it != m_entries.end(); ++it )
+ {
+ const SELECTION_CONDITION& cond = it->Condition();
+
+ try
+ {
+ if( !cond( aSelection ) )
+ continue;
+ }
+ catch( std::exception& e )
+ {
+ continue;
+ }
+
+ switch( it->Type() )
+ {
+ case ENTRY::ACTION:
+ m_menu->Add( *it->Action() );
+ break;
+
+ case ENTRY::MENU:
+ it->Menu()->UpdateAll();
+ m_menu->Add( it->Menu(), it->Label(), it->Expand() );
+ break;
+
+ case ENTRY::WXITEM:
+ m_menu->Append( it->wxItem() );
+ break;
+
+ case ENTRY::SEPARATOR:
+ m_menu->AppendSeparator();
+ break;
+
+ default:
+ assert( false );
+ break;
+ }
+ }
+
+ return m_menu;
+}
+
+
+void CONDITIONAL_MENU::addEntry( ENTRY aEntry )
+{
+ if( aEntry.Order() < 0 ) // Any order, so give it any order number
+ aEntry.SetOrder( m_entries.size() );
+
+ std::list<ENTRY>::iterator it = m_entries.begin();
+
+ // Find the right spot for the entry
+ while( it != m_entries.end() && it->Order() <= aEntry.Order() )
+ ++it;
+
+ m_entries.insert( it, aEntry );
+}
diff --git a/pcbnew/tools/conditional_menu.h b/pcbnew/tools/conditional_menu.h
new file mode 100644
index 0000000..af9ef2f
--- /dev/null
+++ b/pcbnew/tools/conditional_menu.h
@@ -0,0 +1,221 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2015 CERN
+ * @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
+ */
+
+#ifndef CONDITIONAL_MENU_H
+#define CONDITIONAL_MENU_H
+
+#include "selection_conditions.h"
+#include <boost/unordered_map.hpp>
+#include <wx/wx.h>
+
+class SELECTION_TOOL;
+class TOOL_ACTION;
+class TOOL_INTERACTIVE;
+class CONTEXT_MENU;
+
+class CONDITIONAL_MENU
+{
+public:
+ ///> Constant to indicate that we do not care about an ENTRY location in the menu.
+ static const int ANY_ORDER = -1;
+
+ CONDITIONAL_MENU( TOOL_INTERACTIVE* aTool ) :
+ m_tool( aTool )
+ {}
+
+ /**
+ * Function AddItem()
+ *
+ * Adds a menu entry to run a TOOL_ACTION on selected items.
+ * @param aAction is a menu entry to be added.
+ * @param aCondition is a condition that has to be fulfilled to enable the menu entry.
+ * @param aOrder determines location of the added item, higher numbers are put on the bottom.
+ * You may use ANY_ORDER here if you think it does not matter.
+ */
+ void AddItem( const TOOL_ACTION& aAction,
+ const SELECTION_CONDITION& aCondition = SELECTION_CONDITIONS::ShowAlways,
+ int aOrder = ANY_ORDER );
+
+ /**
+ * Function AddMenu()
+ *
+ * Adds a submenu to the menu. CONDITIONAL_MENU takes ownership of the added menu, so it will
+ * be freed when the CONDITIONAL_MENU object is destroyed.
+ * @param aMenu is the submenu to be added.
+ * @param aLabel is the label of added submenu.
+ * @param aExpand determines if the added submenu items should be added as individual items
+ * or as a submenu.
+ * @param aCondition is a condition that has to be fulfilled to enable the submenu entry.
+ * @param aOrder determines location of the added menu, higher numbers are put on the bottom.
+ * You may use ANY_ORDER here if you think it does not matter.
+ */
+ void AddMenu( CONTEXT_MENU* aMenu, const wxString& aLabel, bool aExpand = false,
+ const SELECTION_CONDITION& aCondition = SELECTION_CONDITIONS::ShowAlways,
+ int aOrder = ANY_ORDER );
+
+ /**
+ * Function AddSeparator()
+ *
+ * Adds a separator to the menu.
+ * @param aCondition is a condition that has to be fulfilled to enable the submenu entry.
+ * @param aOrder determines location of the added menu, higher numbers are put on the bottom.
+ * You may use ANY_ORDER here if you think it does not matter.
+ */
+ void AddSeparator( const SELECTION_CONDITION& aCondition = SELECTION_CONDITIONS::ShowAlways,
+ int aOrder = ANY_ORDER );
+
+ /**
+ * Function Generate()
+ *
+ * Generates a context menu that contains only entries that are satisfying assigned conditions.
+ * @param aSelection is selection for which the conditions are checked against.
+ * @return Menu filtered by the entry conditions.
+ */
+ CONTEXT_MENU* Generate( SELECTION& aSelection );
+
+private:
+ ///> Helper class to organize menu entries.
+ class ENTRY
+ {
+ public:
+ ENTRY( const TOOL_ACTION* aAction,
+ const SELECTION_CONDITION& aCondition = SELECTION_CONDITIONS::ShowAlways,
+ int aOrder = ANY_ORDER ) :
+ m_type( ACTION ), m_condition( aCondition ), m_order( aOrder ), m_expand( false )
+ {
+ m_data.action = aAction;
+ }
+
+ ENTRY( CONTEXT_MENU* aMenu, const wxString aLabel, bool aExpand = false,
+ const SELECTION_CONDITION& aCondition = SELECTION_CONDITIONS::ShowAlways,
+ int aOrder = ANY_ORDER ) :
+ m_type( MENU ), m_condition( aCondition ), m_order( aOrder ), m_label( aLabel ), m_expand( aExpand )
+ {
+ m_data.menu = aMenu;
+ }
+
+ ENTRY( wxMenuItem* aItem, const SELECTION_CONDITION& aCondition = SELECTION_CONDITIONS::ShowAlways,
+ int aOrder = ANY_ORDER ) :
+ m_type( WXITEM ), m_condition( aCondition ), m_order( aOrder ), m_expand( false )
+ {
+ m_data.wxItem = aItem;
+ }
+
+ // Separator
+ ENTRY( const SELECTION_CONDITION& aCondition = SELECTION_CONDITIONS::ShowAlways,
+ int aOrder = ANY_ORDER ) :
+ m_type( SEPARATOR ), m_condition( aCondition ), m_order( aOrder ), m_expand( false )
+ {
+ m_data.wxItem = NULL;
+ }
+
+ ///> Possible entry types.
+ enum ENTRY_TYPE {
+ ACTION,
+ MENU,
+ WXITEM,
+ SEPARATOR
+ };
+
+ inline ENTRY_TYPE Type() const
+ {
+ return m_type;
+ }
+
+ inline const TOOL_ACTION* Action() const
+ {
+ assert( m_type == ACTION );
+ return m_data.action;
+ }
+
+ inline CONTEXT_MENU* Menu() const
+ {
+ assert( m_type == MENU );
+ return m_data.menu;
+ }
+
+ inline wxMenuItem* wxItem() const
+ {
+ assert( m_type == WXITEM );
+ return m_data.wxItem;
+ }
+
+ inline const wxString& Label() const
+ {
+ assert( m_type == MENU );
+ return m_label;
+ }
+
+ inline bool Expand() const
+ {
+ assert( m_type == MENU );
+ return m_expand;
+ }
+
+ inline const SELECTION_CONDITION& Condition() const
+ {
+ return m_condition;
+ }
+
+ inline int Order() const
+ {
+ return m_order;
+ }
+
+ inline void SetOrder( int aOrder )
+ {
+ m_order = aOrder;
+ }
+
+ private:
+ ENTRY_TYPE m_type;
+
+ union {
+ const TOOL_ACTION* action;
+ CONTEXT_MENU* menu;
+ wxMenuItem* wxItem;
+ } m_data;
+
+ ///> Condition to be fulfilled to show the entry in menu.
+ SELECTION_CONDITION m_condition;
+
+ ///> Order number, the higher the number the lower position it takes it is in the menu.
+ int m_order;
+
+ /// CONTEXT_MENU specific fields.
+ const wxString m_label;
+ bool m_expand;
+ };
+
+ ///> Inserts the entry, preserving the requested order.
+ void addEntry( ENTRY aEntry );
+
+ ///> List of all menu entries.
+ std::list<ENTRY> m_entries;
+
+ ///> tool owning the menu
+ TOOL_INTERACTIVE* m_tool;
+};
+
+#endif /* CONDITIONAL_MENU_H */
diff --git a/pcbnew/tools/drawing_tool.cpp b/pcbnew/tools/drawing_tool.cpp
new file mode 100644
index 0000000..8628113
--- /dev/null
+++ b/pcbnew/tools/drawing_tool.cpp
@@ -0,0 +1,1541 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2014 CERN
+ * @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 "common_actions.h"
+
+#include <wxPcbStruct.h>
+#include <class_draw_panel_gal.h>
+#include <project.h>
+#include <id.h>
+#include <pcbnew_id.h>
+#include <confirm.h>
+#include <dialog_edit_module_text.h>
+#include <import_dxf/dialog_dxf_import.h>
+
+#include <view/view_group.h>
+#include <view/view_controls.h>
+#include <gal/graphics_abstraction_layer.h>
+#include <tool/tool_manager.h>
+#include <router/direction.h>
+#include <ratsnest_data.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>
+
+DRAWING_TOOL::DRAWING_TOOL() :
+ TOOL_INTERACTIVE( "pcbnew.InteractiveDrawing" ), m_view( NULL ),
+ m_controls( NULL ), m_board( NULL ), m_frame( NULL ), m_editModules( false ), m_lineWidth( 1 )
+{
+}
+
+
+DRAWING_TOOL::~DRAWING_TOOL()
+{
+}
+
+
+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_EDIT_FRAME>();
+}
+
+
+int DRAWING_TOOL::DrawLine( const TOOL_EVENT& aEvent )
+{
+ boost::optional<VECTOR2D> startingPoint;
+
+ if( m_editModules )
+ {
+ m_frame->SetToolID( ID_MODEDIT_LINE_TOOL, wxCURSOR_PENCIL, _( "Add graphic line" ) );
+
+ EDGE_MODULE* line = new EDGE_MODULE( m_board->m_Modules );
+
+ while( drawSegment( S_SEGMENT, reinterpret_cast<DRAWSEGMENT*&>( line ), startingPoint ) )
+ {
+ if( line )
+ {
+ m_frame->OnModify();
+ m_frame->SaveCopyInUndoList( m_board->m_Modules, UR_MODEDIT );
+ line->SetParent( m_board->m_Modules );
+ line->SetLocalCoord();
+ m_board->m_Modules->GraphicalItems().PushFront( line );
+ startingPoint = line->GetEnd();
+ }
+ else
+ {
+ startingPoint = boost::none;
+ }
+
+ line = new EDGE_MODULE( m_board->m_Modules );
+ }
+ }
+ else // !m_editModules case
+ {
+ m_frame->SetToolID( ID_PCB_ADD_LINE_BUTT, wxCURSOR_PENCIL, _( "Add graphic line" ) );
+
+ DRAWSEGMENT* line = new DRAWSEGMENT;
+
+ while( drawSegment( S_SEGMENT, line, startingPoint ) )
+ {
+ if( line )
+ {
+ m_board->Add( line );
+ m_frame->OnModify();
+ m_frame->SaveCopyInUndoList( line, UR_NEW );
+ startingPoint = line->GetEnd();
+ }
+ else
+ {
+ startingPoint = boost::none;
+ }
+
+ line = new DRAWSEGMENT;
+ }
+ }
+
+ m_frame->SetToolID( ID_NO_TOOL_SELECTED, wxCURSOR_DEFAULT, wxEmptyString );
+
+ return 0;
+}
+
+
+int DRAWING_TOOL::DrawCircle( const TOOL_EVENT& aEvent )
+{
+ if( m_editModules )
+ {
+ m_frame->SetToolID( ID_MODEDIT_CIRCLE_TOOL, wxCURSOR_PENCIL, _( "Add graphic circle" ) );
+
+ EDGE_MODULE* circle = new EDGE_MODULE( m_board->m_Modules );
+
+ while( drawSegment( S_CIRCLE, reinterpret_cast<DRAWSEGMENT*&>( circle ) ) )
+ {
+ if( circle )
+ {
+ m_frame->OnModify();
+ m_frame->SaveCopyInUndoList( m_board->m_Modules, UR_MODEDIT );
+ circle->SetParent( m_board->m_Modules );
+ circle->SetLocalCoord();
+ m_board->m_Modules->GraphicalItems().PushFront( circle );
+ }
+
+ circle = new EDGE_MODULE( m_board->m_Modules );
+ }
+ }
+ else // !m_editModules case
+ {
+ m_frame->SetToolID( ID_PCB_CIRCLE_BUTT, wxCURSOR_PENCIL, _( "Add graphic circle" ) );
+
+ DRAWSEGMENT* circle = new DRAWSEGMENT;
+
+ while( drawSegment( S_CIRCLE, circle ) )
+ {
+ if( circle )
+ {
+ m_board->Add( circle );
+ m_frame->OnModify();
+ m_frame->SaveCopyInUndoList( circle, UR_NEW );
+ }
+
+ circle = new DRAWSEGMENT;
+ }
+ }
+
+ m_frame->SetToolID( ID_NO_TOOL_SELECTED, wxCURSOR_DEFAULT, wxEmptyString );
+
+ return 0;
+}
+
+
+int DRAWING_TOOL::DrawArc( const TOOL_EVENT& aEvent )
+{
+ if( m_editModules )
+ {
+ m_frame->SetToolID( ID_MODEDIT_ARC_TOOL, wxCURSOR_PENCIL, _( "Add graphic arc" ) );
+
+ EDGE_MODULE* arc = new EDGE_MODULE( m_board->m_Modules );
+
+ while( drawArc( reinterpret_cast<DRAWSEGMENT*&>( arc ) ) )
+ {
+ if( arc )
+ {
+ m_frame->OnModify();
+ m_frame->SaveCopyInUndoList( m_board->m_Modules, UR_MODEDIT );
+ arc->SetParent( m_board->m_Modules );
+ arc->SetLocalCoord();
+ m_board->m_Modules->GraphicalItems().PushFront( arc );
+ }
+
+ arc = new EDGE_MODULE( m_board->m_Modules );
+ }
+ }
+ else // !m_editModules case
+ {
+ m_frame->SetToolID( ID_PCB_ARC_BUTT, wxCURSOR_PENCIL, _( "Add graphic arc" ) );
+
+ DRAWSEGMENT* arc = new DRAWSEGMENT;
+
+ while( drawArc( arc ) )
+ {
+ if( arc )
+ {
+ m_board->Add( arc );
+ m_frame->OnModify();
+ m_frame->SaveCopyInUndoList( arc, UR_NEW );
+ }
+
+ arc = new DRAWSEGMENT;
+ }
+ }
+
+ m_frame->SetToolID( ID_NO_TOOL_SELECTED, wxCURSOR_DEFAULT, wxEmptyString );
+
+ return 0;
+}
+
+
+int DRAWING_TOOL::PlaceText( const TOOL_EVENT& aEvent )
+{
+ if( m_editModules )
+ return placeTextModule();
+ else
+ return placeTextPcb();
+}
+
+
+int DRAWING_TOOL::DrawDimension( const TOOL_EVENT& aEvent )
+{
+ DIMENSION* dimension = NULL;
+ int width, maxThickness;
+
+ // if one day it is possible to draw dimensions in the footprint editor,
+ // then hereby I'm letting you know that this tool does not handle UR_MODEDIT undo yet
+ assert( !m_editModules );
+
+ // Add a VIEW_GROUP that serves as a preview for the new item
+ KIGFX::VIEW_GROUP preview( m_view );
+ m_view->Add( &preview );
+
+ m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true );
+ m_controls->ShowCursor( true );
+ m_controls->SetSnapping( true );
+
+ Activate();
+ m_frame->SetToolID( ID_PCB_DIMENSION_BUTT, wxCURSOR_PENCIL, _( "Add dimension" ) );
+
+ enum DIMENSION_STEPS
+ {
+ SET_ORIGIN = 0,
+ SET_END,
+ SET_HEIGHT,
+ FINISHED
+ };
+ int step = SET_ORIGIN;
+
+ // Main loop: keep receiving events
+ while( OPT_TOOL_EVENT evt = Wait() )
+ {
+ VECTOR2I cursorPos = m_controls->GetCursorPosition();
+
+ if( evt->IsCancel() || evt->IsActivate() )
+ {
+ if( step != SET_ORIGIN ) // start from the beginning
+ {
+ preview.Clear();
+ preview.ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+
+ delete dimension;
+ step = SET_ORIGIN;
+ }
+ else
+ break;
+
+ if( evt->IsActivate() ) // now finish unconditionally
+ break;
+ }
+
+ else if( evt->IsAction( &COMMON_ACTIONS::incWidth ) && step != SET_ORIGIN )
+ {
+ dimension->SetWidth( dimension->GetWidth() + WIDTH_STEP );
+ preview.ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+ }
+
+ else if( evt->IsAction( &COMMON_ACTIONS::decWidth ) && step != SET_ORIGIN )
+ {
+ int width = dimension->GetWidth();
+
+ if( width > WIDTH_STEP )
+ {
+ dimension->SetWidth( width - WIDTH_STEP );
+ preview.ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+ }
+ }
+
+ else if( evt->IsClick( BUT_LEFT ) )
+ {
+ switch( step )
+ {
+ case SET_ORIGIN:
+ {
+ LAYER_ID layer = m_frame->GetScreen()->m_Active_Layer;
+
+ if( IsCopperLayer( layer ) || layer == Edge_Cuts )
+ {
+ DisplayInfoMessage( NULL, _( "Dimension not allowed on Copper or Edge Cut layers" ) );
+ --step;
+ }
+ else
+ {
+ // Init the new item attributes
+ dimension = new DIMENSION( m_board );
+ dimension->SetLayer( layer );
+ dimension->SetOrigin( wxPoint( cursorPos.x, cursorPos.y ) );
+ dimension->SetEnd( wxPoint( cursorPos.x, cursorPos.y ) );
+ dimension->Text().SetSize( m_board->GetDesignSettings().m_PcbTextSize );
+
+ width = m_board->GetDesignSettings().m_PcbTextWidth;
+ maxThickness = Clamp_Text_PenSize( width, dimension->Text().GetSize() );
+
+ if( width > maxThickness )
+ width = maxThickness;
+
+ dimension->Text().SetThickness( width );
+ dimension->SetWidth( width );
+ dimension->AdjustDimensionDetails();
+
+ preview.Add( dimension );
+
+ m_controls->SetAutoPan( true );
+ m_controls->CaptureCursor( true );
+ }
+ }
+ break;
+
+ case SET_END:
+ dimension->SetEnd( wxPoint( cursorPos.x, cursorPos.y ) );
+
+ // 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.x, cursorPos.y ) != dimension->GetPosition() )
+ {
+ assert( dimension->GetOrigin() != dimension->GetEnd() );
+ assert( dimension->GetWidth() > 0 );
+
+ m_view->Add( dimension );
+ m_board->Add( dimension );
+ dimension->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+
+ m_frame->OnModify();
+ m_frame->SaveCopyInUndoList( dimension, UR_NEW );
+
+ preview.Remove( 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.x, cursorPos.y ) );
+ break;
+
+ case SET_HEIGHT:
+ {
+ // Calculating the direction of travel perpendicular to the selected axis
+ double angle = dimension->GetAngle() + ( M_PI / 2 );
+
+ wxPoint pos( cursorPos.x, cursorPos.y );
+ wxPoint delta( pos - dimension->m_featureLineDO );
+ double height = ( delta.x * cos( angle ) ) + ( delta.y * sin( angle ) );
+ dimension->SetHeight( height );
+ }
+ break;
+ }
+
+ // Show a preview of the item
+ preview.ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+ }
+ }
+
+ if( step != SET_ORIGIN )
+ delete dimension;
+
+ m_controls->ShowCursor( false );
+ m_controls->SetSnapping( false );
+ m_controls->SetAutoPan( false );
+ m_controls->CaptureCursor( false );
+ m_view->Remove( &preview );
+
+ m_frame->SetToolID( ID_NO_TOOL_SELECTED, wxCURSOR_DEFAULT, wxEmptyString );
+
+ return 0;
+}
+
+
+int DRAWING_TOOL::DrawZone( const TOOL_EVENT& aEvent )
+{
+ m_frame->SetToolID( ID_PCB_ZONES_BUTT, wxCURSOR_PENCIL, _( "Add zones" ) );
+
+ return drawZone( false );
+}
+
+
+int DRAWING_TOOL::DrawKeepout( const TOOL_EVENT& aEvent )
+{
+ m_frame->SetToolID( ID_PCB_KEEPOUT_AREA_BUTT, wxCURSOR_PENCIL, _( "Add keepout" ) );
+
+ return drawZone( true );
+}
+
+
+int DRAWING_TOOL::PlaceDXF( const TOOL_EVENT& aEvent )
+{
+ if( m_editModules && !m_board->m_Modules )
+ return 0;
+
+ DIALOG_DXF_IMPORT dlg( m_frame );
+ int dlgResult = dlg.ShowModal();
+
+ const std::list<BOARD_ITEM*>& list = dlg.GetImportedItems();
+
+ if( dlgResult != wxID_OK || list.empty() )
+ return 0;
+
+ VECTOR2I cursorPos = m_controls->GetCursorPosition();
+ VECTOR2I delta = cursorPos - (*list.begin())->GetPosition();
+
+ // Add a VIEW_GROUP that serves as a preview for the new item
+ KIGFX::VIEW_GROUP preview( m_view );
+
+ // Build the undo list & add items to the current view
+ std::list<BOARD_ITEM*>::const_iterator it, itEnd;
+ for( it = list.begin(), itEnd = list.end(); it != itEnd; ++it )
+ {
+ KICAD_T type = (*it)->Type();
+ assert( type == PCB_LINE_T || type == PCB_TEXT_T );
+
+ if( type == PCB_LINE_T || type == PCB_TEXT_T )
+ preview.Add( *it );
+ }
+
+ BOARD_ITEM* firstItem = static_cast<BOARD_ITEM*>( *preview.Begin() );
+ m_view->Add( &preview );
+
+ m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true );
+ m_controls->ShowCursor( true );
+ m_controls->SetSnapping( true );
+
+ Activate();
+
+ // Main loop: keep receiving events
+ while( OPT_TOOL_EVENT evt = Wait() )
+ {
+ cursorPos = m_controls->GetCursorPosition();
+
+ if( evt->IsMotion() )
+ {
+ delta = cursorPos - firstItem->GetPosition();
+
+ for( KIGFX::VIEW_GROUP::iter it = preview.Begin(), end = preview.End(); it != end; ++it )
+ static_cast<BOARD_ITEM*>( *it )->Move( wxPoint( delta.x, delta.y ) );
+
+ preview.ViewUpdate();
+ }
+
+ else if( evt->Category() == TC_COMMAND )
+ {
+ if( evt->IsAction( &COMMON_ACTIONS::rotate ) )
+ {
+ for( KIGFX::VIEW_GROUP::iter it = preview.Begin(), end = preview.End(); it != end; ++it )
+ static_cast<BOARD_ITEM*>( *it )->Rotate( wxPoint( cursorPos.x, cursorPos.y ),
+ m_frame->GetRotationAngle() );
+
+ preview.ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+ }
+ else if( evt->IsAction( &COMMON_ACTIONS::flip ) )
+ {
+ for( KIGFX::VIEW_GROUP::iter it = preview.Begin(), end = preview.End(); it != end; ++it )
+ static_cast<BOARD_ITEM*>( *it )->Flip( wxPoint( cursorPos.x, cursorPos.y ) );
+
+ preview.ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+ }
+ else if( evt->IsCancel() || evt->IsActivate() )
+ {
+ preview.FreeItems();
+ break;
+ }
+ }
+
+ else if( evt->IsClick( BUT_LEFT ) )
+ {
+ // Place the drawing
+ if( m_editModules )
+ {
+ assert( m_board->m_Modules );
+ m_frame->SaveCopyInUndoList( m_board->m_Modules, UR_MODEDIT );
+ m_board->m_Modules->SetLastEditTime();
+
+ for( KIGFX::VIEW_GROUP::iter it = preview.Begin(), end = preview.End(); it != end; ++it )
+ {
+ BOARD_ITEM* item = static_cast<BOARD_ITEM*>( *it );
+ BOARD_ITEM* converted = NULL;
+
+ // Modules use different types for the same things,
+ // so we need to convert imported items to appropriate classes.
+ switch( item->Type() )
+ {
+ case PCB_TEXT_T:
+ converted = new TEXTE_MODULE( m_board->m_Modules );
+ // Copy coordinates, layer, etc.
+ *static_cast<TEXTE_PCB*>( converted ) = *static_cast<TEXTE_PCB*>( item );
+ static_cast<TEXTE_MODULE*>( converted )->SetLocalCoord();
+ break;
+
+ case PCB_LINE_T:
+ converted = new EDGE_MODULE( m_board->m_Modules );
+ // Copy coordinates, layer, etc.
+ *static_cast<DRAWSEGMENT*>( converted ) = *static_cast<DRAWSEGMENT*>( item );
+ static_cast<EDGE_MODULE*>( converted )->SetLocalCoord();
+ break;
+
+ default:
+ assert( false );
+ break;
+ }
+
+ delete item;
+
+ if( converted )
+ {
+ m_board->m_Modules->Add( converted );
+ m_view->Add( converted );
+ }
+ }
+ }
+ else // !m_editModules case
+ {
+ PICKED_ITEMS_LIST picklist;
+
+ for( KIGFX::VIEW_GROUP::iter it = preview.Begin(), end = preview.End(); it != end; ++it )
+ {
+ BOARD_ITEM* item = static_cast<BOARD_ITEM*>( *it );
+ m_board->Add( item );
+
+ ITEM_PICKER itemWrapper( item, UR_NEW );
+ picklist.PushItem( itemWrapper );
+
+ m_view->Add( item );
+ }
+
+ m_frame->SaveCopyInUndoList( picklist, UR_NEW );
+ }
+
+ m_frame->OnModify();
+ break;
+ }
+ }
+
+ preview.Clear();
+
+ m_controls->ShowCursor( false );
+ m_controls->SetSnapping( false );
+ m_controls->SetAutoPan( false );
+ m_controls->CaptureCursor( false );
+ m_view->Remove( &preview );
+
+ return 0;
+}
+
+
+int DRAWING_TOOL::SetAnchor( const TOOL_EVENT& aEvent )
+{
+ assert( m_editModules );
+
+ Activate();
+ m_frame->SetToolID( ID_MODEDIT_ANCHOR_TOOL, wxCURSOR_PENCIL,
+ _( "Place the footprint anchor" ) );
+
+ m_controls->ShowCursor( true );
+ m_controls->SetSnapping( true );
+ m_controls->SetAutoPan( true );
+ m_controls->CaptureCursor( false );
+
+ while( OPT_TOOL_EVENT evt = Wait() )
+ {
+ if( evt->IsClick( BUT_LEFT ) )
+ {
+ m_frame->SaveCopyInUndoList( m_board->m_Modules, UR_MODEDIT );
+ m_board->m_Modules->SetLastEditTime();
+
+ // set the new relative internal local coordinates of footprint items
+ VECTOR2I cursorPos = m_controls->GetCursorPosition();
+ wxPoint moveVector = m_board->m_Modules->GetPosition() - wxPoint( cursorPos.x, cursorPos.y );
+ m_board->m_Modules->MoveAnchorPosition( moveVector );
+
+ m_board->m_Modules->ViewUpdate();
+
+ // Usually, we do not need to change twice the anchor position,
+ // so deselect the active tool
+ break;
+ }
+
+ else if( evt->IsCancel() || evt->IsActivate() )
+ break;
+ }
+
+ m_controls->SetAutoPan( false );
+ m_controls->CaptureCursor( false );
+ m_controls->SetSnapping( false );
+ m_controls->ShowCursor( false );
+
+ m_frame->SetToolID( ID_NO_TOOL_SELECTED, wxCURSOR_DEFAULT, wxEmptyString );
+
+ return 0;
+}
+
+
+bool DRAWING_TOOL::drawSegment( int aShape, DRAWSEGMENT*& aGraphic,
+ boost::optional<VECTOR2D> aStartingPoint )
+{
+ // Only two shapes are currently supported
+ assert( aShape == S_SEGMENT || aShape == S_CIRCLE );
+
+ DRAWSEGMENT line45;
+
+ // Add a VIEW_GROUP that serves as a preview for the new item
+ KIGFX::VIEW_GROUP preview( m_view );
+ m_view->Add( &preview );
+
+ m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true );
+ m_controls->ShowCursor( true );
+ m_controls->SetSnapping( true );
+
+ Activate();
+
+ bool direction45 = false; // 45 degrees only mode
+ bool started = false;
+ VECTOR2I cursorPos = m_controls->GetCursorPosition();
+
+ if( aStartingPoint )
+ {
+ LAYER_ID layer = m_frame->GetScreen()->m_Active_Layer;
+
+ // Init the new item attributes
+ aGraphic->SetShape( (STROKE_T) aShape );
+ aGraphic->SetWidth( m_lineWidth );
+ aGraphic->SetStart( wxPoint( aStartingPoint->x, aStartingPoint->y ) );
+ aGraphic->SetEnd( wxPoint( cursorPos.x, cursorPos.y ) );
+ aGraphic->SetLayer( layer );
+
+ if( aShape == S_SEGMENT )
+ line45 = *aGraphic; // used only for direction 45 mode with lines
+
+ preview.Add( aGraphic );
+ m_controls->SetAutoPan( true );
+ m_controls->CaptureCursor( true );
+
+ started = true;
+ }
+
+ // Main loop: keep receiving events
+ while( OPT_TOOL_EVENT evt = Wait() )
+ {
+ bool updatePreview = false; // should preview be updated
+ cursorPos = m_controls->GetCursorPosition();
+
+ // Enable 45 degrees lines only mode by holding control
+ if( direction45 != evt->Modifier( MD_CTRL ) && started && aShape == S_SEGMENT )
+ {
+ direction45 = evt->Modifier( MD_CTRL );
+
+ if( direction45 )
+ {
+ preview.Add( &line45 );
+ make45DegLine( aGraphic, &line45 );
+ }
+ else
+ {
+ preview.Remove( &line45 );
+ aGraphic->SetEnd( wxPoint( cursorPos.x, cursorPos.y ) );
+ }
+
+ updatePreview = true;
+ }
+
+ if( evt->IsCancel() || evt->IsActivate() || evt->IsAction( &COMMON_ACTIONS::layerChanged ) )
+ {
+ preview.Clear();
+ updatePreview = true;
+ delete aGraphic;
+ aGraphic = NULL;
+ break;
+ }
+
+ else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
+ {
+ if( !started )
+ {
+ LAYER_ID layer = m_frame->GetScreen()->m_Active_Layer;
+
+ if( IsCopperLayer( layer ) )
+ {
+ DisplayInfoMessage( NULL, _( "Graphic not allowed on Copper layers" ) );
+ }
+ else
+ {
+ // Init the new item attributes
+ aGraphic->SetShape( (STROKE_T) aShape );
+ m_lineWidth = getSegmentWidth( layer );
+ aGraphic->SetWidth( m_lineWidth );
+ aGraphic->SetStart( wxPoint( cursorPos.x, cursorPos.y ) );
+ aGraphic->SetEnd( wxPoint( cursorPos.x, cursorPos.y ) );
+ aGraphic->SetLayer( layer );
+
+ if( aShape == S_SEGMENT )
+ line45 = *aGraphic; // used only for direction 45 mode with lines
+
+ preview.Add( aGraphic );
+ m_controls->SetAutoPan( true );
+ m_controls->CaptureCursor( true );
+
+ started = true;
+ }
+ }
+ else
+ {
+ if( aGraphic->GetEnd() == aGraphic->GetStart() ||
+ ( evt->IsDblClick( BUT_LEFT ) && aShape == S_SEGMENT ) )
+ // User has clicked twice in the same spot
+ { // a clear sign that the current drawing is finished
+ if( direction45 )
+ {
+ // Now we have to add the helper line as well
+ if( m_editModules )
+ {
+ EDGE_MODULE* l = new EDGE_MODULE( m_board->m_Modules );
+
+ // Copy coordinates, layer, etc.
+ *static_cast<DRAWSEGMENT*>( l ) = line45;
+ l->SetEnd( aGraphic->GetStart() );
+ l->SetLocalCoord();
+
+ m_frame->SaveCopyInUndoList( m_board->m_Modules, UR_MODEDIT );
+ m_board->m_Modules->SetLastEditTime();
+ m_board->m_Modules->GraphicalItems().PushFront( l );
+
+ m_view->Add( l );
+ l->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+ }
+ else
+ {
+ DRAWSEGMENT* l = static_cast<DRAWSEGMENT*>( line45.Clone() );
+ l->SetEnd( aGraphic->GetStart() );
+
+ m_frame->SaveCopyInUndoList( l, UR_NEW );
+ m_board->Add( l );
+
+ m_view->Add( l );
+ l->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+ }
+
+ m_frame->OnModify();
+ }
+
+ delete aGraphic;
+ aGraphic = NULL;
+ }
+ else
+ {
+ assert( aGraphic->GetLength() > 0 );
+ assert( aGraphic->GetWidth() > 0 );
+
+ m_view->Add( aGraphic );
+ aGraphic->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+ }
+
+ preview.Clear();
+ break;
+ }
+ }
+
+ else if( evt->IsMotion() )
+ {
+ // 45 degree lines
+ if( direction45 && aShape == S_SEGMENT )
+ make45DegLine( aGraphic, &line45 );
+ else
+ aGraphic->SetEnd( wxPoint( cursorPos.x, cursorPos.y ) );
+
+ updatePreview = true;
+ }
+
+ else if( evt->IsAction( &COMMON_ACTIONS::incWidth ) )
+ {
+ m_lineWidth += WIDTH_STEP;
+ aGraphic->SetWidth( m_lineWidth );
+ updatePreview = true;
+ }
+
+ else if( evt->IsAction( &COMMON_ACTIONS::decWidth ) )
+ {
+ if( m_lineWidth > (unsigned) WIDTH_STEP )
+ {
+ m_lineWidth -= WIDTH_STEP;
+ aGraphic->SetWidth( m_lineWidth );
+ updatePreview = true;
+ }
+ }
+
+ if( updatePreview )
+ preview.ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+ }
+
+ m_controls->ShowCursor( false );
+ m_controls->SetSnapping( false );
+ m_controls->SetAutoPan( false );
+ m_controls->CaptureCursor( false );
+ m_view->Remove( &preview );
+
+ return started;
+}
+
+
+bool DRAWING_TOOL::drawArc( DRAWSEGMENT*& aGraphic )
+{
+ bool clockwise = true; // drawing direction of the arc
+ double startAngle = 0.0f; // angle of the first arc line
+ VECTOR2I cursorPos = m_controls->GetCursorPosition();
+
+ DRAWSEGMENT helperLine;
+ helperLine.SetShape( S_SEGMENT );
+ helperLine.SetLayer( Dwgs_User );
+ helperLine.SetWidth( 1 );
+
+ // Add a VIEW_GROUP that serves as a preview for the new item
+ KIGFX::VIEW_GROUP preview( m_view );
+ m_view->Add( &preview );
+
+ m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true );
+ m_controls->ShowCursor( true );
+ m_controls->SetSnapping( true );
+
+ Activate();
+
+ enum ARC_STEPS
+ {
+ SET_ORIGIN = 0,
+ SET_END,
+ SET_ANGLE,
+ FINISHED
+ };
+ int step = SET_ORIGIN;
+
+ // Main loop: keep receiving events
+ while( OPT_TOOL_EVENT evt = Wait() )
+ {
+ cursorPos = m_controls->GetCursorPosition();
+
+ if( evt->IsCancel() || evt->IsActivate() )
+ {
+ preview.Clear();
+ preview.ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+ delete aGraphic;
+ aGraphic = NULL;
+ break;
+ }
+
+ else if( evt->IsClick( BUT_LEFT ) )
+ {
+ switch( step )
+ {
+ case SET_ORIGIN:
+ {
+ LAYER_ID layer = m_frame->GetScreen()->m_Active_Layer;
+
+ if( IsCopperLayer( layer ) )
+ {
+ DisplayInfoMessage( NULL, _( "Graphic not allowed on Copper layers" ) );
+ --step;
+ }
+ else
+ {
+ // Init the new item attributes
+ aGraphic->SetShape( S_ARC );
+ aGraphic->SetAngle( 0.0 );
+ aGraphic->SetWidth( getSegmentWidth( layer ) );
+ aGraphic->SetCenter( wxPoint( cursorPos.x, cursorPos.y ) );
+ aGraphic->SetLayer( layer );
+
+ helperLine.SetStart( aGraphic->GetCenter() );
+ helperLine.SetEnd( aGraphic->GetCenter() );
+
+ preview.Add( aGraphic );
+ preview.Add( &helperLine );
+
+ m_controls->SetAutoPan( true );
+ m_controls->CaptureCursor( true );
+ }
+ }
+ break;
+
+ case SET_END:
+ {
+ if( wxPoint( cursorPos.x, cursorPos.y ) != aGraphic->GetCenter() )
+ {
+ VECTOR2D startLine( aGraphic->GetArcStart() - aGraphic->GetCenter() );
+ startAngle = startLine.Angle();
+ aGraphic->SetArcStart( wxPoint( cursorPos.x, cursorPos.y ) );
+ }
+ else
+ --step; // one another chance to draw a proper arc
+ }
+ break;
+
+ case SET_ANGLE:
+ {
+ if( wxPoint( cursorPos.x, cursorPos.y ) != aGraphic->GetArcStart() && aGraphic->GetAngle() != 0 )
+ {
+ assert( aGraphic->GetArcStart() != aGraphic->GetArcEnd() );
+ assert( aGraphic->GetWidth() > 0 );
+
+ m_view->Add( aGraphic );
+ aGraphic->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+
+ preview.Remove( aGraphic );
+ preview.Remove( &helperLine );
+ }
+ else
+ --step; // one another chance to draw a proper arc
+ }
+ break;
+ }
+
+ if( ++step == FINISHED )
+ break;
+ }
+
+ else if( evt->IsMotion() )
+ {
+ switch( step )
+ {
+ case SET_END:
+ helperLine.SetEnd( wxPoint( cursorPos.x, cursorPos.y ) );
+ aGraphic->SetArcStart( wxPoint( cursorPos.x, cursorPos.y ) );
+ break;
+
+ case SET_ANGLE:
+ {
+ VECTOR2D endLine( wxPoint( cursorPos.x, cursorPos.y ) - aGraphic->GetCenter() );
+ double newAngle = RAD2DECIDEG( endLine.Angle() - startAngle );
+
+ // Adjust the new angle to (counter)clockwise setting
+ if( clockwise && newAngle < 0.0 )
+ newAngle += 3600.0;
+ else if( !clockwise && newAngle > 0.0 )
+ newAngle -= 3600.0;
+
+ aGraphic->SetAngle( newAngle );
+ }
+ break;
+ }
+
+ // Show a preview of the item
+ preview.ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+ }
+
+ else if( evt->IsAction( &COMMON_ACTIONS::incWidth ) )
+ {
+ aGraphic->SetWidth( aGraphic->GetWidth() + WIDTH_STEP );
+ preview.ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+ }
+
+ else if( evt->IsAction( &COMMON_ACTIONS::decWidth ) )
+ {
+ int width = aGraphic->GetWidth();
+
+ if( width > WIDTH_STEP )
+ {
+ aGraphic->SetWidth( width - WIDTH_STEP );
+ preview.ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+ }
+ }
+
+ else if( evt->IsAction( &COMMON_ACTIONS::arcPosture ) )
+ {
+ if( clockwise )
+ aGraphic->SetAngle( aGraphic->GetAngle() - 3600.0 );
+ else
+ aGraphic->SetAngle( aGraphic->GetAngle() + 3600.0 );
+
+ clockwise = !clockwise;
+ preview.ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+ }
+ }
+
+ m_controls->ShowCursor( false );
+ m_controls->SetSnapping( false );
+ m_controls->SetAutoPan( false );
+ m_controls->CaptureCursor( false );
+ m_view->Remove( &preview );
+
+ return ( step > SET_ORIGIN );
+}
+
+
+int DRAWING_TOOL::drawZone( bool aKeepout )
+{
+ ZONE_CONTAINER* zone = NULL;
+ DRAWSEGMENT line45;
+ DRAWSEGMENT* helperLine = NULL; // we will need more than one helper line
+
+ // if one day it is possible to draw zones in the footprint editor,
+ // then hereby I'm letting you know that this tool does not handle UR_MODEDIT undo yet
+ assert( !m_editModules );
+
+ // Add a VIEW_GROUP that serves as a preview for the new item
+ KIGFX::VIEW_GROUP preview( m_view );
+ m_view->Add( &preview );
+
+ m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true );
+ m_controls->ShowCursor( true );
+ m_controls->SetSnapping( true );
+
+ Activate();
+
+ VECTOR2I origin;
+ int numPoints = 0;
+ bool direction45 = false; // 45 degrees only mode
+
+ // Main loop: keep receiving events
+ while( OPT_TOOL_EVENT evt = Wait() )
+ {
+ bool updatePreview = false; // should preview be updated
+ VECTOR2I cursorPos = m_controls->GetCursorPosition();
+
+ // Enable 45 degrees lines only mode by holding control
+ if( direction45 != ( evt->Modifier( MD_CTRL ) && numPoints > 0 ) )
+ {
+ direction45 = evt->Modifier( MD_CTRL );
+
+ if( direction45 )
+ {
+ preview.Add( &line45 );
+ make45DegLine( helperLine, &line45 );
+ }
+ else
+ {
+ preview.Remove( &line45 );
+ helperLine->SetEnd( wxPoint( cursorPos.x, cursorPos.y ) );
+ }
+
+ updatePreview = true;
+ }
+
+ if( evt->IsCancel() || evt->IsActivate() )
+ {
+ if( numPoints > 0 ) // cancel the current zone
+ {
+ delete zone;
+ zone = NULL;
+ m_controls->SetAutoPan( false );
+ m_controls->CaptureCursor( false );
+
+ if( direction45 )
+ {
+ preview.Remove( &line45 );
+ direction45 = false;
+ }
+
+ preview.FreeItems();
+ updatePreview = true;
+
+ numPoints = 0;
+ }
+ else // there is no zone currently drawn - just stop the tool
+ break;
+
+ if( evt->IsActivate() ) // now finish unconditionally
+ break;
+ }
+
+ 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)
+ if( evt->IsDblClick( BUT_LEFT ) || ( numPoints > 0 && cursorPos == origin ) )
+ {
+ if( numPoints > 2 ) // valid zone consists of more than 2 points
+ {
+ assert( zone->GetNumCorners() > 2 );
+
+ // Finish the zone
+ if( direction45 )
+ zone->AppendCorner( cursorPos == origin ? line45.GetStart() : line45.GetEnd() );
+
+ zone->Outline()->CloseLastContour();
+ zone->Outline()->RemoveNullSegments();
+
+ m_board->Add( zone );
+ m_view->Add( zone );
+
+ if( !aKeepout )
+ static_cast<PCB_EDIT_FRAME*>( m_frame )->Fill_Zone( zone );
+
+ zone->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+ m_board->GetRatsnest()->Update( zone );
+
+ m_frame->OnModify();
+ m_frame->SaveCopyInUndoList( zone, UR_NEW );
+
+ zone = NULL;
+ }
+ else
+ {
+ delete zone;
+ zone = NULL;
+ }
+
+ numPoints = 0;
+ m_controls->SetAutoPan( false );
+ m_controls->CaptureCursor( false );
+
+ if( direction45 )
+ {
+ preview.Remove( &line45 );
+ direction45 = false;
+ }
+
+ preview.FreeItems();
+ updatePreview = true;
+ }
+ else
+ {
+ if( numPoints == 0 ) // it's the first click
+ {
+ // Get the current default settings for zones
+ ZONE_SETTINGS zoneInfo = m_frame->GetZoneSettings();
+ zoneInfo.m_CurrentZone_Layer = m_frame->GetScreen()->m_Active_Layer;
+ zoneInfo.SetIsKeepout(aKeepout);
+
+ m_controls->SetAutoPan( true );
+ m_controls->CaptureCursor( true );
+
+ // Show options dialog
+ ZONE_EDIT_T dialogResult;
+
+ if( aKeepout )
+ dialogResult = InvokeKeepoutAreaEditor( m_frame, &zoneInfo );
+ else
+ {
+ if( IsCopperLayer( zoneInfo.m_CurrentZone_Layer ) )
+ dialogResult = InvokeCopperZonesEditor( m_frame, &zoneInfo );
+ else
+ dialogResult = InvokeNonCopperZonesEditor( m_frame, NULL, &zoneInfo );
+ }
+
+ if( dialogResult == ZONE_ABORT )
+ {
+ m_controls->SetAutoPan( false );
+ m_controls->CaptureCursor( false );
+ continue;
+ }
+
+ // Apply the selected settings
+ zone = new ZONE_CONTAINER( m_board );
+ zoneInfo.ExportSetting( *zone );
+ m_frame->GetGalCanvas()->SetTopLayer( zoneInfo.m_CurrentZone_Layer );
+
+ // Add the first point
+ zone->Outline()->Start( zoneInfo.m_CurrentZone_Layer,
+ cursorPos.x, cursorPos.y,
+ zone->GetHatchStyle() );
+ origin = cursorPos;
+
+ // Helper line represents the currently drawn line of the zone polygon
+ helperLine = new DRAWSEGMENT;
+ helperLine->SetShape( S_SEGMENT );
+ helperLine->SetWidth( 1 );
+ helperLine->SetLayer( zoneInfo.m_CurrentZone_Layer );
+ helperLine->SetStart( wxPoint( cursorPos.x, cursorPos.y ) );
+ helperLine->SetEnd( wxPoint( cursorPos.x, cursorPos.y ) );
+ line45 = *helperLine;
+
+ preview.Add( helperLine );
+ }
+ else
+ {
+ zone->AppendCorner( helperLine->GetEnd() );
+ helperLine = new DRAWSEGMENT( *helperLine );
+ helperLine->SetStart( helperLine->GetEnd() );
+ preview.Add( helperLine );
+ }
+
+ ++numPoints;
+ updatePreview = true;
+ }
+ }
+
+ else if( evt->IsMotion() && numPoints > 0 )
+ {
+ // 45 degree lines
+ if( direction45 )
+ make45DegLine( helperLine, &line45 );
+ else
+ helperLine->SetEnd( wxPoint( cursorPos.x, cursorPos.y ) );
+
+ // Show a preview of the item
+ updatePreview = true;
+ }
+
+ if( updatePreview )
+ preview.ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+ }
+
+ m_controls->ShowCursor( false );
+ m_controls->SetSnapping( false );
+ m_controls->SetAutoPan( false );
+ m_controls->CaptureCursor( false );
+ m_view->Remove( &preview );
+
+ m_frame->SetToolID( ID_NO_TOOL_SELECTED, wxCURSOR_DEFAULT, wxEmptyString );
+
+ return 0;
+}
+
+
+int DRAWING_TOOL::placeTextModule()
+{
+ TEXTE_MODULE* text = new TEXTE_MODULE( NULL );
+ const BOARD_DESIGN_SETTINGS& dsnSettings = m_frame->GetDesignSettings();
+
+ assert( m_editModules );
+
+ // Add a VIEW_GROUP that serves as a preview for the new item
+ KIGFX::VIEW_GROUP preview( m_view );
+ m_view->Add( &preview );
+
+ m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true );
+ m_controls->ShowCursor( true );
+ m_controls->SetSnapping( true );
+ // do not capture or auto-pan until we start placing some text
+
+ Activate();
+ m_frame->SetToolID( ID_MODEDIT_TEXT_TOOL, wxCURSOR_PENCIL, _( "Add text" ) );
+ bool placing = false;
+
+ // Main loop: keep receiving events
+ while( OPT_TOOL_EVENT evt = Wait() )
+ {
+ VECTOR2I cursorPos = m_controls->GetCursorPosition();
+
+ if( evt->IsCancel() || evt->IsActivate() )
+ {
+ preview.Clear();
+ preview.ViewUpdate();
+ m_controls->SetAutoPan( false );
+ m_controls->CaptureCursor( false );
+ m_controls->ShowCursor( true );
+
+ if( !placing || evt->IsActivate() )
+ {
+ delete text;
+ break;
+ }
+ else
+ {
+ placing = false; // start from the beginning
+ }
+ }
+
+ else if( text && evt->Category() == TC_COMMAND )
+ {
+ if( evt->IsAction( &COMMON_ACTIONS::rotate ) )
+ {
+ text->Rotate( text->GetPosition(), m_frame->GetRotationAngle() );
+ preview.ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+ }
+ else if( evt->IsAction( &COMMON_ACTIONS::flip ) )
+ {
+ text->Flip( text->GetPosition() );
+ preview.ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+ }
+ }
+
+ else if( evt->IsClick( BUT_LEFT ) )
+ {
+ if( !placing )
+ {
+ text->SetSize( dsnSettings.m_ModuleTextSize );
+ text->SetThickness( dsnSettings.m_ModuleTextWidth );
+ text->SetTextPosition( wxPoint( cursorPos.x, cursorPos.y ) );
+
+ DialogEditModuleText textDialog( m_frame, text, NULL );
+ placing = textDialog.ShowModal() && ( text->GetText().Length() > 0 );
+
+ if( !placing )
+ continue;
+
+ m_controls->CaptureCursor( true );
+ m_controls->SetAutoPan( true );
+ m_controls->ShowCursor( false );
+ text->SetParent( m_board->m_Modules ); // it has to set after the settings dialog
+ // otherwise the dialog stores it in undo buffer
+ preview.Add( text );
+ }
+ else
+ {
+ assert( text->GetText().Length() > 0 );
+ assert( text->GetSize().x > 0 && text->GetSize().y > 0 );
+
+ text->SetLocalCoord();
+ text->ClearFlags();
+
+ // Module has to be saved before any modification is made
+ m_frame->SaveCopyInUndoList( m_board->m_Modules, UR_MODEDIT );
+ m_board->m_Modules->SetLastEditTime();
+ m_board->m_Modules->GraphicalItems().PushFront( text );
+
+ m_view->Add( text );
+ text->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+
+ m_frame->OnModify();
+
+ preview.Remove( text );
+ m_controls->CaptureCursor( false );
+ m_controls->SetAutoPan( false );
+ m_controls->ShowCursor( true );
+
+ text = new TEXTE_MODULE( NULL );
+ placing = false;
+ }
+ }
+
+ else if( text && evt->IsMotion() )
+ {
+ text->SetTextPosition( wxPoint( cursorPos.x, cursorPos.y ) );
+
+ // Show a preview of the item
+ preview.ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+ }
+ }
+
+ m_controls->ShowCursor( false );
+ m_controls->SetSnapping( false );
+ m_controls->SetAutoPan( false );
+ m_controls->CaptureCursor( false );
+ m_view->Remove( &preview );
+
+ m_frame->SetToolID( ID_NO_TOOL_SELECTED, wxCURSOR_DEFAULT, wxEmptyString );
+
+ return 0;
+}
+
+
+int DRAWING_TOOL::placeTextPcb()
+{
+ TEXTE_PCB* text = NULL;
+
+ assert( !m_editModules );
+
+ // Add a VIEW_GROUP that serves as a preview for the new item
+ KIGFX::VIEW_GROUP preview( m_view );
+ m_view->Add( &preview );
+
+ m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true );
+ m_controls->ShowCursor( true );
+ m_controls->SetSnapping( true );
+ // do not capture or auto-pan until we start placing some text
+
+ Activate();
+ m_frame->SetToolID( ID_PCB_ADD_TEXT_BUTT, wxCURSOR_PENCIL, _( "Add text" ) );
+
+ // Main loop: keep receiving events
+ while( OPT_TOOL_EVENT evt = Wait() )
+ {
+ VECTOR2I cursorPos = m_controls->GetCursorPosition();
+
+ if( evt->IsCancel() || evt->IsActivate() )
+ {
+ if( text )
+ {
+ // Delete the old text and have another try
+ m_board->Delete( text ); // it was already added by CreateTextPcb()
+ text = NULL;
+
+ preview.Clear();
+ preview.ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+ m_controls->SetAutoPan( false );
+ m_controls->CaptureCursor( false );
+ m_controls->ShowCursor( true );
+ }
+ else
+ break;
+
+ if( evt->IsActivate() ) // now finish unconditionally
+ break;
+ }
+
+ else if( text && evt->Category() == TC_COMMAND )
+ {
+ if( evt->IsAction( &COMMON_ACTIONS::rotate ) )
+ {
+ text->Rotate( text->GetPosition(), m_frame->GetRotationAngle() );
+ preview.ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+ }
+ else if( evt->IsAction( &COMMON_ACTIONS::flip ) )
+ {
+ text->Flip( text->GetPosition() );
+ preview.ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+ }
+ }
+
+ else if( evt->IsClick( BUT_LEFT ) )
+ {
+ if( !text )
+ {
+ // Init the new item attributes
+ text = static_cast<PCB_EDIT_FRAME*>( m_frame )->CreateTextePcb( NULL );
+
+ if( text == NULL )
+ continue;
+
+ m_controls->CaptureCursor( true );
+ m_controls->SetAutoPan( true );
+ preview.Add( text );
+ }
+ else
+ {
+ assert( text->GetText().Length() > 0 );
+ assert( text->GetSize().x > 0 && text->GetSize().y > 0 );
+
+ text->ClearFlags();
+ m_view->Add( text );
+ // m_board->Add( text ); // it is already added by CreateTextePcb()
+ text->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+
+ m_frame->OnModify();
+ m_frame->SaveCopyInUndoList( text, UR_NEW );
+
+ preview.Remove( text );
+ m_controls->CaptureCursor( false );
+ m_controls->SetAutoPan( false );
+
+ text = NULL;
+ }
+ }
+
+ else if( text && evt->IsMotion() )
+ {
+ text->SetTextPosition( wxPoint( cursorPos.x, cursorPos.y ) );
+
+ // Show a preview of the item
+ preview.ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+ }
+ }
+
+ m_controls->ShowCursor( false );
+ m_controls->SetSnapping( false );
+ m_controls->SetAutoPan( false );
+ m_controls->CaptureCursor( false );
+ m_view->Remove( &preview );
+
+ m_frame->SetToolID( ID_NO_TOOL_SELECTED, wxCURSOR_DEFAULT, wxEmptyString );
+
+ return 0;
+}
+
+
+void DRAWING_TOOL::make45DegLine( DRAWSEGMENT* aSegment, DRAWSEGMENT* aHelper ) const
+{
+ VECTOR2I cursorPos = m_controls->GetCursorPosition();
+ VECTOR2I origin( aSegment->GetStart() );
+ DIRECTION_45 direction( origin - cursorPos );
+ SHAPE_LINE_CHAIN newChain = direction.BuildInitialTrace( origin, cursorPos );
+
+ if( newChain.PointCount() > 2 )
+ {
+ aSegment->SetEnd( wxPoint( newChain.Point( -2 ).x, newChain.Point( -2 ).y ) );
+ aHelper->SetStart( wxPoint( newChain.Point( -2 ).x, newChain.Point( -2 ).y ) );
+ aHelper->SetEnd( wxPoint( newChain.Point( -1 ).x, newChain.Point( -1 ).y ) );
+ }
+ else
+ {
+ aSegment->SetEnd( wxPoint( cursorPos.x, cursorPos.y ) );
+ aHelper->SetStart( wxPoint( cursorPos.x, cursorPos.y ) );
+ aHelper->SetEnd( wxPoint( cursorPos.x, cursorPos.y ) );
+ }
+}
+
+
+void DRAWING_TOOL::SetTransitions()
+{
+ Go( &DRAWING_TOOL::DrawLine, COMMON_ACTIONS::drawLine.MakeEvent() );
+ Go( &DRAWING_TOOL::DrawCircle, COMMON_ACTIONS::drawCircle.MakeEvent() );
+ Go( &DRAWING_TOOL::DrawArc, COMMON_ACTIONS::drawArc.MakeEvent() );
+ Go( &DRAWING_TOOL::DrawDimension, COMMON_ACTIONS::drawDimension.MakeEvent() );
+ Go( &DRAWING_TOOL::DrawZone, COMMON_ACTIONS::drawZone.MakeEvent() );
+ Go( &DRAWING_TOOL::DrawKeepout, COMMON_ACTIONS::drawKeepout.MakeEvent() );
+ Go( &DRAWING_TOOL::PlaceText, COMMON_ACTIONS::placeText.MakeEvent() );
+ Go( &DRAWING_TOOL::PlaceDXF, COMMON_ACTIONS::placeDXF.MakeEvent() );
+ Go( &DRAWING_TOOL::SetAnchor, COMMON_ACTIONS::setAnchor.MakeEvent() );
+}
+
+
+int DRAWING_TOOL::getSegmentWidth( unsigned int aLayer ) const
+{
+ assert( m_board );
+
+ if( aLayer == Edge_Cuts )
+ return m_board->GetDesignSettings().m_EdgeSegmentWidth;
+ else if( m_editModules )
+ return m_board->GetDesignSettings().m_ModuleSegmentWidth;
+ else
+ return m_board->GetDesignSettings().m_DrawSegmentWidth;
+}
+
+
+const int DRAWING_TOOL::WIDTH_STEP = 100000;
diff --git a/pcbnew/tools/drawing_tool.h b/pcbnew/tools/drawing_tool.h
new file mode 100644
index 0000000..432da1e
--- /dev/null
+++ b/pcbnew/tools/drawing_tool.h
@@ -0,0 +1,200 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2014 CERN
+ * @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
+ */
+
+#ifndef __DRAWING_TOOL_H
+#define __DRAWING_TOOL_H
+
+#include <tool/tool_interactive.h>
+#include <boost/optional.hpp>
+
+namespace KIGFX
+{
+ class VIEW;
+ class VIEW_CONTROLS;
+}
+class BOARD;
+class PCB_EDIT_FRAME;
+class DRAWSEGMENT;
+
+/**
+ * Class DRAWING_TOOL
+ *
+ * Tool responsible for drawing graphical elements like lines, arcs, circles, etc.
+ */
+
+class DRAWING_TOOL : public TOOL_INTERACTIVE
+{
+public:
+ DRAWING_TOOL();
+ ~DRAWING_TOOL();
+
+ /// @copydoc TOOL_INTERACTIVE::Reset()
+ void Reset( RESET_REASON aReason );
+
+ /**
+ * Function DrawLine()
+ * Starts interactively drawing a line. After invoking the function it expects the user
+ * to click at least two times to determine the origin and the end for a line. If there are
+ * more clicks, the line is drawn as a continous polyline.
+ */
+ int DrawLine( const TOOL_EVENT& aEvent );
+
+ /**
+ * Function DrawCircle()
+ * Starts interactively drawing a circle. After invoking the function it expects the user
+ * to first click on a point that is going to be used as the center of the circle. The second
+ * click determines the circle radius.
+ */
+ int DrawCircle( const TOOL_EVENT& aEvent );
+
+ /**
+ * Function DrawArc()
+ * Starts interactively drawing an arc. After invoking the function it expects the user
+ * to first click on a point that is going to be used as the center of the arc. The second
+ * click determines the origin and radius, the third one - the angle.
+ */
+ int DrawArc( const TOOL_EVENT& aEvent );
+
+ /**
+ * Function PlaceText()
+ * Displays a dialog that allows to input text and its settings and then lets the user decide
+ * where to place the text in editor.
+ */
+ int PlaceText( const TOOL_EVENT& aEvent );
+
+ /**
+ * Function DrawDimension()
+ * Starts interactively drawing a dimension. After invoking the function it expects the user
+ * to first click on a point that is going to be used as the origin of the dimension.
+ * The second click determines the end and the third click modifies its height.
+ */
+ int DrawDimension( const TOOL_EVENT& aEvent );
+
+ /**
+ * Function DrawZone()
+ * Starts interactively drawing a zone. After invoking the function a zone settings dialog
+ * is displayed. After confirmation it allows the user to set points that are going to be used
+ * as a boundary polygon of the zone. Double click or clicking on the origin of the boundary
+ * polyline finishes the drawing.
+ */
+ int DrawZone( const TOOL_EVENT& aEvent );
+
+ /**
+ * Function DrawKeepout()
+ * Starts interactively drawing a keepout area. After invoking the function an area settings
+ * dialog is displayed. After confirmation it allows the user to set points that are going to
+ * be used as a boundary polygon of the area. Double click or clicking on the origin of the
+ * boundary polyline finishes the drawing.
+ */
+ int DrawKeepout( const TOOL_EVENT& aEvent );
+
+ /**
+ * Function PlaceDXF()
+ * Places a drawing imported from a DXF file in module editor.
+ */
+ int PlaceDXF( const TOOL_EVENT& aEvent );
+
+ /**
+ * Function SetAnchor()
+ * Places the footprint anchor (only in module editor).
+ */
+ int SetAnchor( const TOOL_EVENT& aEvent );
+
+ /**
+ * Function EditModules()
+ * Toggles edit module mode. When enabled, one may select parts of modules individually
+ * (graphics, pads, etc.), so they can be modified.
+ * @param aEnabled decides if the mode should be enabled.
+ */
+ void EditModules( bool aEnabled )
+ {
+ m_editModules = aEnabled;
+ }
+
+ ///> Sets up handlers for various events.
+ void SetTransitions();
+
+private:
+ ///> 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
+ ///> be already created. The tool deletes the object if it is not added to a BOARD.
+ ///> @return False if the tool was cancelled before the origin was set or origin and end are
+ ///> the same point.
+ bool drawSegment( int aShape, DRAWSEGMENT*& aGraphic,
+ boost::optional<VECTOR2D> aStartingPoint = boost::none );
+
+ ///> Starts drawing an arc.
+ ///> @param aGraphic is an object that is going to be used by the tool for drawing. It has to
+ ///> be already created. The tool deletes the object if it is not added to a BOARD.
+ ///> @return False if the tool was cancelled before the origin was set or origin and end are
+ ///> the same point.
+ bool drawArc( DRAWSEGMENT*& aGraphic );
+
+ ///> Draws a polygon, that is added as a zone or a keepout area.
+ ///> @param aKeepout decides if the drawn polygon is a zone or a keepout area.
+ int drawZone( bool aKeepout );
+
+ /**
+ * Function placeTextModule()
+ * Displays a dialog that allows to input text and its settings and then lets the user decide
+ * where to place the text in module .
+ */
+ int placeTextModule();
+
+ /**
+ * Function placeTextPcb()
+ * Displays a dialog that allows to input text and its settings and then lets the user decide
+ * where to place the text in board editor.
+ */
+ int placeTextPcb();
+
+ /**
+ * Function make45DegLine()
+ * Forces a DRAWSEGMENT to be drawn at multiple of 45 degrees. The origin stays the same,
+ * the end of the aSegment is modified according to the current cursor position.
+ * @param aSegment is the segment that is currently drawn.
+ * @param aHelper is a helper line that shows the next possible segment.
+ */
+ void make45DegLine( DRAWSEGMENT* aSegment, DRAWSEGMENT* aHelper ) const;
+
+ ///> Returns the appropriate width for a segment depending on the settings.
+ int getSegmentWidth( unsigned int aLayer ) const;
+
+ KIGFX::VIEW* m_view;
+ KIGFX::VIEW_CONTROLS* m_controls;
+ BOARD* m_board;
+ PCB_EDIT_FRAME* m_frame;
+
+ /// Edit module mode flag
+ bool m_editModules;
+
+ /// Stores the current line width for multisegment drawing.
+ unsigned int m_lineWidth;
+
+ // How does line width change after one -/+ key press.
+ static const int WIDTH_STEP;
+};
+
+#endif /* __DRAWING_TOOL_H */
diff --git a/pcbnew/tools/edit_constraints.cpp b/pcbnew/tools/edit_constraints.cpp
new file mode 100644
index 0000000..b73ac0c
--- /dev/null
+++ b/pcbnew/tools/edit_constraints.cpp
@@ -0,0 +1,191 @@
+/*
+ * This program source code file is part of KICAD, a free EDA CAD application.
+ *
+ * Copyright (C) 2014 CERN
+ * @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 "edit_constraints.h"
+#include "edit_points.h"
+
+#include <geometry/seg.h>
+
+#include <common.h>
+
+void EC_VERTICAL::Apply( EDIT_POINT& aHandle )
+{
+ VECTOR2I point = aHandle.GetPosition();
+ point.x = m_constrainer.GetPosition().x;
+ aHandle.SetPosition( point );
+}
+
+
+void EC_HORIZONTAL::Apply( EDIT_POINT& aHandle )
+{
+ VECTOR2I point = aHandle.GetPosition();
+ point.y = m_constrainer.GetPosition().y;
+ aHandle.SetPosition( point );
+}
+
+
+void EC_45DEGREE::Apply( EDIT_POINT& aHandle )
+{
+ // Current line vector
+ VECTOR2I lineVector( aHandle.GetPosition() - m_constrainer.GetPosition() );
+ double angle = lineVector.Angle();
+
+ // Find the closest angle, which is a multiple of 45 degrees
+ double newAngle = KiROUND( angle / ( M_PI / 4.0 ) ) * M_PI / 4.0;
+ VECTOR2I newLineVector = lineVector.Rotate( newAngle - angle );
+
+ aHandle.SetPosition( m_constrainer.GetPosition() + newLineVector );
+}
+
+
+EC_LINE::EC_LINE( EDIT_POINT& aConstrained, const EDIT_POINT& aConstrainer ) :
+ EDIT_CONSTRAINT<EDIT_POINT>( aConstrained ), m_constrainer( aConstrainer )
+{
+ m_line = m_constrained.GetPosition() - m_constrainer.GetPosition();
+}
+
+
+void EC_LINE::Apply( EDIT_POINT& aHandle )
+{
+ SEG main( m_constrainer.GetPosition(), m_constrainer.GetPosition() + m_line );
+ SEG projection( aHandle.GetPosition(), aHandle.GetPosition() + m_line.Perpendicular() );
+
+ if( OPT_VECTOR2I intersect = projection.IntersectLines( main ) )
+ aHandle.SetPosition( *intersect );
+}
+
+
+void EC_CIRCLE::Apply( EDIT_POINT& aHandle )
+{
+ VECTOR2I centerToEnd = m_end.GetPosition() - m_center.GetPosition();
+ VECTOR2I centerToPoint = aHandle.GetPosition() - m_center.GetPosition();
+
+ int radius = centerToEnd.EuclideanNorm();
+ double angle = centerToPoint.Angle();
+
+ VECTOR2I newLine( radius, 0 );
+ newLine = newLine.Rotate( angle );
+
+ aHandle.SetPosition( m_center.GetPosition() + newLine );
+}
+
+
+EC_CONVERGING::EC_CONVERGING( EDIT_LINE& aLine, EDIT_POINTS& aPoints ) :
+ EDIT_CONSTRAINT<EDIT_LINE>( aLine ),
+ m_colinearConstraint( NULL ), m_editPoints( aPoints )
+{
+ // Dragged segment endings
+ EDIT_POINT& origin = aLine.GetOrigin();
+ EDIT_POINT& end = aLine.GetEnd();
+
+ // Previous and next points, to make constraining lines (adjacent to the dragged line)
+ EDIT_POINT& prevOrigin = *aPoints.Previous( origin, false );
+ EDIT_POINT& nextEnd = *aPoints.Next( end, false );
+
+ // Constraints for segments adjacent to the dragged one
+ m_originSideConstraint = new EC_LINE( origin, prevOrigin );
+ m_endSideConstraint = new EC_LINE( end, nextEnd );
+
+ // Store the current vector of the line
+ m_draggedVector = end.GetPosition() - origin.GetPosition();
+
+ // Check for colinearity
+ SEG originSide( origin.GetPosition(), prevOrigin.GetPosition() );
+ SEG endSide( end.GetPosition(), nextEnd.GetPosition() );
+ SEG dragged( origin.GetPosition(), end.GetPosition() );
+
+ if( dragged.Collinear( originSide ) )
+ m_colinearConstraint = m_originSideConstraint;
+ else if( dragged.Collinear( endSide ) )
+ m_colinearConstraint = m_endSideConstraint;
+}
+
+
+EC_CONVERGING::~EC_CONVERGING()
+{
+ delete m_originSideConstraint;
+ delete m_endSideConstraint;
+ // m_colinearConstraint should not be freed, it is a pointer to one of the above
+}
+
+
+void EC_CONVERGING::Apply( EDIT_LINE& aHandle )
+{
+ // The dragged segment endpoints
+ EDIT_POINT& origin = aHandle.GetOrigin();
+ EDIT_POINT& end = aHandle.GetEnd();
+
+ if( m_colinearConstraint )
+ {
+ m_colinearConstraint->Apply( origin );
+ m_colinearConstraint->Apply( end );
+ }
+
+ // The dragged segment
+ SEG dragged( origin.GetPosition(), origin.GetPosition() + m_draggedVector );
+
+ // Do not allow points on the adjacent segments move freely
+ m_originSideConstraint->Apply();
+ m_endSideConstraint->Apply();
+
+ EDIT_POINT& prevOrigin = *m_editPoints.Previous( origin, false );
+ EDIT_POINT& nextEnd = *m_editPoints.Next( end, false );
+
+ // Two segments adjacent to the dragged segment
+ SEG originSide = SEG( origin.GetPosition(), prevOrigin.GetPosition() );
+ SEG endSide = SEG( end.GetPosition(), nextEnd.GetPosition() );
+
+ // First intersection point (dragged segment against origin side)
+ if( OPT_VECTOR2I originIntersect = dragged.IntersectLines( originSide ) )
+ origin.SetPosition( *originIntersect );
+
+ // Second intersection point (dragged segment against end side)
+ if( OPT_VECTOR2I endIntersect = dragged.IntersectLines( endSide ) )
+ end.SetPosition( *endIntersect );
+
+ // Check if adjacent segments intersect (did we dragged the line to the point that it may
+ // create a selfintersecting polygon?)
+ originSide = SEG( origin.GetPosition(), prevOrigin.GetPosition() );
+ endSide = SEG( end.GetPosition(), nextEnd.GetPosition() );
+
+ if( OPT_VECTOR2I originEndIntersect = endSide.Intersect( originSide ) )
+ {
+ origin.SetPosition( *originEndIntersect );
+ end.SetPosition( *originEndIntersect );
+ }
+}
+
+
+EC_SNAPLINE::EC_SNAPLINE( EDIT_LINE& aLine, V2D_TRANSFORM_FUN aSnapFun ) :
+ EDIT_CONSTRAINT<EDIT_LINE>( aLine ), m_snapFun( aSnapFun )
+{}
+
+
+void EC_SNAPLINE::Apply( EDIT_LINE& aHandle )
+{
+ VECTOR2D delta = aHandle.GetEnd().GetPosition() - aHandle.GetOrigin().GetPosition();
+
+ aHandle.GetOrigin().SetPosition( m_snapFun( aHandle.GetOrigin().GetPosition() ) );
+ aHandle.GetEnd().SetPosition( aHandle.GetOrigin().GetPosition() + delta );
+}
diff --git a/pcbnew/tools/edit_constraints.h b/pcbnew/tools/edit_constraints.h
new file mode 100644
index 0000000..a1744d3
--- /dev/null
+++ b/pcbnew/tools/edit_constraints.h
@@ -0,0 +1,267 @@
+/*
+ * This program source code file is part of KICAD, a free EDA CAD application.
+ *
+ * Copyright (C) 2014 CERN
+ * @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
+ */
+
+#ifndef EDIT_CONSTRAINTS_H_
+#define EDIT_CONSTRAINTS_H_
+
+#include <math/vector2d.h>
+#include <boost/function.hpp>
+
+class EDIT_POINT;
+class EDIT_LINE;
+class EDIT_POINTS;
+
+/**
+ * Class EDIT_CONSTRAINT
+ *
+ * Allows to describe constraints between two edit handles. After the constrained handle is changed,
+ * Apply() has to be called to fix its coordinates according to the implemented constraint.
+ */
+template<class EDIT_TYPE>
+class EDIT_CONSTRAINT
+{
+public:
+ /**
+ * Constructor
+ *
+ * @param aConstrained is EDIT_POINT to which the constraint is applied.
+ */
+ EDIT_CONSTRAINT( EDIT_TYPE& aConstrained ) : m_constrained( aConstrained ) {};
+
+ virtual ~EDIT_CONSTRAINT() {};
+
+ /**
+ * Function Apply()
+ *
+ * Corrects coordinates of the constrained edit handle.
+ */
+ virtual void Apply( EDIT_TYPE& aHandle ) = 0;
+
+ /**
+ * Function Apply()
+ *
+ * Corrects coordinates of the constrained edit handle.
+ */
+ void Apply()
+ {
+ Apply( m_constrained );
+ }
+
+protected:
+ EDIT_TYPE& m_constrained; ///< Point that is constrained by rules implemented by Apply()
+};
+
+
+/**
+ * Class EC_VERTICAL.
+ *
+ * EDIT_CONSTRAINT that imposes a constraint that two points have to have the same X coordinate.
+ */
+class EC_VERTICAL : public EDIT_CONSTRAINT<EDIT_POINT>
+{
+public:
+ /**
+ * Constructor.
+ *
+ * @param aConstrained is the point that is put under constrain.
+ * @param aConstrainer is the point that is the source of the constrain.
+ */
+ EC_VERTICAL( EDIT_POINT& aConstrained, const EDIT_POINT& aConstrainer ) :
+ EDIT_CONSTRAINT<EDIT_POINT>( aConstrained ), m_constrainer( aConstrainer )
+ {}
+
+ ///> @copydoc EDIT_CONSTRAINT::Apply()
+ virtual void Apply( EDIT_POINT& aHandle );
+
+private:
+ const EDIT_POINT& m_constrainer; ///< Point that imposes the constraint.
+};
+
+
+/**
+ * Class EC_HORIZONTAL.
+ *
+ * EDIT_CONSTRAINT that imposes a constraint that two points have to have the same Y coordinate.
+ */
+class EC_HORIZONTAL : public EDIT_CONSTRAINT<EDIT_POINT>
+{
+public:
+ /**
+ * Constructor.
+ *
+ * @param aConstrained is the point that is put under constrain.
+ * @param aConstrainer is the point that is the source of the constrain.
+ */
+ EC_HORIZONTAL( EDIT_POINT& aConstrained, const EDIT_POINT& aConstrainer ) :
+ EDIT_CONSTRAINT<EDIT_POINT>( aConstrained ), m_constrainer( aConstrainer )
+ {}
+
+ ///> @copydoc EDIT_CONSTRAINT::Apply()
+ virtual void Apply( EDIT_POINT& aHandle );
+
+private:
+ const EDIT_POINT& m_constrainer; ///< Point that imposes the constraint.
+};
+
+
+/**
+ * Class EC_45DEGREE
+ *
+ * EDIT_CONSTRAINT that imposes a constraint that two points have to be located at angle of 45
+ * degree multiplicity.
+ */
+class EC_45DEGREE : public EDIT_CONSTRAINT<EDIT_POINT>
+{
+public:
+ /**
+ * Constructor.
+ *
+ * @param aConstrained is the point that is put under constrain.
+ * @param aConstrainer is the point that is the source of the constrain.
+ */
+ EC_45DEGREE( EDIT_POINT& aConstrained, const EDIT_POINT& aConstrainer ) :
+ EDIT_CONSTRAINT<EDIT_POINT>( aConstrained ), m_constrainer( aConstrainer )
+ {}
+
+ ///> @copydoc EDIT_CONSTRAINT::Apply()
+ virtual void Apply( EDIT_POINT& aHandle );
+
+private:
+ const EDIT_POINT& m_constrainer; ///< Point that imposes the constraint.
+};
+
+
+/**
+ * Class EC_LINE
+ *
+ * EDIT_CONSTRAINT that imposes a constraint that a point has to lie on a line (determined
+ * by 2 points).
+ */
+class EC_LINE : public EDIT_CONSTRAINT<EDIT_POINT>
+{
+public:
+ EC_LINE( EDIT_POINT& aConstrained, const EDIT_POINT& aConstrainer );
+
+ ///> @copydoc EDIT_CONSTRAINT::Apply()
+ virtual void Apply( EDIT_POINT& aHandle );
+
+private:
+ const EDIT_POINT& m_constrainer; ///< Point that imposes the constraint.
+ VECTOR2I m_line; ///< Vector representing the constraining line.
+};
+
+
+/**
+ * Class EC_CIRCLE.
+ *
+ * EDIT_CONSTRAINT that imposes a constraint that a point has to lie on a circle.
+ */
+class EC_CIRCLE : public EDIT_CONSTRAINT<EDIT_POINT>
+{
+public:
+ /**
+ * Constructor.
+ *
+ * @param aConstrained is the point that is put under constrain.
+ * @param aCenter is the point that is the center of the circle.
+ * @param aEnd is the point that decides on the radius of the circle.
+ */
+ EC_CIRCLE( EDIT_POINT& aConstrained, const EDIT_POINT& aCenter, const EDIT_POINT& aEnd ) :
+ EDIT_CONSTRAINT<EDIT_POINT>( aConstrained ), m_center( aCenter ), m_end( aEnd )
+ {}
+
+ ///> @copydoc EDIT_CONSTRAINT::Apply()
+ virtual void Apply( EDIT_POINT& aHandle );
+
+private:
+ ///> Point that imposes the constraint (center of the circle).
+ const EDIT_POINT& m_center;
+
+ ///> Point that imposes the constraint (decides on the radius of the circle).
+ const EDIT_POINT& m_end;
+};
+
+
+/**
+ * Class EC_CONVERGING
+ *
+ * EDIT_CONSTRAINT for 3 segments: dragged and two adjacent ones, enforcing to keep their slopes
+ * and allows only to change ending points. Applied to zones.
+ */
+class EC_CONVERGING : public EDIT_CONSTRAINT<EDIT_LINE>
+{
+public:
+ EC_CONVERGING( EDIT_LINE& aLine, EDIT_POINTS& aPoints );
+
+ virtual ~EC_CONVERGING();
+
+ ///> @copydoc EDIT_CONSTRAINT::Apply()
+ virtual void Apply( EDIT_LINE& aHandle );
+
+private:
+ ///> Constraint for origin side segment.
+ EDIT_CONSTRAINT<EDIT_POINT>* m_originSideConstraint;
+
+ ///> Constraint for end side segment.
+ EDIT_CONSTRAINT<EDIT_POINT>* m_endSideConstraint;
+
+ ///> Additional constriant, applied when at least two points are collinear. It is a pointer to
+ ///> m_[origin/end]SideConstraint, so it should not be freed.
+ EDIT_CONSTRAINT<EDIT_POINT>* m_colinearConstraint;
+
+ ///> EDIT_POINTS instance that stores currently modified lines.
+ EDIT_POINTS& m_editPoints;
+
+ ///> Vector that represents the initial direction of the dragged segment.
+ VECTOR2I m_draggedVector;
+};
+
+
+/**
+ * Class EC_SNAPLINE
+ *
+ * EDIT_CONSTRAINT for a EDIT_LINE, one of the ends is snapped to a spot determined by a
+ * transform function passed as parameter (e.g. it can be snapped to a grid), instead of having
+ * the line center snapped to a point.
+ */
+class EC_SNAPLINE : public EDIT_CONSTRAINT<EDIT_LINE>
+{
+public:
+ ///> Typedef for a function that determines snapping point.
+ typedef boost::function<VECTOR2D (const VECTOR2D&)> V2D_TRANSFORM_FUN;
+
+ EC_SNAPLINE( EDIT_LINE& aLine, V2D_TRANSFORM_FUN aSnapFun );
+
+ virtual ~EC_SNAPLINE()
+ {}
+
+ ///> @copydoc EDIT_CONSTRAINT::Apply()
+ virtual void Apply( EDIT_LINE& aHandle );
+
+private:
+ ///> Function that determines snapping point.
+ V2D_TRANSFORM_FUN m_snapFun;
+};
+
+#endif /* EDIT_CONSTRAINTS_H_ */
diff --git a/pcbnew/tools/edit_points.cpp b/pcbnew/tools/edit_points.cpp
new file mode 100644
index 0000000..cbd2a77
--- /dev/null
+++ b/pcbnew/tools/edit_points.cpp
@@ -0,0 +1,227 @@
+/*
+ * This program source code file is part of KICAD, a free EDA CAD application.
+ *
+ * Copyright (C) 2014 CERN
+ * @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 <boost/foreach.hpp>
+
+#include <gal/graphics_abstraction_layer.h>
+#include "edit_points.h"
+
+bool EDIT_POINT::WithinPoint( const VECTOR2I& aPoint, unsigned int aSize ) const
+{
+ // Corners of the EDIT_POINT square
+ VECTOR2I topLeft = GetPosition() - aSize;
+ VECTOR2I bottomRight = GetPosition() + aSize;
+
+ return ( aPoint.x > topLeft.x && aPoint.y > topLeft.y &&
+ aPoint.x < bottomRight.x && aPoint.y < bottomRight.y );
+}
+
+
+EDIT_POINTS::EDIT_POINTS( EDA_ITEM* aParent ) :
+ EDA_ITEM( NOT_USED ), m_parent( aParent )
+{
+}
+
+
+EDIT_POINT* EDIT_POINTS::FindPoint( const VECTOR2I& aLocation )
+{
+ float size = m_view->ToWorld( EDIT_POINT::POINT_SIZE );
+
+ std::deque<EDIT_POINT>::iterator pit, pitEnd;
+ for( pit = m_points.begin(), pitEnd = m_points.end(); pit != pitEnd; ++pit )
+ {
+ EDIT_POINT& point = *pit;
+
+ if( point.WithinPoint( aLocation, size ) )
+ return &point;
+ }
+
+ std::deque<EDIT_LINE>::iterator lit, litEnd;
+ for( lit = m_lines.begin(), litEnd = m_lines.end(); lit != litEnd; ++lit )
+ {
+ EDIT_LINE& line = *lit;
+
+ if( line.WithinPoint( aLocation, size ) )
+ return &line;
+ }
+
+ return NULL;
+}
+
+
+int EDIT_POINTS::GetContourStartIdx( int aPointIdx ) const
+{
+ int lastIdx = 0;
+
+ BOOST_FOREACH( int idx, m_contours )
+ {
+ if( idx >= aPointIdx )
+ return lastIdx;
+
+ lastIdx = idx + 1;
+ }
+
+ return lastIdx;
+}
+
+
+int EDIT_POINTS::GetContourEndIdx( int aPointIdx ) const
+{
+ BOOST_FOREACH( int idx, m_contours )
+ {
+ if( idx >= aPointIdx )
+ return idx;
+ }
+
+ return m_points.size() - 1;
+}
+
+
+bool EDIT_POINTS::IsContourStart( int aPointIdx ) const
+{
+ BOOST_FOREACH( int idx, m_contours )
+ {
+ if( idx + 1 == aPointIdx )
+ return true;
+
+ // the list is sorted, so we cannot expect it any further
+ if( idx > aPointIdx )
+ break;
+ }
+
+ return ( aPointIdx == 0 );
+}
+
+
+bool EDIT_POINTS::IsContourEnd( int aPointIdx ) const
+{
+ BOOST_FOREACH( int idx, m_contours )
+ {
+ if( idx == aPointIdx )
+ return true;
+
+ // the list is sorted, so we cannot expect it any further
+ if( idx > aPointIdx )
+ break;
+ }
+
+ // the end of the list surely is the end of a contour
+ return ( aPointIdx == (int) m_points.size() - 1 );
+}
+
+
+EDIT_POINT* EDIT_POINTS::Previous( const EDIT_POINT& aPoint, bool aTraverseContours )
+{
+ for( unsigned int i = 0; i < m_points.size(); ++i )
+ {
+ if( m_points[i] == aPoint )
+ {
+ if( !aTraverseContours && IsContourStart( i ) )
+ return &m_points[GetContourEndIdx( i )];
+
+ if( i == 0 )
+ return &m_points[m_points.size() - 1];
+ else
+ return &m_points[i - 1];
+ }
+ }
+
+ return NULL;
+}
+
+
+EDIT_LINE* EDIT_POINTS::Previous( const EDIT_LINE& aLine )
+{
+ for( unsigned int i = 0; i < m_lines.size(); ++i )
+ {
+ if( m_lines[i] == aLine )
+ {
+ if( i == 0 )
+ return &m_lines[m_lines.size() - 1];
+ else
+ return &m_lines[i - 1];
+ }
+ }
+
+ return NULL;
+}
+
+
+EDIT_POINT* EDIT_POINTS::Next( const EDIT_POINT& aPoint, bool aTraverseContours )
+{
+ for( unsigned int i = 0; i < m_points.size(); ++i )
+ {
+ if( m_points[i] == aPoint )
+ {
+ if( !aTraverseContours && IsContourEnd( i ) )
+ return &m_points[GetContourStartIdx( i )];
+
+ if( i == m_points.size() - 1 )
+ return &m_points[0];
+ else
+ return &m_points[i + 1];
+ }
+ }
+
+ return NULL;
+}
+
+
+EDIT_LINE* EDIT_POINTS::Next( const EDIT_LINE& aLine )
+{
+ for( unsigned int i = 0; i < m_lines.size(); ++i )
+ {
+ if( m_lines[i] == aLine )
+ {
+ if( i == m_lines.size() - 1 )
+ return &m_lines[0];
+ else
+ return &m_lines[i + 1];
+ }
+ }
+
+ return NULL;
+}
+
+
+void EDIT_POINTS::ViewDraw( int aLayer, KIGFX::GAL* aGal ) const
+{
+ aGal->SetFillColor( KIGFX::COLOR4D( 1.0, 1.0, 1.0, 1.0 ) );
+ aGal->SetIsFill( true );
+ aGal->SetIsStroke( false );
+ aGal->PushDepth();
+ aGal->SetLayerDepth( aGal->GetMinDepth() );
+
+ float size = m_view->ToWorld( EDIT_POINT::POINT_SIZE );
+
+ BOOST_FOREACH( const EDIT_POINT& point, m_points )
+ aGal->DrawRectangle( point.GetPosition() - size / 2, point.GetPosition() + size / 2 );
+
+ BOOST_FOREACH( const EDIT_LINE& line, m_lines )
+ {
+ aGal->DrawCircle( line.GetPosition(), size / 2 );
+ }
+
+ aGal->PopDepth();
+}
diff --git a/pcbnew/tools/edit_points.h b/pcbnew/tools/edit_points.h
new file mode 100644
index 0000000..c939eca
--- /dev/null
+++ b/pcbnew/tools/edit_points.h
@@ -0,0 +1,527 @@
+/*
+ * This program source code file is part of KICAD, a free EDA CAD application.
+ *
+ * Copyright (C) 2014 CERN
+ * @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
+ */
+
+#ifndef EDIT_POINTS_H_
+#define EDIT_POINTS_H_
+
+#include <boost/shared_ptr.hpp>
+
+#include <base_struct.h>
+#include <layers_id_colors_and_visibility.h>
+
+#include "edit_constraints.h"
+
+/**
+ * Class EDIT_POINT
+ *
+ * Represents a single point that can be used for modifying items. It is directly related to one
+ * of points in a graphical item (e.g. vertex of a zone or center of a circle).
+ */
+class EDIT_POINT
+{
+public:
+ /**
+ * Constructor
+ *
+ * @param aPoint stores coordinates for EDIT_POINT.
+ */
+ EDIT_POINT( const VECTOR2I& aPoint ) :
+ m_position( aPoint ) {};
+
+ virtual ~EDIT_POINT() {}
+
+ /**
+ * Function GetPosition()
+ *
+ * Returns coordinates of an EDIT_POINT. Note that it may be different than coordinates of
+ * a graphical item that is bound to the EDIT_POINT.
+ */
+ virtual VECTOR2I GetPosition() const
+ {
+ return m_position;
+ }
+
+ /**
+ * Function GetX()
+ *
+ * Returns X coordinate of an EDIT_POINT.
+ */
+ inline int GetX() const
+ {
+ return GetPosition().x;
+ }
+
+ /**
+ * Function GetY()
+ *
+ * Returns Y coordinate of an EDIT_POINT.
+ */
+ inline int GetY() const
+ {
+ return GetPosition().y;
+ }
+
+ /**
+ * Function SetPosition()
+ *
+ * Sets new coordinates for an EDIT_POINT. It does not change the coordinates of a graphical
+ * item.
+ * @param aPosition are new coordinates.
+ */
+ virtual void SetPosition( const VECTOR2I& aPosition )
+ {
+ m_position = aPosition;
+ }
+
+ /**
+ * Function WithinPoint()
+ *
+ * Checks if given point is within a square centered in the EDIT_POINT position.
+ * @param aPoint is point to be checked.
+ * @param aSize is length of the square side.
+ */
+ bool WithinPoint( const VECTOR2I& aPoint, unsigned int aSize ) const;
+
+ /**
+ * Function SetConstraint()
+ *
+ * Sets a constraint for and EDIT_POINT.
+ * @param aConstraint is the constraint to be set.
+ */
+ void SetConstraint( EDIT_CONSTRAINT<EDIT_POINT>* aConstraint )
+ {
+ m_constraint.reset( aConstraint );
+ }
+
+ /**
+ * Function GetConstraint()
+ *
+ * Returns the constraint imposed on an EDIT_POINT. If there are no constraints, NULL is
+ * returned.
+ */
+ EDIT_CONSTRAINT<EDIT_POINT>* GetConstraint() const
+ {
+ return m_constraint.get();
+ }
+
+ /**
+ * Function ClearConstraint()
+ *
+ * Removes previously set constraint.
+ */
+ inline void ClearConstraint()
+ {
+ m_constraint.reset();
+ }
+
+ /**
+ * Function IsConstrained()
+ *
+ * Checks if point is constrained.
+ * @return True is point is constrained, false otherwise.
+ */
+ inline bool IsConstrained() const
+ {
+ return m_constraint != NULL;
+ }
+
+ /**
+ * Function ApplyConstraint()
+ *
+ * Corrects coordinates of an EDIT_POINT by applying previously set constraint.
+ */
+ virtual void ApplyConstraint()
+ {
+ if( m_constraint )
+ m_constraint->Apply();
+ }
+
+ bool operator==( const EDIT_POINT& aOther ) const
+ {
+ return m_position == aOther.m_position;
+ }
+
+ ///> Single point size in pixels
+ static const int POINT_SIZE = 10;
+
+private:
+ ///> Position of EDIT_POINT
+ VECTOR2I m_position;
+
+ ///> Constraint for the point, NULL if none
+ boost::shared_ptr<EDIT_CONSTRAINT<EDIT_POINT> > m_constraint;
+};
+
+
+/**
+ * Class EDIT_LINE
+ *
+ * Represents a line connecting two EDIT_POINTs. That allows to move them both by dragging the
+ * EDIT_POINT in the middle. As it uses references to EDIT_POINTs, all coordinates are
+ * automatically synchronized.
+ */
+class EDIT_LINE : public EDIT_POINT
+{
+public:
+ /**
+ * Constructor
+ *
+ * @param aOrigin is the origin of EDIT_LINE.
+ * @param aEnd is the end of EDIT_LINE.
+ */
+ EDIT_LINE( EDIT_POINT& aOrigin, EDIT_POINT& aEnd ) :
+ EDIT_POINT( aOrigin.GetPosition() + ( aEnd.GetPosition() - aOrigin.GetPosition() ) / 2 ),
+ m_origin( aOrigin ), m_end( aEnd )
+ {
+ }
+
+ ///> @copydoc EDIT_POINT::GetPosition()
+ virtual VECTOR2I GetPosition() const
+ {
+ return ( m_origin.GetPosition() + m_end.GetPosition() ) / 2;
+ }
+
+ ///> @copydoc EDIT_POINT::GetPosition()
+ virtual void SetPosition( const VECTOR2I& aPosition )
+ {
+ VECTOR2I difference = aPosition - GetPosition();
+
+ m_origin.SetPosition( m_origin.GetPosition() + difference );
+ m_end.SetPosition( m_end.GetPosition() + difference );
+ }
+
+ ///> @copydoc EDIT_POINT::ApplyConstraint()
+ virtual void ApplyConstraint()
+ {
+ m_origin.ApplyConstraint();
+ m_end.ApplyConstraint();
+
+ if( m_constraint )
+ m_constraint->Apply();
+ }
+
+ /**
+ * Function SetConstraint()
+ *
+ * Sets a constraint for and EDIT_POINT.
+ * @param aConstraint is the constraint to be set.
+ */
+ void SetConstraint( EDIT_CONSTRAINT<EDIT_LINE>* aConstraint )
+ {
+ m_constraint.reset( aConstraint );
+ }
+
+ /**
+ * Function GetConstraint()
+ *
+ * Returns the constraint imposed on an EDIT_POINT. If there are no constraints, NULL is
+ * returned.
+ */
+ EDIT_CONSTRAINT<EDIT_LINE>* GetConstraint() const
+ {
+ return m_constraint.get();
+ }
+
+ /**
+ * Function GetOrigin()
+ *
+ * Returns the origin EDIT_POINT.
+ */
+ EDIT_POINT& GetOrigin()
+ {
+ return m_origin;
+ }
+
+ const EDIT_POINT& GetOrigin() const
+ {
+ return m_origin;
+ }
+
+ /**
+ * Function GetEnd()
+ *
+ * Returns the end EDIT_POINT.
+ */
+ EDIT_POINT& GetEnd()
+ {
+ return m_end;
+ }
+
+ const EDIT_POINT& GetEnd() const
+ {
+ return m_end;
+ }
+
+ bool operator==( const EDIT_POINT& aOther ) const
+ {
+ return GetPosition() == aOther.GetPosition();
+ }
+
+ bool operator==( const EDIT_LINE& aOther ) const
+ {
+ return m_origin == aOther.m_origin && m_end == aOther.m_end;
+ }
+
+private:
+ EDIT_POINT& m_origin; ///< Origin point for a line
+ EDIT_POINT& m_end; ///< End point for a line
+
+ ///> Constraint for the point, NULL if none
+ boost::shared_ptr<EDIT_CONSTRAINT<EDIT_LINE> > m_constraint;
+};
+
+
+/**
+ * Class EDIT_POINTS
+ *
+ * EDIT_POINTS is a VIEW_ITEM that manages EDIT_POINTs and EDIT_LINEs and draws them.
+ */
+class EDIT_POINTS : public EDA_ITEM
+{
+public:
+ /**
+ * Constructor.
+ *
+ * @param aParent is the item to which EDIT_POINTs are related.
+ */
+ EDIT_POINTS( EDA_ITEM* aParent );
+
+ /**
+ * Function FindPoint()
+ *
+ * Returns a point that is at given coordinates or NULL if there is no such point.
+ * @param aLocation is the location for searched point.
+ */
+ EDIT_POINT* FindPoint( const VECTOR2I& aLocation );
+
+ /**
+ * Function GetParent()
+ *
+ * Returns parent of the EDIT_POINTS.
+ */
+ EDA_ITEM* GetParent() const
+ {
+ return m_parent;
+ }
+
+ /**
+ * Function AddPoint()
+ *
+ * Adds an EDIT_POINT.
+ * @param aPoint is the new point.
+ */
+ void AddPoint( const EDIT_POINT& aPoint )
+ {
+ m_points.push_back( aPoint );
+ }
+
+ /**
+ * Function AddPoint()
+ *
+ * Adds an EDIT_POINT.
+ * @param aPoint are coordinates of the new point.
+ */
+ void AddPoint( const VECTOR2I& aPoint )
+ {
+ AddPoint( EDIT_POINT( aPoint ) );
+ }
+
+ /**
+ * Function AddLine()
+ *
+ * Adds an EDIT_LINE.
+ * @param aLine is the new line.
+ */
+ void AddLine( const EDIT_LINE& aLine )
+ {
+ m_lines.push_back( aLine );
+ }
+
+ /**
+ * Function AddLine()
+ *
+ * Adds an EDIT_LINE.
+ * @param aOrigin is the origin for a new line.
+ * @param aEnd is the end for a new line.
+ */
+ void AddLine( EDIT_POINT& aOrigin, EDIT_POINT& aEnd )
+ {
+ m_lines.push_back( EDIT_LINE( aOrigin, aEnd ) );
+ }
+
+ /**
+ * Function AddBreak()
+ *
+ * Adds a break, indicating the end of a contour.
+ */
+ void AddBreak()
+ {
+ assert( m_points.size() > 0 );
+ m_contours.push_back( m_points.size() - 1 );
+ }
+
+ /**
+ * Function GetContourStartIdx()
+ *
+ * Returns index of the contour origin for a point with given index.
+ * @param aPointIdx is the index of point for which the contour origin is searched.
+ * @return Index of the contour origin point.
+ */
+ int GetContourStartIdx( int aPointIdx ) const;
+
+ /**
+ * Function GetContourEndIdx()
+ *
+ * Returns index of the contour finish for a point with given index.
+ * @param aPointIdx is the index of point for which the contour finish is searched.
+ * @return Index of the contour finish point.
+ */
+ int GetContourEndIdx( int aPointIdx ) const;
+
+ /**
+ * Function IsContourStart()
+ *
+ * Checks is a point with given index is a contour origin.
+ * @param aPointIdx is the index of the point to be checked.
+ * @return True if the point is an origin of a contour.
+ */
+ bool IsContourStart( int aPointIdx ) const;
+
+ /**
+ * Function IsContourEnd()
+ *
+ * Checks is a point with given index is a contour finish.
+ * @param aPointIdx is the index of the point to be checked.
+ * @return True if the point is a finish of a contour.
+ */
+ bool IsContourEnd( int aPointIdx ) const;
+
+ /**
+ * Function Previous()
+ *
+ * Returns the point that is after the given point in the list.
+ * @param aPoint is the point that is supposed to be preceding the searched point.
+ * @param aTraverseContours decides if in case of breaks should we return to the origin
+ * of contour or continue with the next contour.
+ * @return The point following aPoint in the list. If aPoint is the first in
+ * the list, the last from the list will be returned. If there are no points at all, NULL
+ * is returned.
+ */
+ EDIT_POINT* Previous( const EDIT_POINT& aPoint, bool aTraverseContours = true );
+
+ EDIT_LINE* Previous( const EDIT_LINE& aLine );
+
+ /**
+ * Function Next()
+ *
+ * Returns the point that is before the given point in the list.
+ * @param aPoint is the point that is supposed to be following the searched point.
+ * @param aTraverseContours decides if in case of breaks should we return to the origin
+ * of contour or continue with the next contour.
+ * @return The point preceding aPoint in the list. If aPoint is the last in
+ * the list, the first point from the list will be returned. If there are no points at all,
+ * NULL is returned.
+ */
+ EDIT_POINT* Next( const EDIT_POINT& aPoint, bool aTraverseContours = true );
+
+ EDIT_LINE* Next( const EDIT_LINE& aLine );
+
+ EDIT_POINT& Point( unsigned int aIndex )
+ {
+ return m_points[aIndex];
+ }
+
+ const EDIT_POINT& Point( unsigned int aIndex ) const
+ {
+ return m_points[aIndex];
+ }
+
+ EDIT_LINE& Line( unsigned int aIndex )
+ {
+ return m_lines[aIndex];
+ }
+
+ const EDIT_LINE& Line( unsigned int aIndex ) const
+ {
+ return m_lines[aIndex];
+ }
+
+ /**
+ * Function PointsSize()
+ *
+ * Returns number of stored EDIT_POINTs.
+ */
+ unsigned int PointsSize() const
+ {
+ return m_points.size();
+ }
+
+ /**
+ * Function LinesSize()
+ *
+ * Returns number of stored EDIT_LINEs.
+ */
+ unsigned int LinesSize() const
+ {
+ return m_lines.size();
+ }
+
+ ///> @copydoc VIEW_ITEM::ViewBBox()
+ virtual const BOX2I ViewBBox() const
+ {
+ return m_parent->ViewBBox();
+ }
+
+ ///> @copydoc VIEW_ITEM::ViewDraw()
+ virtual void ViewDraw( int aLayer, KIGFX::GAL* aGal ) const;
+
+ ///> @copydoc VIEW_ITEM::ViewGetLayers()
+ virtual void ViewGetLayers( int aLayers[], int& aCount ) const
+ {
+ aCount = 1;
+ aLayers[0] = ITEM_GAL_LAYER( GP_OVERLAY );
+ }
+
+#if defined(DEBUG)
+ void Show( int x, std::ostream& st ) const
+ {
+ }
+#endif
+
+ /** Get class name
+ * @return string "EDIT_POINTS"
+ */
+ virtual wxString GetClass() const
+ {
+ return wxT( "EDIT_POINTS" );
+ }
+
+private:
+ EDA_ITEM* m_parent; ///< Parent of the EDIT_POINTs
+ std::deque<EDIT_POINT> m_points; ///< EDIT_POINTs for modifying m_parent
+ std::deque<EDIT_LINE> m_lines; ///< EDIT_LINEs for modifying m_parent
+ std::list<int> m_contours; ///< Indices of end contour points
+};
+
+#endif /* EDIT_POINTS_H_ */
diff --git a/pcbnew/tools/edit_tool.cpp b/pcbnew/tools/edit_tool.cpp
new file mode 100644
index 0000000..81d547b
--- /dev/null
+++ b/pcbnew/tools/edit_tool.cpp
@@ -0,0 +1,1099 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2013-2015 CERN
+ * @author Maciej Suminski <maciej.suminski@cern.ch>
+ * @author Tomasz Wlostowski <tomasz.wlostowski@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 <limits>
+
+#include <class_board.h>
+#include <class_module.h>
+#include <class_edge_mod.h>
+#include <class_zone.h>
+#include <wxPcbStruct.h>
+#include <kiway.h>
+#include <class_draw_panel_gal.h>
+#include <module_editor_frame.h>
+
+#include <tool/tool_manager.h>
+#include <view/view_controls.h>
+#include <gal/graphics_abstraction_layer.h>
+#include <ratsnest_data.h>
+#include <confirm.h>
+
+#include <cassert>
+#include <boost/foreach.hpp>
+#include <boost/bind.hpp>
+
+#include "common_actions.h"
+#include "selection_tool.h"
+#include "edit_tool.h"
+#include "grid_helper.h"
+
+#include <router/router_tool.h>
+
+#include <dialogs/dialog_create_array.h>
+#include <dialogs/dialog_move_exact.h>
+#include <dialogs/dialog_track_via_properties.h>
+
+EDIT_TOOL::EDIT_TOOL() :
+ TOOL_INTERACTIVE( "pcbnew.InteractiveEdit" ), m_selectionTool( NULL ),
+ m_dragging( false ), m_editModules( false ), m_undoInhibit( 0 ),
+ m_updateFlag( KIGFX::VIEW_ITEM::NONE )
+{
+}
+
+
+void EDIT_TOOL::Reset( RESET_REASON aReason )
+{
+ m_dragging = false;
+ m_updateFlag = KIGFX::VIEW_ITEM::NONE;
+}
+
+
+bool EDIT_TOOL::Init()
+{
+ // Find the selection tool, so they can cooperate
+ m_selectionTool = static_cast<SELECTION_TOOL*>( m_toolMgr->FindTool( "pcbnew.InteractiveSelection" ) );
+
+ if( !m_selectionTool )
+ {
+ DisplayError( NULL, wxT( "pcbnew.InteractiveSelection tool is not available" ) );
+ return false;
+ }
+
+ // Vector storing track & via types, used for specifying 'Properties' menu entry condition
+ m_tracksViasType.push_back( PCB_TRACE_T );
+ m_tracksViasType.push_back( PCB_VIA_T );
+
+ // Add context menu entries that are displayed when selection tool is active
+ m_selectionTool->GetMenu().AddItem( COMMON_ACTIONS::editActivate, SELECTION_CONDITIONS::NotEmpty );
+ m_selectionTool->GetMenu().AddItem( COMMON_ACTIONS::rotate, SELECTION_CONDITIONS::NotEmpty );
+ m_selectionTool->GetMenu().AddItem( COMMON_ACTIONS::flip, SELECTION_CONDITIONS::NotEmpty );
+ m_selectionTool->GetMenu().AddItem( COMMON_ACTIONS::remove, SELECTION_CONDITIONS::NotEmpty );
+ m_selectionTool->GetMenu().AddItem( COMMON_ACTIONS::properties, SELECTION_CONDITIONS::Count( 1 )
+ || SELECTION_CONDITIONS::OnlyTypes( m_tracksViasType ) );
+ m_selectionTool->GetMenu().AddItem( COMMON_ACTIONS::moveExact, SELECTION_CONDITIONS::NotEmpty );
+ m_selectionTool->GetMenu().AddItem( COMMON_ACTIONS::duplicate, SELECTION_CONDITIONS::NotEmpty );
+ m_selectionTool->GetMenu().AddItem( COMMON_ACTIONS::createArray, SELECTION_CONDITIONS::NotEmpty );
+
+ // Footprint actions
+ m_selectionTool->GetMenu().AddItem( COMMON_ACTIONS::editFootprintInFpEditor,
+ SELECTION_CONDITIONS::OnlyType( PCB_MODULE_T ) &&
+ SELECTION_CONDITIONS::Count( 1 ) );
+
+ m_offset.x = 0;
+ m_offset.y = 0;
+
+ return true;
+}
+
+
+bool EDIT_TOOL::invokeInlineRouter()
+{
+ TRACK* track = uniqueSelected<TRACK>();
+ VIA* via = uniqueSelected<VIA>();
+
+ if( isUndoInhibited() )
+ return false;
+
+ if( track || via )
+ {
+ ROUTER_TOOL* theRouter = static_cast<ROUTER_TOOL*>( m_toolMgr->FindTool( "pcbnew.InteractiveRouter" ) );
+ assert( theRouter );
+
+ if( !theRouter->PNSSettings().InlineDragEnabled() )
+ return false;
+
+ TOOL_EVENT dummy;
+ m_selectionTool->ClearSelection( dummy );
+ m_toolMgr->RunAction( COMMON_ACTIONS::routerInlineDrag, true, track ? track : via );
+ return true;
+ }
+
+ return false;
+}
+
+
+int EDIT_TOOL::Main( const TOOL_EVENT& aEvent )
+{
+ KIGFX::VIEW_CONTROLS* controls = getViewControls();
+ PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
+
+ VECTOR2I originalCursorPos = controls->GetCursorPosition();
+ const SELECTION& selection = m_selectionTool->GetSelection();
+
+ // Shall the selection be cleared at the end?
+ bool unselect = selection.Empty();
+
+ // Be sure that there is at least one item that we can modify. If nothing was selected before,
+ // try looking for the stuff under mouse cursor (i.e. Kicad old-style hover selection)
+ if( !hoverSelection( selection ) )
+ return 0;
+
+ Activate();
+
+ m_dragging = false; // Are selected items being dragged?
+ bool restore = false; // Should items' state be restored when finishing the tool?
+ bool lockOverride = false;
+
+ // By default, modified items need to update their geometry
+ m_updateFlag = KIGFX::VIEW_ITEM::GEOMETRY;
+
+ controls->ShowCursor( true );
+
+ // cumulative translation
+ wxPoint totalMovement( 0, 0 );
+
+ GRID_HELPER grid( editFrame );
+ OPT_TOOL_EVENT evt = aEvent;
+
+ // Main loop: keep receiving events
+ do
+ {
+ if( evt->IsCancel() )
+ {
+ restore = true; // Cancelling the tool means that items have to be restored
+ break; // Finish
+ }
+
+ else if( evt->Action() == TA_UNDO_REDO )
+ {
+ unselect = true;
+ break;
+ }
+
+ else if( evt->IsAction( &COMMON_ACTIONS::editActivate )
+ || evt->IsMotion() || evt->IsDrag( BUT_LEFT ) )
+ {
+ BOARD_ITEM* item = selection.Item<BOARD_ITEM>( 0 );
+
+ if( m_dragging && evt->Category() == TC_MOUSE )
+ {
+ m_cursor = grid.BestSnapAnchor( evt->Position(), item );
+ controls->ForceCursorPosition( true, m_cursor );
+
+ wxPoint movement = wxPoint( m_cursor.x, m_cursor.y ) - item->GetPosition();
+ totalMovement += movement;
+
+ // Drag items to the current cursor position
+ for( unsigned int i = 0; i < selection.items.GetCount(); ++i )
+ selection.Item<BOARD_ITEM>( i )->Move( movement + m_offset );
+
+ updateRatsnest( true );
+ }
+ else if( !m_dragging ) // Prepare to start dragging
+ {
+ if( !invokeInlineRouter() )
+ {
+ m_selectionTool->SanitizeSelection();
+
+ if( selection.Empty() )
+ break;
+
+ // deal with locked items (override lock or abort the operation)
+ SELECTION_LOCK_FLAGS lockFlags = m_selectionTool->CheckLock();
+
+ if( lockFlags == SELECTION_LOCKED )
+ break;
+ else if( lockFlags == SELECTION_LOCK_OVERRIDE )
+ lockOverride = true;
+
+ // Save items, so changes can be undone
+ if( !isUndoInhibited() )
+ {
+ editFrame->OnModify();
+ editFrame->SaveCopyInUndoList( selection.items, UR_CHANGED );
+ }
+
+ m_cursor = controls->GetCursorPosition();
+
+ if( selection.Size() == 1 )
+ {
+ // Set the current cursor position to the first dragged item origin, so the
+ // movement vector could be computed later
+ m_cursor = grid.BestDragOrigin( originalCursorPos, item );
+ grid.SetAuxAxes( true, m_cursor );
+ }
+ else
+ {
+ m_cursor = grid.Align( m_cursor );
+ }
+
+ controls->ForceCursorPosition( true, m_cursor );
+ controls->WarpCursor( m_cursor, true );
+
+ VECTOR2I o = VECTOR2I( item->GetPosition() );
+ m_offset.x = o.x - m_cursor.x;
+ m_offset.y = o.y - m_cursor.y;
+
+ controls->SetAutoPan( true );
+ m_dragging = true;
+ incUndoInhibit();
+ }
+ }
+
+ selection.group->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+ m_toolMgr->RunAction( COMMON_ACTIONS::pointEditorUpdate, true );
+ }
+
+ // Dispatch TOOL_ACTIONs
+ else if( evt->Category() == TC_COMMAND )
+ {
+ if( evt->IsAction( &COMMON_ACTIONS::rotate ) )
+ {
+ Rotate( aEvent );
+ }
+ else if( evt->IsAction( &COMMON_ACTIONS::flip ) )
+ {
+ Flip( aEvent );
+
+ // Flip causes change of layers
+ enableUpdateFlag( KIGFX::VIEW_ITEM::LAYERS );
+ }
+ else if( evt->IsAction( &COMMON_ACTIONS::remove ) )
+ {
+ Remove( aEvent );
+
+ break; // exit the loop, as there is no further processing for removed items
+ }
+ else if( evt->IsAction( &COMMON_ACTIONS::duplicate ) )
+ {
+ // On duplicate, stop moving this item
+ // The duplicate tool should then select the new item and start
+ // a new move procedure
+ break;
+ }
+ else if( evt->IsAction( &COMMON_ACTIONS::moveExact ) )
+ {
+ // Can't do this, because the selection will then contain
+ // stale pointers and it will all go horribly wrong...
+ //editFrame->RestoreCopyFromUndoList( dummy );
+ //
+ // So, instead, reset the position manually
+ for( unsigned int i = 0; i < selection.items.GetCount(); ++i )
+ {
+ BOARD_ITEM* item = selection.Item<BOARD_ITEM>( i );
+ item->SetPosition( item->GetPosition() - totalMovement );
+
+ // And what about flipping and rotation?
+ // for now, they won't be undone, but maybe that is how
+ // it should be, so you can flip and move exact in the
+ // same action?
+ }
+
+ // This causes a double event, so we will get the dialogue
+ // correctly, somehow - why does Rotate not?
+ //MoveExact( aEvent );
+ break; // exit the loop - we move exactly, so we have finished moving
+ }
+ }
+
+ else if( evt->IsMouseUp( BUT_LEFT ) || evt->IsClick( BUT_LEFT ) )
+ {
+ if( !lockOverride )
+ break; // Finish
+
+ lockOverride = false;
+ }
+ } while( evt = Wait() );
+
+ if( m_dragging )
+ decUndoInhibit();
+
+ m_dragging = false;
+ m_offset.x = 0;
+ m_offset.y = 0;
+
+ if( restore )
+ {
+ // Modifications have to be rollbacked, so restore the previous state of items
+ wxCommandEvent dummy;
+ editFrame->RestoreCopyFromUndoList( dummy );
+ }
+ else
+ {
+ // Changes are applied, so update the items
+ selection.group->ItemsViewUpdate( m_updateFlag );
+ }
+
+ if( unselect )
+ m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true );
+
+ RN_DATA* ratsnest = getModel<BOARD>()->GetRatsnest();
+ ratsnest->ClearSimple();
+ ratsnest->Recalculate();
+
+ controls->ShowCursor( false );
+ controls->SetAutoPan( false );
+
+ return 0;
+}
+
+
+int EDIT_TOOL::Properties( const TOOL_EVENT& aEvent )
+{
+ const SELECTION& selection = m_selectionTool->GetSelection();
+ PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
+
+ // Shall the selection be cleared at the end?
+ bool unselect = selection.Empty();
+
+ if( !hoverSelection( selection, false ) )
+ return 0;
+
+ // Tracks & vias are treated in a special way:
+ if( ( SELECTION_CONDITIONS::OnlyTypes( m_tracksViasType ) )( selection ) )
+ {
+ DIALOG_TRACK_VIA_PROPERTIES dlg( editFrame, selection );
+
+ if( dlg.ShowModal() )
+ {
+ RN_DATA* ratsnest = getModel<BOARD>()->GetRatsnest();
+
+ editFrame->OnModify();
+ editFrame->SaveCopyInUndoList( selection.items, UR_CHANGED );
+ dlg.Apply();
+
+ selection.ForAll<KIGFX::VIEW_ITEM>( boost::bind( &KIGFX::VIEW_ITEM::ViewUpdate, _1,
+ KIGFX::VIEW_ITEM::ALL ) );
+ selection.ForAll<BOARD_ITEM>( boost::bind( &RN_DATA::Update, ratsnest, _1 ) );
+ ratsnest->Recalculate();
+ }
+ }
+ else if( selection.Size() == 1 ) // Properties are displayed when there is only one item selected
+ {
+ // Display properties dialog
+ BOARD_ITEM* item = selection.Item<BOARD_ITEM>( 0 );
+
+ // Store the head of the undo list to compare if anything has changed
+ std::vector<PICKED_ITEMS_LIST*>& undoList = editFrame->GetScreen()->m_UndoList.m_CommandsList;
+
+ // Some of properties dialogs alter pointers, so we should deselect them
+ m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true );
+ STATUS_FLAGS flags = item->GetFlags();
+ item->ClearFlags();
+
+ // It is necessary to determine if anything has changed, so store the current undo save point
+ PICKED_ITEMS_LIST* undoSavePoint = undoList.empty() ? NULL : undoList.back();
+
+ // Display properties dialog provided by the legacy canvas frame
+ editFrame->OnEditItemRequest( NULL, item );
+
+ if( !undoList.empty() && undoList.back() != undoSavePoint ) // Undo buffer has changed
+ {
+ // Process changes stored after undoSavePoint
+ processUndoBuffer( undoSavePoint );
+
+ // Update the modified item
+ item->ViewUpdate();
+ RN_DATA* ratsnest = getModel<BOARD>()->GetRatsnest();
+ ratsnest->Recalculate();
+
+ // TODO OBSERVER! I miss you so much..
+ m_toolMgr->RunAction( COMMON_ACTIONS::pointEditorUpdate, true );
+ }
+
+ item->SetFlags( flags );
+ }
+
+ if( unselect )
+ m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true );
+
+ return 0;
+}
+
+
+int EDIT_TOOL::Rotate( const TOOL_EVENT& aEvent )
+{
+ const SELECTION& selection = m_selectionTool->GetSelection();
+ PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
+
+ // Shall the selection be cleared at the end?
+ bool unselect = selection.Empty();
+
+ if( !hoverSelection( selection ) || m_selectionTool->CheckLock() == SELECTION_LOCKED )
+ return 0;
+
+ wxPoint rotatePoint = getModificationPoint( selection );
+
+ // If it is being dragged, then it is already saved with UR_CHANGED flag
+ if( !isUndoInhibited() )
+ {
+ editFrame->OnModify();
+ editFrame->SaveCopyInUndoList( selection.items, UR_ROTATED, rotatePoint );
+ }
+
+ for( unsigned int i = 0; i < selection.items.GetCount(); ++i )
+ {
+ BOARD_ITEM* item = selection.Item<BOARD_ITEM>( i );
+
+ item->Rotate( rotatePoint, editFrame->GetRotationAngle() );
+
+ if( !m_dragging )
+ item->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+ }
+
+ updateRatsnest( m_dragging );
+
+ // Update dragging offset (distance between cursor and the first dragged item)
+ m_offset = static_cast<BOARD_ITEM*>( selection.items.GetPickedItem( 0 ) )->GetPosition() -
+ rotatePoint;
+
+ if( m_dragging )
+ selection.group->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+ else
+ getModel<BOARD>()->GetRatsnest()->Recalculate();
+
+ if( unselect )
+ m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true );
+
+ m_toolMgr->RunAction( COMMON_ACTIONS::pointEditorUpdate, true );
+
+ return 0;
+}
+
+
+int EDIT_TOOL::Flip( const TOOL_EVENT& aEvent )
+{
+ const SELECTION& selection = m_selectionTool->GetSelection();
+ PCB_BASE_FRAME* editFrame = getEditFrame<PCB_BASE_FRAME>();
+
+ // Shall the selection be cleared at the end?
+ bool unselect = selection.Empty();
+
+ if( !hoverSelection( selection ) || m_selectionTool->CheckLock() == SELECTION_LOCKED )
+ return 0;
+
+ wxPoint flipPoint = getModificationPoint( selection );
+
+ if( !isUndoInhibited() ) // If it is being dragged, then it is already saved with UR_CHANGED flag
+ {
+ editFrame->OnModify();
+ editFrame->SaveCopyInUndoList( selection.items, UR_FLIPPED, flipPoint );
+ }
+
+ for( unsigned int i = 0; i < selection.items.GetCount(); ++i )
+ {
+ BOARD_ITEM* item = selection.Item<BOARD_ITEM>( i );
+
+ item->Flip( flipPoint );
+
+ if( !m_dragging )
+ item->ViewUpdate( KIGFX::VIEW_ITEM::LAYERS );
+ }
+
+ updateRatsnest( m_dragging );
+
+ // Update dragging offset (distance between cursor and the first dragged item)
+ m_offset = static_cast<BOARD_ITEM*>( selection.items.GetPickedItem( 0 ) )->GetPosition() -
+ flipPoint;
+
+ if( m_dragging )
+ selection.group->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+ else
+ getModel<BOARD>()->GetRatsnest()->Recalculate();
+
+ if( unselect )
+ m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true );
+
+ m_toolMgr->RunAction( COMMON_ACTIONS::pointEditorUpdate, true );
+
+ return 0;
+}
+
+
+int EDIT_TOOL::Remove( const TOOL_EVENT& aEvent )
+{
+ const SELECTION& selection = m_selectionTool->GetSelection();
+
+ if( !hoverSelection( selection ) || m_selectionTool->CheckLock() == SELECTION_LOCKED )
+ return 0;
+
+ // Get a copy of the selected items set
+ PICKED_ITEMS_LIST selectedItems = selection.items;
+ PCB_BASE_FRAME* editFrame = getEditFrame<PCB_BASE_FRAME>();
+
+ // As we are about to remove items, they have to be removed from the selection first
+ m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true );
+
+ // Save them
+ for( unsigned int i = 0; i < selectedItems.GetCount(); ++i )
+ selectedItems.SetPickedItemStatus( UR_DELETED, i );
+
+ editFrame->OnModify();
+ editFrame->SaveCopyInUndoList( selectedItems, UR_DELETED );
+
+ // And now remove
+ for( unsigned int i = 0; i < selectedItems.GetCount(); ++i )
+ remove( static_cast<BOARD_ITEM*>( selectedItems.GetPickedItem( i ) ) );
+
+ getModel<BOARD>()->GetRatsnest()->Recalculate();
+
+ return 0;
+}
+
+
+void EDIT_TOOL::remove( BOARD_ITEM* aItem )
+{
+ BOARD* board = getModel<BOARD>();
+
+ switch( aItem->Type() )
+ {
+ case PCB_MODULE_T:
+ {
+ MODULE* module = static_cast<MODULE*>( aItem );
+ module->ClearFlags();
+ module->RunOnChildren( boost::bind( &KIGFX::VIEW::Remove, getView(), _1 ) );
+
+ // Module itself is deleted after the switch scope is finished
+ // list of pads is rebuild by BOARD::BuildListOfNets()
+
+ // Clear flags to indicate, that the ratsnest, list of nets & pads are not valid anymore
+ board->m_Status_Pcb = 0;
+ }
+ break;
+
+ // Default removal procedure
+ case PCB_MODULE_TEXT_T:
+ {
+ TEXTE_MODULE* text = static_cast<TEXTE_MODULE*>( aItem );
+
+ switch( text->GetType() )
+ {
+ case TEXTE_MODULE::TEXT_is_REFERENCE:
+ DisplayError( getEditFrame<PCB_BASE_FRAME>(), _( "Cannot delete component reference." ) );
+ return;
+
+ case TEXTE_MODULE::TEXT_is_VALUE:
+ DisplayError( getEditFrame<PCB_BASE_FRAME>(), _( "Cannot delete component value." ) );
+ return;
+
+ case TEXTE_MODULE::TEXT_is_DIVERS: // suppress warnings
+ break;
+ }
+
+ if( m_editModules )
+ {
+ MODULE* module = static_cast<MODULE*>( aItem->GetParent() );
+ module->SetLastEditTime();
+ board->m_Status_Pcb = 0; // it is done in the legacy view
+ aItem->DeleteStructure();
+ }
+
+ return;
+ }
+
+ case PCB_PAD_T:
+ case PCB_MODULE_EDGE_T:
+ {
+ MODULE* module = static_cast<MODULE*>( aItem->GetParent() );
+ module->SetLastEditTime();
+
+ board->m_Status_Pcb = 0; // it is done in the legacy view
+
+
+ if( !m_editModules )
+ {
+ getView()->Remove( aItem );
+ board->Remove( aItem );
+ }
+
+ aItem->DeleteStructure();
+
+ return;
+ }
+
+ case PCB_LINE_T: // a segment not on copper layers
+ case PCB_TEXT_T: // a text on a layer
+ case PCB_TRACE_T: // a track segment (segment on a copper layer)
+ case PCB_VIA_T: // a via (like track segment on a copper layer)
+ case PCB_DIMENSION_T: // a dimension (graphic item)
+ case PCB_TARGET_T: // a target (graphic item)
+ case PCB_MARKER_T: // a marker used to show something
+ case PCB_ZONE_T: // SEG_ZONE items are now deprecated
+ case PCB_ZONE_AREA_T:
+ break;
+
+ default: // other types do not need to (or should not) be handled
+ assert( false );
+ return;
+ }
+
+ getView()->Remove( aItem );
+ board->Remove( aItem );
+}
+
+
+int EDIT_TOOL::MoveExact( const TOOL_EVENT& aEvent )
+{
+ const SELECTION& selection = m_selectionTool->GetSelection();
+
+ // Shall the selection be cleared at the end?
+ bool unselect = selection.Empty();
+
+ if( !hoverSelection( selection ) || m_selectionTool->CheckLock() == SELECTION_LOCKED )
+ return 0;
+
+ wxPoint translation;
+ double rotation = 0;
+
+ PCB_BASE_FRAME* editFrame = getEditFrame<PCB_BASE_FRAME>();
+
+ DIALOG_MOVE_EXACT dialog( editFrame, translation, rotation );
+ int ret = dialog.ShowModal();
+
+ if( ret == wxID_OK )
+ {
+ if( !isUndoInhibited() )
+ {
+ editFrame->OnModify();
+ // Record an action of move and rotate
+ editFrame->SaveCopyInUndoList( selection.items, UR_CHANGED );
+ }
+
+ VECTOR2I rp = selection.GetCenter();
+ wxPoint rotPoint( rp.x, rp.y );
+
+ for( unsigned int i = 0; i < selection.items.GetCount(); ++i )
+ {
+ BOARD_ITEM* item = selection.Item<BOARD_ITEM>( i );
+
+ item->Move( translation );
+ item->Rotate( rotPoint, rotation );
+
+ if( !m_dragging )
+ item->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+ }
+
+ updateRatsnest( m_dragging );
+
+ if( m_dragging )
+ selection.group->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+ else
+ getModel<BOARD>()->GetRatsnest()->Recalculate();
+
+ if( unselect )
+ m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true );
+
+ m_toolMgr->RunAction( COMMON_ACTIONS::pointEditorUpdate, true );
+ }
+
+ return 0;
+}
+
+
+int EDIT_TOOL::Duplicate( const TOOL_EVENT& aEvent )
+{
+ // Note: original items are no more modified.
+
+ bool increment = aEvent.IsAction( &COMMON_ACTIONS::duplicateIncrement );
+
+ // first, check if we have a selection, or try to get one
+ SELECTION_TOOL* selTool = m_toolMgr->GetTool<SELECTION_TOOL>();
+ const SELECTION& selection = selTool->GetSelection();
+
+ // Be sure that there is at least one item that we can modify
+ if( !hoverSelection( selection ) )
+ return 0;
+
+ // we have a selection to work on now, so start the tool process
+
+ PCB_BASE_FRAME* editFrame = getEditFrame<PCB_BASE_FRAME>();
+ editFrame->OnModify();
+
+ // prevent other tools making undo points while the duplicate is going on
+ // so that if you cancel, you don't get a duplicate object hiding over
+ // the original
+ incUndoInhibit();
+
+ if( m_editModules )
+ editFrame->SaveCopyInUndoList( editFrame->GetBoard()->m_Modules, UR_MODEDIT );
+
+ std::vector<BOARD_ITEM*> old_items;
+
+ for( int i = 0; i < selection.Size(); ++i )
+ {
+ BOARD_ITEM* item = selection.Item<BOARD_ITEM>( i );
+
+ if( item )
+ old_items.push_back( item );
+ }
+
+ for( unsigned i = 0; i < old_items.size(); ++i )
+ {
+ BOARD_ITEM* item = old_items[i];
+
+ // Unselect the item, so we won't pick it up again
+ // Do this first, so a single-item duplicate will correctly call
+ // SetCurItem and show the item properties
+ m_toolMgr->RunAction( COMMON_ACTIONS::unselectItem, true, item );
+
+ BOARD_ITEM* new_item = NULL;
+
+ if( m_editModules )
+ new_item = editFrame->GetBoard()->m_Modules->DuplicateAndAddItem( item, increment );
+ else
+ {
+#if 0
+ // @TODO: see if we allow zone duplication here
+ // Duplicate zones is especially tricky (overlaping zones must be merged)
+ // so zones are not duplicated
+ if( item->Type() != PCB_ZONE_AREA_T )
+#endif
+ new_item = editFrame->GetBoard()->DuplicateAndAddItem( item, increment );
+ }
+
+ if( new_item )
+ {
+ if( new_item->Type() == PCB_MODULE_T )
+ {
+ static_cast<MODULE*>( new_item )->RunOnChildren( boost::bind( &KIGFX::VIEW::Add,
+ getView(), _1 ) );
+ }
+
+ editFrame->GetGalCanvas()->GetView()->Add( new_item );
+
+ // Select the new item, so we can pick it up
+ m_toolMgr->RunAction( COMMON_ACTIONS::selectItem, true, new_item );
+ }
+ }
+
+ // record the new items as added
+ if( !m_editModules && !selection.Empty() )
+ {
+ editFrame->SaveCopyInUndoList( selection.items, UR_NEW );
+
+ editFrame->DisplayToolMsg( wxString::Format( _( "Duplicated %d item(s)" ),
+ (int) old_items.size() ) );
+
+ // If items were duplicated, pick them up
+ // this works well for "dropping" copies around
+ TOOL_EVENT evt = COMMON_ACTIONS::editActivate.MakeEvent();
+ Main( evt );
+ }
+
+ // and re-enable undos
+ decUndoInhibit();
+
+ return 0;
+}
+
+
+int EDIT_TOOL::CreateArray( const TOOL_EVENT& aEvent )
+{
+ // first, check if we have a selection, or try to get one
+ SELECTION_TOOL* selTool = m_toolMgr->GetTool<SELECTION_TOOL>();
+ const SELECTION& selection = selTool->GetSelection();
+
+ // Be sure that there is at least one item that we can modify
+ if( !hoverSelection( selection ) )
+ return 0;
+
+ // we have a selection to work on now, so start the tool process
+
+ PCB_BASE_FRAME* editFrame = getEditFrame<PCB_BASE_FRAME>();
+ editFrame->OnModify();
+
+ if( m_editModules )
+ {
+ // Module editors do their undo point upfront for the whole module
+ editFrame->SaveCopyInUndoList( editFrame->GetBoard()->m_Modules, UR_MODEDIT );
+ }
+
+ DIALOG_CREATE_ARRAY::ARRAY_OPTIONS* array_opts = NULL;
+
+ VECTOR2I rp = selection.GetCenter();
+ const wxPoint rotPoint( rp.x, rp.y );
+
+ DIALOG_CREATE_ARRAY dialog( editFrame, rotPoint, &array_opts );
+ int ret = dialog.ShowModal();
+
+ if( ret == wxID_OK && array_opts != NULL )
+ {
+ PICKED_ITEMS_LIST newItemList;
+
+ for( int i = 0; i < selection.Size(); ++i )
+ {
+ BOARD_ITEM* item = selection.Item<BOARD_ITEM>( i );
+
+ if( !item )
+ continue;
+
+ // iterate across the array, laying out the item at the
+ // correct position
+ const unsigned nPoints = array_opts->GetArraySize();
+
+ // The first item in list is the original item. We do not modify it
+ for( unsigned ptN = 1; ptN < nPoints; ++ptN )
+ {
+ BOARD_ITEM* newItem = NULL;
+
+ // Some items cannot be duplicated
+ // i.e. the ref and value fields of a footprint or zones
+ // therefore newItem can be null
+
+ #define INCREMENT_REF false
+ #define INCREMENT_PADNUMBER true
+
+ if( m_editModules )
+ newItem = editFrame->GetBoard()->m_Modules->DuplicateAndAddItem(
+ item, INCREMENT_PADNUMBER );
+ else
+ {
+#if 0
+ // @TODO: see if we allow zone duplication here
+ // Duplicate zones is especially tricky (overlaping zones must be merged)
+ // so zones are not duplicated
+ if( item->Type() == PCB_ZONE_AREA_T )
+ newItem = NULL;
+ else
+#endif
+ newItem = editFrame->GetBoard()->DuplicateAndAddItem(
+ item, INCREMENT_REF );
+ // @TODO: we should merge zones. This is a bit tricky, because
+ // the undo command needs saving old area, if it is merged.
+ }
+
+ if( newItem )
+ {
+ array_opts->TransformItem( ptN, newItem, rotPoint );
+
+ m_toolMgr->RunAction( COMMON_ACTIONS::unselectItem, true, newItem );
+
+ newItemList.PushItem( newItem );
+
+ if( newItem->Type() == PCB_MODULE_T)
+ {
+ static_cast<MODULE*>( newItem )->RunOnChildren( boost::bind( &KIGFX::VIEW::Add,
+ getView(), _1 ) );
+ }
+
+ editFrame->GetGalCanvas()->GetView()->Add( newItem );
+ getModel<BOARD>()->GetRatsnest()->Update( newItem );
+ }
+
+ // Only renumbering pads has meaning:
+ if( newItem && array_opts->ShouldRenumberItems() )
+ {
+ if( newItem->Type() == PCB_PAD_T )
+ {
+ const wxString padName = array_opts->GetItemNumber( ptN );
+ static_cast<D_PAD*>( newItem )->SetPadName( padName );
+ }
+ }
+ }
+ }
+
+ if( !m_editModules )
+ {
+ // Add all items as a single undo point for PCB editors
+ editFrame->SaveCopyInUndoList( newItemList, UR_NEW );
+ }
+ }
+
+ getModel<BOARD>()->GetRatsnest()->Recalculate();
+
+ return 0;
+}
+
+
+void EDIT_TOOL::SetTransitions()
+{
+ Go( &EDIT_TOOL::Main, COMMON_ACTIONS::editActivate.MakeEvent() );
+ Go( &EDIT_TOOL::Rotate, COMMON_ACTIONS::rotate.MakeEvent() );
+ Go( &EDIT_TOOL::Flip, COMMON_ACTIONS::flip.MakeEvent() );
+ Go( &EDIT_TOOL::Remove, COMMON_ACTIONS::remove.MakeEvent() );
+ Go( &EDIT_TOOL::Properties, COMMON_ACTIONS::properties.MakeEvent() );
+ Go( &EDIT_TOOL::MoveExact, COMMON_ACTIONS::moveExact.MakeEvent() );
+ Go( &EDIT_TOOL::Duplicate, COMMON_ACTIONS::duplicate.MakeEvent() );
+ Go( &EDIT_TOOL::Duplicate, COMMON_ACTIONS::duplicateIncrement.MakeEvent() );
+ Go( &EDIT_TOOL::CreateArray,COMMON_ACTIONS::createArray.MakeEvent() );
+ Go( &EDIT_TOOL::editFootprintInFpEditor, COMMON_ACTIONS::editFootprintInFpEditor.MakeEvent() );
+}
+
+
+void EDIT_TOOL::updateRatsnest( bool aRedraw )
+{
+ const SELECTION& selection = m_selectionTool->GetSelection();
+ RN_DATA* ratsnest = getModel<BOARD>()->GetRatsnest();
+
+ ratsnest->ClearSimple();
+
+ for( unsigned int i = 0; i < selection.items.GetCount(); ++i )
+ {
+ BOARD_ITEM* item = selection.Item<BOARD_ITEM>( i );
+
+ ratsnest->Update( item );
+
+ if( aRedraw )
+ ratsnest->AddSimple( item );
+ }
+}
+
+
+wxPoint EDIT_TOOL::getModificationPoint( const SELECTION& aSelection )
+{
+ if( aSelection.Size() == 1 )
+ {
+ return aSelection.Item<BOARD_ITEM>( 0 )->GetPosition() - m_offset;
+ }
+ else
+ {
+ // If EDIT_TOOL is not currently active then it means that the cursor position is not
+ // updated, so we have to fetch the latest value
+ if( m_toolMgr->GetCurrentToolId() != m_toolId )
+ m_cursor = getViewControls()->GetCursorPosition();
+
+ return wxPoint( m_cursor.x, m_cursor.y );
+ }
+}
+
+
+bool EDIT_TOOL::hoverSelection( const SELECTION& aSelection, bool aSanitize )
+{
+ if( aSelection.Empty() ) // Try to find an item that could be modified
+ {
+ m_toolMgr->RunAction( COMMON_ACTIONS::selectionCursor, true );
+
+ if( m_selectionTool->CheckLock() == SELECTION_LOCKED )
+ {
+ m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true );
+ return false;
+ }
+ }
+
+ if( aSanitize )
+ m_selectionTool->SanitizeSelection();
+
+ if( aSelection.Empty() ) // TODO is it necessary?
+ m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true );
+
+ return !aSelection.Empty();
+}
+
+void EDIT_TOOL::processUndoBuffer( const PICKED_ITEMS_LIST* aLastChange )
+{
+ PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
+ const std::vector<PICKED_ITEMS_LIST*>& undoList = editFrame->GetScreen()->m_UndoList.m_CommandsList;
+ bool process = false;
+
+ BOOST_FOREACH( const PICKED_ITEMS_LIST* list, undoList )
+ {
+ if( process )
+ processPickedList( list );
+ else if( list == aLastChange )
+ process = true; // Start processing starting with the next undo save point
+ }
+
+ // If we could not find the requested save point in the current undo list
+ // then the undo list must have been completely altered, so process everything
+ if( !process )
+ {
+ BOOST_FOREACH( const PICKED_ITEMS_LIST* list, undoList )
+ processPickedList( list );
+ }
+}
+
+
+void EDIT_TOOL::processPickedList( const PICKED_ITEMS_LIST* aList )
+{
+ KIGFX::VIEW* view = getView();
+ RN_DATA* ratsnest = getModel<BOARD>()->GetRatsnest();
+
+ for( unsigned int i = 0; i < aList->GetCount(); ++i )
+ {
+ UNDO_REDO_T operation = aList->GetPickedItemStatus( i );
+ BOARD_ITEM* updItem = static_cast<BOARD_ITEM*>( aList->GetPickedItem( i ) );
+
+ switch( operation )
+ {
+ case UR_CHANGED:
+ ratsnest->Update( updItem );
+ // fall through
+
+ case UR_MODEDIT:
+ updItem->ViewUpdate( KIGFX::VIEW_ITEM::ALL );
+ break;
+
+ case UR_DELETED:
+ if( updItem->Type() == PCB_MODULE_T )
+ static_cast<MODULE*>( updItem )->RunOnChildren( boost::bind( &KIGFX::VIEW::Remove,
+ view, _1 ) );
+
+ view->Remove( updItem );
+ //ratsnest->Remove( updItem ); // this is done in BOARD::Remove
+ break;
+
+ case UR_NEW:
+ if( updItem->Type() == PCB_MODULE_T )
+ static_cast<MODULE*>( updItem )->RunOnChildren( boost::bind( &KIGFX::VIEW::Add,
+ view, _1 ) );
+
+ view->Add( updItem );
+ //ratsnest->Add( updItem ); // this is done in BOARD::Add
+ break;
+
+ default:
+ assert( false ); // Not handled
+ break;
+ }
+ }
+}
+
+
+int EDIT_TOOL::editFootprintInFpEditor( const TOOL_EVENT& aEvent )
+{
+ const SELECTION& selection = m_selectionTool->GetSelection();
+ bool unselect = selection.Empty();
+
+ if( !hoverSelection( selection ) )
+ return 0;
+
+ MODULE* mod = uniqueSelected<MODULE>();
+
+ if( !mod )
+ return 0;
+
+ PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
+
+ editFrame-> SetCurItem( mod );
+
+ if( editFrame->GetCurItem()->GetTimeStamp() == 0 ) // Module Editor needs a non null timestamp
+ {
+ editFrame->GetCurItem()->SetTimeStamp( GetNewTimeStamp() );
+ editFrame->OnModify();
+ }
+
+ FOOTPRINT_EDIT_FRAME* editor = (FOOTPRINT_EDIT_FRAME*) editFrame->Kiway().Player( FRAME_PCB_MODULE_EDITOR, true );
+
+ editor->Load_Module_From_BOARD( (MODULE*) editFrame->GetCurItem() );
+ editFrame->SetCurItem( NULL ); // the current module could be deleted by
+
+ editor->Show( true );
+ editor->Raise(); // Iconize( false );
+
+ if( unselect )
+ m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true );
+
+ return 0;
+}
diff --git a/pcbnew/tools/edit_tool.h b/pcbnew/tools/edit_tool.h
new file mode 100644
index 0000000..0c24018
--- /dev/null
+++ b/pcbnew/tools/edit_tool.h
@@ -0,0 +1,238 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2013-2015 CERN
+ * @author Maciej Suminski <maciej.suminski@cern.ch>
+ * @author Tomasz Wlostowski <tomasz.wlostowski@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
+ */
+
+#ifndef __EDIT_TOOL_H
+#define __EDIT_TOOL_H
+
+#include <math/vector2d.h>
+#include <tool/tool_interactive.h>
+#include <view/view_group.h>
+
+class BOARD_ITEM;
+class SELECTION_TOOL;
+
+namespace KIGFX
+{
+class VIEW_GROUP;
+}
+
+/**
+ * Class EDIT_TOOL
+ *
+ * The interactive edit tool. Allows to move, rotate, flip and change properties of items selected
+ * using the pcbnew.InteractiveSelection tool.
+ */
+
+class EDIT_TOOL : public TOOL_INTERACTIVE
+{
+public:
+ EDIT_TOOL();
+
+ /// @copydoc TOOL_INTERACTIVE::Reset()
+ void Reset( RESET_REASON aReason );
+
+ /// @copydoc TOOL_INTERACTIVE::Init()
+ bool Init();
+
+ /**
+ * Function Main()
+ *
+ * Main loop in which events are handled.
+ * @param aEvent is the handled event.
+ */
+ int Main( const TOOL_EVENT& aEvent );
+
+ /**
+ * Function Edit()
+ *
+ * Displays properties window for the selected object.
+ */
+ int Properties( const TOOL_EVENT& aEvent );
+
+ /**
+ * Function Rotate()
+ *
+ * Rotates currently selected items.
+ */
+ int Rotate( const TOOL_EVENT& aEvent );
+
+ /**
+ * Function Flip()
+ *
+ * Rotates currently selected items. The rotation point is the current cursor position.
+ */
+ int Flip( const TOOL_EVENT& aEvent );
+
+ /**
+ * Function Remove()
+ *
+ * Deletes currently selected items. The rotation point is the current cursor position.
+ */
+ int Remove( const TOOL_EVENT& aEvent );
+
+ /**
+ * Function Duplicate()
+ *
+ * Duplicates a selection and starts a move action
+ */
+ int Duplicate( const TOOL_EVENT& aEvent );
+
+ /**
+ * Function MoveExact()
+ *
+ * Invokes a dialog box to allow moving of the item by an exact amount.
+ */
+ int MoveExact( const TOOL_EVENT& aEvent );
+
+ /**
+ * Function CreateArray()
+ *
+ * Creates an array of the selected items, invoking the array editor dialog
+ * to set the array options
+ */
+ int CreateArray( const TOOL_EVENT& aEvent );
+
+ /**
+ * Function EditModules()
+ *
+ * Toggles edit module mode. When enabled, one may select parts of modules individually
+ * (graphics, pads, etc.), so they can be modified.
+ * @param aEnabled decides if the mode should be enabled.
+ */
+ void EditModules( bool aEnabled )
+ {
+ m_editModules = aEnabled;
+ }
+
+ ///> Sets up handlers for various events.
+ void SetTransitions();
+
+private:
+ ///> Selection tool used for obtaining selected items
+ SELECTION_TOOL* m_selectionTool;
+
+ ///> Flag determining if anything is being dragged right now
+ bool m_dragging;
+
+ ///> Offset from the dragged item's center (anchor)
+ wxPoint m_offset;
+
+ ///> Last cursor position (needed for getModificationPoint() to avoid changes
+ ///> of edit reference point).
+ VECTOR2I m_cursor;
+
+ /// Edit module mode flag
+ bool m_editModules;
+
+ /// Counter of undo inhibitions. When zero, undo is not inhibited.
+ int m_undoInhibit;
+
+ // Vector storing track & via types, used for specifying 'Properties' menu entry condition
+ std::vector<KICAD_T> m_tracksViasType;
+
+ ///> Removes and frees a single BOARD_ITEM.
+ void remove( BOARD_ITEM* aItem );
+
+ ///> The required update flag for modified items
+ KIGFX::VIEW_ITEM::VIEW_UPDATE_FLAGS m_updateFlag;
+
+ ///> Enables higher order update flag
+ void enableUpdateFlag( KIGFX::VIEW_ITEM::VIEW_UPDATE_FLAGS aFlag )
+ {
+ if( m_updateFlag < aFlag )
+ m_updateFlag = aFlag;
+ }
+
+ ///> Updates ratsnest for selected items.
+ ///> @param aRedraw says if selected items should be drawn using the simple mode (e.g. one line
+ ///> per item).
+ void updateRatsnest( bool aRedraw );
+
+ ///> Returns the right modification point (e.g. for rotation), depending on the number of
+ ///> selected items.
+ wxPoint getModificationPoint( const SELECTION& aSelection );
+
+ ///> If there are no items currently selected, it tries to choose the item that is under
+ ///> the cursor or displays a disambiguation menu if there are multpile items.
+ bool hoverSelection( const SELECTION& aSelection, bool aSanitize = true );
+
+ ///> Processes the current undo buffer since the last change. If the last change does not occur
+ ///> in the current buffer, then the whole list is processed.
+ void processUndoBuffer( const PICKED_ITEMS_LIST* aLastChange );
+
+ ///> Updates items stored in the list.
+ void processPickedList( const PICKED_ITEMS_LIST* aList );
+
+ /**
+ * Increments the undo inhibit counter. This will indicate that tools
+ * should not create an undo point, as another tool is doing it already,
+ * and considers that its operation is atomic, even if it calls another one
+ * (for example a duplicate calls a move).
+ */
+ inline void incUndoInhibit()
+ {
+ m_undoInhibit++;
+ }
+
+ /**
+ * Decrements the inhibit counter. An assert is raised if the counter drops
+ * below zero.
+ */
+ inline void decUndoInhibit()
+ {
+ m_undoInhibit--;
+
+ wxASSERT_MSG( m_undoInhibit >= 0, wxT( "Undo inhibit count decremented past zero" ) );
+ }
+
+ /**
+ * Report if the tool manager has been told at least once that undo
+ * points should not be created. This can be ignored if the undo point
+ * is still required.
+ *
+ * @return true if undo are inhibited
+ */
+ inline bool isUndoInhibited() const
+ {
+ return m_undoInhibit > 0;
+ }
+
+ int editFootprintInFpEditor( const TOOL_EVENT& aEvent );
+
+ bool invokeInlineRouter();
+
+ template<class T> T* uniqueSelected()
+ {
+ const SELECTION& selection = m_selectionTool->GetSelection();
+
+ if( selection.items.GetCount() != 1 )
+ return NULL;
+
+ BOARD_ITEM* item = selection.Item<BOARD_ITEM>( 0 );
+ return dyn_cast<T*>( item );
+ }
+};
+
+#endif
diff --git a/pcbnew/tools/grid_helper.cpp b/pcbnew/tools/grid_helper.cpp
new file mode 100644
index 0000000..4e811ca
--- /dev/null
+++ b/pcbnew/tools/grid_helper.cpp
@@ -0,0 +1,393 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2014 CERN
+ * @author Tomasz Wlostowski <tomasz.wlostowski@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 <boost/foreach.hpp>
+#include <boost/bind.hpp>
+
+#include <wxPcbStruct.h>
+
+#include <class_board.h>
+#include <class_module.h>
+#include <class_edge_mod.h>
+#include <class_zone.h>
+#include <class_draw_panel_gal.h>
+
+#include <view/view_controls.h>
+#include <gal/graphics_abstraction_layer.h>
+
+#include <geometry/shape_line_chain.h>
+
+#include "grid_helper.h"
+
+GRID_HELPER::GRID_HELPER( PCB_BASE_FRAME* aFrame ) :
+ m_frame( aFrame )
+{
+ m_diagonalAuxAxesEnable = true;
+}
+
+
+GRID_HELPER::~GRID_HELPER()
+{
+}
+
+
+void GRID_HELPER::SetGrid( int aSize )
+{
+ assert( false );
+}
+
+
+void GRID_HELPER::SetOrigin( const VECTOR2I& aOrigin )
+{
+ assert( false );
+}
+
+
+VECTOR2I GRID_HELPER::GetGrid() const
+{
+ PCB_SCREEN* screen = m_frame->GetScreen();
+
+ const wxRealPoint& size = screen->GetGridSize();
+
+ return VECTOR2I( KiROUND( size.x ), KiROUND( size.y ) );
+}
+
+
+VECTOR2I GRID_HELPER::GetOrigin() const
+{
+ return VECTOR2I( m_frame->GetGridOrigin() );
+}
+
+
+void GRID_HELPER::SetAuxAxes( bool aEnable, const VECTOR2I& aOrigin, bool aEnableDiagonal )
+{
+ if( aEnable )
+ m_auxAxis = aOrigin;
+ else
+ m_auxAxis = boost::optional<VECTOR2I>();
+
+ m_diagonalAuxAxesEnable = aEnable;
+}
+
+
+VECTOR2I GRID_HELPER::Align( const VECTOR2I& aPoint ) const
+{
+ const VECTOR2D gridOffset( GetOrigin() );
+ const VECTOR2D gridSize( GetGrid() );
+
+ VECTOR2I nearest( KiROUND( ( aPoint.x - gridOffset.x ) / gridSize.x ) * gridSize.x + gridOffset.x,
+ KiROUND( ( aPoint.y - gridOffset.y ) / gridSize.y ) * gridSize.y + gridOffset.y );
+
+ if( !m_auxAxis )
+ return nearest;
+
+ if( std::abs( m_auxAxis->x - aPoint.x ) < std::abs( nearest.x - aPoint.x ) )
+ nearest.x = m_auxAxis->x;
+
+ if( std::abs( m_auxAxis->y - aPoint.y ) < std::abs( nearest.y - aPoint.y ) )
+ nearest.y = m_auxAxis->y;
+
+ return nearest;
+}
+
+
+VECTOR2I GRID_HELPER::AlignToSegment ( const VECTOR2I& aPoint, const SEG& aSeg )
+{
+ OPT_VECTOR2I pts[6];
+
+ VECTOR2I origin( GetOrigin() );
+ VECTOR2I grid( GetGrid() );
+
+ const VECTOR2D gridOffset( GetOrigin() );
+ const VECTOR2D gridSize( GetGrid() );
+
+ VECTOR2I nearest( KiROUND( ( aPoint.x - gridOffset.x ) / gridSize.x ) * gridSize.x + gridOffset.x,
+ KiROUND( ( aPoint.y - gridOffset.y ) / gridSize.y ) * gridSize.y + gridOffset.y );
+
+ pts[0] = aSeg.A;
+ pts[1] = aSeg.B;
+ pts[2] = aSeg.IntersectLines( SEG( nearest, nearest + VECTOR2I( 1, 0 ) ) );
+ pts[3] = aSeg.IntersectLines( SEG( nearest, nearest + VECTOR2I( 0, 1 ) ) );
+
+ int min_d = std::numeric_limits<int>::max();
+
+ for( int i = 0; i < 4; i++ )
+ {
+ if( pts[i] && aSeg.Contains( *pts[i] ) )
+ {
+ int d = (*pts[i] - aPoint).EuclideanNorm();
+
+ if( d < min_d )
+ {
+ min_d = d;
+ nearest = *pts[i];
+ }
+ }
+ }
+
+ return nearest;
+}
+
+VECTOR2I GRID_HELPER::BestDragOrigin( const VECTOR2I &aMousePos, BOARD_ITEM* aItem )
+{
+ clearAnchors();
+ computeAnchors( aItem, aMousePos );
+
+ double worldScale = m_frame->GetGalCanvas()->GetGAL()->GetWorldScale();
+ double lineSnapMinCornerDistance = 50.0 / worldScale;
+
+ ANCHOR* nearestOutline = nearestAnchor( aMousePos, OUTLINE, LSET::AllLayersMask() );
+ ANCHOR* nearestCorner = nearestAnchor( aMousePos, CORNER, LSET::AllLayersMask() );
+ ANCHOR* nearestOrigin = nearestAnchor( aMousePos, ORIGIN, LSET::AllLayersMask() );
+ ANCHOR* best = NULL;
+ double minDist = std::numeric_limits<double>::max();
+
+ if( nearestOrigin )
+ {
+ minDist = nearestOrigin->Distance( aMousePos );
+ best = nearestOrigin;
+ }
+
+ if( nearestCorner )
+ {
+ double dist = nearestCorner->Distance( aMousePos );
+
+ if( dist < minDist )
+ {
+ minDist = dist;
+ best = nearestCorner;
+ }
+ }
+
+ if( nearestOutline )
+ {
+ double dist = nearestOutline->Distance( aMousePos );
+
+ if( minDist > lineSnapMinCornerDistance && dist < minDist )
+ best = nearestOutline;
+ }
+
+ return best ? best->pos : aMousePos;
+}
+
+
+std::set<BOARD_ITEM*> GRID_HELPER::queryVisible( const BOX2I& aArea ) const
+{
+ std::set<BOARD_ITEM*> items;
+
+ std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> selectedItems;
+ std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR>::iterator it, it_end;
+
+ m_frame->GetGalCanvas()->GetView()->Query( aArea, selectedItems ); // Get the list of selected items
+
+ for( it = selectedItems.begin(), it_end = selectedItems.end(); it != it_end; ++it )
+ {
+ BOARD_ITEM* item = static_cast<BOARD_ITEM*>( it->first );
+ if( item->ViewIsVisible() )
+ items.insert ( item );
+ }
+
+ return items;
+}
+
+
+VECTOR2I GRID_HELPER::BestSnapAnchor( const VECTOR2I& aOrigin, BOARD_ITEM* aDraggedItem )
+{
+ double worldScale = m_frame->GetGalCanvas()->GetGAL()->GetWorldScale();
+ int snapRange = (int) ( 100.0 / worldScale );
+
+ BOX2I bb( VECTOR2I( aOrigin.x - snapRange / 2, aOrigin.y - snapRange / 2 ), VECTOR2I( snapRange, snapRange ) );
+
+ clearAnchors();
+
+ BOOST_FOREACH( BOARD_ITEM* item, queryVisible( bb ) )
+ {
+ computeAnchors( item, aOrigin );
+ }
+
+ LSET layers( aDraggedItem->GetLayer() );
+ ANCHOR* nearest = nearestAnchor( aOrigin, CORNER | SNAPPABLE, layers );
+
+ VECTOR2I nearestGrid = Align( aOrigin );
+ double gridDist = ( nearestGrid - aOrigin ).EuclideanNorm();
+ if( nearest )
+ {
+ double snapDist = nearest->Distance( aOrigin );
+
+ if( nearest && snapDist < gridDist )
+ return nearest->pos;
+ }
+
+ return nearestGrid;
+}
+
+
+void GRID_HELPER::computeAnchors( BOARD_ITEM* aItem, const VECTOR2I& aRefPos )
+{
+ VECTOR2I origin;
+
+ switch( aItem->Type() )
+ {
+ case PCB_MODULE_T:
+ {
+ MODULE* mod = static_cast<MODULE*>( aItem );
+ addAnchor( mod->GetPosition(), ORIGIN | SNAPPABLE, mod );
+
+ for( D_PAD* pad = mod->Pads(); pad; pad = pad->Next() )
+ addAnchor( pad->GetPosition(), CORNER | SNAPPABLE, pad );
+
+ break;
+ }
+
+
+ case PCB_PAD_T:
+ {
+ D_PAD* pad = static_cast<D_PAD*>( aItem );
+ addAnchor( pad->GetPosition(), CORNER | SNAPPABLE, pad );
+
+ break;
+ }
+
+ case PCB_MODULE_EDGE_T:
+ case PCB_LINE_T:
+ {
+ DRAWSEGMENT* dseg = static_cast<DRAWSEGMENT*>( aItem );
+ VECTOR2I start = dseg->GetStart();
+ VECTOR2I end = dseg->GetEnd();
+ //LAYER_ID layer = dseg->GetLayer();
+
+ switch( dseg->GetShape() )
+ {
+ case S_CIRCLE:
+ {
+ int r = ( start - end ).EuclideanNorm();
+
+ addAnchor( start, ORIGIN | SNAPPABLE, dseg );
+ addAnchor( start + VECTOR2I( -r, 0 ), OUTLINE | SNAPPABLE, dseg );
+ addAnchor( start + VECTOR2I( r, 0 ), OUTLINE | SNAPPABLE, dseg );
+ addAnchor( start + VECTOR2I( 0, -r ), OUTLINE | SNAPPABLE, dseg );
+ addAnchor( start + VECTOR2I( 0, r ), OUTLINE | SNAPPABLE, dseg );
+ break;
+ }
+
+ case S_ARC:
+ {
+ origin = dseg->GetCenter();
+ addAnchor( dseg->GetArcStart(), CORNER | SNAPPABLE, dseg );
+ addAnchor( dseg->GetArcEnd(), CORNER | SNAPPABLE, dseg );
+ addAnchor( origin, ORIGIN | SNAPPABLE, dseg );
+ break;
+ }
+
+ case S_SEGMENT:
+ {
+ origin.x = start.x + ( start.x - end.x ) / 2;
+ origin.y = start.y + ( start.y - end.y ) / 2;
+ addAnchor( start, CORNER | SNAPPABLE, dseg );
+ addAnchor( end, CORNER | SNAPPABLE, dseg );
+ addAnchor( origin, ORIGIN, dseg );
+ break;
+ }
+
+ default:
+ {
+ origin = dseg->GetStart();
+ addAnchor( origin, ORIGIN | SNAPPABLE, dseg );
+ break;
+ }
+ }
+ break;
+ }
+
+ case PCB_TRACE_T:
+ {
+ TRACK* track = static_cast<TRACK*>( aItem );
+ VECTOR2I start = track->GetStart();
+ VECTOR2I end = track->GetEnd();
+ origin.x = start.x + ( start.x - end.x ) / 2;
+ origin.y = start.y + ( start.y - end.y ) / 2;
+ addAnchor( start, CORNER | SNAPPABLE, track );
+ addAnchor( end, CORNER | SNAPPABLE, track );
+ addAnchor( origin, ORIGIN, track);
+ break;
+ }
+
+ case PCB_VIA_T:
+ addAnchor( aItem->GetPosition(), CORNER | SNAPPABLE, aItem );
+ break;
+
+ case PCB_ZONE_AREA_T:
+ {
+ const CPolyLine* outline = static_cast<const ZONE_CONTAINER*>( aItem )->Outline();
+ int cornersCount = outline->GetCornersCount();
+
+ SHAPE_LINE_CHAIN lc;
+ lc.SetClosed( true );
+
+ for( int i = 0; i < cornersCount; ++i )
+ {
+ const VECTOR2I p ( outline->GetPos( i ) );
+ addAnchor( p, CORNER, aItem );
+ lc.Append( p );
+ }
+
+ addAnchor( lc.NearestPoint( aRefPos ), OUTLINE, aItem );
+
+ break;
+ }
+
+ case PCB_MODULE_TEXT_T:
+ case PCB_TEXT_T:
+ addAnchor( aItem->GetPosition(), ORIGIN, aItem );
+ default:
+
+ break;
+ }
+}
+
+
+GRID_HELPER::ANCHOR* GRID_HELPER::nearestAnchor( const VECTOR2I& aPos, int aFlags, LSET aMatchLayers )
+{
+ double minDist = std::numeric_limits<double>::max();
+ ANCHOR* best = NULL;
+
+ BOOST_FOREACH( ANCHOR& a, m_anchors )
+ {
+ if( !aMatchLayers[a.item->GetLayer()] )
+ continue;
+
+ if( ( aFlags & a.flags ) != aFlags )
+ continue;
+
+ double dist = a.Distance( aPos );
+
+ if( dist < minDist )
+ {
+ minDist = dist;
+ best = &a;
+ }
+ }
+
+ return best;
+}
diff --git a/pcbnew/tools/grid_helper.h b/pcbnew/tools/grid_helper.h
new file mode 100644
index 0000000..6401b69
--- /dev/null
+++ b/pcbnew/tools/grid_helper.h
@@ -0,0 +1,108 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2014 CERN
+ * @author Tomasz Wlostowski <tomasz.wlostowski@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
+ */
+
+#ifndef __GRID_HELPER_H
+#define __GRID_HELPER_H
+
+#include <vector>
+
+#include <math/vector2d.h>
+#include <boost/optional.hpp>
+
+#include <layers_id_colors_and_visibility.h>
+
+#include <geometry/seg.h>
+
+class PCB_BASE_FRAME;
+
+class GRID_HELPER {
+public:
+
+ GRID_HELPER( PCB_BASE_FRAME* aFrame );
+ ~GRID_HELPER();
+
+ void SetGrid( int aSize );
+ void SetOrigin( const VECTOR2I& aOrigin );
+
+ VECTOR2I GetGrid() const;
+ VECTOR2I GetOrigin() const;
+
+ void SetAuxAxes( bool aEnable, const VECTOR2I& aOrigin = VECTOR2I( 0, 0 ), bool aEnableDiagonal = false );
+
+ VECTOR2I Align( const VECTOR2I& aPoint ) const;
+
+ VECTOR2I AlignToSegment ( const VECTOR2I& aPoint, const SEG& aSeg );
+
+ VECTOR2I BestDragOrigin( const VECTOR2I& aMousePos, BOARD_ITEM* aItem );
+ VECTOR2I BestSnapAnchor( const VECTOR2I& aOrigin, BOARD_ITEM* aDraggedItem );
+
+private:
+ enum ANCHOR_FLAGS {
+ CORNER = 0x1,
+ OUTLINE = 0x2,
+ SNAPPABLE = 0x4,
+ ORIGIN = 0x8
+ };
+
+ struct ANCHOR
+ {
+ ANCHOR( VECTOR2I aPos, int aFlags = CORNER | SNAPPABLE, BOARD_ITEM* aItem = NULL ):
+ pos( aPos ), flags( aFlags ), item( aItem ) {} ;
+
+ VECTOR2I pos;
+ int flags;
+ BOARD_ITEM* item;
+
+ double Distance( const VECTOR2I& aP ) const
+ {
+ return ( aP - pos ).EuclideanNorm();
+ }
+
+ //bool CanSnapItem( const BOARD_ITEM* aItem ) const;
+ };
+
+ std::vector<ANCHOR> m_anchors;
+
+ std::set<BOARD_ITEM*> queryVisible( const BOX2I& aArea ) const;
+
+ void addAnchor( const VECTOR2I& aPos, int aFlags = CORNER | SNAPPABLE, BOARD_ITEM* aItem = NULL )
+ {
+ m_anchors.push_back( ANCHOR( aPos, aFlags, aItem ) );
+ }
+
+ ANCHOR* nearestAnchor( const VECTOR2I& aPos, int aFlags, LSET aMatchLayers );
+
+ void computeAnchors( BOARD_ITEM* aItem, const VECTOR2I& aRefPos );
+
+ void clearAnchors()
+ {
+ m_anchors.clear();
+ }
+
+ PCB_BASE_FRAME* m_frame;
+ boost::optional<VECTOR2I> m_auxAxis;
+ bool m_diagonalAuxAxesEnable;
+};
+
+#endif
diff --git a/pcbnew/tools/grid_menu.cpp b/pcbnew/tools/grid_menu.cpp
new file mode 100644
index 0000000..4d23351
--- /dev/null
+++ b/pcbnew/tools/grid_menu.cpp
@@ -0,0 +1,68 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2015 CERN
+ * @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 "grid_menu.h"
+#include <id.h>
+#include <draw_frame.h>
+#include <class_base_screen.h>
+#include <tools/common_actions.h>
+
+#include <boost/bind.hpp>
+
+GRID_MENU::GRID_MENU( EDA_DRAW_FRAME* aParent ) : m_parent( aParent )
+{
+ BASE_SCREEN* screen = aParent->GetScreen();
+
+ SetIcon( grid_select_xpm );
+ SetMenuHandler( boost::bind( &GRID_MENU::EventHandler, this, _1 ) );
+ SetUpdateHandler( boost::bind( &GRID_MENU::Update, this ) );
+
+ wxArrayString gridsList;
+ screen->BuildGridsChoiceList( gridsList, g_UserUnit != INCHES );
+
+ for( unsigned int i = 0; i < gridsList.GetCount(); ++i )
+ {
+ GRID_TYPE& grid = screen->GetGrid( i );
+ Append( grid.m_CmdId, gridsList[i], wxEmptyString, true );
+ }
+}
+
+
+OPT_TOOL_EVENT GRID_MENU::EventHandler( const wxMenuEvent& aEvent )
+{
+ OPT_TOOL_EVENT event( COMMON_ACTIONS::gridPreset.MakeEvent() );
+ long idx = aEvent.GetId() - ID_POPUP_GRID_SELECT - 1;
+ event->SetParameter( idx );
+
+ return event;
+}
+
+
+void GRID_MENU::Update()
+{
+ for( unsigned int i = 0; i < GetMenuItemCount(); ++i )
+ Check( ID_POPUP_GRID_SELECT + 1 + i, false );
+
+ Check( m_parent->GetScreen()->GetGridCmdId(), true );
+}
diff --git a/pcbnew/tools/grid_menu.h b/pcbnew/tools/grid_menu.h
new file mode 100644
index 0000000..b73920c
--- /dev/null
+++ b/pcbnew/tools/grid_menu.h
@@ -0,0 +1,44 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2015 CERN
+ * @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
+ */
+
+#ifndef GRID_MENU_H
+#define GRID_MENU_H
+
+#include <tool/context_menu.h>
+
+class EDA_DRAW_FRAME;
+
+class GRID_MENU : public CONTEXT_MENU
+{
+public:
+ GRID_MENU( EDA_DRAW_FRAME* aParent );
+
+ OPT_TOOL_EVENT EventHandler( const wxMenuEvent& aEvent );
+ void Update();
+
+private:
+ EDA_DRAW_FRAME* m_parent;
+};
+
+#endif /* GRID_MENU_H */
diff --git a/pcbnew/tools/module_tools.cpp b/pcbnew/tools/module_tools.cpp
new file mode 100644
index 0000000..3a5d9a3
--- /dev/null
+++ b/pcbnew/tools/module_tools.cpp
@@ -0,0 +1,608 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2014-2015 CERN
+ * @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 "module_tools.h"
+#include "selection_tool.h"
+#include "common_actions.h"
+#include <tool/tool_manager.h>
+
+#include <class_draw_panel_gal.h>
+#include <view/view_controls.h>
+#include <view/view_group.h>
+#include <pcb_painter.h>
+#include <origin_viewitem.h>
+
+#include <kicad_plugin.h>
+#include <pcbnew_id.h>
+#include <collectors.h>
+#include <confirm.h>
+#include <dialogs/dialog_enum_pads.h>
+
+#include <wxPcbStruct.h>
+#include <class_board.h>
+#include <class_module.h>
+#include <class_edge_mod.h>
+
+#include <boost/bind.hpp>
+#include <boost/foreach.hpp>
+#include <wx/defs.h>
+
+MODULE_TOOLS::MODULE_TOOLS() :
+ TOOL_INTERACTIVE( "pcbnew.ModuleEditor" ), m_view( NULL ), m_controls( NULL ),
+ m_board( NULL ), m_frame( NULL )
+{
+ // Generate an origin marker at 0,0 which is used as an axis origin marker (0,0)
+ m_axisOrigin = new KIGFX::ORIGIN_VIEWITEM( KIGFX::COLOR4D(0.0, 0.0, 0.8, 1.0),
+ KIGFX::ORIGIN_VIEWITEM::CROSS,
+ 20000,
+ VECTOR2D(0,0) );
+ m_axisOrigin->SetDrawAtZero( true );
+}
+
+
+MODULE_TOOLS::~MODULE_TOOLS()
+{
+ delete m_axisOrigin;
+}
+
+
+void MODULE_TOOLS::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_EDIT_FRAME>();
+
+ if( aReason == MODEL_RELOAD || aReason == GAL_SWITCH )
+ {
+ // Draw the axis origin if we're editing modules (essentially in the footprint editor)
+ m_view->Remove( m_axisOrigin );
+ m_view->Add( m_axisOrigin );
+ }
+}
+
+
+bool MODULE_TOOLS::Init()
+{
+ // Find the selection tool, so they can cooperate
+ SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<SELECTION_TOOL>();
+
+ if( !selectionTool )
+ {
+ DisplayError( NULL, wxT( "pcbnew.InteractiveSelection tool is not available" ) );
+ return false;
+ }
+
+ selectionTool->GetMenu().AddItem( COMMON_ACTIONS::enumeratePads );
+
+ return true;
+}
+
+
+int MODULE_TOOLS::PlacePad( const TOOL_EVENT& aEvent )
+{
+ m_frame->SetToolID( ID_MODEDIT_PAD_TOOL, wxCURSOR_PENCIL, _( "Add pads" ) );
+
+ assert( m_board->m_Modules );
+
+ D_PAD* pad = new D_PAD( m_board->m_Modules );
+ m_frame->Import_Pad_Settings( pad, false ); // use the global settings for pad
+
+ VECTOR2I cursorPos = m_controls->GetCursorPosition();
+ pad->SetPosition( wxPoint( cursorPos.x, cursorPos.y ) );
+
+ // Add a VIEW_GROUP that serves as a preview for the new item
+ KIGFX::VIEW_GROUP preview( m_view );
+ preview.Add( pad );
+ m_view->Add( &preview );
+
+ m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true );
+ m_controls->ShowCursor( true );
+ m_controls->SetSnapping( true );
+
+ Activate();
+
+ // Main loop: keep receiving events
+ while( OPT_TOOL_EVENT evt = Wait() )
+ {
+ cursorPos = m_controls->GetCursorPosition();
+
+ if( evt->IsMotion() )
+ {
+ pad->SetPosition( wxPoint( cursorPos.x, cursorPos.y ) );
+ preview.ViewUpdate();
+ }
+
+ else if( evt->Category() == TC_COMMAND )
+ {
+ if( evt->IsAction( &COMMON_ACTIONS::rotate ) )
+ {
+ pad->Rotate( pad->GetPosition(), m_frame->GetRotationAngle() );
+ preview.ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+ }
+ else if( evt->IsAction( &COMMON_ACTIONS::flip ) )
+ {
+ pad->Flip( pad->GetPosition() );
+ preview.ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+ }
+ else if( evt->IsCancel() || evt->IsActivate() )
+ {
+ preview.Clear();
+ delete pad;
+ break;
+ }
+ }
+
+ else if( evt->IsClick( BUT_LEFT ) )
+ {
+ m_frame->OnModify();
+ m_frame->SaveCopyInUndoList( m_board->m_Modules, UR_MODEDIT );
+
+ m_board->m_Status_Pcb = 0; // I have no clue why, but it is done in the legacy view
+ pad->SetParent( m_board->m_Modules );
+ m_board->m_Modules->SetLastEditTime();
+ m_board->m_Modules->Pads().PushBack( pad );
+
+ // Set the relative pad position
+ // ( pad position for module orient, 0, and relative to the module position)
+ pad->SetLocalCoord();
+
+ // Take the next available pad number
+ pad->IncrementPadName( true, true );
+
+ // Handle the view aspect
+ preview.Remove( pad );
+ m_view->Add( pad );
+
+ // Start placing next pad
+ pad = new D_PAD( m_board->m_Modules );
+ m_frame->Import_Pad_Settings( pad, false );
+ pad->SetPosition( wxPoint( cursorPos.x, cursorPos.y ) );
+ preview.Add( pad );
+ }
+ }
+
+ m_controls->ShowCursor( false );
+ m_controls->SetSnapping( false );
+ m_controls->SetAutoPan( false );
+ m_view->Remove( &preview );
+
+ m_frame->SetToolID( ID_NO_TOOL_SELECTED, wxCURSOR_DEFAULT, wxEmptyString );
+
+ return 0;
+}
+
+
+int MODULE_TOOLS::EnumeratePads( const TOOL_EVENT& aEvent )
+{
+ std::list<D_PAD*> pads;
+ std::set<D_PAD*> allPads;
+
+ if( !m_board->m_Modules || !m_board->m_Modules->Pads() )
+ return 0;
+
+ GENERAL_COLLECTOR collector;
+ const KICAD_T types[] = { PCB_PAD_T, EOT };
+
+ GENERAL_COLLECTORS_GUIDE guide = m_frame->GetCollectorsGuide();
+ guide.SetIgnoreMTextsMarkedNoShow( true );
+ guide.SetIgnoreMTextsOnBack( true );
+ guide.SetIgnoreMTextsOnFront( true );
+ guide.SetIgnoreModulesVals( true );
+ guide.SetIgnoreModulesRefs( true );
+
+ // Create a set containing all pads (to avoid double adding to the list)
+ for( D_PAD* p = m_board->m_Modules->Pads(); p; p = p->Next() )
+ allPads.insert( p );
+
+ DIALOG_ENUM_PADS settingsDlg( m_frame );
+
+ if( settingsDlg.ShowModal() == wxID_CANCEL )
+ return 0;
+
+ int padNumber = settingsDlg.GetStartNumber();
+ wxString padPrefix = settingsDlg.GetPrefix();
+
+ m_frame->DisplayToolMsg( _( "Hold left mouse button and move cursor over pads to enumerate them" ) );
+
+ Activate();
+
+ m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true );
+ m_controls->ShowCursor( true );
+ VECTOR2I oldCursorPos = m_controls->GetCursorPosition();
+ std::list<D_PAD*> selectedPads;
+
+ while( OPT_TOOL_EVENT evt = Wait() )
+ {
+ if( evt->IsDrag( BUT_LEFT ) || evt->IsClick( BUT_LEFT ) )
+ {
+ selectedPads.clear();
+ VECTOR2I cursorPos = m_controls->GetCursorPosition();
+
+ if( evt->IsClick( BUT_LEFT ) )
+ {
+ oldCursorPos = m_controls->GetCursorPosition();
+ collector.Empty();
+ collector.Collect( m_board, types, wxPoint( cursorPos.x, cursorPos.y ), guide );
+
+ for( int i = 0; i < collector.GetCount(); ++i )
+ {
+ if( collector[i]->Type() == PCB_PAD_T )
+ selectedPads.push_back( static_cast<D_PAD*>( collector[i] ) );
+ }
+ }
+ else //evt->IsDrag( BUT_LEFT )
+ {
+ // wxWidgets deliver mouse move events not frequently enough, resulting in skipping
+ // pads if the user moves cursor too fast. To solve it, create a line that approximates
+ // the mouse move and select items intersecting with the line.
+ int distance = ( cursorPos - oldCursorPos ).EuclideanNorm();
+ int segments = distance / 100000 + 1;
+ const wxPoint LINE_STEP( ( cursorPos - oldCursorPos ).x / segments,
+ ( cursorPos - oldCursorPos ).y / segments );
+
+ collector.Empty();
+ for( int j = 0; j < segments; ++j ) {
+ collector.Collect( m_board, types,
+ wxPoint( oldCursorPos.x, oldCursorPos.y ) + j * LINE_STEP,
+ guide );
+
+ for( int i = 0; i < collector.GetCount(); ++i )
+ {
+ if( collector[i]->Type() == PCB_PAD_T )
+ selectedPads.push_back( static_cast<D_PAD*>( collector[i] ) );
+ }
+ }
+
+ selectedPads.unique();
+ }
+
+ BOOST_FOREACH( D_PAD* pad, selectedPads )
+ {
+ std::set<D_PAD*>::iterator it = allPads.find( pad );
+
+ // Add the pad to the list, if it was not selected previously..
+ if( it != allPads.end() )
+ {
+ allPads.erase( it );
+ pads.push_back( pad );
+ pad->SetSelected();
+ }
+
+ // ..or remove it from the list if it was clicked
+ else if( evt->IsClick( BUT_LEFT ) )
+ {
+ allPads.insert( pad );
+ pads.remove( pad );
+ pad->ClearSelected();
+ }
+ }
+
+ oldCursorPos = cursorPos;
+ }
+
+ else if( ( evt->IsKeyPressed() && evt->KeyCode() == WXK_RETURN ) ||
+ evt->IsDblClick( BUT_LEFT ) )
+ {
+ // Accept changes
+ m_frame->OnModify();
+ m_frame->SaveCopyInUndoList( m_board->m_Modules, UR_MODEDIT );
+
+ BOOST_FOREACH( D_PAD* pad, pads )
+ pad->SetPadName( wxString::Format( wxT( "%s%d" ), padPrefix.c_str(), padNumber++ ) );
+
+ break;
+ }
+
+ else if( evt->IsCancel() || evt->IsActivate() )
+ {
+ break;
+ }
+ }
+
+ BOOST_FOREACH( D_PAD* pad, pads )
+ pad->ClearSelected();
+
+ m_frame->DisplayToolMsg( wxEmptyString );
+ m_controls->ShowCursor( false );
+
+ return 0;
+}
+
+
+int MODULE_TOOLS::CopyItems( const TOOL_EVENT& aEvent )
+{
+ const SELECTION& selection = m_toolMgr->GetTool<SELECTION_TOOL>()->GetSelection();
+
+ Activate();
+
+ m_controls->SetSnapping( true );
+ m_controls->ShowCursor( true );
+ m_controls->SetAutoPan( true );
+
+ m_frame->DisplayToolMsg( _( "Select reference point" ) );
+
+ bool cancelled = false;
+ VECTOR2I cursorPos = m_controls->GetCursorPosition();
+
+ while( OPT_TOOL_EVENT evt = Wait() )
+ {
+ if( evt->IsMotion() )
+ {
+ cursorPos = m_controls->GetCursorPosition();
+ }
+ else if( evt->IsClick( BUT_LEFT ) )
+ {
+ break;
+ }
+ else if( evt->IsCancel() || evt->IsActivate() )
+ {
+ cancelled = true;
+ break;
+ }
+ }
+
+ if( !cancelled )
+ {
+ PCB_IO io( CTL_FOR_CLIPBOARD );
+
+ // Create a temporary module that contains selected items to ease serialization
+ MODULE module( m_board );
+
+ for( int i = 0; i < selection.Size(); ++i )
+ {
+ BOARD_ITEM* clone = static_cast<BOARD_ITEM*>( selection.Item<BOARD_ITEM>( i )->Clone() );
+
+ // Do not add reference/value - convert them to the common type
+ if( TEXTE_MODULE* text = dyn_cast<TEXTE_MODULE*>( clone ) )
+ text->SetType( TEXTE_MODULE::TEXT_is_DIVERS );
+
+ module.Add( clone );
+ }
+
+ // Set the new relative internal local coordinates of copied items
+ MODULE* editedModule = m_board->m_Modules;
+ wxPoint moveVector = module.GetPosition() + editedModule->GetPosition() -
+ wxPoint( cursorPos.x, cursorPos.y );
+ module.MoveAnchorPosition( moveVector );
+
+ io.Format( &module, 0 );
+ std::string data = io.GetStringOutput( true );
+ m_toolMgr->SaveClipboard( data );
+ }
+
+ m_frame->DisplayToolMsg( wxString::Format( _( "Copied %d item(s)" ), selection.Size() ) );
+ m_controls->SetSnapping( false );
+ m_controls->ShowCursor( false );
+ m_controls->SetAutoPan( false );
+
+ return 0;
+}
+
+
+int MODULE_TOOLS::PasteItems( const TOOL_EVENT& aEvent )
+{
+ // Parse clipboard
+ PCB_IO io( CTL_FOR_CLIPBOARD );
+ MODULE* currentModule = m_board->m_Modules;
+ MODULE* pastedModule = NULL;
+
+ try
+ {
+ BOARD_ITEM* item = io.Parse( wxString( m_toolMgr->GetClipboard().c_str(), wxConvUTF8 ) );
+ assert( item->Type() == PCB_MODULE_T );
+ pastedModule = dyn_cast<MODULE*>( item );
+ }
+ catch( ... )
+ {
+ m_frame->DisplayToolMsg( _( "Invalid clipboard contents" ) );
+ return 0;
+ }
+
+ // Placement tool part
+ VECTOR2I cursorPos = m_controls->GetCursorPosition();
+
+ // Add a VIEW_GROUP that serves as a preview for the new item
+ KIGFX::VIEW_GROUP preview( m_view );
+ pastedModule->SetParent( m_board );
+ pastedModule->SetPosition( wxPoint( cursorPos.x, cursorPos.y ) );
+ pastedModule->RunOnChildren( boost::bind( &KIGFX::VIEW_GROUP::Add, boost::ref( preview ), _1 ) );
+ preview.Add( pastedModule );
+ m_view->Add( &preview );
+
+ m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true );
+ m_controls->ShowCursor( true );
+ m_controls->SetSnapping( true );
+ m_controls->SetAutoPan( true );
+
+ Activate();
+
+ // Main loop: keep receiving events
+ while( OPT_TOOL_EVENT evt = Wait() )
+ {
+ cursorPos = m_controls->GetCursorPosition();
+
+ if( evt->IsMotion() )
+ {
+ pastedModule->SetPosition( wxPoint( cursorPos.x, cursorPos.y ) );
+ preview.ViewUpdate();
+ }
+
+ else if( evt->Category() == TC_COMMAND )
+ {
+ if( evt->IsAction( &COMMON_ACTIONS::rotate ) )
+ {
+ pastedModule->Rotate( pastedModule->GetPosition(), m_frame->GetRotationAngle() );
+ preview.ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+ }
+ else if( evt->IsAction( &COMMON_ACTIONS::flip ) )
+ {
+ pastedModule->Flip( pastedModule->GetPosition() );
+ preview.ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+ }
+ else if( evt->IsCancel() || evt->IsActivate() )
+ {
+ preview.Clear();
+ break;
+ }
+ }
+
+ else if( evt->IsClick( BUT_LEFT ) )
+ {
+ m_frame->OnModify();
+ m_frame->SaveCopyInUndoList( currentModule, UR_MODEDIT );
+
+ m_board->m_Status_Pcb = 0; // I have no clue why, but it is done in the legacy view
+ currentModule->SetLastEditTime();
+
+ // MODULE::RunOnChildren is infeasible here: we need to create copies of items, do not
+ // directly modify them
+
+ for( D_PAD* pad = pastedModule->Pads(); pad; pad = pad->Next() )
+ {
+ D_PAD* clone = static_cast<D_PAD*>( pad->Clone() );
+
+ currentModule->Add( clone );
+ clone->SetLocalCoord();
+ m_view->Add( clone );
+ }
+
+ for( BOARD_ITEM* drawing = pastedModule->GraphicalItems();
+ drawing; drawing = drawing->Next() )
+ {
+ BOARD_ITEM* clone = static_cast<BOARD_ITEM*>( drawing->Clone() );
+
+ if( TEXTE_MODULE* text = dyn_cast<TEXTE_MODULE*>( clone ) )
+ {
+ // Do not add reference/value - convert them to the common type
+ text->SetType( TEXTE_MODULE::TEXT_is_DIVERS );
+ currentModule->Add( text );
+ text->SetLocalCoord();
+
+ // Whyyyyyyyyyyyyyyyyyyyyyy?! All other items conform to rotation performed
+ // on its parent module, but texts are so independent..
+ text->Rotate( text->GetPosition(), pastedModule->GetOrientation() );
+ }
+ else if( EDGE_MODULE* edge = dyn_cast<EDGE_MODULE*>( clone ) )
+ {
+ currentModule->Add( edge );
+ edge->SetLocalCoord();
+ }
+
+ m_view->Add( clone );
+ }
+
+ preview.Clear();
+
+ break;
+ }
+ }
+
+ delete pastedModule;
+ m_controls->ShowCursor( false );
+ m_controls->SetSnapping( false );
+ m_controls->SetAutoPan( false );
+ m_view->Remove( &preview );
+
+ return 0;
+}
+
+
+int MODULE_TOOLS::ModuleTextOutlines( const TOOL_EVENT& aEvent )
+{
+ KIGFX::PCB_PAINTER* painter =
+ static_cast<KIGFX::PCB_PAINTER*>( m_frame->GetGalCanvas()->GetView()->GetPainter() );
+ KIGFX::PCB_RENDER_SETTINGS* settings =
+ static_cast<KIGFX::PCB_RENDER_SETTINGS*>( painter->GetSettings() );
+
+ const LAYER_NUM layers[] = { ITEM_GAL_LAYER( MOD_TEXT_BK_VISIBLE ),
+ ITEM_GAL_LAYER( MOD_TEXT_FR_VISIBLE ),
+ ITEM_GAL_LAYER( MOD_TEXT_INVISIBLE ),
+ ITEM_GAL_LAYER( MOD_REFERENCES_VISIBLE ),
+ ITEM_GAL_LAYER( MOD_VALUES_VISIBLE ) };
+
+ bool enable = !settings->GetSketchMode( layers[0] );
+
+ BOOST_FOREACH( LAYER_NUM layer, layers )
+ settings->SetSketchMode( layer, enable );
+
+ for( MODULE* module = getModel<BOARD>()->m_Modules; module; module = module->Next() )
+ {
+ for( BOARD_ITEM* item = module->GraphicalItems(); item; item = item ->Next() )
+ {
+ if( item->Type() == PCB_MODULE_TEXT_T )
+ item->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+ }
+
+ module->Reference().ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+ module->Value().ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+ }
+
+ m_frame->GetGalCanvas()->Refresh();
+
+ return 0;
+}
+
+
+int MODULE_TOOLS::ModuleEdgeOutlines( const TOOL_EVENT& aEvent )
+{
+ KIGFX::PCB_PAINTER* painter =
+ static_cast<KIGFX::PCB_PAINTER*>( m_frame->GetGalCanvas()->GetView()->GetPainter() );
+ KIGFX::PCB_RENDER_SETTINGS* settings =
+ static_cast<KIGFX::PCB_RENDER_SETTINGS*>( painter->GetSettings() );
+
+ const LAYER_ID layers[] = { F_Adhes, B_Adhes, F_Paste, B_Paste,
+ F_SilkS, B_SilkS, F_Mask, B_Mask,
+ Dwgs_User, Cmts_User, Eco1_User, Eco2_User, Edge_Cuts };
+
+ bool enable = !settings->GetSketchMode( layers[0] );
+
+ BOOST_FOREACH( LAYER_NUM layer, layers )
+ settings->SetSketchMode( layer, enable );
+
+ for( MODULE* module = getModel<BOARD>()->m_Modules; module; module = module->Next() )
+ {
+ for( BOARD_ITEM* item = module->GraphicalItems(); item; item = item ->Next() )
+ {
+ if( item->Type() == PCB_MODULE_EDGE_T )
+ item->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+ }
+ }
+
+ m_frame->GetGalCanvas()->Refresh();
+
+ return 0;
+}
+
+
+void MODULE_TOOLS::SetTransitions()
+{
+ Go( &MODULE_TOOLS::PlacePad, COMMON_ACTIONS::placePad.MakeEvent() );
+ Go( &MODULE_TOOLS::EnumeratePads, COMMON_ACTIONS::enumeratePads.MakeEvent() );
+ Go( &MODULE_TOOLS::CopyItems, COMMON_ACTIONS::copyItems.MakeEvent() );
+ Go( &MODULE_TOOLS::PasteItems, COMMON_ACTIONS::pasteItems.MakeEvent() );
+ Go( &MODULE_TOOLS::ModuleTextOutlines, COMMON_ACTIONS::moduleTextOutlines.MakeEvent() );
+ Go( &MODULE_TOOLS::ModuleEdgeOutlines, COMMON_ACTIONS::moduleEdgeOutlines.MakeEvent() );
+}
diff --git a/pcbnew/tools/module_tools.h b/pcbnew/tools/module_tools.h
new file mode 100644
index 0000000..b2178ba
--- /dev/null
+++ b/pcbnew/tools/module_tools.h
@@ -0,0 +1,117 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2014 CERN
+ * @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
+ */
+
+#ifndef MODULE_TOOLS_H
+#define MODULE_TOOLS_H
+
+#include <tool/tool_interactive.h>
+#include <origin_viewitem.h>
+
+namespace KIGFX
+{
+ class VIEW;
+ class VIEW_CONTROLS;
+}
+class BOARD;
+class PCB_EDIT_FRAME;
+
+/**
+ * Class MODULE_TOOLS
+ *
+ * Module editor specific tools.
+ */
+class MODULE_TOOLS : public TOOL_INTERACTIVE
+{
+public:
+ MODULE_TOOLS();
+ ~MODULE_TOOLS();
+
+ /// @copydoc TOOL_INTERACTIVE::Reset()
+ void Reset( RESET_REASON aReason );
+
+ /// @copydoc TOOL_INTERACTIVE::Init()
+ bool Init();
+
+ /**
+ * Function PlacePad()
+ * Places a pad in module editor.
+ */
+ int PlacePad( const TOOL_EVENT& aEvent );
+
+ /**
+ * Function EnumeratePads()
+ * Tool for quick pad enumeration.
+ */
+ int EnumeratePads( const TOOL_EVENT& aEvent );
+
+ /**
+ * Function CopyItems()
+ *
+ * Copies selected items to the clipboard. Works only in "edit modules" mode.
+ */
+ int CopyItems( const TOOL_EVENT& aEvent );
+
+ /**
+ * Function PastePad()
+ *
+ * Pastes items from the clipboard. Works only in "edit modules" mode.
+ */
+ int PasteItems( const TOOL_EVENT& aEvent );
+
+ /**
+ * Function CreateArray
+ *
+ * Creates an array of objects using settings from a dialog
+ */
+ int CreateArray( TOOL_EVENT& aEvent );
+
+ /**
+ * Function ModuleTextOutlines()
+ *
+ * Toggles display mode for module texts (outline/filled).
+ */
+ int ModuleTextOutlines( const TOOL_EVENT& aEvent );
+
+ /**
+ * Function ModuleEdgeOutlines()
+ *
+ * Toggles display mode for module edges (outline/filled).
+ */
+ int ModuleEdgeOutlines( const TOOL_EVENT& aEvent );
+
+ ///> Sets up handlers for various events.
+ void SetTransitions();
+
+private:
+ KIGFX::VIEW* m_view;
+ KIGFX::VIEW_CONTROLS* m_controls;
+ BOARD* m_board;
+ PCB_EDIT_FRAME* m_frame;
+
+ ///> Axis 0 marker
+ KIGFX::ORIGIN_VIEWITEM* m_axisOrigin;
+
+};
+
+#endif
diff --git a/pcbnew/tools/pcb_editor_control.cpp b/pcbnew/tools/pcb_editor_control.cpp
new file mode 100644
index 0000000..15a0fc6
--- /dev/null
+++ b/pcbnew/tools/pcb_editor_control.cpp
@@ -0,0 +1,792 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2014 CERN
+ * @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 <boost/bind.hpp>
+
+#include "pcb_editor_control.h"
+#include "common_actions.h"
+#include <tool/tool_manager.h>
+
+#include "selection_tool.h"
+#include "picker_tool.h"
+
+#include <painter.h>
+#include <project.h>
+#include <pcbnew_id.h>
+#include <wxPcbStruct.h>
+#include <class_board.h>
+#include <class_zone.h>
+#include <class_draw_panel_gal.h>
+#include <class_module.h>
+#include <class_mire.h>
+#include <ratsnest_data.h>
+#include <collectors.h>
+#include <zones_functions_for_undo_redo.h>
+
+#include <view/view_group.h>
+#include <view/view_controls.h>
+#include <origin_viewitem.h>
+
+#include <boost/bind.hpp>
+
+
+class ZONE_CONTEXT_MENU : public CONTEXT_MENU
+{
+public:
+ ZONE_CONTEXT_MENU()
+ {
+ SetIcon( add_zone_xpm );
+ SetUpdateHandler( boost::bind( &ZONE_CONTEXT_MENU::update, this ) );
+ Add( COMMON_ACTIONS::zoneFill );
+ Add( COMMON_ACTIONS::zoneFillAll );
+ Add( COMMON_ACTIONS::zoneUnfill );
+ Add( COMMON_ACTIONS::zoneUnfillAll );
+ Add( COMMON_ACTIONS::zoneMerge );
+ }
+
+private:
+ void update()
+ {
+ SELECTION_TOOL* selTool = getToolManager()->GetTool<SELECTION_TOOL>();
+
+ // 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 ) &&*/
+ SELECTION_CONDITIONS::SameNet( true ) &&
+ SELECTION_CONDITIONS::SameLayer() )( selTool->GetSelection() );
+
+ Enable( getMenuId( COMMON_ACTIONS::zoneMerge ), mergeEnabled );
+ }
+};
+
+
+PCB_EDITOR_CONTROL::PCB_EDITOR_CONTROL() :
+ TOOL_INTERACTIVE( "pcbnew.EditorControl" ), m_frame( NULL ), m_zoneMenu( NULL )
+{
+ m_placeOrigin = new KIGFX::ORIGIN_VIEWITEM( KIGFX::COLOR4D( 0.8, 0.0, 0.0, 1.0 ),
+ KIGFX::ORIGIN_VIEWITEM::CIRCLE_CROSS );
+ m_probingSchToPcb = false;
+}
+
+
+PCB_EDITOR_CONTROL::~PCB_EDITOR_CONTROL()
+{
+ delete m_placeOrigin;
+ delete m_zoneMenu;
+}
+
+
+void PCB_EDITOR_CONTROL::Reset( RESET_REASON aReason )
+{
+ m_frame = getEditFrame<PCB_EDIT_FRAME>();
+
+ if( aReason == MODEL_RELOAD || aReason == GAL_SWITCH )
+ {
+ m_placeOrigin->SetPosition( getModel<BOARD>()->GetAuxOrigin() );
+ getView()->Remove( m_placeOrigin );
+ getView()->Add( m_placeOrigin );
+ }
+}
+
+
+bool PCB_EDITOR_CONTROL::Init()
+{
+ 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 ) );
+ }
+
+ return true;
+}
+
+
+// Track & via size control
+int PCB_EDITOR_CONTROL::TrackWidthInc( const TOOL_EVENT& aEvent )
+{
+ BOARD* board = getModel<BOARD>();
+ int widthIndex = board->GetDesignSettings().GetTrackWidthIndex() + 1;
+
+ if( widthIndex >= (int) board->GetDesignSettings().m_TrackWidthList.size() )
+ widthIndex = board->GetDesignSettings().m_TrackWidthList.size() - 1;
+
+ board->GetDesignSettings().SetTrackWidthIndex( widthIndex );
+ board->GetDesignSettings().UseCustomTrackViaSize( false );
+
+ wxUpdateUIEvent dummy;
+ m_frame->OnUpdateSelectTrackWidth( dummy );
+ m_toolMgr->RunAction( COMMON_ACTIONS::trackViaSizeChanged );
+
+ return 0;
+}
+
+
+int PCB_EDITOR_CONTROL::TrackWidthDec( const TOOL_EVENT& aEvent )
+{
+ BOARD* board = getModel<BOARD>();
+ int widthIndex = board->GetDesignSettings().GetTrackWidthIndex() - 1;
+
+ if( widthIndex < 0 )
+ widthIndex = 0;
+
+ board->GetDesignSettings().SetTrackWidthIndex( widthIndex );
+ board->GetDesignSettings().UseCustomTrackViaSize( false );
+
+ wxUpdateUIEvent dummy;
+ m_frame->OnUpdateSelectTrackWidth( dummy );
+ m_toolMgr->RunAction( COMMON_ACTIONS::trackViaSizeChanged );
+
+ return 0;
+}
+
+
+int PCB_EDITOR_CONTROL::ViaSizeInc( const TOOL_EVENT& aEvent )
+{
+ BOARD* board = getModel<BOARD>();
+ int sizeIndex = board->GetDesignSettings().GetViaSizeIndex() + 1;
+
+ if( sizeIndex >= (int) board->GetDesignSettings().m_ViasDimensionsList.size() )
+ sizeIndex = board->GetDesignSettings().m_ViasDimensionsList.size() - 1;
+
+ board->GetDesignSettings().SetViaSizeIndex( sizeIndex );
+ board->GetDesignSettings().UseCustomTrackViaSize( false );
+
+ wxUpdateUIEvent dummy;
+ m_frame->OnUpdateSelectViaSize( dummy );
+ m_toolMgr->RunAction( COMMON_ACTIONS::trackViaSizeChanged );
+
+ return 0;
+}
+
+
+int PCB_EDITOR_CONTROL::ViaSizeDec( const TOOL_EVENT& aEvent )
+{
+ BOARD* board = getModel<BOARD>();
+ int sizeIndex = board->GetDesignSettings().GetViaSizeIndex() - 1;
+
+ if( sizeIndex < 0 )
+ sizeIndex = 0;
+
+ board->GetDesignSettings().SetViaSizeIndex( sizeIndex );
+ board->GetDesignSettings().UseCustomTrackViaSize( false );
+
+ wxUpdateUIEvent dummy;
+ m_frame->OnUpdateSelectViaSize( dummy );
+ m_toolMgr->RunAction( COMMON_ACTIONS::trackViaSizeChanged );
+
+ return 0;
+}
+
+
+int PCB_EDITOR_CONTROL::PlaceModule( const TOOL_EVENT& aEvent )
+{
+ MODULE* module = NULL;
+ KIGFX::VIEW* view = getView();
+ KIGFX::VIEW_CONTROLS* controls = getViewControls();
+ BOARD* board = getModel<BOARD>();
+
+ // Add a VIEW_GROUP that serves as a preview for the new item
+ KIGFX::VIEW_GROUP preview( view );
+ view->Add( &preview );
+
+ m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true );
+ controls->ShowCursor( true );
+ controls->SetSnapping( true );
+
+ Activate();
+ m_frame->SetToolID( ID_PCB_MODULE_BUTT, wxCURSOR_HAND, _( "Add footprint" ) );
+
+ // Main loop: keep receiving events
+ while( OPT_TOOL_EVENT evt = Wait() )
+ {
+ VECTOR2I cursorPos = controls->GetCursorPosition();
+
+ if( evt->IsCancel() || evt->IsActivate() )
+ {
+ if( module )
+ {
+ board->Delete( module ); // it was added by LoadModuleFromLibrary()
+ module = NULL;
+
+ preview.Clear();
+ preview.ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+ controls->ShowCursor( true );
+ }
+ else
+ break;
+
+ if( evt->IsActivate() ) // now finish unconditionally
+ break;
+ }
+
+ else if( module && evt->Category() == TC_COMMAND )
+ {
+ if( evt->IsAction( &COMMON_ACTIONS::rotate ) )
+ {
+ module->Rotate( module->GetPosition(), m_frame->GetRotationAngle() );
+ preview.ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+ }
+ else if( evt->IsAction( &COMMON_ACTIONS::flip ) )
+ {
+ module->Flip( module->GetPosition() );
+ preview.ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+ }
+ }
+
+ else if( evt->IsClick( BUT_LEFT ) )
+ {
+ if( !module )
+ {
+ // Pick the module to be placed
+ module = m_frame->LoadModuleFromLibrary( wxEmptyString,
+ m_frame->Prj().PcbFootprintLibs(),
+ true, NULL );
+ if( module == NULL )
+ continue;
+
+ module->SetPosition( wxPoint( cursorPos.x, cursorPos.y ) );
+
+ // Add all the drawable parts to preview
+ preview.Add( module );
+ module->RunOnChildren( boost::bind( &KIGFX::VIEW_GROUP::Add, &preview, _1 ) );
+
+ preview.ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+ }
+ else
+ {
+ // Place the selected module
+ module->RunOnChildren( boost::bind( &KIGFX::VIEW::Add, view, _1 ) );
+ view->Add( module );
+ module->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+
+ m_frame->OnModify();
+ m_frame->SaveCopyInUndoList( module, UR_NEW );
+
+ // Remove from preview
+ preview.Remove( module );
+ module->RunOnChildren( boost::bind( &KIGFX::VIEW_GROUP::Remove, &preview, _1 ) );
+ module = NULL; // to indicate that there is no module that we currently modify
+ }
+
+ bool placing = ( module != NULL );
+
+ controls->SetAutoPan( placing );
+ controls->CaptureCursor( placing );
+ controls->ShowCursor( !placing );
+ }
+
+ else if( module && evt->IsMotion() )
+ {
+ module->SetPosition( wxPoint( cursorPos.x, cursorPos.y ) );
+ preview.ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+ }
+ }
+
+ controls->ShowCursor( false );
+ controls->SetSnapping( false );
+ controls->SetAutoPan( false );
+ controls->CaptureCursor( false );
+ view->Remove( &preview );
+
+ m_frame->SetToolID( ID_NO_TOOL_SELECTED, wxCURSOR_DEFAULT, wxEmptyString );
+
+ return 0;
+}
+
+
+int PCB_EDITOR_CONTROL::ToggleLockModule( const TOOL_EVENT& aEvent )
+{
+ SELECTION_TOOL* selTool = m_toolMgr->GetTool<SELECTION_TOOL>();
+ const SELECTION& selection = selTool->GetSelection();
+ bool clearSelection = selection.Empty();
+
+ if( clearSelection )
+ m_toolMgr->RunAction( COMMON_ACTIONS::selectionCursor, true );
+
+ for( int i = 0; i < selection.Size(); ++i )
+ {
+ if( selection.Item<BOARD_ITEM>( i )->Type() == PCB_MODULE_T )
+ {
+ MODULE* module = selection.Item<MODULE>( i );
+ module->SetLocked( !module->IsLocked() );
+ }
+ }
+
+ if( clearSelection )
+ m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true );
+
+ return 0;
+}
+
+
+int PCB_EDITOR_CONTROL::PlaceTarget( const TOOL_EVENT& aEvent )
+{
+ KIGFX::VIEW* view = getView();
+ KIGFX::VIEW_CONTROLS* controls = getViewControls();
+ BOARD* board = getModel<BOARD>();
+ PCB_TARGET* target = new PCB_TARGET( board );
+
+ // Init the new item attributes
+ target->SetLayer( Edge_Cuts );
+ target->SetWidth( board->GetDesignSettings().m_EdgeSegmentWidth );
+ target->SetSize( Millimeter2iu( 5 ) );
+ VECTOR2I cursorPos = controls->GetCursorPosition();
+ target->SetPosition( wxPoint( cursorPos.x, cursorPos.y ) );
+
+ // Add a VIEW_GROUP that serves as a preview for the new item
+ KIGFX::VIEW_GROUP preview( view );
+ preview.Add( target );
+ view->Add( &preview );
+ preview.ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+
+ m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true );
+ controls->SetSnapping( true );
+
+ Activate();
+ m_frame->SetToolID( ID_PCB_MIRE_BUTT, wxCURSOR_PENCIL, _( "Add layer alignment target" ) );
+
+ // Main loop: keep receiving events
+ while( OPT_TOOL_EVENT evt = Wait() )
+ {
+ cursorPos = controls->GetCursorPosition();
+
+ if( evt->IsCancel() || evt->IsActivate() )
+ break;
+
+ else if( evt->IsAction( &COMMON_ACTIONS::incWidth ) )
+ {
+ target->SetWidth( target->GetWidth() + WIDTH_STEP );
+ preview.ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+ }
+
+ else if( evt->IsAction( &COMMON_ACTIONS::decWidth ) )
+ {
+ int width = target->GetWidth();
+
+ if( width > WIDTH_STEP )
+ {
+ target->SetWidth( width - WIDTH_STEP );
+ preview.ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+ }
+ }
+
+ else if( evt->IsClick( BUT_LEFT ) )
+ {
+ assert( target->GetSize() > 0 );
+ assert( target->GetWidth() > 0 );
+
+ view->Add( target );
+ board->Add( target );
+ target->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+
+ m_frame->OnModify();
+ m_frame->SaveCopyInUndoList( target, UR_NEW );
+
+ preview.Remove( target );
+
+ // Create next PCB_TARGET
+ target = new PCB_TARGET( *target );
+ preview.Add( target );
+ }
+
+ else if( evt->IsMotion() )
+ {
+ target->SetPosition( wxPoint( cursorPos.x, cursorPos.y ) );
+ preview.ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+ }
+ }
+
+ delete target;
+
+ controls->SetSnapping( false );
+ view->Remove( &preview );
+
+ m_frame->SetToolID( ID_NO_TOOL_SELECTED, wxCURSOR_DEFAULT, wxEmptyString );
+
+ return 0;
+}
+
+
+// Zone actions
+int PCB_EDITOR_CONTROL::ZoneFill( const TOOL_EVENT& aEvent )
+{
+ SELECTION_TOOL* selTool = m_toolMgr->GetTool<SELECTION_TOOL>();
+ const SELECTION& selection = selTool->GetSelection();
+ RN_DATA* ratsnest = getModel<BOARD>()->GetRatsnest();
+
+ for( int i = 0; i < selection.Size(); ++i )
+ {
+ assert( selection.Item<BOARD_ITEM>( i )->Type() == PCB_ZONE_AREA_T );
+
+ ZONE_CONTAINER* zone = selection.Item<ZONE_CONTAINER>( i );
+ m_frame->Fill_Zone( zone );
+ zone->SetIsFilled( true );
+ ratsnest->Update( zone );
+ zone->ViewUpdate();
+ }
+
+ ratsnest->Recalculate();
+
+ return 0;
+}
+
+
+int PCB_EDITOR_CONTROL::ZoneFillAll( const TOOL_EVENT& aEvent )
+{
+ BOARD* board = getModel<BOARD>();
+ RN_DATA* ratsnest = board->GetRatsnest();
+
+ for( int i = 0; i < board->GetAreaCount(); ++i )
+ {
+ ZONE_CONTAINER* zone = board->GetArea( i );
+ m_frame->Fill_Zone( zone );
+ zone->SetIsFilled( true );
+ ratsnest->Update( zone );
+ zone->ViewUpdate();
+ }
+
+ ratsnest->Recalculate();
+
+ return 0;
+}
+
+
+int PCB_EDITOR_CONTROL::ZoneUnfill( const TOOL_EVENT& aEvent )
+{
+ SELECTION_TOOL* selTool = m_toolMgr->GetTool<SELECTION_TOOL>();
+ const SELECTION& selection = selTool->GetSelection();
+ RN_DATA* ratsnest = getModel<BOARD>()->GetRatsnest();
+
+ for( int i = 0; i < selection.Size(); ++i )
+ {
+ assert( selection.Item<BOARD_ITEM>( i )->Type() == PCB_ZONE_AREA_T );
+
+ ZONE_CONTAINER* zone = selection.Item<ZONE_CONTAINER>( i );
+ zone->SetIsFilled( false );
+ zone->ClearFilledPolysList();
+ ratsnest->Update( zone );
+ zone->ViewUpdate();
+ }
+
+ ratsnest->Recalculate();
+
+ return 0;
+}
+
+
+int PCB_EDITOR_CONTROL::ZoneUnfillAll( const TOOL_EVENT& aEvent )
+{
+ BOARD* board = getModel<BOARD>();
+ RN_DATA* ratsnest = board->GetRatsnest();
+
+ for( int i = 0; i < board->GetAreaCount(); ++i )
+ {
+ ZONE_CONTAINER* zone = board->GetArea( i );
+ zone->SetIsFilled( false );
+ zone->ClearFilledPolysList();
+ ratsnest->Update( zone );
+ zone->ViewUpdate();
+ }
+
+ ratsnest->Recalculate();
+
+ return 0;
+}
+
+
+int PCB_EDITOR_CONTROL::ZoneMerge( const TOOL_EVENT& aEvent )
+{
+ SELECTION selection = m_toolMgr->GetTool<SELECTION_TOOL>()->GetSelection();
+ BOARD* board = getModel<BOARD>();
+ RN_DATA* ratsnest = board->GetRatsnest();
+ KIGFX::VIEW* view = getView();
+
+ if( selection.Size() < 2 )
+ return 0;
+
+ PICKED_ITEMS_LIST changes;
+ int netcode = -1;
+
+ // Loop through all combinations
+ for( int ia1 = 0; ia1 < selection.Size() - 1; ++ia1 )
+ {
+ ZONE_CONTAINER* curr_area = dynamic_cast<ZONE_CONTAINER*>( selection.Item<EDA_ITEM>( ia1 ) );
+
+ if( !curr_area )
+ continue;
+
+ netcode = curr_area->GetNetCode();
+
+ EDA_RECT b1 = curr_area->Outline()->GetBoundingBox();
+ bool mod_ia1 = false;
+
+ for( int ia2 = selection.Size() - 1; ia2 > ia1; --ia2 )
+ {
+ ZONE_CONTAINER* area2 = dynamic_cast<ZONE_CONTAINER*>( selection.Item<EDA_ITEM>( ia2 ) );
+
+ if( !area2 )
+ continue;
+
+ if( area2->GetNetCode() != netcode )
+ continue;
+
+ if( curr_area->GetPriority() != area2->GetPriority() )
+ continue;
+
+ if( curr_area->GetIsKeepout() != area2->GetIsKeepout() )
+ continue;
+
+ if( curr_area->GetLayer() != area2->GetLayer() )
+ continue;
+
+ EDA_RECT b2 = area2->Outline()->GetBoundingBox();
+
+ if( b1.Intersects( b2 ) )
+ {
+ EDA_ITEM* backup = curr_area->Clone();
+ bool ret = board->TestAreaIntersection( curr_area, area2 );
+
+ if( ret && board->CombineAreas( &changes, curr_area, area2 ) )
+ {
+ mod_ia1 = true;
+ selection.items.RemovePicker( ia2 );
+
+ ITEM_PICKER picker( curr_area, UR_CHANGED );
+ picker.SetLink( backup );
+ changes.PushItem( picker );
+ }
+ else
+ {
+ delete backup;
+ }
+ }
+ }
+
+ if( mod_ia1 )
+ --ia1; // if modified, we need to check it again
+ }
+
+ m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true );
+ m_frame->SaveCopyInUndoList( changes, UR_UNSPECIFIED );
+
+ for( unsigned i = 0; i < changes.GetCount(); ++i )
+ {
+ ITEM_PICKER picker = changes.GetItemWrapper( i );
+ BOARD_ITEM* item = static_cast<BOARD_ITEM*>( picker.GetItem() );
+
+ if( picker.GetStatus() == UR_DELETED )
+ {
+ view->Remove( item );
+ ratsnest->Remove( item );
+ }
+ else if( picker.GetStatus() == UR_CHANGED )
+ {
+ item->ViewUpdate( KIGFX::VIEW_ITEM::ALL );
+ m_toolMgr->RunAction( COMMON_ACTIONS::selectItem, true, item );
+ }
+ }
+
+ return 0;
+}
+
+
+int PCB_EDITOR_CONTROL::CrossProbePcbToSch( const TOOL_EVENT& aEvent )
+{
+ if( m_probingSchToPcb )
+ {
+ m_probingSchToPcb = false;
+ return 0;
+ }
+
+ SELECTION_TOOL* selTool = m_toolMgr->GetTool<SELECTION_TOOL>();
+ const SELECTION& selection = selTool->GetSelection();
+
+ if( selection.Size() == 1 )
+ m_frame->SendMessageToEESCHEMA( selection.Item<BOARD_ITEM>( 0 ) );
+
+ return 0;
+}
+
+
+int PCB_EDITOR_CONTROL::CrossProbeSchToPcb( const TOOL_EVENT& aEvent )
+{
+ BOARD_ITEM* item = aEvent.Parameter<BOARD_ITEM*>();
+
+ if( item )
+ {
+ m_probingSchToPcb = true;
+ getView()->SetCenter( VECTOR2D( item->GetPosition() ) );
+ m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true );
+
+ // If it is a pad and the net highlighting tool is enabled, highlight the net
+ if( item->Type() == PCB_PAD_T && m_frame->GetToolId() == ID_PCB_HIGHLIGHT_BUTT )
+ {
+ int net = static_cast<D_PAD*>( item )->GetNetCode();
+ m_toolMgr->RunAction( COMMON_ACTIONS::highlightNet, false, net );
+ }
+ else
+ // Otherwise simply select the corresponding item
+ {
+ m_toolMgr->RunAction( COMMON_ACTIONS::selectItem, true, item );
+ }
+ }
+
+ return 0;
+}
+
+
+static bool setDrillOrigin( KIGFX::VIEW* aView, PCB_BASE_FRAME* aFrame,
+ KIGFX::ORIGIN_VIEWITEM* aItem, const VECTOR2D& aPosition )
+{
+ aFrame->SetAuxOrigin( wxPoint( aPosition.x, aPosition.y ) );
+ aItem->SetPosition( aPosition );
+ aView->MarkDirty();
+
+ return true;
+}
+
+
+int PCB_EDITOR_CONTROL::DrillOrigin( const TOOL_EVENT& aEvent )
+{
+ Activate();
+
+ PICKER_TOOL* picker = m_toolMgr->GetTool<PICKER_TOOL>();
+ assert( picker );
+
+ m_frame->SetToolID( ID_PCB_PLACE_OFFSET_COORD_BUTT, wxCURSOR_PENCIL, _( "Adjust zero" ) );
+ picker->SetClickHandler( boost::bind( setDrillOrigin, getView(), m_frame, m_placeOrigin, _1 ) );
+ picker->Activate();
+ Wait();
+
+ return 0;
+}
+
+/**
+ * Function highlightNet()
+ * Looks for a BOARD_CONNECTED_ITEM in a given spot, and if one is found - it enables
+ * highlight for its net.
+ * @param aPoint is the point where an item is expected (world coordinates).
+ */
+static bool highlightNet( TOOL_MANAGER* aToolMgr, const VECTOR2D& aPosition )
+{
+ KIGFX::RENDER_SETTINGS* render = aToolMgr->GetView()->GetPainter()->GetSettings();
+ GENERAL_COLLECTORS_GUIDE guide = static_cast<PCB_BASE_FRAME*>( aToolMgr->GetEditFrame() )->GetCollectorsGuide();
+ BOARD* board = static_cast<BOARD*>( aToolMgr->GetModel() );
+ GENERAL_COLLECTOR collector;
+ int net = -1;
+
+ // Find a connected item for which we are going to highlight a net
+ collector.Collect( board, GENERAL_COLLECTOR::PadsTracksOrZones,
+ wxPoint( aPosition.x, aPosition.y ), guide );
+ bool enableHighlight = ( collector.GetCount() > 0 );
+
+ // Obtain net code for the clicked item
+ if( enableHighlight )
+ net = static_cast<BOARD_CONNECTED_ITEM*>( collector[0] )->GetNetCode();
+
+ // Toggle highlight when the same net was picked
+ if( net > 0 && net == render->GetHighlightNetCode() )
+ enableHighlight = !render->IsHighlightEnabled();
+
+ if( enableHighlight != render->IsHighlightEnabled() || net != render->GetHighlightNetCode() )
+ {
+ render->SetHighlight( enableHighlight, net );
+ aToolMgr->GetView()->UpdateAllLayersColor();
+ }
+
+ return true;
+}
+
+
+int PCB_EDITOR_CONTROL::HighlightNet( const TOOL_EVENT& aEvent )
+{
+ int netcode = aEvent.Parameter<long>();
+
+ if( netcode > 0 )
+ {
+ KIGFX::RENDER_SETTINGS* render = m_toolMgr->GetView()->GetPainter()->GetSettings();
+ render->SetHighlight( true, netcode );
+ m_toolMgr->GetView()->UpdateAllLayersColor();
+ }
+ else
+ {
+ // No net code specified, pick the net code belonging to the item under the cursor
+ highlightNet( m_toolMgr, getView()->ToWorld( getViewControls()->GetMousePosition() ) );
+ }
+
+ return 0;
+}
+
+
+int PCB_EDITOR_CONTROL::HighlightNetCursor( const TOOL_EVENT& aEvent )
+{
+ Activate();
+
+ PICKER_TOOL* picker = m_toolMgr->GetTool<PICKER_TOOL>();
+ assert( picker );
+
+ m_frame->SetToolID( ID_PCB_HIGHLIGHT_BUTT, wxCURSOR_PENCIL, _( "Highlight net" ) );
+ picker->SetClickHandler( boost::bind( highlightNet, m_toolMgr, _1 ) );
+ picker->SetSnapping( false );
+ picker->Activate();
+ Wait();
+
+ return 0;
+}
+
+
+void PCB_EDITOR_CONTROL::SetTransitions()
+{
+ // Track & via size control
+ Go( &PCB_EDITOR_CONTROL::TrackWidthInc, COMMON_ACTIONS::trackWidthInc.MakeEvent() );
+ Go( &PCB_EDITOR_CONTROL::TrackWidthDec, COMMON_ACTIONS::trackWidthDec.MakeEvent() );
+ Go( &PCB_EDITOR_CONTROL::ViaSizeInc, COMMON_ACTIONS::viaSizeInc.MakeEvent() );
+ Go( &PCB_EDITOR_CONTROL::ViaSizeDec, COMMON_ACTIONS::viaSizeDec.MakeEvent() );
+
+ // Zone actions
+ Go( &PCB_EDITOR_CONTROL::ZoneFill, COMMON_ACTIONS::zoneFill.MakeEvent() );
+ Go( &PCB_EDITOR_CONTROL::ZoneFillAll, COMMON_ACTIONS::zoneFillAll.MakeEvent() );
+ Go( &PCB_EDITOR_CONTROL::ZoneUnfill, COMMON_ACTIONS::zoneUnfill.MakeEvent() );
+ Go( &PCB_EDITOR_CONTROL::ZoneUnfillAll, COMMON_ACTIONS::zoneUnfillAll.MakeEvent() );
+ Go( &PCB_EDITOR_CONTROL::ZoneMerge, COMMON_ACTIONS::zoneMerge.MakeEvent() );
+
+ // Placing tools
+ Go( &PCB_EDITOR_CONTROL::PlaceTarget, COMMON_ACTIONS::placeTarget.MakeEvent() );
+ Go( &PCB_EDITOR_CONTROL::PlaceModule, COMMON_ACTIONS::placeModule.MakeEvent() );
+
+ // Other
+ Go( &PCB_EDITOR_CONTROL::ToggleLockModule, COMMON_ACTIONS::toggleLockModule.MakeEvent() );
+ Go( &PCB_EDITOR_CONTROL::CrossProbePcbToSch, SELECTION_TOOL::SelectedEvent );
+ Go( &PCB_EDITOR_CONTROL::CrossProbeSchToPcb, COMMON_ACTIONS::crossProbeSchToPcb.MakeEvent() );
+ Go( &PCB_EDITOR_CONTROL::DrillOrigin, COMMON_ACTIONS::drillOrigin.MakeEvent() );
+ Go( &PCB_EDITOR_CONTROL::HighlightNet, COMMON_ACTIONS::highlightNet.MakeEvent() );
+ Go( &PCB_EDITOR_CONTROL::HighlightNetCursor, COMMON_ACTIONS::highlightNetCursor.MakeEvent() );
+}
+
+
+const int PCB_EDITOR_CONTROL::WIDTH_STEP = 100000;
diff --git a/pcbnew/tools/pcb_editor_control.h b/pcbnew/tools/pcb_editor_control.h
new file mode 100644
index 0000000..51de402
--- /dev/null
+++ b/pcbnew/tools/pcb_editor_control.h
@@ -0,0 +1,116 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2014 CERN
+ * @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
+ */
+
+#ifndef PCB_EDITOR_CONTROL_H
+#define PCB_EDITOR_CONTROL_H
+
+#include <tool/tool_interactive.h>
+
+namespace KIGFX {
+ class ORIGIN_VIEWITEM;
+}
+
+class PCB_EDIT_FRAME;
+class ZONE_CONTEXT_MENU;
+
+/**
+ * Class PCB_EDITOR_CONTROL
+ *
+ * Handles actions specific to the board editor in pcbnew.
+ */
+class PCB_EDITOR_CONTROL : public TOOL_INTERACTIVE
+{
+public:
+ PCB_EDITOR_CONTROL();
+ ~PCB_EDITOR_CONTROL();
+
+ /// @copydoc TOOL_INTERACTIVE::Reset()
+ void Reset( RESET_REASON aReason );
+
+ /// @copydoc TOOL_INTERACTIVE::Init()
+ bool Init();
+
+ // Track & via size control
+ int TrackWidthInc( const TOOL_EVENT& aEvent );
+ int TrackWidthDec( const TOOL_EVENT& aEvent );
+ int ViaSizeInc( const TOOL_EVENT& aEvent );
+ int ViaSizeDec( const TOOL_EVENT& aEvent );
+
+ // Zone actions
+ int ZoneFill( const TOOL_EVENT& aEvent );
+ int ZoneFillAll( const TOOL_EVENT& aEvent );
+ int ZoneUnfill( const TOOL_EVENT& aEvent );
+ int ZoneUnfillAll( const TOOL_EVENT& aEvent );
+ int ZoneMerge( const TOOL_EVENT& aEvent );
+
+ /**
+ * Function PlaceTarget()
+ * Allows user to place a layer alignment target.
+ */
+ int PlaceTarget( const TOOL_EVENT& aEvent );
+
+ /**
+ * Function PlaceModule()
+ * Displays a dialog to select a module to be added and allows the user to set its position.
+ */
+ int PlaceModule( const TOOL_EVENT& aEvent );
+
+ ///> (Un)locks module.
+ int ToggleLockModule( const TOOL_EVENT& aEvent );
+
+ ///> Notifies eeschema about the selected item.
+ int CrossProbePcbToSch( const TOOL_EVENT& aEvent );
+
+ ///> Reacts to selection change in eeschema.
+ int CrossProbeSchToPcb( const TOOL_EVENT& aEvent );
+
+ ///> Places the origin point for drill and pick-and-place files.
+ int DrillOrigin( const TOOL_EVENT& aEvent );
+
+ ///> Highlights net belonging to the item under the cursor.
+ int HighlightNet( const TOOL_EVENT& aEvent );
+
+ ///> Launches a tool to pick the item whose net is going to be highlighted.
+ int HighlightNetCursor( const TOOL_EVENT& aEvent );
+
+ ///> Sets up handlers for various events.
+ void SetTransitions();
+
+private:
+ ///> Pointer to the currently used edit frame.
+ PCB_EDIT_FRAME* m_frame;
+
+ ///> Place & drill origin marker.
+ KIGFX::ORIGIN_VIEWITEM* m_placeOrigin;
+
+ ///> Flag to ignore a single crossprobe message from eeschema.
+ bool m_probingSchToPcb;
+
+ // How does line width change after one -/+ key press.
+ static const int WIDTH_STEP;
+
+ ZONE_CONTEXT_MENU* m_zoneMenu;
+};
+
+#endif
diff --git a/pcbnew/tools/pcbnew_control.cpp b/pcbnew/tools/pcbnew_control.cpp
new file mode 100644
index 0000000..1afe10f
--- /dev/null
+++ b/pcbnew/tools/pcbnew_control.cpp
@@ -0,0 +1,1014 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2014-2016 CERN
+ * @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 "pcbnew_control.h"
+#include "common_actions.h"
+#include "selection_tool.h"
+#include "picker_tool.h"
+#include "grid_helper.h"
+
+#include <class_board.h>
+#include <class_module.h>
+#include <class_track.h>
+#include <class_zone.h>
+#include <class_pcb_screen.h>
+
+#include <confirm.h>
+#include <hotkeys_basic.h>
+#include <io_mgr.h>
+
+#include <pcbnew_id.h>
+#include <wxPcbStruct.h>
+#include <pcb_draw_panel_gal.h>
+#include <ratsnest_data.h>
+#include <tool/tool_manager.h>
+#include <gal/graphics_abstraction_layer.h>
+#include <view/view_controls.h>
+#include <pcb_painter.h>
+#include <origin_viewitem.h>
+
+#include <boost/bind.hpp>
+
+
+// files.cpp
+extern bool AskLoadBoardFileName( wxWindow* aParent, int* aCtl, wxString* aFileName,
+ bool aKicadFilesOnly = false );
+extern IO_MGR::PCB_FILE_T plugin_type( const wxString& aFileName, int aCtl );
+
+
+PCBNEW_CONTROL::PCBNEW_CONTROL() :
+ TOOL_INTERACTIVE( "pcbnew.Control" ), m_frame( NULL )
+{
+ m_gridOrigin = new KIGFX::ORIGIN_VIEWITEM();
+}
+
+
+PCBNEW_CONTROL::~PCBNEW_CONTROL()
+{
+ delete m_gridOrigin;
+}
+
+
+void PCBNEW_CONTROL::Reset( RESET_REASON aReason )
+{
+ m_frame = getEditFrame<PCB_BASE_FRAME>();
+
+ if( aReason == MODEL_RELOAD || aReason == GAL_SWITCH )
+ {
+ m_gridOrigin->SetPosition( getModel<BOARD>()->GetGridOrigin() );
+ getView()->Remove( m_gridOrigin );
+ getView()->Add( m_gridOrigin );
+ }
+}
+
+
+int PCBNEW_CONTROL::ZoomInOut( const TOOL_EVENT& aEvent )
+{
+ KIGFX::VIEW* view = m_frame->GetGalCanvas()->GetView();
+ KIGFX::VIEW_CONTROLS* ctls = getViewControls();
+ double zoomScale = 1.0;
+
+ if( aEvent.IsAction( &COMMON_ACTIONS::zoomIn ) )
+ zoomScale = 1.3;
+ else if( aEvent.IsAction( &COMMON_ACTIONS::zoomOut ) )
+ zoomScale = 0.7;
+
+ view->SetScale( view->GetScale() * zoomScale, getViewControls()->GetCursorPosition() );
+
+ if( ctls->IsCursorWarpingEnabled() )
+ ctls->CenterOnCursor();
+
+ return 0;
+}
+
+
+int PCBNEW_CONTROL::ZoomInOutCenter( const TOOL_EVENT& aEvent )
+{
+ KIGFX::VIEW* view = getView();
+ double zoomScale = 1.0;
+
+ if( aEvent.IsAction( &COMMON_ACTIONS::zoomInCenter ) )
+ zoomScale = 1.3;
+ else if( aEvent.IsAction( &COMMON_ACTIONS::zoomOutCenter ) )
+ zoomScale = 0.7;
+
+ view->SetScale( view->GetScale() * zoomScale );
+
+ return 0;
+}
+
+
+int PCBNEW_CONTROL::ZoomCenter( const TOOL_EVENT& aEvent )
+{
+ KIGFX::VIEW_CONTROLS* ctls = getViewControls();
+
+ if( ctls->IsCursorWarpingEnabled() )
+ ctls->CenterOnCursor();
+ else
+ getView()->SetCenter( getViewControls()->GetCursorPosition() );
+
+ return 0;
+}
+
+
+int PCBNEW_CONTROL::ZoomFitScreen( const TOOL_EVENT& aEvent )
+{
+ KIGFX::VIEW* view = getView();
+ EDA_DRAW_PANEL_GAL* galCanvas = m_frame->GetGalCanvas();
+ BOARD* board = getModel<BOARD>();
+ board->ComputeBoundingBox();
+
+ BOX2I boardBBox = board->ViewBBox();
+ VECTOR2D scrollbarSize = VECTOR2D( galCanvas->GetSize() - galCanvas->GetClientSize() );
+ VECTOR2D screenSize = view->ToWorld( galCanvas->GetClientSize(), false );
+
+ if( boardBBox.GetWidth() == 0 || boardBBox.GetHeight() == 0 )
+ {
+ // Empty view
+ view->SetScale( 17.0 ); // works fine for the standard worksheet frame
+
+ view->SetCenter( screenSize / 2.0 );
+ }
+ else
+ {
+ VECTOR2D vsize = boardBBox.GetSize();
+ double scale = view->GetScale() / std::max( fabs( vsize.x / screenSize.x ),
+ fabs( vsize.y / screenSize.y ) );
+
+ view->SetScale( scale );
+ view->SetCenter( boardBBox.Centre() );
+ }
+
+
+ // Take scrollbars into account
+ VECTOR2D worldScrollbarSize = view->ToWorld( scrollbarSize, false );
+ view->SetCenter( view->GetCenter() + worldScrollbarSize / 2.0 );
+
+ return 0;
+}
+
+
+int PCBNEW_CONTROL::ZoomPreset( const TOOL_EVENT& aEvent )
+{
+ unsigned int idx = aEvent.Parameter<long>();
+ std::vector<double>& zoomList = m_frame->GetScreen()->m_ZoomList;
+ KIGFX::VIEW* view = m_frame->GetGalCanvas()->GetView();
+ KIGFX::GAL* gal = m_frame->GetGalCanvas()->GetGAL();
+
+ m_frame->SetPresetZoom( idx );
+
+ if( idx == 0 ) // Zoom Auto
+ {
+ return ZoomFitScreen( aEvent );
+ }
+ else if( idx >= zoomList.size() )
+ {
+ assert( false );
+ return 0;
+ }
+
+ double selectedZoom = zoomList[idx];
+ double zoomFactor = gal->GetWorldScale() / gal->GetZoomFactor();
+ view->SetScale( 1.0 / ( zoomFactor * selectedZoom ) );
+
+ return 0;
+}
+
+
+int PCBNEW_CONTROL::TrackDisplayMode( const TOOL_EVENT& aEvent )
+{
+ KIGFX::PCB_PAINTER* painter =
+ static_cast<KIGFX::PCB_PAINTER*>( m_frame->GetGalCanvas()->GetView()->GetPainter() );
+ KIGFX::PCB_RENDER_SETTINGS* settings =
+ static_cast<KIGFX::PCB_RENDER_SETTINGS*>( painter->GetSettings() );
+
+ // Apply new display options to the GAL canvas
+ DISPLAY_OPTIONS* displ_opts = (DISPLAY_OPTIONS*)m_frame->GetDisplayOptions();
+ displ_opts->m_DisplayPcbTrackFill = !displ_opts->m_DisplayPcbTrackFill;
+ settings->LoadDisplayOptions( displ_opts );
+
+ for( TRACK* track = getModel<BOARD>()->m_Track; track; track = track->Next() )
+ {
+ if( track->Type() == PCB_TRACE_T )
+ track->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+ }
+
+ m_frame->GetGalCanvas()->Refresh();
+
+ return 0;
+}
+
+
+int PCBNEW_CONTROL::PadDisplayMode( const TOOL_EVENT& aEvent )
+{
+ KIGFX::PCB_PAINTER* painter =
+ static_cast<KIGFX::PCB_PAINTER*>( m_frame->GetGalCanvas()->GetView()->GetPainter() );
+ KIGFX::PCB_RENDER_SETTINGS* settings =
+ static_cast<KIGFX::PCB_RENDER_SETTINGS*>( painter->GetSettings() );
+ DISPLAY_OPTIONS* displ_opts = (DISPLAY_OPTIONS*)m_frame->GetDisplayOptions();
+
+ // Apply new display options to the GAL canvas
+ displ_opts->m_DisplayPadFill = !displ_opts->m_DisplayPadFill;
+ settings->LoadDisplayOptions( displ_opts );
+
+ for( MODULE* module = getModel<BOARD>()->m_Modules; module; module = module->Next() )
+ {
+ for( D_PAD* pad = module->Pads(); pad; pad = pad->Next() )
+ pad->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+ }
+
+ m_frame->GetGalCanvas()->Refresh();
+
+ return 0;
+}
+
+
+int PCBNEW_CONTROL::ViaDisplayMode( const TOOL_EVENT& aEvent )
+{
+ KIGFX::PCB_PAINTER* painter =
+ static_cast<KIGFX::PCB_PAINTER*>( m_frame->GetGalCanvas()->GetView()->GetPainter() );
+ KIGFX::PCB_RENDER_SETTINGS* settings =
+ static_cast<KIGFX::PCB_RENDER_SETTINGS*>( painter->GetSettings() );
+ DISPLAY_OPTIONS* displ_opts = (DISPLAY_OPTIONS*)m_frame->GetDisplayOptions();
+
+ // Apply new display options to the GAL canvas
+ displ_opts->m_DisplayViaFill = !displ_opts->m_DisplayViaFill;
+ settings->LoadDisplayOptions( displ_opts );
+
+ for( TRACK* track = getModel<BOARD>()->m_Track; track; track = track->Next() )
+ {
+ if( track->Type() == PCB_VIA_T )
+ track->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+ }
+
+ m_frame->GetGalCanvas()->Refresh();
+
+ return 0;
+}
+
+
+int PCBNEW_CONTROL::ZoneDisplayMode( const TOOL_EVENT& aEvent )
+{
+ KIGFX::PCB_PAINTER* painter =
+ static_cast<KIGFX::PCB_PAINTER*>( m_frame->GetGalCanvas()->GetView()->GetPainter() );
+ KIGFX::PCB_RENDER_SETTINGS* settings =
+ static_cast<KIGFX::PCB_RENDER_SETTINGS*>( painter->GetSettings() );
+ DISPLAY_OPTIONS* displ_opts = (DISPLAY_OPTIONS*)m_frame->GetDisplayOptions();
+
+ // Apply new display options to the GAL canvas
+ if( aEvent.IsAction( &COMMON_ACTIONS::zoneDisplayEnable ) )
+ displ_opts->m_DisplayZonesMode = 0;
+ else if( aEvent.IsAction( &COMMON_ACTIONS::zoneDisplayDisable ) )
+ displ_opts->m_DisplayZonesMode = 1;
+ else if( aEvent.IsAction( &COMMON_ACTIONS::zoneDisplayOutlines ) )
+ displ_opts->m_DisplayZonesMode = 2;
+ else
+ assert( false );
+
+ settings->LoadDisplayOptions( displ_opts );
+
+ BOARD* board = getModel<BOARD>();
+ for( int i = 0; i < board->GetAreaCount(); ++i )
+ board->GetArea( i )->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+
+ m_frame->GetGalCanvas()->Refresh();
+
+ return 0;
+}
+
+
+int PCBNEW_CONTROL::HighContrastMode( const TOOL_EVENT& aEvent )
+{
+ KIGFX::PCB_PAINTER* painter =
+ static_cast<KIGFX::PCB_PAINTER*>( m_frame->GetGalCanvas()->GetView()->GetPainter() );
+ KIGFX::PCB_RENDER_SETTINGS* settings =
+ static_cast<KIGFX::PCB_RENDER_SETTINGS*> ( painter->GetSettings() );
+ DISPLAY_OPTIONS* displ_opts = (DISPLAY_OPTIONS*)m_frame->GetDisplayOptions();
+
+ displ_opts->m_ContrastModeDisplay = !displ_opts->m_ContrastModeDisplay;
+ settings->LoadDisplayOptions( displ_opts );
+ m_frame->GetGalCanvas()->SetHighContrastLayer( m_frame->GetActiveLayer() );
+
+ return 0;
+}
+
+
+int PCBNEW_CONTROL::HighContrastInc( const TOOL_EVENT& aEvent )
+{
+ std::cout << __PRETTY_FUNCTION__ << std::endl;
+
+ return 0;
+}
+
+
+int PCBNEW_CONTROL::HighContrastDec( const TOOL_EVENT& aEvent )
+{
+ std::cout << __PRETTY_FUNCTION__ << std::endl;
+
+ return 0;
+}
+
+
+// Layer control
+int PCBNEW_CONTROL::LayerSwitch( const TOOL_EVENT& aEvent )
+{
+ m_frame->SwitchLayer( NULL, (LAYER_ID) aEvent.Parameter<long>() );
+
+ return 0;
+}
+
+
+int PCBNEW_CONTROL::LayerNext( const TOOL_EVENT& aEvent )
+{
+ PCB_BASE_FRAME* editFrame = m_frame;
+ LAYER_NUM layer = editFrame->GetActiveLayer();
+
+ if( layer < F_Cu || layer > B_Cu )
+ return 0;
+
+ int layerCount = getModel<BOARD>()->GetCopperLayerCount();
+
+ if( layer == layerCount - 2 || layerCount < 2 )
+ layer = B_Cu;
+ else if( layer == B_Cu )
+ layer = F_Cu;
+ else
+ ++layer;
+
+ assert( IsCopperLayer( layer ) );
+ editFrame->SwitchLayer( NULL, ToLAYER_ID( layer ) );
+
+ return 0;
+}
+
+
+int PCBNEW_CONTROL::LayerPrev( const TOOL_EVENT& aEvent )
+{
+ PCB_BASE_FRAME* editFrame = m_frame;
+ LAYER_NUM layer = editFrame->GetActiveLayer();
+
+ if( layer < F_Cu || layer > B_Cu )
+ return 0;
+
+ int layerCount = getModel<BOARD>()->GetCopperLayerCount();
+
+ if( layer == F_Cu || layerCount < 2 )
+ layer = B_Cu;
+ else if( layer == B_Cu )
+ layer = layerCount - 2;
+ else
+ --layer;
+
+ assert( IsCopperLayer( layer ) );
+ editFrame->SwitchLayer( NULL, ToLAYER_ID( layer ) );
+
+ return 0;
+}
+
+
+int PCBNEW_CONTROL::LayerToggle( const TOOL_EVENT& aEvent )
+{
+ LAYER_NUM currentLayer = m_frame->GetActiveLayer();
+ PCB_SCREEN* screen = m_frame->GetScreen();
+
+ if( currentLayer == screen->m_Route_Layer_TOP )
+ m_frame->SwitchLayer( NULL, screen->m_Route_Layer_BOTTOM );
+ else
+ m_frame->SwitchLayer( NULL, screen->m_Route_Layer_TOP );
+
+ return 0;
+}
+
+
+int PCBNEW_CONTROL::LayerAlphaInc( const TOOL_EVENT& aEvent )
+{
+ KIGFX::PCB_PAINTER* painter =
+ static_cast<KIGFX::PCB_PAINTER*>( m_frame->GetGalCanvas()->GetView()->GetPainter() );
+ KIGFX::PCB_RENDER_SETTINGS* settings =
+ static_cast<KIGFX::PCB_RENDER_SETTINGS*> ( painter->GetSettings() );
+
+ LAYER_NUM currentLayer = m_frame->GetActiveLayer();
+ KIGFX::COLOR4D currentColor = settings->GetLayerColor( currentLayer );
+
+ if( currentColor.a <= 0.95 )
+ {
+ currentColor.a += 0.05;
+ settings->SetLayerColor( currentLayer, currentColor );
+ m_frame->GetGalCanvas()->GetView()->UpdateLayerColor( currentLayer );
+ }
+
+ return 0;
+}
+
+
+int PCBNEW_CONTROL::LayerAlphaDec( const TOOL_EVENT& aEvent )
+{
+ KIGFX::PCB_PAINTER* painter =
+ static_cast<KIGFX::PCB_PAINTER*>( m_frame->GetGalCanvas()->GetView()->GetPainter() );
+ KIGFX::PCB_RENDER_SETTINGS* settings =
+ static_cast<KIGFX::PCB_RENDER_SETTINGS*> ( painter->GetSettings() );
+
+ LAYER_NUM currentLayer = m_frame->GetActiveLayer();
+ KIGFX::COLOR4D currentColor = settings->GetLayerColor( currentLayer );
+
+ if( currentColor.a >= 0.05 )
+ {
+ currentColor.a -= 0.05;
+ settings->SetLayerColor( currentLayer, currentColor );
+ m_frame->GetGalCanvas()->GetView()->UpdateLayerColor( currentLayer );
+ }
+
+ return 0;
+}
+
+
+// Cursor control
+int PCBNEW_CONTROL::CursorControl( const TOOL_EVENT& aEvent )
+{
+ long type = aEvent.Parameter<long>();
+ bool fastMove = type & COMMON_ACTIONS::CURSOR_FAST_MOVE;
+ type &= ~COMMON_ACTIONS::CURSOR_FAST_MOVE;
+
+ GRID_HELPER gridHelper( m_frame );
+ VECTOR2D cursor = getViewControls()->GetCursorPosition();
+ VECTOR2I gridSize = gridHelper.GetGrid();
+ VECTOR2D newCursor = gridHelper.Align( cursor );
+
+ if( fastMove )
+ gridSize = gridSize * 10;
+
+ switch( type )
+ {
+ case COMMON_ACTIONS::CURSOR_UP:
+ newCursor -= VECTOR2D( 0, gridSize.y );
+ break;
+
+ case COMMON_ACTIONS::CURSOR_DOWN:
+ newCursor += VECTOR2D( 0, gridSize.y );
+ break;
+
+ case COMMON_ACTIONS::CURSOR_LEFT:
+ newCursor -= VECTOR2D( gridSize.x, 0 );
+ break;
+
+ case COMMON_ACTIONS::CURSOR_RIGHT:
+ newCursor += VECTOR2D( gridSize.x, 0 );
+ break;
+
+ case COMMON_ACTIONS::CURSOR_CLICK: // fall through
+ case COMMON_ACTIONS::CURSOR_DBL_CLICK:
+ {
+ TOOL_ACTIONS action;
+ int modifiers = 0;
+
+ modifiers |= wxGetKeyState( WXK_SHIFT ) ? MD_SHIFT : 0;
+ modifiers |= wxGetKeyState( WXK_CONTROL ) ? MD_CTRL : 0;
+ modifiers |= wxGetKeyState( WXK_ALT ) ? MD_ALT : 0;
+
+ if( type == COMMON_ACTIONS::CURSOR_CLICK )
+ action = TA_MOUSE_CLICK;
+ else if( type == COMMON_ACTIONS::CURSOR_DBL_CLICK )
+ action = TA_MOUSE_DBLCLICK;
+ else
+ assert( false );
+
+ TOOL_EVENT evt( TC_MOUSE, action, BUT_LEFT | modifiers );
+ evt.SetMousePosition( getViewControls()->GetCursorPosition() );
+ m_toolMgr->ProcessEvent( evt );
+
+ return 0;
+ }
+ break;
+ }
+
+ // Handler cursor movement
+ KIGFX::VIEW* view = getView();
+ newCursor = view->ToScreen( newCursor );
+ newCursor.x = KiROUND( newCursor.x );
+ newCursor.y = KiROUND( newCursor.y );
+
+ // Pan the screen if required
+ const VECTOR2I& screenSize = view->GetGAL()->GetScreenPixelSize();
+ BOX2I screenBox( VECTOR2I( 0, 0 ), screenSize );
+
+ if( !screenBox.Contains( newCursor ) )
+ {
+ VECTOR2D delta( 0, 0 );
+
+ if( newCursor.x < screenBox.GetLeft() )
+ {
+ delta.x = newCursor.x - screenBox.GetLeft();
+ newCursor.x = screenBox.GetLeft();
+ }
+ else if( newCursor.x > screenBox.GetRight() )
+ {
+ delta.x = newCursor.x - screenBox.GetRight();
+ // -1 is to keep the cursor within the drawing area,
+ // so the cursor coordinates are still updated
+ newCursor.x = screenBox.GetRight() - 1;
+ }
+
+ if( newCursor.y < screenBox.GetTop() )
+ {
+ delta.y = newCursor.y - screenBox.GetTop();
+ newCursor.y = screenBox.GetTop();
+ }
+ else if( newCursor.y > screenBox.GetBottom() )
+ {
+ delta.y = newCursor.y - screenBox.GetBottom();
+ // -1 is to keep the cursor within the drawing area,
+ // so the cursor coordinates are still updated
+ newCursor.y = screenBox.GetBottom() - 1;
+ }
+
+ view->SetCenter( view->GetCenter() + view->ToWorld( delta, false ) );
+ }
+
+ m_frame->GetGalCanvas()->WarpPointer( newCursor.x, newCursor.y );
+
+ return 0;
+}
+
+
+int PCBNEW_CONTROL::PanControl( const TOOL_EVENT& aEvent )
+{
+ long type = aEvent.Parameter<long>();
+ KIGFX::VIEW* view = getView();
+ GRID_HELPER gridHelper( m_frame );
+ VECTOR2D center = view->GetCenter();
+ VECTOR2I gridSize = gridHelper.GetGrid() * 10;
+
+ switch( type )
+ {
+ case COMMON_ACTIONS::CURSOR_UP:
+ center -= VECTOR2D( 0, gridSize.y );
+ break;
+
+ case COMMON_ACTIONS::CURSOR_DOWN:
+ center += VECTOR2D( 0, gridSize.y );
+ break;
+
+ case COMMON_ACTIONS::CURSOR_LEFT:
+ center -= VECTOR2D( gridSize.x, 0 );
+ break;
+
+ case COMMON_ACTIONS::CURSOR_RIGHT:
+ center += VECTOR2D( gridSize.x, 0 );
+ break;
+
+ default:
+ assert( false );
+ break;
+ }
+
+ view->SetCenter( center );
+
+ return 0;
+}
+
+
+// Grid control
+int PCBNEW_CONTROL::GridFast1( const TOOL_EVENT& aEvent )
+{
+ m_frame->SetFastGrid1();
+ updateGrid();
+
+ return 0;
+}
+
+
+int PCBNEW_CONTROL::GridFast2( const TOOL_EVENT& aEvent )
+{
+ m_frame->SetFastGrid2();
+ updateGrid();
+
+ return 0;
+}
+
+
+int PCBNEW_CONTROL::GridNext( const TOOL_EVENT& aEvent )
+{
+ m_frame->SetNextGrid();
+ updateGrid();
+
+ return 0;
+}
+
+
+int PCBNEW_CONTROL::GridPrev( const TOOL_EVENT& aEvent )
+{
+ m_frame->SetPrevGrid();
+ updateGrid();
+
+ return 0;
+}
+
+
+static bool setOrigin( KIGFX::VIEW* aView, PCB_BASE_FRAME* aFrame,
+ KIGFX::ORIGIN_VIEWITEM* aItem, const VECTOR2D& aPoint )
+{
+ aFrame->SetGridOrigin( wxPoint( aPoint.x, aPoint.y ) );
+ aView->GetGAL()->SetGridOrigin( aPoint );
+ aItem->SetPosition( aPoint );
+ aView->MarkDirty();
+
+ return true;
+}
+
+
+int PCBNEW_CONTROL::GridSetOrigin( const TOOL_EVENT& aEvent )
+{
+ VECTOR2D* origin = aEvent.Parameter<VECTOR2D*>();
+
+ if( origin )
+ {
+ setOrigin( getView(), m_frame, m_gridOrigin, *origin );
+ delete origin;
+ }
+ else
+ {
+ Activate();
+
+ PICKER_TOOL* picker = m_toolMgr->GetTool<PICKER_TOOL>();
+ assert( picker );
+
+ // TODO it will not check the toolbar button in module editor, as it uses a different ID..
+ m_frame->SetToolID( ID_PCB_PLACE_GRID_COORD_BUTT, wxCURSOR_PENCIL, _( "Adjust grid origin" ) );
+ picker->SetClickHandler( boost::bind( setOrigin, getView(), m_frame, m_gridOrigin, _1 ) );
+ picker->Activate();
+ Wait();
+ }
+
+ return 0;
+}
+
+
+int PCBNEW_CONTROL::GridResetOrigin( const TOOL_EVENT& aEvent )
+{
+ getModel<BOARD>()->SetGridOrigin( wxPoint( 0, 0 ) );
+ m_gridOrigin->SetPosition( VECTOR2D( 0, 0 ) );
+
+ return 0;
+}
+
+
+int PCBNEW_CONTROL::GridPreset( const TOOL_EVENT& aEvent )
+{
+ long idx = aEvent.Parameter<long>();
+
+ m_frame->SetPresetGrid( idx );
+ updateGrid();
+
+ return 0;
+}
+
+
+// Miscellaneous
+int PCBNEW_CONTROL::ResetCoords( const TOOL_EVENT& aEvent )
+{
+ VECTOR2I cursorPos = getViewControls()->GetCursorPosition();
+
+ m_frame->GetScreen()->m_O_Curseur = wxPoint( cursorPos.x, cursorPos.y );
+ m_frame->UpdateStatusBar();
+
+ return 0;
+}
+
+
+int PCBNEW_CONTROL::SwitchCursor( const TOOL_EVENT& aEvent )
+{
+ const unsigned int BIG_CURSOR = 8000;
+ const unsigned int SMALL_CURSOR = 80;
+
+ PCB_BASE_FRAME* frame = getEditFrame<PCB_BASE_FRAME>();
+ KIGFX::GAL* gal = frame->GetGalCanvas()->GetGAL();
+ gal->SetCursorSize( frame->GetCursorShape() ? BIG_CURSOR : SMALL_CURSOR );
+
+ return 0;
+}
+
+
+int PCBNEW_CONTROL::SwitchUnits( const TOOL_EVENT& aEvent )
+{
+ // TODO should not it be refactored to pcb_frame member function?
+ wxCommandEvent evt( wxEVT_COMMAND_MENU_SELECTED );
+
+ if( g_UserUnit == INCHES )
+ evt.SetId( ID_TB_OPTIONS_SELECT_UNIT_MM );
+ else
+ evt.SetId( ID_TB_OPTIONS_SELECT_UNIT_INCH );
+
+ m_frame->ProcessEvent( evt );
+
+ return 0;
+}
+
+
+static bool deleteItem( TOOL_MANAGER* aToolMgr, const VECTOR2D& aPosition )
+{
+ SELECTION_TOOL* selectionTool = aToolMgr->GetTool<SELECTION_TOOL>();
+ assert( selectionTool );
+
+ aToolMgr->RunAction( COMMON_ACTIONS::selectionClear, true );
+ aToolMgr->RunAction( COMMON_ACTIONS::selectionCursor, true );
+ selectionTool->SanitizeSelection();
+
+ const SELECTION& selection = selectionTool->GetSelection();
+
+ if( selection.Empty() )
+ return true;
+
+ bool canBeRemoved = ( selection.Item<EDA_ITEM>( 0 )->Type() != PCB_MODULE_T );
+
+ if( canBeRemoved || IsOK( aToolMgr->GetEditFrame(), _( "Are you sure you want to delete item?" ) ) )
+ aToolMgr->RunAction( COMMON_ACTIONS::remove, true );
+ else
+ aToolMgr->RunAction( COMMON_ACTIONS::selectionClear, true );
+
+ return true;
+}
+
+
+int PCBNEW_CONTROL::DeleteItemCursor( const TOOL_EVENT& aEvent )
+{
+ Activate();
+
+ PICKER_TOOL* picker = m_toolMgr->GetTool<PICKER_TOOL>();
+ assert( picker );
+
+ // TODO it will not check the toolbar button in the module editor, as it uses a different ID..
+ m_frame->SetToolID( ID_PCB_DELETE_ITEM_BUTT, wxCURSOR_PENCIL, _( "Delete item" ) );
+ picker->SetSnapping( false );
+ picker->SetClickHandler( boost::bind( deleteItem, m_toolMgr, _1 ) );
+ picker->Activate();
+ Wait();
+
+ return 0;
+}
+
+
+int PCBNEW_CONTROL::AppendBoard( const TOOL_EVENT& aEvent )
+{
+ int open_ctl;
+ wxString fileName;
+ PICKED_ITEMS_LIST undoListPicker;
+ ITEM_PICKER picker( NULL, UR_NEW );
+
+ PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( m_frame );
+ BOARD* board = getModel<BOARD>();
+ KIGFX::VIEW* view = getView();
+
+ if( !editFrame )
+ return 0;
+
+ // Pick a file to append
+ if( !AskLoadBoardFileName( editFrame, &open_ctl, &fileName, true ) )
+ return 0;
+
+ IO_MGR::PCB_FILE_T pluginType = plugin_type( fileName, open_ctl );
+ PLUGIN::RELEASER pi( IO_MGR::PluginFind( pluginType ) );
+
+ // keep track of existing items, in order to know what are the new items
+ // (for undo command for instance)
+
+ // Tracks are inserted, not appended, so mark the existing tracks to know what are the new tracks
+ for( TRACK* track = board->m_Track; track; track = track->Next() )
+ track->SetFlags( FLAG0 );
+
+ // Other items are appended to the item list, so keep trace to the last existing item is enough
+ MODULE* module = board->m_Modules.GetLast();
+ BOARD_ITEM* drawing = board->m_Drawings.GetLast();
+ int zonescount = board->GetAreaCount();
+
+ // Keep also the count of copper layers, to adjust if necessary
+ int initialCopperLayerCount = board->GetCopperLayerCount();
+ LSET initialEnabledLayers = board->GetEnabledLayers();
+
+ // Load the data
+ try
+ {
+ PROPERTIES props;
+ char xbuf[30];
+ char ybuf[30];
+
+ // EAGLE_PLUGIN can use this info to center the BOARD, but it does not yet.
+ sprintf( xbuf, "%d", editFrame->GetPageSizeIU().x );
+ sprintf( ybuf, "%d", editFrame->GetPageSizeIU().y );
+
+ props["page_width"] = xbuf;
+ props["page_height"] = ybuf;
+
+ editFrame->GetDesignSettings().m_NetClasses.Clear();
+ pi->Load( fileName, board, &props );
+ }
+ catch( const IO_ERROR& ioe )
+ {
+ wxString msg = wxString::Format( _( "Error loading board.\n%s" ), GetChars( ioe.errorText ));
+ DisplayError( editFrame, msg );
+
+ return 0;
+ }
+
+ // rebuild nets and ratsnest before any use of nets
+ board->BuildListOfNets();
+ board->GetRatsnest()->Recalculate();
+ board->SynchronizeNetsAndNetClasses();
+
+ m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true );
+
+ // Process the new items
+ for( TRACK* track = board->m_Track; track; track = track->Next() )
+ {
+ if( track->GetFlags() & FLAG0 )
+ {
+ track->ClearFlags( FLAG0 );
+ continue;
+ }
+
+ picker.SetItem( track );
+ undoListPicker.PushItem( picker );
+ view->Add( track );
+ m_toolMgr->RunAction( COMMON_ACTIONS::selectItem, true, track );
+ }
+
+ module = module ? module->Next() : board->m_Modules;
+
+ for( ; module; module = module->Next() )
+ {
+ picker.SetItem( module );
+ undoListPicker.PushItem( picker );
+
+ module->RunOnChildren( boost::bind( &KIGFX::VIEW::Add, view, _1 ) );
+ view->Add( module );
+ m_toolMgr->RunAction( COMMON_ACTIONS::selectItem, true, module );
+ }
+
+ drawing = drawing ? drawing->Next() : board->m_Drawings;
+
+ for( ; drawing; drawing = drawing->Next() )
+ {
+ picker.SetItem( drawing );
+ undoListPicker.PushItem( picker );
+ view->Add( drawing );
+ m_toolMgr->RunAction( COMMON_ACTIONS::selectItem, true, drawing );
+ }
+
+ for( ZONE_CONTAINER* zone = board->GetArea( zonescount ); zone;
+ zone = board->GetArea( zonescount ) )
+ {
+ picker.SetItem( zone );
+ undoListPicker.PushItem( picker );
+ zonescount++;
+ view->Add( zone );
+ m_toolMgr->RunAction( COMMON_ACTIONS::selectItem, true, zone );
+ }
+
+ if( undoListPicker.GetCount() == 0 )
+ return 0;
+
+ editFrame->SaveCopyInUndoList( undoListPicker, UR_NEW );
+
+ // Synchronize layers
+ // we should not ask PLUGINs to do these items:
+ int copperLayerCount = board->GetCopperLayerCount();
+
+ if( copperLayerCount > initialCopperLayerCount )
+ board->SetCopperLayerCount( copperLayerCount );
+
+ // Enable all used layers, and make them visible:
+ LSET enabledLayers = board->GetEnabledLayers();
+ enabledLayers |= initialEnabledLayers;
+ board->SetEnabledLayers( enabledLayers );
+ board->SetVisibleLayers( enabledLayers );
+ editFrame->ReCreateLayerBox();
+ editFrame->ReFillLayerWidget();
+ static_cast<PCB_DRAW_PANEL_GAL*>( editFrame->GetGalCanvas() )->SyncLayersVisibility( board );
+
+ // Start dragging the appended board
+ VECTOR2D v( static_cast<BOARD_ITEM*>( undoListPicker.GetPickedItem( 0 ) )->GetPosition() );
+ getViewControls()->WarpCursor( v, true, true );
+ m_toolMgr->InvokeTool( "pcbnew.InteractiveEdit" );
+
+ return 0;
+}
+
+
+int PCBNEW_CONTROL::ShowHelp( const TOOL_EVENT& aEvent )
+{
+ DisplayHotkeyList( m_frame, m_frame->GetHotkeyConfig() );
+
+ return 0;
+}
+
+
+int PCBNEW_CONTROL::ToBeDone( const TOOL_EVENT& aEvent )
+{
+ DisplayInfoMessage( m_frame, _( "Not available in OpenGL/Cairo canvases." ) );
+
+ return 0;
+}
+
+
+void PCBNEW_CONTROL::SetTransitions()
+{
+ // View controls
+ Go( &PCBNEW_CONTROL::ZoomInOut, COMMON_ACTIONS::zoomIn.MakeEvent() );
+ Go( &PCBNEW_CONTROL::ZoomInOut, COMMON_ACTIONS::zoomOut.MakeEvent() );
+ Go( &PCBNEW_CONTROL::ZoomInOutCenter, COMMON_ACTIONS::zoomInCenter.MakeEvent() );
+ Go( &PCBNEW_CONTROL::ZoomInOutCenter, COMMON_ACTIONS::zoomOutCenter.MakeEvent() );
+ Go( &PCBNEW_CONTROL::ZoomCenter, COMMON_ACTIONS::zoomCenter.MakeEvent() );
+ Go( &PCBNEW_CONTROL::ZoomFitScreen, COMMON_ACTIONS::zoomFitScreen.MakeEvent() );
+ Go( &PCBNEW_CONTROL::ZoomPreset, COMMON_ACTIONS::zoomPreset.MakeEvent() );
+
+ // Display modes
+ Go( &PCBNEW_CONTROL::TrackDisplayMode, COMMON_ACTIONS::trackDisplayMode.MakeEvent() );
+ Go( &PCBNEW_CONTROL::PadDisplayMode, COMMON_ACTIONS::padDisplayMode.MakeEvent() );
+ Go( &PCBNEW_CONTROL::ViaDisplayMode, COMMON_ACTIONS::viaDisplayMode.MakeEvent() );
+ Go( &PCBNEW_CONTROL::ZoneDisplayMode, COMMON_ACTIONS::zoneDisplayEnable.MakeEvent() );
+ Go( &PCBNEW_CONTROL::ZoneDisplayMode, COMMON_ACTIONS::zoneDisplayDisable.MakeEvent() );
+ Go( &PCBNEW_CONTROL::ZoneDisplayMode, COMMON_ACTIONS::zoneDisplayOutlines.MakeEvent() );
+ Go( &PCBNEW_CONTROL::HighContrastMode, COMMON_ACTIONS::highContrastMode.MakeEvent() );
+ Go( &PCBNEW_CONTROL::HighContrastInc, COMMON_ACTIONS::highContrastInc.MakeEvent() );
+ Go( &PCBNEW_CONTROL::HighContrastDec, COMMON_ACTIONS::highContrastDec.MakeEvent() );
+
+ // Layer control
+ Go( &PCBNEW_CONTROL::LayerSwitch, COMMON_ACTIONS::layerTop.MakeEvent() );
+ Go( &PCBNEW_CONTROL::LayerSwitch, COMMON_ACTIONS::layerInner1.MakeEvent() );
+ Go( &PCBNEW_CONTROL::LayerSwitch, COMMON_ACTIONS::layerInner2.MakeEvent() );
+ Go( &PCBNEW_CONTROL::LayerSwitch, COMMON_ACTIONS::layerInner3.MakeEvent() );
+ Go( &PCBNEW_CONTROL::LayerSwitch, COMMON_ACTIONS::layerInner4.MakeEvent() );
+ Go( &PCBNEW_CONTROL::LayerSwitch, COMMON_ACTIONS::layerInner5.MakeEvent() );
+ Go( &PCBNEW_CONTROL::LayerSwitch, COMMON_ACTIONS::layerInner6.MakeEvent() );
+ Go( &PCBNEW_CONTROL::LayerSwitch, COMMON_ACTIONS::layerBottom.MakeEvent() );
+ Go( &PCBNEW_CONTROL::LayerNext, COMMON_ACTIONS::layerNext.MakeEvent() );
+ Go( &PCBNEW_CONTROL::LayerPrev, COMMON_ACTIONS::layerPrev.MakeEvent() );
+ Go( &PCBNEW_CONTROL::LayerToggle, COMMON_ACTIONS::layerToggle.MakeEvent() );
+ Go( &PCBNEW_CONTROL::LayerAlphaInc, COMMON_ACTIONS::layerAlphaInc.MakeEvent() );
+ Go( &PCBNEW_CONTROL::LayerAlphaDec, COMMON_ACTIONS::layerAlphaDec.MakeEvent() );
+
+ // Cursor control
+ Go( &PCBNEW_CONTROL::CursorControl, COMMON_ACTIONS::cursorUp.MakeEvent() );
+ Go( &PCBNEW_CONTROL::CursorControl, COMMON_ACTIONS::cursorDown.MakeEvent() );
+ Go( &PCBNEW_CONTROL::CursorControl, COMMON_ACTIONS::cursorLeft.MakeEvent() );
+ Go( &PCBNEW_CONTROL::CursorControl, COMMON_ACTIONS::cursorRight.MakeEvent() );
+ Go( &PCBNEW_CONTROL::CursorControl, COMMON_ACTIONS::cursorUpFast.MakeEvent() );
+ Go( &PCBNEW_CONTROL::CursorControl, COMMON_ACTIONS::cursorDownFast.MakeEvent() );
+ Go( &PCBNEW_CONTROL::CursorControl, COMMON_ACTIONS::cursorLeftFast.MakeEvent() );
+ Go( &PCBNEW_CONTROL::CursorControl, COMMON_ACTIONS::cursorRightFast.MakeEvent() );
+ Go( &PCBNEW_CONTROL::CursorControl, COMMON_ACTIONS::cursorClick.MakeEvent() );
+ Go( &PCBNEW_CONTROL::CursorControl, COMMON_ACTIONS::cursorDblClick.MakeEvent() );
+
+ // Pan control
+ Go( &PCBNEW_CONTROL::PanControl, COMMON_ACTIONS::panUp.MakeEvent() );
+ Go( &PCBNEW_CONTROL::PanControl, COMMON_ACTIONS::panDown.MakeEvent() );
+ Go( &PCBNEW_CONTROL::PanControl, COMMON_ACTIONS::panLeft.MakeEvent() );
+ Go( &PCBNEW_CONTROL::PanControl, COMMON_ACTIONS::panRight.MakeEvent() );
+
+ // Grid control
+ Go( &PCBNEW_CONTROL::GridFast1, COMMON_ACTIONS::gridFast1.MakeEvent() );
+ Go( &PCBNEW_CONTROL::GridFast2, COMMON_ACTIONS::gridFast2.MakeEvent() );
+ Go( &PCBNEW_CONTROL::GridNext, COMMON_ACTIONS::gridNext.MakeEvent() );
+ Go( &PCBNEW_CONTROL::GridPrev, COMMON_ACTIONS::gridPrev.MakeEvent() );
+ Go( &PCBNEW_CONTROL::GridSetOrigin, COMMON_ACTIONS::gridSetOrigin.MakeEvent() );
+ Go( &PCBNEW_CONTROL::GridResetOrigin, COMMON_ACTIONS::gridResetOrigin.MakeEvent() );
+ Go( &PCBNEW_CONTROL::GridPreset, COMMON_ACTIONS::gridPreset.MakeEvent() );
+
+ // Miscellaneous
+ Go( &PCBNEW_CONTROL::ResetCoords, COMMON_ACTIONS::resetCoords.MakeEvent() );
+ Go( &PCBNEW_CONTROL::SwitchCursor, COMMON_ACTIONS::switchCursor.MakeEvent() );
+ Go( &PCBNEW_CONTROL::SwitchUnits, COMMON_ACTIONS::switchUnits.MakeEvent() );
+ Go( &PCBNEW_CONTROL::DeleteItemCursor, COMMON_ACTIONS::deleteItemCursor.MakeEvent() );
+ Go( &PCBNEW_CONTROL::AppendBoard, COMMON_ACTIONS::appendBoard.MakeEvent() );
+ Go( &PCBNEW_CONTROL::ShowHelp, COMMON_ACTIONS::showHelp.MakeEvent() );
+ Go( &PCBNEW_CONTROL::ToBeDone, COMMON_ACTIONS::toBeDone.MakeEvent() );
+}
+
+
+void PCBNEW_CONTROL::updateGrid()
+{
+ BASE_SCREEN* screen = m_frame->GetScreen();
+ //GRID_TYPE grid = screen->GetGrid( idx );
+ getView()->GetGAL()->SetGridSize( VECTOR2D( screen->GetGridSize() ) );
+ getView()->MarkTargetDirty( KIGFX::TARGET_NONCACHED );
+}
diff --git a/pcbnew/tools/pcbnew_control.h b/pcbnew/tools/pcbnew_control.h
new file mode 100644
index 0000000..1117b77
--- /dev/null
+++ b/pcbnew/tools/pcbnew_control.h
@@ -0,0 +1,109 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2014-2016 CERN
+ * @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
+ */
+
+#ifndef PCBNEW_CONTROL_H
+#define PCBNEW_CONTROL_H
+
+#include <tool/tool_interactive.h>
+
+namespace KIGFX {
+ class ORIGIN_VIEWITEM;
+}
+class PCB_BASE_FRAME;
+
+/**
+ * Class PCBNEW_CONTROL
+ *
+ * Handles actions that are shared between different frames in pcbnew.
+ */
+
+class PCBNEW_CONTROL : public TOOL_INTERACTIVE
+{
+public:
+ PCBNEW_CONTROL();
+ ~PCBNEW_CONTROL();
+
+ /// @copydoc TOOL_INTERACTIVE::Reset()
+ void Reset( RESET_REASON aReason );
+
+ // View controls
+ int ZoomInOut( const TOOL_EVENT& aEvent );
+ int ZoomInOutCenter( const TOOL_EVENT& aEvent );
+ int ZoomCenter( const TOOL_EVENT& aEvent );
+ int ZoomFitScreen( const TOOL_EVENT& aEvent );
+ int ZoomPreset( const TOOL_EVENT& aEvent );
+
+ // Display modes
+ int TrackDisplayMode( const TOOL_EVENT& aEvent );
+ int PadDisplayMode( const TOOL_EVENT& aEvent );
+ int ViaDisplayMode( const TOOL_EVENT& aEvent );
+ int ZoneDisplayMode( const TOOL_EVENT& aEvent );
+ int HighContrastMode( const TOOL_EVENT& aEvent );
+ int HighContrastInc( const TOOL_EVENT& aEvent );
+ int HighContrastDec( const TOOL_EVENT& aEvent );
+
+ // Layer control
+ int LayerSwitch( const TOOL_EVENT& aEvent );
+ int LayerNext( const TOOL_EVENT& aEvent );
+ int LayerPrev( const TOOL_EVENT& aEvent );
+ int LayerToggle( const TOOL_EVENT& aEvent );
+ int LayerAlphaInc( const TOOL_EVENT& aEvent );
+ int LayerAlphaDec( const TOOL_EVENT& aEvent );
+
+ int CursorControl( const TOOL_EVENT& aEvent );
+ int PanControl( const TOOL_EVENT& aEvent );
+
+ // Grid control
+ int GridFast1( const TOOL_EVENT& aEvent );
+ int GridFast2( const TOOL_EVENT& aEvent );
+ int GridNext( const TOOL_EVENT& aEvent );
+ int GridPrev( const TOOL_EVENT& aEvent );
+ int GridSetOrigin( const TOOL_EVENT& aEvent );
+ int GridResetOrigin( const TOOL_EVENT& aEvent );
+ int GridPreset( const TOOL_EVENT& aEvent );
+
+ // Miscellaneous
+ int ResetCoords( const TOOL_EVENT& aEvent );
+ int SwitchCursor( const TOOL_EVENT& aEvent );
+ int SwitchUnits( const TOOL_EVENT& aEvent );
+ int DeleteItemCursor( const TOOL_EVENT& aEvent );
+ int AppendBoard( const TOOL_EVENT& aEvent );
+ int ShowHelp( const TOOL_EVENT& aEvent );
+ int ToBeDone( const TOOL_EVENT& aEvent );
+
+ ///> Sets up handlers for various events.
+ void SetTransitions();
+
+private:
+ ///> Pointer to the currently used edit frame.
+ PCB_BASE_FRAME* m_frame;
+
+ ///> Grid origin marker.
+ KIGFX::ORIGIN_VIEWITEM* m_gridOrigin;
+
+ ///> Applies the legacy canvas grid settings for GAL.
+ void updateGrid();
+};
+
+#endif
diff --git a/pcbnew/tools/picker_tool.cpp b/pcbnew/tools/picker_tool.cpp
new file mode 100644
index 0000000..6234346
--- /dev/null
+++ b/pcbnew/tools/picker_tool.cpp
@@ -0,0 +1,115 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2015 CERN
+ * @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 "picker_tool.h"
+#include "common_actions.h"
+
+#include <wxPcbStruct.h>
+#include <view/view_controls.h>
+#include <tool/tool_manager.h>
+
+PICKER_TOOL::PICKER_TOOL()
+ : TOOL_INTERACTIVE( "pcbnew.Picker" )
+{
+ reset();
+}
+
+
+int PICKER_TOOL::Main( const TOOL_EVENT& aEvent )
+{
+ KIGFX::VIEW_CONTROLS* controls = getViewControls();
+
+ assert( !m_picking );
+ m_picking = true;
+ m_picked = boost::none;
+
+ setControls();
+
+ while( OPT_TOOL_EVENT evt = Wait() )
+ {
+ if( evt->IsClick( BUT_LEFT ) )
+ {
+ bool getNext = false;
+ m_picked = controls->GetCursorPosition();
+
+ if( m_clickHandler )
+ {
+ try
+ {
+ getNext = (*m_clickHandler)( *m_picked );
+ }
+ catch( std::exception& e )
+ {
+ std::cerr << "PICKER_TOOL click handler error: " << e.what() << std::endl;
+ break;
+ }
+ }
+
+ if( !getNext )
+ break;
+ else
+ m_toolMgr->PassEvent();
+ }
+
+ else if( evt->IsCancel() || evt->IsActivate() )
+ break;
+
+ else
+ m_toolMgr->PassEvent();
+ }
+
+ reset();
+ getEditFrame<PCB_BASE_FRAME>()->SetToolID( ID_NO_TOOL_SELECTED, wxCURSOR_DEFAULT, wxEmptyString );
+
+ return 0;
+}
+
+
+void PICKER_TOOL::SetTransitions()
+{
+ Go( &PICKER_TOOL::Main, COMMON_ACTIONS::pickerTool.MakeEvent() );
+}
+
+
+void PICKER_TOOL::reset()
+{
+ m_cursorSnapping = true;
+ m_cursorVisible = true;
+ m_cursorCapture = false;
+ m_autoPanning = false;
+
+ m_picking = false;
+ m_clickHandler = boost::none;
+}
+
+
+void PICKER_TOOL::setControls()
+{
+ KIGFX::VIEW_CONTROLS* controls = getViewControls();
+
+ controls->ShowCursor( m_cursorVisible );
+ controls->SetSnapping( m_cursorSnapping );
+ controls->CaptureCursor( m_cursorCapture );
+ controls->SetAutoPan( m_autoPanning );
+}
diff --git a/pcbnew/tools/picker_tool.h b/pcbnew/tools/picker_tool.h
new file mode 100644
index 0000000..6512d95
--- /dev/null
+++ b/pcbnew/tools/picker_tool.h
@@ -0,0 +1,127 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2015 CERN
+ * @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
+ */
+
+#ifndef PICKER_TOOL_H
+#define PICKER_TOOL_H
+
+#include <tool/tool_interactive.h>
+#include <boost/optional/optional.hpp>
+#include <boost/function.hpp>
+
+/**
+ * @brief Generic tool for picking a point.
+ */
+class PICKER_TOOL : public TOOL_INTERACTIVE
+{
+public:
+ PICKER_TOOL();
+ ~PICKER_TOOL() {}
+
+ ///> Mouse event click handler type.
+ typedef boost::function<bool(const VECTOR2D&)> CLICK_HANDLER;
+
+ ///> @copydoc TOOL_INTERACTIVE::Reset()
+ void Reset( RESET_REASON aReason ) {}
+
+ ///> Main event loop.
+ int Main( const TOOL_EVENT& aEvent );
+
+ /**
+ * Function SetSnapping()
+ * Sets cursor snapping to grid for the period when the tool is active.
+ */
+ inline void SetSnapping( bool aEnable ) { m_cursorSnapping = aEnable; }
+
+ /**
+ * Function SetCursorVisible()
+ * Sets cursor visibility for the period when the tool is active.
+ */
+ inline void SetCursorVisible( bool aEnable ) { m_cursorVisible = aEnable; }
+
+ /**
+ * Function SetAutoPanning()
+ * Sets autopanning mode for the period when the tool is active.
+ */
+ inline void SetAutoPanning( bool aEnable ) { m_autoPanning = aEnable; }
+
+ /**
+ * Function SetAutoPanning()
+ * Toggles cursor capture mode for the period when the tool is active.
+ */
+ inline void SetCursorCapture( bool aEnable ) { m_cursorCapture = aEnable; }
+
+ /**
+ * Function GetPoint()
+ * Returns picked point.
+ */
+ inline boost::optional<VECTOR2D> GetPoint() const
+ {
+ assert( !m_picking );
+ return m_picked;
+ }
+
+ /**
+ * Function IsPicking()
+ * Returns information whether the tool is still active.
+ */
+ bool IsPicking() const { return m_picking; }
+
+ /**
+ * Function SetClickHandler()
+ * Sets a handler for mouse click event. Handler may decide to receive further click by
+ * returning true.
+ */
+ inline void SetClickHandler( CLICK_HANDLER aHandler )
+ {
+ assert( !m_clickHandler );
+ m_clickHandler = aHandler;
+ }
+
+ ///> @copydoc TOOL_INTERACTIVE::SetTransitions();
+ void SetTransitions();
+
+private:
+ // Tool settings.
+ bool m_cursorSnapping;
+ bool m_cursorVisible;
+ bool m_cursorCapture;
+ bool m_autoPanning;
+
+ ///> Optional mouse click event handler.
+ boost::optional<CLICK_HANDLER> m_clickHandler;
+
+ ///> Picked point (if any).
+ boost::optional<VECTOR2D> m_picked;
+
+ ///> Activity status.
+ bool m_picking;
+
+ ///> Reinitializes tool to its initial state.
+ void reset();
+
+ ///> Applies the requested VIEW_CONTROLS settings.
+ void setControls();
+};
+
+#endif /* PICKER_TOOL_H */
diff --git a/pcbnew/tools/placement_tool.cpp b/pcbnew/tools/placement_tool.cpp
new file mode 100644
index 0000000..d962492
--- /dev/null
+++ b/pcbnew/tools/placement_tool.cpp
@@ -0,0 +1,354 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2014 CERN
+ * @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 "placement_tool.h"
+#include "common_actions.h"
+#include "selection_tool.h"
+#include <tool/tool_manager.h>
+
+#include <wxPcbStruct.h>
+#include <class_board.h>
+#include <ratsnest_data.h>
+
+#include <confirm.h>
+#include <boost/foreach.hpp>
+
+PLACEMENT_TOOL::PLACEMENT_TOOL() :
+ TOOL_INTERACTIVE( "pcbnew.Placement" ), m_selectionTool( NULL ), m_placementMenu( NULL )
+{
+}
+
+PLACEMENT_TOOL::~PLACEMENT_TOOL()
+{
+ delete m_placementMenu;
+}
+
+
+bool PLACEMENT_TOOL::Init()
+{
+ // Find the selection tool, so they can cooperate
+ m_selectionTool = static_cast<SELECTION_TOOL*>( m_toolMgr->FindTool( "pcbnew.InteractiveSelection" ) );
+
+ if( !m_selectionTool )
+ {
+ DisplayError( NULL, wxT( "pcbnew.InteractiveSelection tool is not available" ) );
+ return false;
+ }
+
+ // Create a context menu and make it available through selection tool
+ m_placementMenu = new CONTEXT_MENU;
+ m_placementMenu->Add( COMMON_ACTIONS::alignTop );
+ m_placementMenu->Add( COMMON_ACTIONS::alignBottom );
+ m_placementMenu->Add( COMMON_ACTIONS::alignLeft );
+ m_placementMenu->Add( COMMON_ACTIONS::alignRight );
+ m_placementMenu->AppendSeparator();
+ m_placementMenu->Add( COMMON_ACTIONS::distributeHorizontally );
+ m_placementMenu->Add( COMMON_ACTIONS::distributeVertically );
+ m_selectionTool->GetMenu().AddMenu( m_placementMenu, _( "Align/distribute" ), false,
+ SELECTION_CONDITIONS::MoreThan( 1 ) );
+
+ return true;
+}
+
+
+int PLACEMENT_TOOL::AlignTop( const TOOL_EVENT& aEvent )
+{
+ const SELECTION& selection = m_selectionTool->GetSelection();
+
+ if( selection.Size() > 1 )
+ {
+ PCB_BASE_FRAME* editFrame = getEditFrame<PCB_BASE_FRAME>();
+ RN_DATA* ratsnest = getModel<BOARD>()->GetRatsnest();
+
+ editFrame->OnModify();
+ editFrame->SaveCopyInUndoList( selection.items, UR_CHANGED );
+
+ // Compute the highest point of selection - it will be the edge of alignment
+ int top = selection.Item<BOARD_ITEM>( 0 )->GetBoundingBox().GetY();
+
+ for( int i = 1; i < selection.Size(); ++i )
+ {
+ int currentTop = selection.Item<BOARD_ITEM>( i )->GetBoundingBox().GetY();
+
+ if( top > currentTop ) // Y decreases when going up
+ top = currentTop;
+ }
+
+ // Move the selected items
+ for( int i = 0; i < selection.Size(); ++i )
+ {
+ BOARD_ITEM* item = selection.Item<BOARD_ITEM>( i );
+ int difference = top - item->GetBoundingBox().GetY();
+
+ item->Move( wxPoint( 0, difference ) );
+ item->ViewUpdate();
+ ratsnest->Update( item );
+ }
+
+ getModel<BOARD>()->GetRatsnest()->Recalculate();
+ }
+
+ return 0;
+}
+
+
+int PLACEMENT_TOOL::AlignBottom( const TOOL_EVENT& aEvent )
+{
+ const SELECTION& selection = m_selectionTool->GetSelection();
+
+ if( selection.Size() > 1 )
+ {
+ PCB_BASE_FRAME* editFrame = getEditFrame<PCB_BASE_FRAME>();
+ RN_DATA* ratsnest = getModel<BOARD>()->GetRatsnest();
+
+ editFrame->OnModify();
+ editFrame->SaveCopyInUndoList( selection.items, UR_CHANGED );
+
+ // Compute the lowest point of selection - it will be the edge of alignment
+ int bottom = selection.Item<BOARD_ITEM>( 0 )->GetBoundingBox().GetBottom();
+
+ for( int i = 1; i < selection.Size(); ++i )
+ {
+ int currentBottom = selection.Item<BOARD_ITEM>( i )->GetBoundingBox().GetBottom();
+
+ if( bottom < currentBottom ) // Y increases when going down
+ bottom = currentBottom;
+ }
+
+ // Move the selected items
+ for( int i = 0; i < selection.Size(); ++i )
+ {
+ BOARD_ITEM* item = selection.Item<BOARD_ITEM>( i );
+ int difference = bottom - item->GetBoundingBox().GetBottom();
+
+ item->Move( wxPoint( 0, difference ) );
+ item->ViewUpdate();
+ ratsnest->Update( item );
+ }
+
+ getModel<BOARD>()->GetRatsnest()->Recalculate();
+ }
+
+ return 0;
+}
+
+
+int PLACEMENT_TOOL::AlignLeft( const TOOL_EVENT& aEvent )
+{
+ const SELECTION& selection = m_selectionTool->GetSelection();
+
+ if( selection.Size() > 1 )
+ {
+ PCB_BASE_FRAME* editFrame = getEditFrame<PCB_BASE_FRAME>();
+ RN_DATA* ratsnest = getModel<BOARD>()->GetRatsnest();
+
+ editFrame->OnModify();
+ editFrame->SaveCopyInUndoList( selection.items, UR_CHANGED );
+
+ // Compute the leftmost point of selection - it will be the edge of alignment
+ int left = selection.Item<BOARD_ITEM>( 0 )->GetBoundingBox().GetX();
+
+ for( int i = 1; i < selection.Size(); ++i )
+ {
+ int currentLeft = selection.Item<BOARD_ITEM>( i )->GetBoundingBox().GetX();
+
+ if( left > currentLeft ) // X decreases when going left
+ left = currentLeft;
+ }
+
+ // Move the selected items
+ for( int i = 0; i < selection.Size(); ++i )
+ {
+ BOARD_ITEM* item = selection.Item<BOARD_ITEM>( i );
+ int difference = left - item->GetBoundingBox().GetX();
+
+ item->Move( wxPoint( difference, 0 ) );
+ item->ViewUpdate();
+ ratsnest->Update( item );
+ }
+
+ getModel<BOARD>()->GetRatsnest()->Recalculate();
+ }
+
+ return 0;
+}
+
+
+int PLACEMENT_TOOL::AlignRight( const TOOL_EVENT& aEvent )
+{
+ const SELECTION& selection = m_selectionTool->GetSelection();
+
+ if( selection.Size() > 1 )
+ {
+ PCB_BASE_FRAME* editFrame = getEditFrame<PCB_BASE_FRAME>();
+ RN_DATA* ratsnest = getModel<BOARD>()->GetRatsnest();
+
+ editFrame->OnModify();
+ editFrame->SaveCopyInUndoList( selection.items, UR_CHANGED );
+
+ // Compute the rightmost point of selection - it will be the edge of alignment
+ int right = selection.Item<BOARD_ITEM>( 0 )->GetBoundingBox().GetRight();
+
+ for( int i = 1; i < selection.Size(); ++i )
+ {
+ int currentRight = selection.Item<BOARD_ITEM>( i )->GetBoundingBox().GetRight();
+
+ if( right < currentRight ) // X increases when going right
+ right = currentRight;
+ }
+
+ // Move the selected items
+ for( int i = 0; i < selection.Size(); ++i )
+ {
+ BOARD_ITEM* item = selection.Item<BOARD_ITEM>( i );
+ int difference = right - item->GetBoundingBox().GetRight();
+
+ item->Move( wxPoint( difference, 0 ) );
+ item->ViewUpdate();
+ ratsnest->Update( item );
+ }
+
+ getModel<BOARD>()->GetRatsnest()->Recalculate();
+ }
+
+ return 0;
+}
+
+
+static bool compareX( const BOARD_ITEM* aA, const BOARD_ITEM* aB )
+{
+ return aA->GetBoundingBox().Centre().x < aB->GetBoundingBox().Centre().x;
+}
+
+
+static bool compareY( const BOARD_ITEM* aA, const BOARD_ITEM* aB )
+{
+ return aA->GetBoundingBox().Centre().y < aB->GetBoundingBox().Centre().y;
+}
+
+
+int PLACEMENT_TOOL::DistributeHorizontally( const TOOL_EVENT& aEvent )
+{
+ const SELECTION& selection = m_selectionTool->GetSelection();
+
+ if( selection.Size() > 1 )
+ {
+ PCB_BASE_FRAME* editFrame = getEditFrame<PCB_BASE_FRAME>();
+ RN_DATA* ratsnest = getModel<BOARD>()->GetRatsnest();
+
+ editFrame->OnModify();
+ editFrame->SaveCopyInUndoList( selection.items, UR_CHANGED );
+
+ // Prepare a list, so the items can be sorted by their X coordinate
+ std::list<BOARD_ITEM*> itemsList;
+ for( int i = 0; i < selection.Size(); ++i )
+ itemsList.push_back( selection.Item<BOARD_ITEM>( i ) );
+
+ // Sort items by X coordinate
+ itemsList.sort( compareX );
+
+ // Expected X coordinate for the next item (=minX)
+ int position = (*itemsList.begin())->GetBoundingBox().Centre().x;
+
+ // X coordinate for the last item
+ const int maxX = (*itemsList.rbegin())->GetBoundingBox().Centre().x;
+
+ // Distance between items
+ const int distance = ( maxX - position ) / ( itemsList.size() - 1 );
+
+ BOOST_FOREACH( BOARD_ITEM* item, itemsList )
+ {
+ int difference = position - item->GetBoundingBox().Centre().x;
+
+ item->Move( wxPoint( difference, 0 ) );
+ item->ViewUpdate();
+ ratsnest->Update( item );
+
+ position += distance;
+ }
+
+ getModel<BOARD>()->GetRatsnest()->Recalculate();
+ }
+
+ return 0;
+}
+
+
+int PLACEMENT_TOOL::DistributeVertically( const TOOL_EVENT& aEvent )
+{
+ const SELECTION& selection = m_selectionTool->GetSelection();
+
+ if( selection.Size() > 1 )
+ {
+ PCB_BASE_FRAME* editFrame = getEditFrame<PCB_BASE_FRAME>();
+ RN_DATA* ratsnest = getModel<BOARD>()->GetRatsnest();
+
+ editFrame->OnModify();
+ editFrame->SaveCopyInUndoList( selection.items, UR_CHANGED );
+
+ // Prepare a list, so the items can be sorted by their Y coordinate
+ std::list<BOARD_ITEM*> itemsList;
+ for( int i = 0; i < selection.Size(); ++i )
+ itemsList.push_back( selection.Item<BOARD_ITEM>( i ) );
+
+ // Sort items by Y coordinate
+ itemsList.sort( compareY );
+
+ // Expected Y coordinate for the next item (=minY)
+ int position = (*itemsList.begin())->GetBoundingBox().Centre().y;
+
+ // Y coordinate for the last item
+ const int maxY = (*itemsList.rbegin())->GetBoundingBox().Centre().y;
+
+ // Distance between items
+ const int distance = ( maxY - position ) / ( itemsList.size() - 1 );
+
+ BOOST_FOREACH( BOARD_ITEM* item, itemsList )
+ {
+ int difference = position - item->GetBoundingBox().Centre().y;
+
+ item->Move( wxPoint( 0, difference ) );
+ item->ViewUpdate();
+ ratsnest->Update( item );
+
+ position += distance;
+ }
+
+ getModel<BOARD>()->GetRatsnest()->Recalculate();
+ }
+
+ return 0;
+}
+
+
+void PLACEMENT_TOOL::SetTransitions()
+{
+ Go( &PLACEMENT_TOOL::AlignTop, COMMON_ACTIONS::alignTop.MakeEvent() );
+ Go( &PLACEMENT_TOOL::AlignBottom, COMMON_ACTIONS::alignBottom.MakeEvent() );
+ Go( &PLACEMENT_TOOL::AlignLeft, COMMON_ACTIONS::alignLeft.MakeEvent() );
+ Go( &PLACEMENT_TOOL::AlignRight, COMMON_ACTIONS::alignRight.MakeEvent() );
+
+ Go( &PLACEMENT_TOOL::DistributeHorizontally, COMMON_ACTIONS::distributeHorizontally.MakeEvent() );
+ Go( &PLACEMENT_TOOL::DistributeVertically, COMMON_ACTIONS::distributeVertically.MakeEvent() );
+}
diff --git a/pcbnew/tools/placement_tool.h b/pcbnew/tools/placement_tool.h
new file mode 100644
index 0000000..db8ec92
--- /dev/null
+++ b/pcbnew/tools/placement_tool.h
@@ -0,0 +1,89 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2014 CERN
+ * @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
+ */
+
+#ifndef PLACEMENT_TOOL_H_
+#define PLACEMENT_TOOL_H_
+
+#include <tool/tool_interactive.h>
+
+class SELECTION_TOOL;
+
+class PLACEMENT_TOOL : public TOOL_INTERACTIVE
+{
+public:
+ PLACEMENT_TOOL();
+ virtual ~PLACEMENT_TOOL();
+
+ /// @copydoc TOOL_INTERACTIVE::Reset()
+ void Reset( RESET_REASON aReason ) {};
+
+ /// @copydoc TOOL_INTERACTIVE::Init()
+ bool Init();
+
+ /**
+ * Function AlignTop()
+ * Sets Y coordinate of the selected items to the value of the top-most selected item Y coordinate.
+ */
+ int AlignTop( const TOOL_EVENT& aEvent );
+
+ /**
+ * Function AlignBottom()
+ * Sets Y coordinate of the selected items to the value of the bottom-most selected item Y coordinate.
+ */
+ int AlignBottom( const TOOL_EVENT& aEvent );
+
+ /**
+ * Function AlignLeft()
+ * Sets X coordinate of the selected items to the value of the left-most selected item X coordinate.
+ */
+ int AlignLeft( const TOOL_EVENT& aEvent );
+
+ /**
+ * Function AlignRight()
+ * Sets X coordinate of the selected items to the value of the right-most selected item X coordinate.
+ */
+ int AlignRight( const TOOL_EVENT& aEvent );
+
+ /**
+ * Function DistributeHorizontally()
+ * Distributes the selected items along the X axis.
+ */
+ int DistributeHorizontally( const TOOL_EVENT& aEvent );
+
+ /**
+ * Function DistributeVertically()
+ * Distributes the selected items along the Y axis.
+ */
+ int DistributeVertically( const TOOL_EVENT& aEvent );
+
+ ///> Sets up handlers for various events.
+ void SetTransitions();
+
+private:
+ SELECTION_TOOL* m_selectionTool;
+
+ CONTEXT_MENU* m_placementMenu;
+};
+
+#endif /* PLACEMENT_TOOL_H_ */
diff --git a/pcbnew/tools/point_editor.cpp b/pcbnew/tools/point_editor.cpp
new file mode 100644
index 0000000..bb1bf39
--- /dev/null
+++ b/pcbnew/tools/point_editor.cpp
@@ -0,0 +1,890 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2013 CERN
+ * @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 <boost/make_shared.hpp>
+#include <boost/bind.hpp>
+
+#include <tool/tool_manager.h>
+#include <view/view_controls.h>
+#include <gal/graphics_abstraction_layer.h>
+#include <geometry/seg.h>
+#include <confirm.h>
+
+#include "common_actions.h"
+#include "selection_tool.h"
+#include "point_editor.h"
+
+#include <wxPcbStruct.h>
+#include <class_edge_mod.h>
+#include <class_dimension.h>
+#include <class_zone.h>
+#include <class_board.h>
+#include <class_module.h>
+
+// Few constants to avoid using bare numbers for point indices
+enum SEG_POINTS
+{
+ SEG_START, SEG_END
+};
+
+enum ARC_POINTS
+{
+ ARC_CENTER, ARC_START, ARC_END
+};
+
+enum CIRCLE_POINTS
+{
+ CIRC_CENTER, CIRC_END
+};
+
+enum DIMENSION_POINTS
+{
+ DIM_CROSSBARO,
+ DIM_CROSSBARF,
+ DIM_FEATUREGO,
+ DIM_FEATUREDO,
+};
+
+
+class EDIT_POINTS_FACTORY
+{
+public:
+ static boost::shared_ptr<EDIT_POINTS> Make( EDA_ITEM* aItem, KIGFX::GAL* aGal )
+ {
+ boost::shared_ptr<EDIT_POINTS> points = boost::make_shared<EDIT_POINTS>( aItem );
+
+ // Generate list of edit points basing on the item type
+ switch( aItem->Type() )
+ {
+ case PCB_LINE_T:
+ case PCB_MODULE_EDGE_T:
+ {
+ const DRAWSEGMENT* segment = static_cast<const DRAWSEGMENT*>( aItem );
+
+ switch( segment->GetShape() )
+ {
+ case S_SEGMENT:
+ points->AddPoint( segment->GetStart() );
+ points->AddPoint( segment->GetEnd() );
+ break;
+
+ case S_ARC:
+ points->AddPoint( segment->GetCenter() );
+ points->AddPoint( segment->GetArcStart() );
+ points->AddPoint( segment->GetArcEnd() );
+
+ // Set constraints
+ // Arc end has to stay at the same radius as the start
+ points->Point( ARC_END ).SetConstraint( new EC_CIRCLE( points->Point( ARC_END ),
+ points->Point( ARC_CENTER ),
+ points->Point( ARC_START ) ) );
+ break;
+
+ case S_CIRCLE:
+ points->AddPoint( segment->GetCenter() );
+ points->AddPoint( segment->GetEnd() );
+ break;
+
+ default: // suppress warnings
+ break;
+ }
+
+ break;
+ }
+
+ case PCB_ZONE_AREA_T:
+ {
+ const CPolyLine* outline = static_cast<const ZONE_CONTAINER*>( aItem )->Outline();
+ int cornersCount = outline->GetCornersCount();
+
+ for( int i = 0; i < cornersCount; ++i )
+ {
+ points->AddPoint( outline->GetPos( i ) );
+
+ if( outline->IsEndContour( i ) )
+ points->AddBreak();
+ }
+
+ // Lines have to be added after creating edit points,
+ // as they use EDIT_POINT references
+ for( int i = 0; i < cornersCount - 1; ++i )
+ {
+ if( points->IsContourEnd( i ) )
+ {
+ points->AddLine( points->Point( i ),
+ points->Point( points->GetContourStartIdx( i ) ) );
+ }
+ else
+ {
+ points->AddLine( points->Point( i ), points->Point( i + 1 ) );
+ }
+
+ points->Line( i ).SetConstraint( new EC_SNAPLINE( points->Line( i ),
+ boost::bind( &KIGFX::GAL::GetGridPoint, aGal, _1 ) ) );
+ }
+
+ // The last missing line, connecting the last and the first polygon point
+ points->AddLine( points->Point( cornersCount - 1 ),
+ points->Point( points->GetContourStartIdx( cornersCount - 1 ) ) );
+
+ points->Line( points->LinesSize() - 1 ).SetConstraint(
+ new EC_SNAPLINE( points->Line( points->LinesSize() - 1 ),
+ boost::bind( &KIGFX::GAL::GetGridPoint, aGal, _1 ) ) );
+ break;
+ }
+
+ case PCB_DIMENSION_T:
+ {
+ const DIMENSION* dimension = static_cast<const DIMENSION*>( aItem );
+
+ points->AddPoint( dimension->m_crossBarO );
+ points->AddPoint( dimension->m_crossBarF );
+ points->AddPoint( dimension->m_featureLineGO );
+ points->AddPoint( dimension->m_featureLineDO );
+
+ // Dimension height setting - edit points should move only along the feature lines
+ points->Point( DIM_CROSSBARO ).SetConstraint( new EC_LINE( points->Point( DIM_CROSSBARO ),
+ points->Point( DIM_FEATUREGO ) ) );
+ points->Point( DIM_CROSSBARF ).SetConstraint( new EC_LINE( points->Point( DIM_CROSSBARF ),
+ points->Point( DIM_FEATUREDO ) ) );
+
+ break;
+ }
+
+ default:
+ points.reset();
+ break;
+ }
+
+ return points;
+ }
+
+private:
+ EDIT_POINTS_FACTORY() {};
+};
+
+
+POINT_EDITOR::POINT_EDITOR() :
+ TOOL_INTERACTIVE( "pcbnew.PointEditor" ), m_selectionTool( NULL ), m_editedPoint( NULL ),
+ m_original( VECTOR2I( 0, 0 ) ), m_altConstrainer( VECTOR2I( 0, 0 ) )
+{
+}
+
+
+void POINT_EDITOR::Reset( RESET_REASON aReason )
+{
+ m_editPoints.reset();
+ m_altConstraint.reset();
+}
+
+
+bool POINT_EDITOR::Init()
+{
+ // Find the selection tool, so they can cooperate
+ m_selectionTool = static_cast<SELECTION_TOOL*>( m_toolMgr->FindTool( "pcbnew.InteractiveSelection" ) );
+
+ if( !m_selectionTool )
+ {
+ DisplayError( NULL, wxT( "pcbnew.InteractiveSelection tool is not available" ) );
+ return false;
+ }
+
+ m_selectionTool->GetMenu().AddItem( COMMON_ACTIONS::pointEditorAddCorner,
+ POINT_EDITOR::addCornerCondition );
+ m_selectionTool->GetMenu().AddItem( COMMON_ACTIONS::pointEditorRemoveCorner,
+ boost::bind( &POINT_EDITOR::removeCornerCondition, this, _1 ) );
+
+ return true;
+}
+
+
+void POINT_EDITOR::updateEditedPoint( const TOOL_EVENT& aEvent )
+{
+ EDIT_POINT* point = m_editedPoint;
+
+ if( aEvent.IsMotion() )
+ {
+ point = m_editPoints->FindPoint( aEvent.Position() );
+ }
+ else if( aEvent.IsDrag( BUT_LEFT ) )
+ {
+ point = m_editPoints->FindPoint( aEvent.DragOrigin() );
+ }
+
+ if( m_editedPoint != point )
+ setEditedPoint( point );
+}
+
+
+int POINT_EDITOR::OnSelectionChange( const TOOL_EVENT& aEvent )
+{
+ const SELECTION& selection = m_selectionTool->GetSelection();
+
+ if( selection.Size() == 1 )
+ {
+ Activate();
+
+ KIGFX::VIEW_CONTROLS* controls = getViewControls();
+ KIGFX::VIEW* view = getView();
+ PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
+ EDA_ITEM* item = selection.items.GetPickedItem( 0 );
+
+ m_editPoints = EDIT_POINTS_FACTORY::Make( item, getView()->GetGAL() );
+
+ if( !m_editPoints )
+ return 0;
+
+ view->Add( m_editPoints.get() );
+ m_editedPoint = NULL;
+ bool modified = false;
+
+ // Main loop: keep receiving events
+ while( OPT_TOOL_EVENT evt = Wait() )
+ {
+ if( !m_editPoints ||
+ evt->Matches( m_selectionTool->ClearedEvent ) ||
+ evt->Matches( m_selectionTool->UnselectedEvent ) ||
+ evt->Matches( m_selectionTool->SelectedEvent ) )
+ {
+ break;
+ }
+
+ if ( !modified )
+ updateEditedPoint( *evt );
+
+ if( evt->IsAction( &COMMON_ACTIONS::pointEditorAddCorner ) )
+ {
+ addCorner( controls->GetCursorPosition() );
+ updatePoints();
+ }
+
+ else if( evt->IsAction( &COMMON_ACTIONS::pointEditorRemoveCorner ) )
+ {
+ if( m_editedPoint )
+ {
+ removeCorner( m_editedPoint );
+ updatePoints();
+ }
+ }
+
+ else if( evt->IsDrag( BUT_LEFT ) && m_editedPoint )
+ {
+ if( !modified )
+ {
+ // Save items, so changes can be undone
+ editFrame->OnModify();
+ editFrame->SaveCopyInUndoList( selection.items, UR_CHANGED );
+ controls->ForceCursorPosition( false );
+ m_original = *m_editedPoint; // Save the original position
+ controls->SetAutoPan( true );
+ modified = true;
+ }
+
+ bool enableAltConstraint = !!evt->Modifier( MD_CTRL );
+ if( enableAltConstraint != (bool) m_altConstraint ) // alternative constraint
+ setAltConstraint( enableAltConstraint );
+
+ m_editedPoint->SetPosition( controls->GetCursorPosition() );
+
+ if( m_altConstraint )
+ m_altConstraint->Apply();
+ else
+ m_editedPoint->ApplyConstraint();
+
+ updateItem();
+ updatePoints();
+
+ m_editPoints->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+ }
+
+ else if( evt->IsAction( &COMMON_ACTIONS::pointEditorUpdate ) )
+ {
+ updatePoints();
+ }
+
+ else if( evt->IsMouseUp( BUT_LEFT ) )
+ {
+ controls->SetAutoPan( false );
+ setAltConstraint( false );
+ modified = false;
+ m_toolMgr->PassEvent();
+ }
+
+ else if( evt->IsCancel() )
+ {
+ if( modified ) // Restore the last change
+ {
+ wxCommandEvent dummy;
+ editFrame->RestoreCopyFromUndoList( dummy );
+
+ updatePoints();
+ modified = false;
+ }
+
+ // Let the selection tool receive the event too
+ m_toolMgr->PassEvent();
+
+ break;
+ }
+
+ else
+ {
+ m_toolMgr->PassEvent();
+ }
+ }
+
+ if( m_editPoints )
+ {
+ finishItem();
+ item->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+ view->Remove( m_editPoints.get() );
+ m_editPoints.reset();
+ }
+
+ controls->ShowCursor( false );
+ controls->SetAutoPan( false );
+ controls->SetSnapping( false );
+ }
+
+ return 0;
+}
+
+
+void POINT_EDITOR::updateItem() const
+{
+ EDA_ITEM* item = m_editPoints->GetParent();
+
+ switch( item->Type() )
+ {
+ case PCB_LINE_T:
+ case PCB_MODULE_EDGE_T:
+ {
+ DRAWSEGMENT* segment = static_cast<DRAWSEGMENT*>( item );
+ switch( segment->GetShape() )
+ {
+ case S_SEGMENT:
+ if( isModified( m_editPoints->Point( SEG_START ) ) )
+ segment->SetStart( wxPoint( m_editPoints->Point( SEG_START ).GetPosition().x,
+ m_editPoints->Point( SEG_START ).GetPosition().y ) );
+
+ else if( isModified( m_editPoints->Point( SEG_END ) ) )
+ segment->SetEnd( wxPoint( m_editPoints->Point( SEG_END ).GetPosition().x,
+ m_editPoints->Point( SEG_END ).GetPosition().y ) );
+
+ break;
+
+ case S_ARC:
+ {
+ const VECTOR2I& center = m_editPoints->Point( ARC_CENTER ).GetPosition();
+ const VECTOR2I& start = m_editPoints->Point( ARC_START ).GetPosition();
+ const VECTOR2I& end = m_editPoints->Point( ARC_END ).GetPosition();
+
+ if( center != segment->GetCenter() )
+ {
+ wxPoint moveVector = wxPoint( center.x, center.y ) - segment->GetCenter();
+ segment->Move( moveVector );
+
+ m_editPoints->Point( ARC_START ).SetPosition( segment->GetArcStart() );
+ m_editPoints->Point( ARC_END ).SetPosition( segment->GetArcEnd() );
+ }
+
+ else
+ {
+ segment->SetArcStart( wxPoint( start.x, start.y ) );
+
+ VECTOR2D startLine = start - center;
+ VECTOR2I endLine = end - center;
+ double newAngle = RAD2DECIDEG( endLine.Angle() - startLine.Angle() );
+
+ // Adjust the new angle to (counter)clockwise setting
+ bool clockwise = ( segment->GetAngle() > 0 );
+
+ if( clockwise && newAngle < 0.0 )
+ newAngle += 3600.0;
+ else if( !clockwise && newAngle > 0.0 )
+ newAngle -= 3600.0;
+
+ segment->SetAngle( newAngle );
+ }
+
+ break;
+ }
+
+ case S_CIRCLE:
+ {
+ const VECTOR2I& center = m_editPoints->Point( CIRC_CENTER ).GetPosition();
+ const VECTOR2I& end = m_editPoints->Point( CIRC_END ).GetPosition();
+
+ if( isModified( m_editPoints->Point( CIRC_CENTER ) ) )
+ {
+ wxPoint moveVector = wxPoint( center.x, center.y ) - segment->GetCenter();
+ segment->Move( moveVector );
+ }
+ else
+ {
+ segment->SetEnd( wxPoint( end.x, end.y ) );
+ }
+
+ break;
+ }
+
+ default: // suppress warnings
+ break;
+ }
+
+ // Update relative coordinates for module edges
+ if( EDGE_MODULE* edge = dyn_cast<EDGE_MODULE*>( item ) )
+ edge->SetLocalCoord();
+
+ break;
+ }
+
+ case PCB_ZONE_AREA_T:
+ {
+ ZONE_CONTAINER* zone = static_cast<ZONE_CONTAINER*>( item );
+ zone->ClearFilledPolysList();
+ CPolyLine* outline = zone->Outline();
+
+ for( int i = 0; i < outline->GetCornersCount(); ++i )
+ {
+ VECTOR2I point = m_editPoints->Point( i ).GetPosition();
+ outline->SetX( i, point.x );
+ outline->SetY( i, point.y );
+ }
+
+ break;
+ }
+
+ case PCB_DIMENSION_T:
+ {
+ DIMENSION* dimension = static_cast<DIMENSION*>( item );
+
+ // Check which point is currently modified and updated dimension's points respectively
+ if( isModified( m_editPoints->Point( DIM_CROSSBARO ) ) )
+ {
+ VECTOR2D featureLine( m_editedPoint->GetPosition() - dimension->GetOrigin() );
+ VECTOR2D crossBar( dimension->GetEnd() - dimension->GetOrigin() );
+
+ if( featureLine.Cross( crossBar ) > 0 )
+ dimension->SetHeight( -featureLine.EuclideanNorm() );
+ else
+ dimension->SetHeight( featureLine.EuclideanNorm() );
+ }
+
+ else if( isModified( m_editPoints->Point( DIM_CROSSBARF ) ) )
+ {
+ VECTOR2D featureLine( m_editedPoint->GetPosition() - dimension->GetEnd() );
+ VECTOR2D crossBar( dimension->GetEnd() - dimension->GetOrigin() );
+
+ if( featureLine.Cross( crossBar ) > 0 )
+ dimension->SetHeight( -featureLine.EuclideanNorm() );
+ else
+ dimension->SetHeight( featureLine.EuclideanNorm() );
+ }
+
+ else if( isModified( m_editPoints->Point( DIM_FEATUREGO ) ) )
+ {
+ dimension->SetOrigin( wxPoint( m_editedPoint->GetPosition().x, m_editedPoint->GetPosition().y ) );
+ m_editPoints->Point( DIM_CROSSBARO ).SetConstraint( new EC_LINE( m_editPoints->Point( DIM_CROSSBARO ),
+ m_editPoints->Point( DIM_FEATUREGO ) ) );
+ m_editPoints->Point( DIM_CROSSBARF ).SetConstraint( new EC_LINE( m_editPoints->Point( DIM_CROSSBARF ),
+ m_editPoints->Point( DIM_FEATUREDO ) ) );
+ }
+
+ else if( isModified( m_editPoints->Point( DIM_FEATUREDO ) ) )
+ {
+ dimension->SetEnd( wxPoint( m_editedPoint->GetPosition().x, m_editedPoint->GetPosition().y ) );
+ m_editPoints->Point( DIM_CROSSBARO ).SetConstraint( new EC_LINE( m_editPoints->Point( DIM_CROSSBARO ),
+ m_editPoints->Point( DIM_FEATUREGO ) ) );
+ m_editPoints->Point( DIM_CROSSBARF ).SetConstraint( new EC_LINE( m_editPoints->Point( DIM_CROSSBARF ),
+ m_editPoints->Point( DIM_FEATUREDO ) ) );
+ }
+
+ break;
+ }
+
+ default:
+ break;
+ }
+}
+
+
+void POINT_EDITOR::finishItem() const
+{
+ EDA_ITEM* item = m_editPoints->GetParent();
+
+ if( item->Type() == PCB_ZONE_AREA_T )
+ {
+ ZONE_CONTAINER* zone = static_cast<ZONE_CONTAINER*>( item );
+
+ if( zone->IsFilled() )
+ getEditFrame<PCB_EDIT_FRAME>()->Fill_Zone( zone );
+ }
+}
+
+
+void POINT_EDITOR::updatePoints()
+{
+ EDA_ITEM* item = m_editPoints->GetParent();
+
+ switch( item->Type() )
+ {
+ case PCB_LINE_T:
+ case PCB_MODULE_EDGE_T:
+ {
+ const DRAWSEGMENT* segment = static_cast<const DRAWSEGMENT*>( item );
+ {
+ switch( segment->GetShape() )
+ {
+ case S_SEGMENT:
+ m_editPoints->Point( SEG_START ).SetPosition( segment->GetStart() );
+ m_editPoints->Point( SEG_END ).SetPosition( segment->GetEnd() );
+ break;
+
+ case S_ARC:
+ m_editPoints->Point( ARC_CENTER ).SetPosition( segment->GetCenter() );
+ m_editPoints->Point( ARC_START).SetPosition( segment->GetArcStart() );
+ m_editPoints->Point( ARC_END ).SetPosition( segment->GetArcEnd() );
+ break;
+
+ case S_CIRCLE:
+ m_editPoints->Point( CIRC_CENTER ).SetPosition( segment->GetCenter() );
+ m_editPoints->Point( CIRC_END ).SetPosition( segment->GetEnd() );
+ break;
+
+ default: // suppress warnings
+ break;
+ }
+
+ break;
+ }
+ }
+
+ case PCB_ZONE_AREA_T:
+ {
+ const ZONE_CONTAINER* zone = static_cast<const ZONE_CONTAINER*>( item );
+ const CPolyLine* outline = zone->Outline();
+
+ if( m_editPoints->PointsSize() != (unsigned) outline->GetCornersCount() )
+ {
+ getView()->Remove( m_editPoints.get() );
+ m_editPoints = EDIT_POINTS_FACTORY::Make( item, getView()->GetGAL() );
+ getView()->Add( m_editPoints.get() );
+ }
+ else
+ {
+ for( int i = 0; i < outline->GetCornersCount(); ++i )
+ m_editPoints->Point( i ).SetPosition( outline->GetPos( i ) );
+ }
+
+ break;
+ }
+
+ case PCB_DIMENSION_T:
+ {
+ const DIMENSION* dimension = static_cast<const DIMENSION*>( item );
+
+ m_editPoints->Point( DIM_CROSSBARO ).SetPosition( dimension->m_crossBarO );
+ m_editPoints->Point( DIM_CROSSBARF ).SetPosition( dimension->m_crossBarF );
+ m_editPoints->Point( DIM_FEATUREGO ).SetPosition( dimension->m_featureLineGO );
+ m_editPoints->Point( DIM_FEATUREDO ).SetPosition( dimension->m_featureLineDO );
+ break;
+ }
+
+ default:
+ break;
+ }
+}
+
+
+void POINT_EDITOR::setEditedPoint( EDIT_POINT* aPoint )
+{
+ KIGFX::VIEW_CONTROLS* controls = getViewControls();
+
+ if( aPoint )
+ {
+ controls->ForceCursorPosition( true, aPoint->GetPosition() );
+ controls->ShowCursor( true );
+ controls->SetSnapping( true );
+ }
+ else
+ {
+ controls->ShowCursor( false );
+ controls->SetSnapping( false );
+ controls->ForceCursorPosition( false );
+ }
+
+ m_editedPoint = aPoint;
+}
+
+
+void POINT_EDITOR::setAltConstraint( bool aEnabled )
+{
+ if( aEnabled )
+ {
+ EDIT_LINE* line = dynamic_cast<EDIT_LINE*>( m_editedPoint );
+
+ if( line )
+ {
+ if( m_editPoints->GetParent()->Type() == PCB_ZONE_AREA_T )
+ m_altConstraint.reset( (EDIT_CONSTRAINT<EDIT_POINT>*)( new EC_CONVERGING( *line, *m_editPoints ) ) );
+ }
+ else
+ {
+ // Find a proper constraining point for 45 degrees mode
+ m_altConstrainer = get45DegConstrainer();
+ m_altConstraint.reset( new EC_45DEGREE( *m_editedPoint, m_altConstrainer ) );
+ }
+ }
+ else
+ {
+ m_altConstraint.reset();
+ }
+}
+
+
+EDIT_POINT POINT_EDITOR::get45DegConstrainer() const
+{
+ EDA_ITEM* item = m_editPoints->GetParent();
+
+ switch( item->Type() )
+ {
+ case PCB_LINE_T:
+ case PCB_MODULE_EDGE_T:
+ {
+ const DRAWSEGMENT* segment = static_cast<const DRAWSEGMENT*>( item );
+ {
+ switch( segment->GetShape() )
+ {
+ case S_SEGMENT:
+ return *( m_editPoints->Next( *m_editedPoint ) ); // select the other end of line
+
+ case S_ARC:
+ case S_CIRCLE:
+ return m_editPoints->Point( CIRC_CENTER );
+
+ default: // suppress warnings
+ break;
+ }
+ }
+
+ break;
+ }
+
+ case PCB_DIMENSION_T:
+ {
+ // Constraint for crossbar
+ if( isModified( m_editPoints->Point( DIM_FEATUREGO ) ) )
+ return m_editPoints->Point( DIM_FEATUREDO );
+
+ else if( isModified( m_editPoints->Point( DIM_FEATUREDO ) ) )
+ return m_editPoints->Point( DIM_FEATUREGO );
+
+ else
+ return EDIT_POINT( m_editedPoint->GetPosition() ); // no constraint
+
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ // In any other case we may align item to its original position
+ return m_original;
+}
+
+
+void POINT_EDITOR::addCorner( const VECTOR2I& aBreakPoint )
+{
+ EDA_ITEM* item = m_editPoints->GetParent();
+ const SELECTION& selection = m_selectionTool->GetSelection();
+
+ if( item->Type() == PCB_ZONE_AREA_T )
+ {
+ getEditFrame<PCB_BASE_FRAME>()->OnModify();
+ getEditFrame<PCB_BASE_FRAME>()->SaveCopyInUndoList( selection.items, UR_CHANGED );
+
+ ZONE_CONTAINER* zone = static_cast<ZONE_CONTAINER*>( item );
+ CPolyLine* outline = zone->Outline();
+
+ // Handle the last segment, so other segments can be easily handled in a loop
+ unsigned int nearestIdx = outline->GetCornersCount() - 1, nextNearestIdx = 0;
+ SEG side( VECTOR2I( outline->GetPos( nearestIdx ) ),
+ VECTOR2I( outline->GetPos( nextNearestIdx ) ) );
+ unsigned int nearestDist = side.Distance( aBreakPoint );
+
+ for( int i = 0; i < outline->GetCornersCount() - 1; ++i )
+ {
+ side = SEG( VECTOR2I( outline->GetPos( i ) ), VECTOR2I( outline->GetPos( i + 1 ) ) );
+
+ unsigned int distance = side.Distance( aBreakPoint );
+ if( distance < nearestDist )
+ {
+ nearestDist = distance;
+ nearestIdx = i;
+ nextNearestIdx = i + 1;
+ }
+ }
+
+ // Find the point on the closest segment
+ VECTOR2I sideOrigin( outline->GetPos( nearestIdx ) );
+ VECTOR2I sideEnd( outline->GetPos( nextNearestIdx ) );
+ SEG nearestSide( sideOrigin, sideEnd );
+ VECTOR2I nearestPoint = nearestSide.NearestPoint( aBreakPoint );
+
+ // Do not add points that have the same coordinates as ones that already belong to polygon
+ // instead, add a point in the middle of the side
+ if( nearestPoint == sideOrigin || nearestPoint == sideEnd )
+ nearestPoint = ( sideOrigin + sideEnd ) / 2;
+
+ outline->InsertCorner( nearestIdx, nearestPoint.x, nearestPoint.y );
+ }
+
+ else if( item->Type() == PCB_LINE_T || item->Type() == PCB_MODULE_EDGE_T )
+ {
+ bool moduleEdge = item->Type() == PCB_MODULE_EDGE_T;
+ PCB_BASE_FRAME* frame = getEditFrame<PCB_BASE_FRAME>();
+
+ frame->OnModify();
+
+ if( moduleEdge )
+ frame->SaveCopyInUndoList( getModel<BOARD>()->m_Modules, UR_MODEDIT );
+ else
+ frame->SaveCopyInUndoList( selection.items, UR_CHANGED );
+
+ DRAWSEGMENT* segment = static_cast<DRAWSEGMENT*>( item );
+
+ if( segment->GetShape() == S_SEGMENT )
+ {
+ SEG seg( segment->GetStart(), segment->GetEnd() );
+ VECTOR2I nearestPoint = seg.NearestPoint( aBreakPoint );
+
+ // Move the end of the line to the break point..
+ segment->SetEnd( wxPoint( nearestPoint.x, nearestPoint.y ) );
+
+ // and add another one starting from the break point
+ DRAWSEGMENT* newSegment;
+
+ if( moduleEdge )
+ {
+ EDGE_MODULE* edge = static_cast<EDGE_MODULE*>( segment );
+ assert( segment->GetParent()->Type() == PCB_MODULE_T );
+ newSegment = new EDGE_MODULE( *edge );
+ edge->SetLocalCoord();
+ }
+ else
+ {
+ newSegment = new DRAWSEGMENT( *segment );
+ }
+
+ newSegment->ClearSelected();
+ newSegment->SetStart( wxPoint( nearestPoint.x, nearestPoint.y ) );
+ newSegment->SetEnd( wxPoint( seg.B.x, seg.B.y ) );
+
+ if( moduleEdge )
+ {
+ static_cast<EDGE_MODULE*>( newSegment )->SetLocalCoord();
+ getModel<BOARD>()->m_Modules->Add( newSegment );
+ }
+ else
+ {
+ getModel<BOARD>()->Add( newSegment );
+ }
+
+ getView()->Add( newSegment );
+ }
+ }
+}
+
+
+void POINT_EDITOR::removeCorner( EDIT_POINT* aPoint )
+{
+ EDA_ITEM* item = m_editPoints->GetParent();
+
+ if( item->Type() == PCB_ZONE_AREA_T )
+ {
+ const SELECTION& selection = m_selectionTool->GetSelection();
+ PCB_BASE_FRAME* frame = getEditFrame<PCB_BASE_FRAME>();
+
+ ZONE_CONTAINER* zone = static_cast<ZONE_CONTAINER*>( item );
+ CPolyLine* outline = zone->Outline();
+
+ for( int i = 0; i < outline->GetCornersCount(); ++i )
+ {
+ if( VECTOR2I( outline->GetPos( i ) ) == aPoint->GetPosition() )
+ {
+ frame->OnModify();
+ frame->SaveCopyInUndoList( selection.items, UR_CHANGED );
+ outline->DeleteCorner( i );
+ setEditedPoint( NULL );
+ break;
+ }
+ }
+ }
+}
+
+
+void POINT_EDITOR::SetTransitions()
+{
+ Go( &POINT_EDITOR::OnSelectionChange, SELECTION_TOOL::SelectedEvent );
+ Go( &POINT_EDITOR::OnSelectionChange, SELECTION_TOOL::UnselectedEvent );
+}
+
+
+bool POINT_EDITOR::addCornerCondition( const SELECTION& aSelection )
+{
+ if( aSelection.Size() != 1 )
+ return false;
+
+ BOARD_ITEM* item = aSelection.Item<BOARD_ITEM>( 0 );
+
+ // Works only for zones and line segments
+ return item->Type() == PCB_ZONE_AREA_T ||
+ ( ( item->Type() == PCB_LINE_T || item->Type() == PCB_MODULE_EDGE_T ) &&
+ static_cast<DRAWSEGMENT*>( item )->GetShape() == S_SEGMENT );
+}
+
+
+bool POINT_EDITOR::removeCornerCondition( const SELECTION& )
+{
+ if( !m_editPoints )
+ return false;
+
+ EDA_ITEM* item = m_editPoints->GetParent();
+
+ if( item->Type() != PCB_ZONE_AREA_T )
+ return false;
+
+ ZONE_CONTAINER* zone = static_cast<ZONE_CONTAINER*>( item );
+
+ if( zone->GetNumCorners() <= 3 )
+ return false;
+
+ // Remove corner does not work with lines
+ if( dynamic_cast<EDIT_LINE*>( m_editedPoint ) )
+ return false;
+
+ return m_editedPoint != NULL;
+}
diff --git a/pcbnew/tools/point_editor.h b/pcbnew/tools/point_editor.h
new file mode 100644
index 0000000..fc4c6a0
--- /dev/null
+++ b/pcbnew/tools/point_editor.h
@@ -0,0 +1,120 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2013 CERN
+ * @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
+ */
+
+#ifndef __POINT_EDITOR_H
+#define __POINT_EDITOR_H
+
+#include <boost/shared_ptr.hpp>
+
+#include <tool/tool_interactive.h>
+#include "edit_points.h"
+
+class SELECTION_TOOL;
+
+/**
+ * Class POINT_EDITOR
+ *
+ * Tool that displays edit points allowing to modify items by dragging the points.
+ */
+class POINT_EDITOR : public TOOL_INTERACTIVE
+{
+public:
+ POINT_EDITOR();
+
+ /// @copydoc TOOL_INTERACTIVE::Reset()
+ void Reset( RESET_REASON aReason );
+
+ /// @copydoc TOOL_INTERACTIVE::Init()
+ bool Init();
+
+ /**
+ * Function OnSelected()
+ *
+ * Change selection event handler.
+ */
+ int OnSelectionChange( const TOOL_EVENT& aEvent );
+
+ ///> Sets up handlers for various events.
+ void SetTransitions();
+
+private:
+ ///> Selection tool used for obtaining selected items
+ SELECTION_TOOL* m_selectionTool;
+
+ ///> Currently edited point, NULL if there is none.
+ EDIT_POINT* m_editedPoint;
+
+ ///> Original position for the current drag point.
+ EDIT_POINT m_original;
+
+ ///> Currently available edit points.
+ boost::shared_ptr<EDIT_POINTS> m_editPoints;
+
+ // Alternative constraint, enabled while a modifier key is held
+ boost::shared_ptr<EDIT_CONSTRAINT<EDIT_POINT> > m_altConstraint;
+
+ // EDIT_POINT for alternative constraint mode
+ EDIT_POINT m_altConstrainer;
+
+ ///> Updates item's points with edit points.
+ void updateItem() const;
+
+ ///> Applies the last changes to the edited item.
+ void finishItem() const;
+
+ ///> Updates edit points with item's points.
+ void updatePoints();
+
+ ///> Updates which point is being edited.
+ void updateEditedPoint( const TOOL_EVENT& aEvent );
+
+ ///> Sets the current point being edited. NULL means none.
+ void setEditedPoint( EDIT_POINT* aPoint );
+
+ ///> Returns true if aPoint is the currently modified point.
+ inline bool isModified( const EDIT_POINT& aPoint ) const
+ {
+ return m_editedPoint == &aPoint;
+ }
+
+ ///> Sets up an alternative constraint (typically enabled upon a modifier key being pressed).
+ void setAltConstraint( bool aEnabled );
+
+ ///> Returns a point that should be used as a constrainer for 45 degrees mode.
+ EDIT_POINT get45DegConstrainer() const;
+
+ ///> Adds a new edit point on a zone outline/line.
+ void addCorner( const VECTOR2I& aPoint );
+
+ ///> Removes a corner.
+ void removeCorner( EDIT_POINT* aPoint );
+
+ ///> Condition to display "Create corner" context menu entry.
+ static bool addCornerCondition( const SELECTION& aSelection );
+
+ ///> Condition to display "Remove corner" context menu entry.
+ bool removeCornerCondition( const SELECTION& aSelection );
+};
+
+#endif
diff --git a/pcbnew/tools/selection_area.cpp b/pcbnew/tools/selection_area.cpp
new file mode 100644
index 0000000..8bfdcec
--- /dev/null
+++ b/pcbnew/tools/selection_area.cpp
@@ -0,0 +1,63 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2013 CERN
+ * @author Tomasz Wlostowski <tomasz.wlostowski@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 "selection_area.h"
+#include <gal/graphics_abstraction_layer.h>
+#include <gal/color4d.h>
+
+using namespace KIGFX;
+
+const BOX2I SELECTION_AREA::ViewBBox() const
+{
+ BOX2I tmp;
+
+ tmp.SetOrigin( m_origin );
+ tmp.SetEnd( m_end );
+ tmp.Normalize();
+ return tmp;
+}
+
+
+void SELECTION_AREA::ViewGetLayers( int aLayers[], int& aCount ) const
+{
+ aLayers[0] = SelectionLayer;
+ aCount = 1;
+}
+
+
+void SELECTION_AREA::ViewDraw( int aLayer, KIGFX::GAL* aGal ) const
+{
+ aGal->SetLineWidth( 1.0 );
+ aGal->SetStrokeColor( COLOR4D( 1.0, 1.0, 0.4, 1.0 ) );
+ aGal->SetFillColor( COLOR4D( 0.3, 0.3, 0.5, 0.3 ) );
+ aGal->SetIsStroke( true );
+ aGal->SetIsFill( true );
+ aGal->DrawRectangle( m_origin, m_end );
+}
+
+
+SELECTION_AREA::SELECTION_AREA() :
+ EDA_ITEM( NOT_USED ) // this item is never added to a BOARD so it needs no type.
+{
+}
diff --git a/pcbnew/tools/selection_area.h b/pcbnew/tools/selection_area.h
new file mode 100644
index 0000000..737e7be
--- /dev/null
+++ b/pcbnew/tools/selection_area.h
@@ -0,0 +1,84 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2013 CERN
+ * @author Tomasz Wlostowski <tomasz.wlostowski@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
+ */
+
+#ifndef __SELECTION_AREA_H
+#define __SELECTION_AREA_H
+
+#include <base_struct.h>
+#include <layers_id_colors_and_visibility.h>
+#include <math/box2.h>
+
+namespace KIGFX
+{
+class GAL;
+}
+
+/**
+ * Class SELECTION_AREA
+ *
+ * Represents a selection area (currently a rectangle) in a VIEW.
+ */
+class SELECTION_AREA : public EDA_ITEM
+{
+public:
+ static const int SelectionLayer = ITEM_GAL_LAYER( GP_OVERLAY );
+
+ SELECTION_AREA();
+ ~SELECTION_AREA() {};
+
+ virtual const BOX2I ViewBBox() const;
+
+ void ViewDraw( int aLayer, KIGFX::GAL* aGal ) const;
+
+ void ViewGetLayers( int aLayers[], int& aCount ) const;
+
+ void SetOrigin( VECTOR2I aOrigin )
+ {
+ m_origin = aOrigin;
+ }
+
+ void SetEnd( VECTOR2I aEnd )
+ {
+ m_end = aEnd;
+ }
+
+#if defined(DEBUG)
+ void Show( int x, std::ostream& st ) const
+ {
+ }
+#endif
+
+ /** Get class name
+ * @return string "SELECTION_AREA"
+ */
+ virtual wxString GetClass() const
+ {
+ return wxT( "SELECTION_AREA" );
+ }
+
+private:
+ VECTOR2I m_origin, m_end;
+};
+
+#endif
diff --git a/pcbnew/tools/selection_conditions.cpp b/pcbnew/tools/selection_conditions.cpp
new file mode 100644
index 0000000..55cbc77
--- /dev/null
+++ b/pcbnew/tools/selection_conditions.cpp
@@ -0,0 +1,256 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2014 CERN
+ * @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 "selection_conditions.h"
+#include "selection_tool.h"
+#include <class_board_connected_item.h>
+
+#include <boost/bind.hpp>
+
+
+bool SELECTION_CONDITIONS::NotEmpty( const SELECTION& aSelection )
+{
+ return !aSelection.Empty();
+}
+
+
+bool SELECTION_CONDITIONS::OnlyConnectedItems( const SELECTION& aSelection )
+{
+ if( aSelection.Empty() )
+ return false;
+
+ for( int i = 0; i < aSelection.Size(); ++i )
+ {
+ KICAD_T type = aSelection.Item<EDA_ITEM>( i )->Type();
+
+ if( type != PCB_PAD_T && type != PCB_VIA_T && type != PCB_TRACE_T && type != PCB_ZONE_T )
+ return false;
+ }
+
+ return true;
+}
+
+
+SELECTION_CONDITION SELECTION_CONDITIONS::SameNet( bool aAllowUnconnected )
+{
+ return boost::bind( &SELECTION_CONDITIONS::sameNetFunc, _1, aAllowUnconnected );
+}
+
+
+SELECTION_CONDITION SELECTION_CONDITIONS::SameLayer()
+{
+ return boost::bind( &SELECTION_CONDITIONS::sameLayerFunc, _1 );
+}
+
+
+SELECTION_CONDITION SELECTION_CONDITIONS::HasType( KICAD_T aType )
+{
+ return boost::bind( &SELECTION_CONDITIONS::hasTypeFunc, _1, aType );
+}
+
+
+SELECTION_CONDITION SELECTION_CONDITIONS::OnlyType( KICAD_T aType )
+{
+ return boost::bind( &SELECTION_CONDITIONS::onlyTypeFunc, _1, aType );
+}
+
+
+SELECTION_CONDITION SELECTION_CONDITIONS::OnlyTypes( const std::vector<KICAD_T>& aTypes )
+{
+ return boost::bind( &SELECTION_CONDITIONS::onlyTypesFunc, _1, aTypes );
+}
+
+
+SELECTION_CONDITION SELECTION_CONDITIONS::Count( int aNumber )
+{
+ return boost::bind( &SELECTION_CONDITIONS::countFunc, _1, aNumber );
+}
+
+
+SELECTION_CONDITION SELECTION_CONDITIONS::MoreThan( int aNumber )
+{
+ return boost::bind( &SELECTION_CONDITIONS::moreThanFunc, _1, aNumber );
+}
+
+
+SELECTION_CONDITION SELECTION_CONDITIONS::LessThan( int aNumber )
+{
+ return boost::bind( &SELECTION_CONDITIONS::lessThanFunc, _1, aNumber );
+}
+
+
+bool SELECTION_CONDITIONS::sameNetFunc( const SELECTION& aSelection, bool aAllowUnconnected )
+{
+ if( aSelection.Empty() )
+ return false;
+
+ int netcode = -1; // -1 stands for 'net code is not yet determined'
+
+ for( int i = 0; i < aSelection.Size(); ++i )
+ {
+ int current_netcode = -1;
+
+ const BOARD_CONNECTED_ITEM* item =
+ dynamic_cast<const BOARD_CONNECTED_ITEM*>( aSelection.Item<EDA_ITEM>( i ) );
+
+ if( item )
+ {
+ current_netcode = item->GetNetCode();
+ }
+ else
+ {
+ if( !aAllowUnconnected )
+ return false;
+ else
+ // if it is not a BOARD_CONNECTED_ITEM, treat it as if there was no net assigned
+ current_netcode = 0;
+ }
+
+ assert( current_netcode >= 0 );
+
+ if( netcode < 0 )
+ {
+ netcode = current_netcode;
+
+ if( netcode == NETINFO_LIST::UNCONNECTED && !aAllowUnconnected )
+ return false;
+ }
+ else if( netcode != current_netcode )
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+bool SELECTION_CONDITIONS::sameLayerFunc( const SELECTION& aSelection )
+{
+ if( aSelection.Empty() )
+ return false;
+
+ LSET layerSet;
+ layerSet.set();
+
+ for( int i = 0; i < aSelection.Size(); ++i )
+ {
+ const BOARD_ITEM* item = dynamic_cast<const BOARD_ITEM*>( aSelection.Item<EDA_ITEM>( i ) );
+
+ if( !item )
+ return false;
+
+ layerSet &= item->GetLayerSet();
+
+ if( !layerSet.any() ) // there are no common layers left
+ return false;
+ }
+
+ return true;
+}
+
+
+bool SELECTION_CONDITIONS::hasTypeFunc( const SELECTION& aSelection, KICAD_T aType )
+{
+ for( int i = 0; i < aSelection.Size(); ++i )
+ {
+ if( aSelection.Item<EDA_ITEM>( i )->Type() == aType )
+ return true;
+ }
+
+ return false;
+}
+
+
+bool SELECTION_CONDITIONS::onlyTypeFunc( const SELECTION& aSelection, KICAD_T aType )
+{
+ if( aSelection.Empty() )
+ return false;
+
+ for( int i = 0; i < aSelection.Size(); ++i )
+ {
+ if( aSelection.Item<EDA_ITEM>( i )->Type() != aType )
+ return false;
+ }
+
+ return true;
+}
+
+
+bool SELECTION_CONDITIONS::onlyTypesFunc( const SELECTION& aSelection, const std::vector<KICAD_T>& aTypes )
+{
+ if( aSelection.Empty() )
+ return false;
+
+ for( int i = 0; i < aSelection.Size(); ++i )
+ {
+ bool valid = false;
+
+ for( std::vector<KICAD_T>::const_iterator it = aTypes.begin(); it != aTypes.end(); ++it )
+ {
+ if( aSelection.Item<EDA_ITEM>( i )->Type() == *it )
+ {
+ valid = true;
+ break;
+ }
+ }
+
+ if( !valid )
+ return false;
+ }
+
+ return true;
+}
+
+
+bool SELECTION_CONDITIONS::countFunc( const SELECTION& aSelection, int aNumber )
+{
+ return aSelection.Size() == aNumber;
+}
+
+
+bool SELECTION_CONDITIONS::moreThanFunc( const SELECTION& aSelection, int aNumber )
+{
+ return aSelection.Size() > aNumber;
+}
+
+
+bool SELECTION_CONDITIONS::lessThanFunc( const SELECTION& aSelection, int aNumber )
+{
+ return aSelection.Size() < aNumber;
+}
+
+
+SELECTION_CONDITION operator||( const SELECTION_CONDITION& aConditionA,
+ const SELECTION_CONDITION& aConditionB )
+{
+ return boost::bind( &SELECTION_CONDITIONS::orFunc, aConditionA, aConditionB, _1 );
+}
+
+
+SELECTION_CONDITION operator&&( const SELECTION_CONDITION& aConditionA,
+ const SELECTION_CONDITION& aConditionB )
+{
+ return boost::bind( &SELECTION_CONDITIONS::andFunc, aConditionA, aConditionB, _1 );
+}
diff --git a/pcbnew/tools/selection_conditions.h b/pcbnew/tools/selection_conditions.h
new file mode 100644
index 0000000..0c508a8
--- /dev/null
+++ b/pcbnew/tools/selection_conditions.h
@@ -0,0 +1,193 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2014 CERN
+ * @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
+ */
+
+#ifndef SELECTION_CONDITIONS_H_
+#define SELECTION_CONDITIONS_H_
+
+#include <boost/function.hpp>
+#include <core/typeinfo.h>
+#include <vector>
+
+struct SELECTION;
+
+///> Functor type that checks a specific condition for selected items.
+typedef boost::function<bool (const SELECTION&)> SELECTION_CONDITION;
+
+SELECTION_CONDITION operator||( const SELECTION_CONDITION& aConditionA,
+ const SELECTION_CONDITION& aConditionB );
+
+SELECTION_CONDITION operator&&( const SELECTION_CONDITION& aConditionA,
+ const SELECTION_CONDITION& aConditionB );
+
+
+/**
+ * Class that groups generic conditions for selected items.
+ */
+class SELECTION_CONDITIONS
+{
+public:
+ /**
+ * Function ShowAlways
+ * The default condition function (always returns true).
+ * @param aSelection is the selection to be tested.
+ * @return Always true;
+ */
+ static bool ShowAlways( const SELECTION& aSelection )
+ {
+ return true;
+ }
+
+ /**
+ * Function NotEmpty
+ * Tests if there are any items selected.
+ * @param aSelection is the selection to be tested.
+ * @return True if there is at least one item selected.
+ */
+ static bool NotEmpty( const SELECTION& aSelection );
+
+ /**
+ * Function OnlyConnectedItems
+ * Tests if selection contains exclusively connected items (pads, tracks, vias, zones).
+ * @param aSelection is the selection to be tested.
+ * @return True if there are only connected items connected.
+ */
+ static bool OnlyConnectedItems( const SELECTION& aSelection );
+
+ /**
+ * Function SameNet
+ * Creates a functor that tests if selection contains items belonging to the same net or are
+ * unconnected if aAllowUnconnected == true.
+ * @param aAllowUnconnected determines if unconnected items (with no net code assigned) should
+ * be treated as connected to the same net.
+ * @return Functor testing if selected items are belonging to the same net.
+ */
+ static SELECTION_CONDITION SameNet( bool aAllowUnconnected = false );
+
+ /**
+ * Function SameLayer
+ * Creates a functor that tests if selection contains items that belong exclusively to the same
+ * layer. In case of items belonging to multiple layers, it is enough to have a single common
+ * layer with other items.
+ * @return Functor testing if selected items share at least one common layer.
+ */
+ static SELECTION_CONDITION SameLayer();
+
+ /**
+ * Function HasType
+ * Creates a functor that tests if among the selected items there is at least one of a given type.
+ * @param aType is the type that is searched.
+ * @return Functor testing for presence of items of a given type.
+ */
+ static SELECTION_CONDITION HasType( KICAD_T aType );
+
+ /**
+ * Function OnlyType
+ * Creates a functor that tests if the selected items are *only* of given type.
+ * @param aType is the type that is searched.
+ * @return Functor testing if selected items are exclusively of one type.
+ */
+ static SELECTION_CONDITION OnlyType( KICAD_T aType );
+
+ /**
+ * Function OnlyTypes
+ * Creates a functor that tests if the selected items are *only* of given types.
+ * @param aType is a vector containing types that are searched.
+ * @return Functor testing if selected items are exclusively of the requested types.
+ */
+ static SELECTION_CONDITION OnlyTypes( const std::vector<KICAD_T>& aTypes );
+
+ /**
+ * Function Count
+ * Creates a functor that tests if the number of selected items is equal to the value given as
+ * parameter.
+ * @param aNumber is the number of expected items.
+ * @return Functor testing if the number of selected items is equal aNumber.
+ */
+ static SELECTION_CONDITION Count( int aNumber );
+
+ /**
+ * Function MoreThan
+ * Creates a functor that tests if the number of selected items is greater than the value given
+ * as parameter.
+ * @param aNumber is the number used for comparison.
+ * @return Functor testing if the number of selected items is greater than aNumber.
+ */
+ static SELECTION_CONDITION MoreThan( int aNumber );
+
+ /**
+ * Function LessThan
+ * Creates a functor that tests if the number of selected items is smaller than the value given
+ * as parameter.
+ * @param aNumber is the number used for comparison.
+ * @return Functor testing if the number of selected items is smaller than aNumber.
+ */
+ static SELECTION_CONDITION LessThan( int aNumber );
+
+private:
+ ///> Helper function used by SameNet()
+ static bool sameNetFunc( const SELECTION& aSelection, bool aAllowUnconnected );
+
+ ///> Helper function used by SameLayer()
+ static bool sameLayerFunc( const SELECTION& aSelection );
+
+ ///> Helper function used by HasType()
+ static bool hasTypeFunc( const SELECTION& aSelection, KICAD_T aType );
+
+ ///> Helper function used by OnlyType()
+ static bool onlyTypeFunc( const SELECTION& aSelection, KICAD_T aType );
+
+ ///> Helper function used by OnlyTypes()
+ static bool onlyTypesFunc( const SELECTION& aSelection, const std::vector<KICAD_T>& aTypes );
+
+ ///> Helper function used by Count()
+ static bool countFunc( const SELECTION& aSelection, int aNumber );
+
+ ///> Helper function used by MoreThan()
+ static bool moreThanFunc( const SELECTION& aSelection, int aNumber );
+
+ ///> Helper function used by LessThan()
+ static bool lessThanFunc( const SELECTION& aSelection, int aNumber );
+
+ ///> Helper function used by operator||
+ static bool orFunc( const SELECTION_CONDITION& aConditionA,
+ const SELECTION_CONDITION& aConditionB, const SELECTION& aSelection )
+ {
+ return aConditionA( aSelection ) || aConditionB( aSelection );
+ }
+
+ ///> Helper function used by operator&&
+ static bool andFunc( const SELECTION_CONDITION& aConditionA,
+ const SELECTION_CONDITION& aConditionB, const SELECTION& aSelection )
+ {
+ return aConditionA( aSelection ) && aConditionB( aSelection );
+ }
+
+ friend SELECTION_CONDITION operator||( const SELECTION_CONDITION& aConditionA,
+ const SELECTION_CONDITION& aConditionB );
+
+ friend SELECTION_CONDITION operator&&( const SELECTION_CONDITION& aConditionA,
+ const SELECTION_CONDITION& aConditionB );
+};
+
+#endif /* SELECTION_CONDITIONS_H_ */
diff --git a/pcbnew/tools/selection_tool.cpp b/pcbnew/tools/selection_tool.cpp
new file mode 100644
index 0000000..fb18e54
--- /dev/null
+++ b/pcbnew/tools/selection_tool.cpp
@@ -0,0 +1,1459 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2013-2016 CERN
+ * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
+ * @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 <limits>
+
+#include <boost/foreach.hpp>
+#include <boost/bind.hpp>
+#include <boost/function.hpp>
+
+#include <class_board.h>
+#include <class_board_item.h>
+#include <class_track.h>
+#include <class_module.h>
+#include <class_pcb_text.h>
+#include <class_drawsegment.h>
+
+#include <wxPcbStruct.h>
+#include <collectors.h>
+#include <confirm.h>
+#include <dialog_find.h>
+
+#include <class_draw_panel_gal.h>
+#include <view/view_controls.h>
+#include <view/view_group.h>
+#include <painter.h>
+
+#include <tool/tool_event.h>
+#include <tool/tool_manager.h>
+#include <ratsnest_data.h>
+
+#include "selection_tool.h"
+#include "selection_area.h"
+#include "zoom_menu.h"
+#include "grid_menu.h"
+#include "bright_box.h"
+#include "common_actions.h"
+
+class SELECT_MENU: public CONTEXT_MENU
+{
+public:
+ SELECT_MENU()
+ {
+ Add( COMMON_ACTIONS::selectConnection );
+ Add( COMMON_ACTIONS::selectCopper );
+ Add( COMMON_ACTIONS::selectNet );
+ }
+};
+
+
+SELECTION_TOOL::SELECTION_TOOL() :
+ TOOL_INTERACTIVE( "pcbnew.InteractiveSelection" ),
+ m_frame( NULL ), m_additive( false ), m_multiple( false ), m_editModules( false ),
+ m_locked( true ), m_menu( this ), m_contextMenu( NULL ), m_selectMenu( NULL ),
+ m_zoomMenu( NULL ), m_gridMenu( NULL )
+{
+ // Do not leave uninitialized members:
+ m_preliminary = false;
+}
+
+
+SELECTION_TOOL::~SELECTION_TOOL()
+{
+ delete m_selection.group;
+ delete m_contextMenu;
+ delete m_selectMenu;
+ delete m_zoomMenu;
+ delete m_gridMenu;
+}
+
+
+bool SELECTION_TOOL::Init()
+{
+ m_selection.group = new KIGFX::VIEW_GROUP;
+
+ m_selectMenu = new SELECT_MENU;
+ m_selectMenu->SetTool( this );
+
+ m_menu.AddMenu( m_selectMenu, _( "Select..." ), false,
+ ( SELECTION_CONDITIONS::OnlyType( PCB_VIA_T ) || SELECTION_CONDITIONS::OnlyType( PCB_TRACE_T ) ) &&
+ SELECTION_CONDITIONS::Count( 1 ) );
+
+ m_menu.AddSeparator( SELECTION_CONDITIONS::ShowAlways, 1000 );
+
+ 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 SELECTION_TOOL::Reset( RESET_REASON aReason )
+{
+ m_frame = getEditFrame<PCB_BASE_FRAME>();
+ m_locked = true;
+ m_preliminary = true;
+
+ if( aReason == TOOL_BASE::MODEL_RELOAD )
+ {
+ // Remove pointers to the selected items from containers
+ // without changing their properties (as they are already deleted
+ // while a new board is loaded)
+ m_selection.clear();
+ getView()->GetPainter()->GetSettings()->SetHighlight( false );
+ }
+ else
+ // Restore previous properties of selected items and remove them from containers
+ clearSelection();
+
+ // Reinsert the VIEW_GROUP, in case it was removed from the VIEW
+ getView()->Remove( m_selection.group );
+ getView()->Add( m_selection.group );
+}
+
+
+int SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
+{
+ // Main loop: keep receiving events
+ while( OPT_TOOL_EVENT evt = Wait() )
+ {
+ // Should selected items be added to the current selection or
+ // become the new selection (discarding previously selected items)
+ m_additive = evt->Modifier( MD_SHIFT );
+
+ // single click? Select single object
+ if( evt->IsClick( BUT_LEFT ) )
+ {
+ if( evt->Modifier( MD_CTRL ) && !m_editModules )
+ {
+ m_toolMgr->RunAction( COMMON_ACTIONS::highlightNet, true );
+ }
+ else
+ {
+ if( !m_additive )
+ clearSelection();
+
+ selectPoint( evt->Position() );
+ }
+ }
+
+ // right click? if there is any object - show the context menu
+ else if( evt->IsClick( BUT_RIGHT ) )
+ {
+ bool emptySelection = m_selection.Empty();
+
+ 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_preliminary = emptySelection;
+ }
+
+ // double click? Display the properties window
+ else if( evt->IsDblClick( BUT_LEFT ) )
+ {
+ if( m_selection.Empty() )
+ selectPoint( evt->Position() );
+
+ m_toolMgr->RunAction( COMMON_ACTIONS::properties );
+ }
+
+ // drag with LMB? Select multiple objects (or at least draw a selection box) or drag them
+ else if( evt->IsDrag( BUT_LEFT ) )
+ {
+ if( m_additive )
+ {
+ m_preliminary = false;
+
+ selectMultiple();
+ }
+ else if( m_selection.Empty() )
+ {
+ m_preliminary = false;
+
+ // There is nothing selected, so try to select something
+ if( !selectCursor() )
+ {
+ // If nothings has been selected or user wants to select more
+ // draw the selection box
+ selectMultiple();
+ }
+ else
+ {
+ m_toolMgr->InvokeTool( "pcbnew.InteractiveEdit" );
+ }
+ }
+
+ else
+ {
+ // Check if dragging has started within any of selected items bounding box
+ if( selectionContains( evt->Position() ) )
+ {
+ // Yes -> run the move tool and wait till it finishes
+ m_toolMgr->InvokeTool( "pcbnew.InteractiveEdit" );
+ }
+ else
+ {
+ // No -> clear the selection list
+ clearSelection();
+ }
+ }
+ }
+
+ else if( evt->IsAction( &COMMON_ACTIONS::selectionCursor ) )
+ {
+ selectCursor( true );
+ }
+
+ else if( evt->IsAction( &COMMON_ACTIONS::find ) )
+ {
+ find( *evt );
+ }
+
+ else if( evt->IsAction( &COMMON_ACTIONS::findMove ) )
+ {
+ findMove( *evt );
+ }
+
+ else if( evt->IsAction( &COMMON_ACTIONS::selectItem ) )
+ {
+ SelectItem( *evt );
+ }
+
+ else if( evt->IsAction( &COMMON_ACTIONS::unselectItem ) )
+ {
+ UnselectItem( *evt );
+ }
+
+ else if( evt->IsCancel() || evt->Action() == TA_UNDO_REDO ||
+ evt->IsAction( &COMMON_ACTIONS::selectionClear ) )
+ {
+ clearSelection();
+ }
+
+ else if( evt->IsAction( &COMMON_ACTIONS::selectConnection ) )
+ {
+ selectConnection( *evt );
+ }
+
+ else if( evt->IsAction( &COMMON_ACTIONS::selectCopper ) )
+ {
+ selectCopper( *evt );
+ }
+
+ else if( evt->IsAction( &COMMON_ACTIONS::selectNet ) )
+ {
+ selectNet( *evt );
+ }
+
+ else if( evt->Action() == TA_CONTEXT_MENU_CLOSED )
+ {
+ if( m_preliminary )
+ clearSelection();
+
+ if( evt->Parameter<CONTEXT_MENU*>() == m_contextMenu )
+ {
+ delete m_contextMenu;
+ m_contextMenu = NULL;
+ }
+ }
+ }
+
+ // This tool is supposed to be active forever
+ assert( false );
+
+ return 0;
+}
+
+
+const SELECTION& SELECTION_TOOL::GetSelection()
+{
+ // The selected items list has been requested, so it is no longer preliminary
+ m_preliminary = false;
+
+ // Filter out not modifiable items
+ for( int i = 0; i < m_selection.Size(); )
+ {
+ BOARD_ITEM* item = m_selection.Item<BOARD_ITEM>( i );
+
+ if( !modifiable( item ) )
+ {
+ m_selection.items.RemovePicker( i );
+ m_selection.group->Remove( item );
+ }
+ else
+ {
+ ++i;
+ }
+ }
+
+ return m_selection;
+}
+
+
+void SELECTION_TOOL::toggleSelection( BOARD_ITEM* aItem )
+{
+ if( aItem->IsSelected() )
+ {
+ unselect( aItem );
+
+ // Inform other potentially interested tools
+ m_toolMgr->ProcessEvent( UnselectedEvent );
+ }
+ else
+ {
+ if( !m_additive )
+ clearSelection();
+
+ // Prevent selection of invisible or inactive items
+ if( selectable( aItem ) )
+ {
+ select( aItem );
+
+ // Inform other potentially interested tools
+ m_toolMgr->ProcessEvent( SelectedEvent );
+ }
+ }
+}
+
+
+bool SELECTION_TOOL::selectPoint( const VECTOR2I& aWhere, bool aOnDrag )
+{
+ BOARD_ITEM* item;
+ GENERAL_COLLECTORS_GUIDE guide = m_frame->GetCollectorsGuide();
+ GENERAL_COLLECTOR collector;
+
+ if( m_editModules )
+ collector.Collect( getModel<BOARD>(), GENERAL_COLLECTOR::ModuleItems,
+ wxPoint( aWhere.x, aWhere.y ), guide );
+ else
+ collector.Collect( getModel<BOARD>(), GENERAL_COLLECTOR::AllBoardItems,
+ wxPoint( aWhere.x, aWhere.y ), guide );
+
+ bool anyCollected = collector.GetCount() != 0;
+
+ // Remove unselectable items
+ for( int i = collector.GetCount() - 1; i >= 0; --i )
+ {
+ if( !selectable( collector[i] ) || ( aOnDrag && collector[i]->IsLocked() ) )
+ collector.Remove( i );
+ }
+
+ switch( collector.GetCount() )
+ {
+ case 0:
+ if( !m_additive && anyCollected )
+ clearSelection();
+
+ return false;
+
+ case 1:
+ toggleSelection( collector[0] );
+
+ return true;
+
+ default:
+ // Apply some ugly heuristics to avoid disambiguation menus whenever possible
+ guessSelectionCandidates( collector );
+
+ // Let's see if there is still disambiguation in selection..
+ if( collector.GetCount() == 1 )
+ {
+ toggleSelection( collector[0] );
+
+ return true;
+ }
+ else if( collector.GetCount() > 1 )
+ {
+ if( aOnDrag )
+ Wait( TOOL_EVENT( TC_ANY, TA_MOUSE_UP, BUT_LEFT ) );
+
+ item = disambiguationMenu( &collector );
+
+ if( item )
+ {
+ toggleSelection( item );
+
+ return true;
+ }
+ }
+ break;
+ }
+
+ return false;
+}
+
+
+bool SELECTION_TOOL::selectCursor( bool aSelectAlways )
+{
+ if( aSelectAlways || m_selection.Empty() )
+ {
+ clearSelection();
+ selectPoint( getViewControls()->GetCursorPosition() );
+ }
+
+ return !m_selection.Empty();
+}
+
+
+bool SELECTION_TOOL::selectMultiple()
+{
+ bool cancelled = false; // Was the tool cancelled while it was running?
+ m_multiple = true; // Multiple selection mode is active
+ KIGFX::VIEW* view = getView();
+ getViewControls()->SetAutoPan( true );
+
+ SELECTION_AREA area;
+ view->Add( &area );
+
+ while( OPT_TOOL_EVENT evt = Wait() )
+ {
+ if( evt->IsCancel() )
+ {
+ cancelled = true;
+ break;
+ }
+
+ if( evt->IsDrag( BUT_LEFT ) )
+ {
+ if( !m_additive )
+ clearSelection();
+
+ // Start drawing a selection box
+ area.SetOrigin( evt->DragOrigin() );
+ area.SetEnd( evt->Position() );
+ area.ViewSetVisible( true );
+ area.ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+ }
+
+ if( evt->IsMouseUp( BUT_LEFT ) )
+ {
+ // End drawing the selection box
+ area.ViewSetVisible( false );
+
+ // Mark items within the selection box as selected
+ std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> selectedItems;
+ BOX2I selectionBox = area.ViewBBox();
+ view->Query( selectionBox, selectedItems ); // Get the list of selected items
+
+ std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR>::iterator it, it_end;
+
+ for( it = selectedItems.begin(), it_end = selectedItems.end(); it != it_end; ++it )
+ {
+ BOARD_ITEM* item = static_cast<BOARD_ITEM*>( it->first );
+
+ // Add only those items that are visible and fully within the selection box
+ if( !item->IsSelected() && selectable( item ) &&
+ selectionBox.Contains( item->ViewBBox() ) )
+ {
+ select( item );
+ }
+ }
+
+ if( m_selection.Size() == 1 )
+ m_frame->SetCurItem( m_selection.Item<BOARD_ITEM>( 0 ) );
+ else
+ m_frame->SetCurItem( NULL );
+
+ // Inform other potentially interested tools
+ if( !m_selection.Empty() )
+ m_toolMgr->ProcessEvent( SelectedEvent );
+
+ break; // Stop waiting for events
+ }
+ }
+
+ // Stop drawing the selection box
+ area.ViewSetVisible( false );
+ view->Remove( &area );
+ m_multiple = false; // Multiple selection mode is inactive
+ getViewControls()->SetAutoPan( false );
+
+ return cancelled;
+}
+
+
+void SELECTION_TOOL::SetTransitions()
+{
+ Go( &SELECTION_TOOL::Main, COMMON_ACTIONS::selectionActivate.MakeEvent() );
+ Go( &SELECTION_TOOL::CursorSelection, COMMON_ACTIONS::selectionCursor.MakeEvent() );
+ Go( &SELECTION_TOOL::ClearSelection, COMMON_ACTIONS::selectionClear.MakeEvent() );
+ Go( &SELECTION_TOOL::SelectItem, COMMON_ACTIONS::selectItem.MakeEvent() );
+ Go( &SELECTION_TOOL::UnselectItem, COMMON_ACTIONS::unselectItem.MakeEvent() );
+ Go( &SELECTION_TOOL::find, COMMON_ACTIONS::find.MakeEvent() );
+ Go( &SELECTION_TOOL::findMove, COMMON_ACTIONS::findMove.MakeEvent() );
+ Go( &SELECTION_TOOL::selectConnection, COMMON_ACTIONS::selectConnection.MakeEvent() );
+ Go( &SELECTION_TOOL::selectCopper, COMMON_ACTIONS::selectCopper.MakeEvent() );
+ Go( &SELECTION_TOOL::selectNet, COMMON_ACTIONS::selectNet.MakeEvent() );
+}
+
+
+SELECTION_LOCK_FLAGS SELECTION_TOOL::CheckLock()
+{
+ if( !m_locked || m_editModules )
+ return SELECTION_UNLOCKED;
+
+ bool containsLocked = false;
+
+ // Check if the selection contains locked items
+ for( int i = 0; i < m_selection.Size(); ++i )
+ {
+ BOARD_ITEM* item = m_selection.Item<BOARD_ITEM>( i );
+
+ switch( item->Type() )
+ {
+ case PCB_MODULE_T:
+ if( static_cast<MODULE*>( item )->IsLocked() )
+ containsLocked = true;
+ break;
+
+ case PCB_MODULE_EDGE_T:
+ case PCB_MODULE_TEXT_T:
+ if( static_cast<MODULE*>( item->GetParent() )->IsLocked() )
+ containsLocked = true;
+ break;
+
+ default: // suppress warnings
+ break;
+ }
+ }
+
+ if( containsLocked )
+ {
+ if( IsOK( m_frame, _( "Selection contains locked items. Do you want to continue?" ) ) )
+ {
+ m_locked = false;
+ return SELECTION_LOCK_OVERRIDE;
+ }
+ else
+ return SELECTION_LOCKED;
+ }
+
+ m_locked = false;
+
+ return SELECTION_UNLOCKED;
+}
+
+
+int SELECTION_TOOL::CursorSelection( const TOOL_EVENT& aEvent )
+{
+ selectCursor( true );
+
+ return 0;
+}
+
+
+int SELECTION_TOOL::ClearSelection( const TOOL_EVENT& aEvent )
+{
+ clearSelection();
+
+ return 0;
+}
+
+
+int SELECTION_TOOL::SelectItem( const TOOL_EVENT& aEvent )
+{
+ // Check if there is an item to be selected
+ BOARD_ITEM* item = aEvent.Parameter<BOARD_ITEM*>();
+
+ if( item )
+ {
+ select( item );
+
+ // Inform other potentially interested tools
+ m_toolMgr->ProcessEvent( SelectedEvent );
+ }
+
+ return 0;
+}
+
+
+int SELECTION_TOOL::UnselectItem( const TOOL_EVENT& aEvent )
+{
+ // Check if there is an item to be selected
+ BOARD_ITEM* item = aEvent.Parameter<BOARD_ITEM*>();
+
+ if( item )
+ {
+ unselect( item );
+
+ // Inform other potentially interested tools
+ m_toolMgr->ProcessEvent( UnselectedEvent );
+ }
+
+ return 0;
+}
+
+
+int SELECTION_TOOL::selectConnection( const TOOL_EVENT& aEvent )
+{
+ if( !selectCursor( true ) )
+ return 0;
+
+ BOARD_CONNECTED_ITEM* item = m_selection.Item<BOARD_CONNECTED_ITEM>( 0 );
+ clearSelection();
+
+ if( item->Type() != PCB_TRACE_T && item->Type() != PCB_VIA_T )
+ return 0;
+
+ int segmentCount;
+ TRACK* trackList = getModel<BOARD>()->MarkTrace( static_cast<TRACK*>( item ), &segmentCount,
+ NULL, NULL, true );
+
+ if( segmentCount == 0 )
+ return 0;
+
+ for( int i = 0; i < segmentCount; ++i )
+ {
+ select( trackList );
+ trackList = trackList->Next();
+ }
+
+ // Inform other potentially interested tools
+ m_toolMgr->ProcessEvent( SelectedEvent );
+
+ return 0;
+}
+
+
+int SELECTION_TOOL::selectCopper( const TOOL_EVENT& aEvent )
+{
+ if( !selectCursor( true ) )
+ return 0;
+
+ BOARD_CONNECTED_ITEM* item = m_selection.Item<BOARD_CONNECTED_ITEM>( 0 );
+ clearSelection();
+
+ if( item->Type() != PCB_TRACE_T && item->Type() != PCB_VIA_T )
+ return 0;
+
+ std::list<BOARD_CONNECTED_ITEM*> itemsList;
+ RN_DATA* ratsnest = getModel<BOARD>()->GetRatsnest();
+
+ ratsnest->GetConnectedItems( item, itemsList, (RN_ITEM_TYPE)( RN_TRACKS | RN_VIAS ) );
+
+ BOOST_FOREACH( BOARD_CONNECTED_ITEM* i, itemsList )
+ select( i );
+
+ // Inform other potentially interested tools
+ if( itemsList.size() > 0 )
+ m_toolMgr->ProcessEvent( SelectedEvent );
+
+ return 0;
+}
+
+
+int SELECTION_TOOL::selectNet( const TOOL_EVENT& aEvent )
+{
+ if( !selectCursor( true ) )
+ return 0;
+
+ BOARD_CONNECTED_ITEM* item = m_selection.Item<BOARD_CONNECTED_ITEM>( 0 );
+
+ std::list<BOARD_CONNECTED_ITEM*> itemsList;
+ RN_DATA* ratsnest = getModel<BOARD>()->GetRatsnest();
+ int netCode = item->GetNetCode();
+
+ clearSelection();
+ ratsnest->GetNetItems( netCode, itemsList, (RN_ITEM_TYPE)( RN_TRACKS | RN_VIAS ) );
+
+ BOOST_FOREACH( BOARD_CONNECTED_ITEM* i, itemsList )
+ select( i );
+
+ // Inform other potentially interested tools
+ if( itemsList.size() > 0 )
+ m_toolMgr->ProcessEvent( SelectedEvent );
+
+ return 0;
+}
+
+
+void SELECTION_TOOL::findCallback( BOARD_ITEM* aItem )
+{
+ clearSelection();
+
+ if( aItem )
+ {
+ select( aItem );
+ EDA_RECT bbox = aItem->GetBoundingBox();
+ BOX2D viewport( VECTOR2D( bbox.GetOrigin() ), VECTOR2D( bbox.GetSize() ) );
+ getView()->SetViewport( viewport );
+
+ // Inform other potentially interested tools
+ m_toolMgr->ProcessEvent( SelectedEvent );
+ }
+
+ m_frame->GetGalCanvas()->ForceRefresh();
+}
+
+
+int SELECTION_TOOL::find( const TOOL_EVENT& aEvent )
+{
+ DIALOG_FIND dlg( m_frame );
+ dlg.EnableWarp( false );
+ dlg.SetCallback( boost::bind( &SELECTION_TOOL::findCallback, this, _1 ) );
+ dlg.ShowModal();
+
+ return 0;
+}
+
+
+int SELECTION_TOOL::findMove( const TOOL_EVENT& aEvent )
+{
+ MODULE* module = m_frame->GetModuleByName();
+
+ if( module )
+ {
+ clearSelection();
+ toggleSelection( module );
+ m_toolMgr->InvokeTool( "pcbnew.InteractiveEdit" );
+ }
+
+ return 0;
+}
+
+
+void SELECTION_TOOL::clearSelection()
+{
+ if( m_selection.Empty() )
+ return;
+
+ KIGFX::VIEW_GROUP::const_iter it, it_end;
+
+ // Restore the initial properties
+ for( it = m_selection.group->Begin(), it_end = m_selection.group->End(); it != it_end; ++it )
+ {
+ BOARD_ITEM* item = static_cast<BOARD_ITEM*>( *it );
+
+ item->ViewHide( false );
+ item->ClearSelected();
+ item->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY ) ;
+ }
+
+ m_selection.clear();
+
+ m_frame->SetCurItem( NULL );
+ m_locked = true;
+
+ // Inform other potentially interested tools
+ m_toolMgr->ProcessEvent( ClearedEvent );
+}
+
+
+BOARD_ITEM* SELECTION_TOOL::disambiguationMenu( GENERAL_COLLECTOR* aCollector )
+{
+ BOARD_ITEM* current = NULL;
+ boost::shared_ptr<BRIGHT_BOX> brightBox;
+ CONTEXT_MENU menu;
+
+ int limit = std::min( 10, aCollector->GetCount() );
+
+ for( int i = 0; i < limit; ++i )
+ {
+ wxString text;
+ BOARD_ITEM* item = ( *aCollector )[i];
+ text = item->GetSelectMenuText();
+ menu.Add( text, i + 1 );
+ }
+
+ menu.SetTitle( _( "Clarify selection" ) );
+ SetContextMenu( &menu, CMENU_NOW );
+
+ while( OPT_TOOL_EVENT evt = Wait() )
+ {
+ if( evt->Action() == TA_CONTEXT_MENU_UPDATE )
+ {
+ if( current )
+ current->ClearBrightened();
+
+ int id = *evt->GetCommandId();
+
+ // User has pointed an item, so show it in a different way
+ if( id > 0 && id <= limit )
+ {
+ current = ( *aCollector )[id - 1];
+ current->SetBrightened();
+ }
+ else
+ {
+ current = NULL;
+ }
+ }
+ else if( evt->Action() == TA_CONTEXT_MENU_CHOICE )
+ {
+ boost::optional<int> id = evt->GetCommandId();
+
+ // User has selected an item, so this one will be returned
+ if( id && ( *id > 0 ) )
+ current = ( *aCollector )[*id - 1];
+ else
+ current = NULL;
+
+ break;
+ }
+
+ // Draw a mark to show which item is available to be selected
+ if( current && current->IsBrightened() )
+ {
+ brightBox.reset( new BRIGHT_BOX( current ) );
+ getView()->Add( brightBox.get() );
+ // BRIGHT_BOX is removed from view on destruction
+ }
+ }
+
+ return current;
+}
+
+
+BOARD_ITEM* SELECTION_TOOL::pickSmallestComponent( GENERAL_COLLECTOR* aCollector )
+{
+ int count = aCollector->GetPrimaryCount(); // try to use preferred layer
+
+ if( 0 == count )
+ count = aCollector->GetCount();
+
+ for( int i = 0; i < count; ++i )
+ {
+ if( ( *aCollector )[i]->Type() != PCB_MODULE_T )
+ return NULL;
+ }
+
+ // All are modules, now find smallest MODULE
+ int minDim = 0x7FFFFFFF;
+ int minNdx = 0;
+
+ for( int i = 0; i < count; ++i )
+ {
+ MODULE* module = (MODULE*) ( *aCollector )[i];
+
+ int lx = module->GetBoundingBox().GetWidth();
+ int ly = module->GetBoundingBox().GetHeight();
+
+ int lmin = std::min( lx, ly );
+
+ if( lmin < minDim )
+ {
+ minDim = lmin;
+ minNdx = i;
+ }
+ }
+
+ return (*aCollector)[minNdx];
+}
+
+
+bool SELECTION_TOOL::selectable( const BOARD_ITEM* aItem ) const
+{
+ // Is high contrast mode enabled?
+ bool highContrast = getView()->GetPainter()->GetSettings()->GetHighContrast();
+
+ if( highContrast )
+ {
+ bool onActive = false; // Is the item on any of active layers?
+ int layers[KIGFX::VIEW::VIEW_MAX_LAYERS], layers_count;
+
+ // Filter out items that do not belong to active layers
+ const std::set<unsigned int>& activeLayers = getView()->GetPainter()->
+ GetSettings()->GetActiveLayers();
+ aItem->ViewGetLayers( layers, layers_count );
+
+ for( int i = 0; i < layers_count; ++i )
+ {
+ if( activeLayers.count( layers[i] ) > 0 ) // Item is on at least one of the active layers
+ {
+ onActive = true;
+ break;
+ }
+ }
+
+ if( !onActive ) // We do not want to select items that are in the background
+ return false;
+ }
+
+ BOARD* board = getModel<BOARD>();
+
+ switch( aItem->Type() )
+ {
+ case PCB_VIA_T:
+ {
+ // For vias it is enough if only one of layers is visible
+ LAYER_ID top, bottom;
+
+ static_cast<const VIA*>( aItem )->LayerPair( &top, &bottom );
+
+ return board->IsLayerVisible( top ) || board->IsLayerVisible( bottom );
+ }
+ break;
+
+ case PCB_MODULE_T:
+ if( aItem->IsOnLayer( F_Cu ) && board->IsElementVisible( MOD_FR_VISIBLE ) )
+ return !m_editModules;
+
+ if( aItem->IsOnLayer( B_Cu ) && board->IsElementVisible( MOD_BK_VISIBLE ) )
+ return !m_editModules;
+
+ return false;
+
+ break;
+
+ case PCB_MODULE_TEXT_T:
+ if( m_multiple && !m_editModules )
+ return false;
+
+ return aItem->ViewIsVisible() && board->IsLayerVisible( aItem->GetLayer() );
+
+ case PCB_MODULE_EDGE_T:
+ case PCB_PAD_T:
+ {
+ if( m_multiple && !m_editModules )
+ return false;
+
+ MODULE* mod = static_cast<const D_PAD*>( aItem )->GetParent();
+ if( mod && mod->IsLocked() )
+ return false;
+
+ break;
+ }
+
+ // These are not selectable
+ case NOT_USED:
+ case TYPE_NOT_INIT:
+ return false;
+
+ default: // Suppress warnings
+ break;
+ }
+
+ // All other items are selected only if the layer on which they exist is visible
+ return board->IsLayerVisible( aItem->GetLayer() );
+}
+
+
+bool SELECTION_TOOL::modifiable( const BOARD_ITEM* aItem ) const
+{
+ if( aItem->Type() == PCB_MARKER_T )
+ return false;
+
+ return true;
+}
+
+
+void SELECTION_TOOL::select( BOARD_ITEM* aItem )
+{
+ if( aItem->IsSelected() )
+ return;
+
+ // Modules are treated in a special way - when they are selected, we have to mark
+ // all the parts that make the module as selected
+ if( aItem->Type() == PCB_MODULE_T )
+ {
+ MODULE* module = static_cast<MODULE*>( aItem );
+ module->RunOnChildren( boost::bind( &SELECTION_TOOL::selectVisually, this, _1 ) );
+ }
+
+ if( aItem->Type() == PCB_PAD_T )
+ {
+ MODULE* module = static_cast<MODULE*>( aItem->GetParent() );
+
+ if( m_selection.items.FindItem( module ) >= 0 )
+ return;
+ }
+
+ selectVisually( aItem );
+ ITEM_PICKER picker( aItem );
+ m_selection.items.PushItem( picker );
+
+ if( m_selection.Size() == 1 )
+ {
+ // Set as the current item, so the information about selection is displayed
+ m_frame->SetCurItem( aItem, true );
+ }
+ else if( m_selection.Size() == 2 ) // Check only for 2, so it will not be
+ { // called for every next selected item
+ // If multiple items are selected, do not show the information about the selected item
+ m_frame->SetCurItem( NULL, true );
+ }
+}
+
+
+void SELECTION_TOOL::unselect( BOARD_ITEM* aItem )
+{
+ if( !aItem->IsSelected() )
+ return;
+
+ // Modules are treated in a special way - when they are selected, we have to
+ // unselect all the parts that make the module, not the module itself
+ if( aItem->Type() == PCB_MODULE_T )
+ {
+ MODULE* module = static_cast<MODULE*>( aItem );
+ module->RunOnChildren( boost::bind( &SELECTION_TOOL::unselectVisually, this, _1 ) );
+ }
+
+ unselectVisually( aItem );
+
+ int itemIdx = m_selection.items.FindItem( aItem );
+ if( itemIdx >= 0 )
+ m_selection.items.RemovePicker( itemIdx );
+
+ if( m_selection.Empty() )
+ {
+ m_frame->SetCurItem( NULL );
+ m_locked = true;
+ }
+}
+
+
+void SELECTION_TOOL::selectVisually( BOARD_ITEM* aItem ) const
+{
+ m_selection.group->Add( aItem );
+
+ // Hide the original item, so it is shown only on overlay
+ aItem->ViewHide( true );
+ aItem->SetSelected();
+}
+
+
+void SELECTION_TOOL::unselectVisually( BOARD_ITEM* aItem ) const
+{
+ m_selection.group->Remove( aItem );
+
+ // Restore original item visibility
+ aItem->ViewHide( false );
+ aItem->ClearSelected();
+ aItem->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+}
+
+
+bool SELECTION_TOOL::selectionContains( const VECTOR2I& aPoint ) const
+{
+ const unsigned GRIP_MARGIN = 20;
+ VECTOR2D margin = getView()->ToWorld( VECTOR2D( GRIP_MARGIN, GRIP_MARGIN ), false );
+
+ // Check if the point is located within any of the currently selected items bounding boxes
+ for( unsigned int i = 0; i < m_selection.items.GetCount(); ++i )
+ {
+ BOARD_ITEM* item = m_selection.Item<BOARD_ITEM>( i );
+ BOX2I itemBox = item->ViewBBox();
+ itemBox.Inflate( margin.x, margin.y ); // Give some margin for gripping an item
+
+ if( itemBox.Contains( aPoint ) )
+ return true;
+ }
+
+ return false;
+}
+
+
+static EDA_RECT getRect( const BOARD_ITEM* aItem )
+{
+ if( aItem->Type() == PCB_MODULE_T )
+ return static_cast<const MODULE*>( aItem )->GetFootprintRect();
+
+ return aItem->GetBoundingBox();
+}
+
+
+static double calcArea( const BOARD_ITEM* aItem )
+{
+ if( aItem->Type() == PCB_TRACE_T )
+ {
+ const TRACK* t = static_cast<const TRACK*>( aItem );
+ return ( t->GetWidth() + t->GetLength() ) * t->GetWidth();
+ }
+
+ return getRect( aItem ).GetArea();
+}
+
+
+static double calcMinArea( GENERAL_COLLECTOR& aCollector, KICAD_T aType )
+{
+ double best = std::numeric_limits<double>::max();
+
+ if( !aCollector.GetCount() )
+ return 0.0;
+
+ for( int i = 0; i < aCollector.GetCount(); i++ )
+ {
+ BOARD_ITEM* item = aCollector[i];
+ if( item->Type() == aType )
+ best = std::min( best, calcArea( item ) );
+ }
+
+ return best;
+}
+
+
+static double calcMaxArea( GENERAL_COLLECTOR& aCollector, KICAD_T aType )
+{
+ double best = 0.0;
+
+ for( int i = 0; i < aCollector.GetCount(); i++ )
+ {
+ BOARD_ITEM* item = aCollector[i];
+ if( item->Type() == aType )
+ best = std::max( best, calcArea( item ) );
+ }
+
+ return best;
+}
+
+
+static inline double calcCommonArea( const BOARD_ITEM* aItem, const BOARD_ITEM* aOther )
+{
+ return getRect( aItem ).Common( getRect( aOther ) ).GetArea();
+}
+
+
+double calcRatio( double a, double b )
+{
+ if( a == 0.0 && b == 0.0 )
+ return 1.0;
+ if( b == 0.0 )
+ return 10000000.0; // something arbitrarily big for the moment
+
+ return a / b;
+}
+
+
+// todo: explain the selection heuristics
+void SELECTION_TOOL::guessSelectionCandidates( GENERAL_COLLECTOR& aCollector ) const
+{
+ std::set<BOARD_ITEM*> rejected;
+
+ const double footprintAreaRatio = 0.2;
+ const double modulePadMinCoverRatio = 0.45;
+ const double padViaAreaRatio = 0.5;
+ const double trackViaLengthRatio = 2.0;
+ const double trackTrackLengthRatio = 0.3;
+ const double textToFeatureMinRatio = 0.2;
+ const double textToFootprintMinRatio = 0.4;
+ // If the common area of two compared items is above the following threshold, they cannot
+ // be rejected (it means they overlap and it might be hard to pick one by selecting
+ // its unique area).
+ const double commonAreaRatio = 0.6;
+
+ LAYER_ID actLayer = m_frame->GetActiveLayer();
+
+ LSET silkLayers( 2, B_SilkS, F_SilkS );
+
+ if( silkLayers[actLayer] )
+ {
+ std::set<BOARD_ITEM*> preferred;
+
+ for( int i = 0; i < aCollector.GetCount(); ++i )
+ {
+ BOARD_ITEM* item = aCollector[i];
+ KICAD_T type = item->Type();
+
+ if( ( type == PCB_MODULE_TEXT_T || type == PCB_TEXT_T || type == PCB_LINE_T )
+ && silkLayers[item->GetLayer()] )
+ {
+ preferred.insert( item );
+ }
+ }
+
+ if( preferred.size() != 0 )
+ {
+ aCollector.Empty();
+
+ BOOST_FOREACH( BOARD_ITEM* item, preferred )
+ aCollector.Append( item );
+ return;
+ }
+ }
+
+ if( aCollector.CountType( PCB_MODULE_TEXT_T ) > 0 )
+ {
+ for( int i = 0; i < aCollector.GetCount(); ++i )
+ {
+ if( TEXTE_MODULE* txt = dyn_cast<TEXTE_MODULE*>( aCollector[i] ) )
+ {
+ double textArea = calcArea( txt );
+
+ for( int j = 0; j < aCollector.GetCount(); ++j )
+ {
+ if( i == j )
+ continue;
+
+ BOARD_ITEM* item = aCollector[j];
+ double itemArea = calcArea( item );
+ double areaRatio = calcRatio( textArea, itemArea );
+ double commonArea = calcCommonArea( txt, item );
+ double itemCommonRatio = calcRatio( commonArea, itemArea );
+ double txtCommonRatio = calcRatio( commonArea, textArea );
+
+ if( item->Type() == PCB_MODULE_T && areaRatio < textToFootprintMinRatio &&
+ itemCommonRatio < commonAreaRatio )
+ rejected.insert( item );
+
+ switch( item->Type() )
+ {
+ case PCB_TRACE_T:
+ case PCB_PAD_T:
+ case PCB_LINE_T:
+ case PCB_VIA_T:
+ case PCB_MODULE_T:
+ if( areaRatio > textToFeatureMinRatio && txtCommonRatio < commonAreaRatio )
+ rejected.insert( txt );
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if( aCollector.CountType( PCB_MODULE_T ) > 0 )
+ {
+ double minArea = calcMinArea( aCollector, PCB_MODULE_T );
+ double maxArea = calcMaxArea( aCollector, PCB_MODULE_T );
+
+ if( calcRatio( minArea, maxArea ) <= footprintAreaRatio )
+ {
+ for( int i = 0; i < aCollector.GetCount(); ++i )
+ {
+ if( MODULE* mod = dyn_cast<MODULE*>( aCollector[i] ) )
+ {
+ double normalizedArea = calcRatio( calcArea( mod ), maxArea );
+
+ if( normalizedArea > footprintAreaRatio )
+ rejected.insert( mod );
+ }
+ }
+ }
+ }
+
+ if( aCollector.CountType( PCB_PAD_T ) > 0 )
+ {
+ for( int i = 0; i < aCollector.GetCount(); ++i )
+ {
+ if( D_PAD* pad = dyn_cast<D_PAD*>( aCollector[i] ) )
+ {
+ double ratio = pad->GetParent()->PadCoverageRatio();
+
+ if( ratio < modulePadMinCoverRatio )
+ rejected.insert( pad->GetParent() );
+ }
+ }
+ }
+
+ if( aCollector.CountType( PCB_VIA_T ) > 0 )
+ {
+ for( int i = 0; i < aCollector.GetCount(); ++i )
+ {
+ if( VIA* via = dyn_cast<VIA*>( aCollector[i] ) )
+ {
+ double viaArea = calcArea( via );
+
+ for( int j = 0; j < aCollector.GetCount(); ++j )
+ {
+ if( i == j )
+ continue;
+
+ BOARD_ITEM* item = aCollector[j];
+ double areaRatio = calcRatio( viaArea, calcArea( item ) );
+
+ if( item->Type() == PCB_MODULE_T && areaRatio < modulePadMinCoverRatio )
+ rejected.insert( item );
+
+ if( item->Type() == PCB_PAD_T && areaRatio < padViaAreaRatio )
+ rejected.insert( item );
+
+ if( TRACK* track = dyn_cast<TRACK*>( item ) )
+ {
+ if( track->GetNetCode() != via->GetNetCode() )
+ continue;
+
+ double lenRatio = (double) ( track->GetLength() + track->GetWidth() ) /
+ (double) via->GetWidth();
+
+ if( lenRatio > trackViaLengthRatio )
+ rejected.insert( track );
+ }
+ }
+ }
+ }
+ }
+
+ int nTracks = aCollector.CountType( PCB_TRACE_T );
+
+ if( nTracks > 0 )
+ {
+ double maxLength = 0.0;
+ double minLength = std::numeric_limits<double>::max();
+ double maxArea = 0.0;
+
+ for( int i = 0; i < aCollector.GetCount(); ++i )
+ {
+ if( TRACK* track = dyn_cast<TRACK*> ( aCollector[i] ) )
+ {
+ maxLength = std::max( track->GetLength(), maxLength );
+ maxLength = std::max( (double) track->GetWidth(), maxLength );
+
+ minLength = std::min( std::max( track->GetLength(), (double)track->GetWidth() ), minLength );
+
+ double area = ( track->GetLength() + track->GetWidth() * track->GetWidth() );
+ maxArea = std::max(area, maxArea);
+ }
+ }
+
+ if( maxLength > 0.0 && minLength / maxLength < trackTrackLengthRatio && nTracks > 1 )
+ {
+ for( int i = 0; i < aCollector.GetCount(); ++i )
+ {
+ if( TRACK* track = dyn_cast<TRACK*>( aCollector[i] ) )
+ {
+ double ratio = std::max( (double) track->GetWidth(), track->GetLength() ) / maxLength;
+
+ if( ratio > trackTrackLengthRatio )
+ rejected.insert( track );
+ }
+ }
+ }
+
+ for( int j = 0; j < aCollector.GetCount(); ++j )
+ {
+ if( MODULE* mod = dyn_cast<MODULE*>( aCollector[j] ) )
+ {
+ double ratio = maxArea / mod->GetFootprintRect().GetArea();
+
+ if( ratio < modulePadMinCoverRatio )
+ rejected.insert( mod );
+ }
+ }
+ }
+
+ if( (unsigned) aCollector.GetCount() > rejected.size() ) // do not remove everything
+ {
+ BOOST_FOREACH( BOARD_ITEM* item, rejected )
+ {
+ aCollector.Remove( item );
+ }
+ }
+}
+
+
+bool SELECTION_TOOL::SanitizeSelection()
+{
+ std::set<BOARD_ITEM*> rejected;
+ std::set<BOARD_ITEM*> added;
+
+ if( !m_editModules )
+ {
+ for( unsigned int i = 0; i < m_selection.items.GetCount(); ++i )
+ {
+ BOARD_ITEM* item = m_selection.Item<BOARD_ITEM>( i );
+
+ if( item->Type() == PCB_PAD_T )
+ {
+ MODULE* mod = static_cast<MODULE*>( item->GetParent() );
+
+ // case 1: module (or its pads) are locked
+ if( mod && ( mod->PadsLocked() || mod->IsLocked() ) )
+ {
+ rejected.insert( item );
+
+ if( !mod->IsLocked() && !mod->IsSelected() )
+ added.insert( mod );
+ }
+
+ // case 2: multi-item selection contains both the module and its pads - remove the pads
+ if( mod && m_selection.items.FindItem( mod ) >= 0 )
+ rejected.insert( item );
+ }
+ }
+ }
+
+ if( !rejected.empty() )
+ {
+ BOOST_FOREACH( BOARD_ITEM* item, rejected )
+ unselect( item );
+
+ // Inform other potentially interested tools
+ m_toolMgr->ProcessEvent( UnselectedEvent );
+ }
+
+ if( !added.empty() )
+ {
+ BOOST_FOREACH( BOARD_ITEM* item, added )
+ select( item );
+
+ // Inform other potentially interested tools
+ m_toolMgr->ProcessEvent( UnselectedEvent );
+ }
+
+ return true;
+}
+
+
+void SELECTION::clear()
+{
+ items.ClearItemsList();
+ group->Clear();
+}
+
+
+VECTOR2I SELECTION::GetCenter() const
+{
+ VECTOR2I centre;
+
+ if( Size() == 1 )
+ {
+ centre = Item<BOARD_ITEM>( 0 )->GetCenter();
+ }
+ else
+ {
+ EDA_RECT bbox = Item<BOARD_ITEM>( 0 )->GetBoundingBox();
+ for( unsigned int i = 1; i < items.GetCount(); ++i )
+ {
+ BOARD_ITEM* item = Item<BOARD_ITEM>( i );
+ bbox.Merge( item->GetBoundingBox() );
+ }
+
+ centre = bbox.Centre();
+ }
+
+ return centre;
+}
+
+
+const TOOL_EVENT SELECTION_TOOL::SelectedEvent( TC_MESSAGE, TA_ACTION, "pcbnew.InteractiveSelection.selected" );
+const TOOL_EVENT SELECTION_TOOL::UnselectedEvent( TC_MESSAGE, TA_ACTION, "pcbnew.InteractiveSelection.unselected" );
+const TOOL_EVENT SELECTION_TOOL::ClearedEvent( TC_MESSAGE, TA_ACTION, "pcbnew.InteractiveSelection.cleared" );
diff --git a/pcbnew/tools/selection_tool.h b/pcbnew/tools/selection_tool.h
new file mode 100644
index 0000000..4e7046c
--- /dev/null
+++ b/pcbnew/tools/selection_tool.h
@@ -0,0 +1,361 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2013-2016 CERN
+ * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
+ * @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
+ */
+
+#ifndef __SELECTION_TOOL_H
+#define __SELECTION_TOOL_H
+
+#include <math/vector2d.h>
+#include <tool/tool_interactive.h>
+#include <tool/context_menu.h>
+#include <class_undoredo_container.h>
+
+#include "selection_conditions.h"
+#include "conditional_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
+{
+class VIEW_GROUP;
+}
+
+struct SELECTION
+{
+ /// Set of selected items
+ PICKED_ITEMS_LIST items;
+
+ /// VIEW_GROUP that holds currently selected items
+ KIGFX::VIEW_GROUP* group;
+
+ /// Checks if there is anything selected
+ bool Empty() const
+ {
+ return ( items.GetCount() == 0 );
+ }
+
+ /// Returns the number of selected parts
+ int Size() const
+ {
+ return items.GetCount();
+ }
+
+ /// Alias to make code shorter and clearer
+ template <typename T>
+ T* Item( unsigned int aIndex ) const
+ {
+ return static_cast<T*>( items.GetPickedItem( aIndex ) );
+ }
+
+ /// Returns the center point of the selection area bounding box.
+ VECTOR2I GetCenter() const;
+
+ /// Runs a function on all selected items.
+ template <typename T>
+ void ForAll( boost::function<void (T*)> aFunction ) const
+ {
+ for( unsigned int i = 0; i < items.GetCount(); ++i )
+ aFunction( Item<T>( i ) );
+ }
+
+private:
+ /// Clears both the VIEW_GROUP and set of selected items. Please note that it does not
+ /// change properties of selected items (e.g. selection flag).
+ void clear();
+
+ friend class SELECTION_TOOL;
+};
+
+enum SELECTION_LOCK_FLAGS
+{
+ SELECTION_UNLOCKED = 0,
+ SELECTION_LOCK_OVERRIDE = 1,
+ SELECTION_LOCKED = 2
+};
+
+/**
+ * Class SELECTION_TOOL
+ *
+ * Our sample selection tool: currently supports:
+ * - pick single objects (click LMB)
+ * - add objects to existing selection (Shift+LMB)
+ * - draw selection box (drag LMB)
+ * - handles MODULEs properly (i.e. selects either MODULE or its PADs, TEXTs, etc.)
+ * - takes into account high-contrast & layer visibility settings
+ * - invokes InteractiveEdit tool when user starts to drag selected items
+ */
+class SELECTION_TOOL : public TOOL_INTERACTIVE
+{
+public:
+ SELECTION_TOOL();
+ ~SELECTION_TOOL();
+
+ /// @copydoc TOOL_BASE::Init()
+ bool Init();
+
+ /// @copydoc TOOL_BASE::Reset()
+ void Reset( RESET_REASON aReason );
+
+ /**
+ * Function Main()
+ *
+ * The main loop.
+ */
+ int Main( const TOOL_EVENT& aEvent );
+
+ /**
+ * Function GetSelection()
+ *
+ * Returns the set of currently selected items.
+ */
+ const SELECTION& GetSelection();
+
+ /**
+ * Function EditModules()
+ *
+ * Toggles edit module mode. When enabled, one may select parts of modules individually
+ * (graphics, pads, etc.), so they can be modified.
+ * @param aEnabled decides if the mode should be enabled.
+ */
+ inline void EditModules( bool aEnabled )
+ {
+ m_editModules = aEnabled;
+ }
+
+ inline CONDITIONAL_MENU& GetMenu()
+ {
+ return m_menu;
+ }
+
+ ///> Checks if the user has agreed to modify locked items for the given selection.
+ SELECTION_LOCK_FLAGS CheckLock();
+
+ ///> Select a single item under cursor event handler.
+ int CursorSelection( const TOOL_EVENT& aEvent );
+
+ ///> Clear current selection event handler.
+ int ClearSelection( const TOOL_EVENT& aEvent );
+
+ ///> Makes sure a group selection does not contain items that would cause
+ ///> conflicts when moving/rotating together (e.g. a footprint and one of the same footprint's pads)
+ bool SanitizeSelection();
+
+ ///> Item selection event handler.
+ int SelectItem( const TOOL_EVENT& aEvent );
+
+ ///> Item unselection event handler.
+ int UnselectItem( const TOOL_EVENT& aEvent );
+
+ ///> Event sent after an item is selected.
+ static const TOOL_EVENT SelectedEvent;
+
+ ///> Event sent after an item is unselected.
+ static const TOOL_EVENT UnselectedEvent;
+
+ ///> Event sent after selection is cleared.
+ static const TOOL_EVENT ClearedEvent;
+
+ ///> Sets up handlers for various events.
+ void SetTransitions();
+
+private:
+ /**
+ * Function selectPoint()
+ * Selects an item pointed by the parameter aWhere. If there is more than one item at that
+ * place, there is a menu displayed that allows to choose the item.
+ *
+ * @param aWhere is the place where the item should be selected.
+ * @param aAllowDisambiguation decides what to do in case of disambiguation. If true, then
+ * a menu is shown, otherise function finishes without selecting anything.
+ * @return True if an item was selected, false otherwise.
+ */
+ bool selectPoint( const VECTOR2I& aWhere, bool aOnDrag = false );
+
+ /**
+ * Function selectCursor()
+ * Selects an item under the cursor unless there is something already selected or aSelectAlways
+ * is true.
+ * @param aSelectAlways forces to select an item even if there is an item already selected.
+ * @return true if eventually there is an item selected, false otherwise.
+ */
+ bool selectCursor( bool aSelectAlways = false );
+
+ /**
+ * Function selectMultiple()
+ * Handles drawing a selection box that allows to select many items at the same time.
+ *
+ * @return true if the function was cancelled (i.e. CancelEvent was received).
+ */
+ bool selectMultiple();
+
+ ///> Selects a trivial connection (between two junctions).
+ int selectConnection( const TOOL_EVENT& aEvent );
+
+ ///> Selects a continuous copper connection.
+ int selectCopper( const TOOL_EVENT& aEvent );
+
+ ///> Selects all copper connections belonging to a single net.
+ int selectNet( const TOOL_EVENT& aEvent );
+
+ ///> Find dialog callback.
+ void findCallback( BOARD_ITEM* aItem );
+
+ ///> Find an item.
+ int find( const TOOL_EVENT& aEvent );
+
+ ///> Find an item and start moving.
+ int findMove( const TOOL_EVENT& aEvent );
+
+ /**
+ * Function clearSelection()
+ * Clears the current selection.
+ */
+ void clearSelection();
+
+ /**
+ * Function disambiguationMenu()
+ * Handles the menu that allows to select one of many items in case there is more than one
+ * item at the selected point (@see selectCursor()).
+ *
+ * @param aItems contains list of items that are displayed to the user.
+ */
+ BOARD_ITEM* disambiguationMenu( GENERAL_COLLECTOR* aItems );
+
+ /**
+ * Function pickSmallestComponent()
+ * Allows to find the smallest (in terms of bounding box area) item from the list.
+ *
+ * @param aCollector containes the list of items.
+ */
+ BOARD_ITEM* pickSmallestComponent( GENERAL_COLLECTOR* aCollector );
+
+ /**
+ * Function toggleSelection()
+ * Changes selection status of a given item.
+ *
+ * @param aItem is the item to have selection status changed.
+ */
+ void toggleSelection( BOARD_ITEM* aItem );
+
+ /**
+ * Function selectable()
+ * Checks conditions for an item to be selected.
+ *
+ * @return True if the item fulfills conditions to be selected.
+ */
+ bool selectable( const BOARD_ITEM* aItem ) const;
+
+ /**
+ * Function modifiable()
+ * Checks if an item might be modified. This function is used to filter out items
+ * from the selection when it is passed to other tools.
+ *
+ * @return True if the item fulfills conditions to be modified.
+ */
+ bool modifiable( const BOARD_ITEM* aItem ) const;
+
+ /**
+ * Function select()
+ * Takes necessary action mark an item as selected.
+ *
+ * @param aItem is an item to be selected.
+ */
+ void select( BOARD_ITEM* aItem );
+
+ /**
+ * Function unselect()
+ * Takes necessary action mark an item as unselected.
+ *
+ * @param aItem is an item to be unselected.
+ */
+ void unselect( BOARD_ITEM* aItem );
+
+ /**
+ * Function selectVisually()
+ * Marks item as selected, but does not add it to the ITEMS_PICKED_LIST.
+ * @param aItem is an item to be be marked.
+ */
+ void selectVisually( BOARD_ITEM* aItem ) const;
+
+ /**
+ * Function unselectVisually()
+ * Marks item as selected, but does not add it to the ITEMS_PICKED_LIST.
+ * @param aItem is an item to be be marked.
+ */
+ void unselectVisually( BOARD_ITEM* aItem ) const;
+
+ /**
+ * Function selectionContains()
+ * Checks if the given point is placed within any of selected items' bounding box.
+ *
+ * @return True if the given point is contained in any of selected items' bouding box.
+ */
+ bool selectionContains( const VECTOR2I& aPoint ) const;
+
+ /**
+ * Function guessSelectionCandidates()
+ * Tries to guess best selection candidates in case multiple items are clicked, by
+ * doing some braindead heuristics.
+ * @param aCollector is the collector that has a list of items to be queried.
+ */
+ void guessSelectionCandidates( GENERAL_COLLECTOR& aCollector ) const;
+
+ /// Pointer to the parent frame.
+ PCB_BASE_FRAME* m_frame;
+
+ /// Current state of selection.
+ SELECTION m_selection;
+
+ /// Flag saying if items should be added to the current selection or rather replace it.
+ bool m_additive;
+
+ /// Flag saying if multiple selection mode is active.
+ bool m_multiple;
+
+ /// Edit module mode flag.
+ bool m_editModules;
+
+ /// Can other tools modify locked items.
+ bool m_locked;
+
+ /// 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;
+};
+
+#endif
diff --git a/pcbnew/tools/size_menu.cpp b/pcbnew/tools/size_menu.cpp
new file mode 100644
index 0000000..7dc63e4
--- /dev/null
+++ b/pcbnew/tools/size_menu.cpp
@@ -0,0 +1,85 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2015 CERN
+ * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
+ * @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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "size_menu.h"
+
+#include <class_board.h>
+#include <pcbnew_id.h>
+
+CONTEXT_TRACK_VIA_SIZE_MENU::CONTEXT_TRACK_VIA_SIZE_MENU( bool aTrackSizes, bool aViaSizes ) :
+ m_tracks( aTrackSizes ), m_vias( aViaSizes )
+{
+ SetIcon( width_track_via_xpm );
+}
+
+
+void CONTEXT_TRACK_VIA_SIZE_MENU::AppendSizes( const BOARD* aBoard )
+{
+ wxString msg;
+
+ const BOARD_DESIGN_SETTINGS& bds = aBoard->GetDesignSettings();
+
+ if( m_tracks )
+ {
+ for( unsigned i = 0; i < bds.m_TrackWidthList.size(); i++ )
+ {
+ if( m_vias ) // == if( m_tracks && m_vias )
+ msg = _( "Track ");
+
+ if( i == 0 )
+ msg << _( "net class width" );
+ else
+ msg << StringFromValue( g_UserUnit, bds.m_TrackWidthList[i], true );
+
+ Append( ID_POPUP_PCB_SELECT_WIDTH1 + i, msg, wxEmptyString, wxITEM_CHECK );
+ }
+ }
+
+ if( m_tracks && m_vias )
+ AppendSeparator();
+
+ if( m_vias )
+ {
+ for( unsigned i = 0; i < bds.m_ViasDimensionsList.size(); i++ )
+ {
+ if( m_tracks ) // == if( m_tracks && m_vias )
+ msg = _( "Via " );
+
+ if( i == 0 )
+ {
+ msg << _( "net class size" );
+ }
+ else
+ {
+ msg << StringFromValue( g_UserUnit, bds.m_ViasDimensionsList[i].m_Diameter, true );
+ wxString drill = StringFromValue( g_UserUnit,
+ bds.m_ViasDimensionsList[i].m_Drill, true );
+
+ if( bds.m_ViasDimensionsList[i].m_Drill <= 0 )
+ msg << _( ", drill: default" );
+ else
+ msg << _( ", drill: " ) << drill;
+ }
+
+ Append( ID_POPUP_PCB_SELECT_VIASIZE1 + i, msg, wxEmptyString, wxITEM_CHECK );
+ }
+ }
+}
diff --git a/pcbnew/tools/size_menu.h b/pcbnew/tools/size_menu.h
new file mode 100644
index 0000000..248e366
--- /dev/null
+++ b/pcbnew/tools/size_menu.h
@@ -0,0 +1,55 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2015 CERN
+ * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
+ * @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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <tool/context_menu.h>
+
+class BOARD;
+
+/**
+ * @brief Context menu that displays track and/or via sizes basing on the board design settings
+ * of a BOARD object.
+ */
+class CONTEXT_TRACK_VIA_SIZE_MENU: public CONTEXT_MENU
+{
+public:
+ /**
+ * Constructor.
+ * @param aTrackSizes decides if the context menu should contain track sizes.
+ * @param aTrackSizes decides if the context menu should contain via sizes.
+ */
+ CONTEXT_TRACK_VIA_SIZE_MENU( bool aTrackSizes, bool aViaSizes );
+
+ virtual ~CONTEXT_TRACK_VIA_SIZE_MENU() {}
+
+ /**
+ * Function AppendSizes()
+ * Appends the list of tracks/vias (depending on the parameters passed to the constructor).
+ * @param aBoard is the BOARD object whose board settings will be used to generate the list.
+ */
+ virtual void AppendSizes( const BOARD* aBoard );
+
+protected:
+ ///> Whether the generated menu should contain track sizes.
+ bool m_tracks;
+
+ ///> Whether the generated menu should contain via sizes.
+ bool m_vias;
+};
diff --git a/pcbnew/tools/tools_common.cpp b/pcbnew/tools/tools_common.cpp
new file mode 100644
index 0000000..ee46245
--- /dev/null
+++ b/pcbnew/tools/tools_common.cpp
@@ -0,0 +1,54 @@
+/*
+ * This program source code file is part of KICAD, a free EDA CAD application.
+ *
+ * Copyright (C) 2015 CERN
+ * @author Tomasz Wlostowski <tomasz.wlostowski@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 <io_mgr.h>
+
+#include <tool/tool_manager.h>
+
+#include <tools/selection_tool.h>
+#include <tools/picker_tool.h>
+#include <tools/edit_tool.h>
+#include <tools/drawing_tool.h>
+#include <tools/point_editor.h>
+#include <tools/pcbnew_control.h>
+#include <tools/pcb_editor_control.h>
+#include <tools/placement_tool.h>
+#include <tools/common_actions.h>
+
+#include <router/router_tool.h>
+#include <router/length_tuner_tool.h>
+
+void registerAllTools( TOOL_MANAGER *aToolManager )
+{
+ aToolManager->RegisterTool( new SELECTION_TOOL );
+ aToolManager->RegisterTool( new PICKER_TOOL );
+ aToolManager->RegisterTool( new ROUTER_TOOL );
+ aToolManager->RegisterTool( new LENGTH_TUNER_TOOL );
+ aToolManager->RegisterTool( new EDIT_TOOL );
+ aToolManager->RegisterTool( new DRAWING_TOOL );
+ aToolManager->RegisterTool( new POINT_EDITOR );
+ aToolManager->RegisterTool( new PCBNEW_CONTROL );
+ aToolManager->RegisterTool( new PCB_EDITOR_CONTROL );
+ aToolManager->RegisterTool( new PLACEMENT_TOOL );
+} \ No newline at end of file
diff --git a/pcbnew/tools/zoom_menu.cpp b/pcbnew/tools/zoom_menu.cpp
new file mode 100644
index 0000000..42aa093
--- /dev/null
+++ b/pcbnew/tools/zoom_menu.cpp
@@ -0,0 +1,71 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2015 CERN
+ * @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 "zoom_menu.h"
+#include <id.h>
+#include <draw_frame.h>
+#include <class_base_screen.h>
+#include <tools/common_actions.h>
+
+#include <boost/bind.hpp>
+
+ZOOM_MENU::ZOOM_MENU( EDA_DRAW_FRAME* aParent ) : m_parent( aParent )
+{
+ BASE_SCREEN* screen = aParent->GetScreen();
+
+ SetIcon( zoom_selection_xpm );
+ SetMenuHandler( boost::bind( &ZOOM_MENU::EventHandler, this, _1 ) );
+ SetUpdateHandler( boost::bind( &ZOOM_MENU::Update, this ) );
+
+ //int zoom = screen->GetZoom();
+ int maxZoomIds = std::min( ID_POPUP_ZOOM_LEVEL_END - ID_POPUP_ZOOM_LEVEL_START,
+ (int) screen->m_ZoomList.size() );
+
+ for( int i = 0; i < maxZoomIds; ++i )
+ {
+ Append( ID_POPUP_ZOOM_LEVEL_START + i,
+ wxString::Format( _( "Zoom: %.2f" ), aParent->GetZoomLevelCoeff() / screen->m_ZoomList[i] ),
+ wxEmptyString, wxITEM_CHECK );
+ }
+}
+
+
+OPT_TOOL_EVENT ZOOM_MENU::EventHandler( const wxMenuEvent& aEvent )
+{
+ OPT_TOOL_EVENT event( COMMON_ACTIONS::zoomPreset.MakeEvent() );
+ long idx = aEvent.GetId() - ID_POPUP_ZOOM_LEVEL_START;
+ event->SetParameter( idx );
+
+ return event;
+}
+
+
+void ZOOM_MENU::Update()
+{
+ double zoom = m_parent->GetScreen()->GetZoom();
+ const std::vector<double>& zoomList = m_parent->GetScreen()->m_ZoomList;
+
+ for( unsigned int i = 0; i < GetMenuItemCount(); ++i )
+ Check( ID_POPUP_ZOOM_LEVEL_START + i, std::fabs( zoomList[i] - zoom ) < 1e-6 );
+}
diff --git a/pcbnew/tools/zoom_menu.h b/pcbnew/tools/zoom_menu.h
new file mode 100644
index 0000000..0a7de81
--- /dev/null
+++ b/pcbnew/tools/zoom_menu.h
@@ -0,0 +1,44 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2015 CERN
+ * @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
+ */
+
+#ifndef ZOOM_MENU_H
+#define ZOOM_MENU_H
+
+#include <tool/context_menu.h>
+
+class EDA_DRAW_FRAME;
+
+class ZOOM_MENU : public CONTEXT_MENU
+{
+public:
+ ZOOM_MENU( EDA_DRAW_FRAME* aParent );
+
+private:
+ OPT_TOOL_EVENT EventHandler( const wxMenuEvent& aEvent );
+ void Update();
+
+ EDA_DRAW_FRAME* m_parent;
+};
+
+#endif /* ZOOM_MENU_H */