diff options
author | saurabhb17 | 2020-02-26 15:57:49 +0530 |
---|---|---|
committer | saurabhb17 | 2020-02-26 15:57:49 +0530 |
commit | aa35045840b78d3f48212db45da59a2e5c69b223 (patch) | |
tree | 6acee185a4dc19113fcbf0f9a3d6941085dedaf7 /pcbnew/tools | |
parent | 0db48f6533517ecebfd9f0693f89deca28408b76 (diff) | |
download | KiCad-eSim-aa35045840b78d3f48212db45da59a2e5c69b223.tar.gz KiCad-eSim-aa35045840b78d3f48212db45da59a2e5c69b223.tar.bz2 KiCad-eSim-aa35045840b78d3f48212db45da59a2e5c69b223.zip |
Added main execs
Diffstat (limited to 'pcbnew/tools')
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 */ |