diff --git a/eeschema/CMakeLists.txt b/eeschema/CMakeLists.txt index 02a4c91156..0e1835ff46 100644 --- a/eeschema/CMakeLists.txt +++ b/eeschema/CMakeLists.txt @@ -215,7 +215,6 @@ set( EESCHEMA_SRCS sch_sheet_pin.cpp sch_text.cpp sch_validators.cpp - schedit.cpp schematic_undo_redo.cpp sch_edit_frame.cpp selpart.cpp @@ -243,6 +242,7 @@ set( EESCHEMA_SRCS tools/sch_edit_tool.cpp tools/sch_editor_control.cpp tools/sch_inspection_tool.cpp + tools/sch_line_drawing_tool.cpp tools/sch_picker_tool.cpp tools/sch_selection_tool.cpp tools/selection.cpp diff --git a/eeschema/bus-wire-junction.cpp b/eeschema/bus-wire-junction.cpp index 6304c8927c..48c6974256 100644 --- a/eeschema/bus-wire-junction.cpp +++ b/eeschema/bus-wire-junction.cpp @@ -33,7 +33,10 @@ #include #include #include - +#include +#include +#include +#include "eeschema_id.h" void SCH_EDIT_FRAME::GetSchematicConnections( std::vector< wxPoint >& aConnections ) { @@ -452,3 +455,106 @@ SCH_JUNCTION* SCH_EDIT_FRAME::AddJunction( const wxPoint& aPosition, bool aAppen } +void SCH_EDIT_FRAME::OnUnfoldBus( wxCommandEvent& event ) +{ + wxMenuItem* item = static_cast( event.GetEventUserData() ); + wxString net = item->GetItemLabelText(); + + GetToolManager()->RunAction( SCH_ACTIONS::unfoldBus, true, &net ); + + // Now that we have handled the chosen bus unfold, disconnect all the events so they can be + // recreated with updated data on the next unfold + Unbind( wxEVT_COMMAND_MENU_SELECTED, &SCH_EDIT_FRAME::OnUnfoldBus, this ); +} + + +void SCH_EDIT_FRAME::OnUnfoldBusHotkey( wxCommandEvent& aEvent ) +{ + SCH_SELECTION_TOOL* selTool = GetToolManager()->GetTool(); + EDA_HOTKEY_CLIENT_DATA* data = (EDA_HOTKEY_CLIENT_DATA*) aEvent.GetClientObject(); + SCH_ITEM* item = GetScreen()->GetCurItem(); + + wxCHECK_RET( data != NULL, wxT( "Invalid hot key client object." ) ); + + if( item == NULL ) + { + // If we didn't get here by a hot key, then something has gone wrong. + if( aEvent.GetInt() == 0 ) + return; + + item = selTool->SelectPoint( data->GetPosition(), SCH_COLLECTOR::EditableItems ); + + // Exit if no item found at the current location or the item is already being edited. + if( item == NULL || item->GetEditFlags() != 0 ) + return; + } + + if( item->Type() != SCH_LINE_T ) + return; + + wxMenu* bus_unfold_menu = GetUnfoldBusMenu( static_cast( item ) ); + + if( bus_unfold_menu ) + { + auto controls = GetCanvas()->GetViewControls(); + auto vmp = controls->GetMousePosition( false ); + wxPoint mouse_pos( (int) vmp.x, (int) vmp.y ); + + GetGalCanvas()->PopupMenu( bus_unfold_menu, mouse_pos ); + } +} + + +wxMenu* SCH_EDIT_FRAME::GetUnfoldBusMenu( SCH_LINE* aBus ) +{ + auto connection = aBus->Connection( *g_CurrentSheet ); + + if( !connection || !connection->IsBus() || connection->Members().empty() ) + return nullptr; + + int idx = 0; + wxMenu* bus_unfolding_menu = new wxMenu; + + for( const auto& member : connection->Members() ) + { + int id = ID_POPUP_SCH_UNFOLD_BUS + ( idx++ ); + wxString name = member->Name( true ); + + if( member->Type() == CONNECTION_BUS ) + { + wxMenu* submenu = new wxMenu; + bus_unfolding_menu->AppendSubMenu( submenu, _( name ) ); + + for( const auto& sub_member : member->Members() ) + { + id = ID_POPUP_SCH_UNFOLD_BUS + ( idx++ ); + + submenu->Append( id, sub_member->Name( true ), wxEmptyString ); + + // See comment in else clause below + auto sub_item_clone = new wxMenuItem(); + sub_item_clone->SetItemLabel( sub_member->Name( true ) ); + + Bind( wxEVT_COMMAND_MENU_SELECTED, &SCH_EDIT_FRAME::OnUnfoldBus, this, id, id, + sub_item_clone ); + } + } + else + { + bus_unfolding_menu->Append( id, name, wxEmptyString ); + + // Because Bind() takes ownership of the user data item, we + // make a new menu item here and set its label. Why create a + // menu item instead of just a wxString or something? Because + // Bind() requires a pointer to wxObject rather than a void + // pointer. Maybe at some point I'll think of a better way... + auto item_clone = new wxMenuItem(); + item_clone->SetItemLabel( name ); + + Bind( wxEVT_COMMAND_MENU_SELECTED, &SCH_EDIT_FRAME::OnUnfoldBus, this, id, id, + item_clone ); + } + } + + return bus_unfolding_menu; +} diff --git a/eeschema/hierarch.cpp b/eeschema/hierarch.cpp index 451be2929d..c7ac4bed7e 100644 --- a/eeschema/hierarch.cpp +++ b/eeschema/hierarch.cpp @@ -108,7 +108,7 @@ private: SCH_SHEET_PATH m_currSheet; // The currently opened scheet in hierarchy public: - HIERARCHY_NAVIG_DLG( SCH_EDIT_FRAME* aParent, const wxPoint& aPos ); + HIERARCHY_NAVIG_DLG( SCH_EDIT_FRAME* aParent ); ~HIERARCHY_NAVIG_DLG(); @@ -130,16 +130,16 @@ private: }; -void SCH_EDIT_FRAME::InstallHierarchyFrame( wxPoint& pos ) +void SCH_EDIT_FRAME::InstallHierarchyFrame( wxCommandEvent& event ) { - HIERARCHY_NAVIG_DLG* treeframe = new HIERARCHY_NAVIG_DLG( this, pos ); + HIERARCHY_NAVIG_DLG* treeframe = new HIERARCHY_NAVIG_DLG( this ); treeframe->ShowQuasiModal(); treeframe->Destroy(); } -HIERARCHY_NAVIG_DLG::HIERARCHY_NAVIG_DLG( SCH_EDIT_FRAME* aParent, const wxPoint& aPos ) : +HIERARCHY_NAVIG_DLG::HIERARCHY_NAVIG_DLG( SCH_EDIT_FRAME* aParent ) : DIALOG_SHIM( aParent, wxID_ANY, _( "Navigator" ), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER ) { diff --git a/eeschema/onrightclick.cpp b/eeschema/onrightclick.cpp index cad67e1a7b..966188f7bc 100644 --- a/eeschema/onrightclick.cpp +++ b/eeschema/onrightclick.cpp @@ -96,8 +96,6 @@ void AddMenusForComponent( wxMenu* PopMenu, SCH_COMPONENT* Component, SYMBOL_LIB void AddMenusForBus( wxMenu* PopMenu, SCH_LINE* Bus, SCH_EDIT_FRAME* frame ) { SCH_SELECTION_TOOL* selTool = frame->GetToolManager()->GetTool(); - wxPoint pos = frame->GetCrossHairPosition(); - wxString msg; // TODO(JE) remove once real-time is enabled if( !ADVANCED_CFG::GetCfg().m_realTimeConnectivity || !CONNECTION_GRAPH::m_allowRealTime ) @@ -105,9 +103,9 @@ void AddMenusForBus( wxMenu* PopMenu, SCH_LINE* Bus, SCH_EDIT_FRAME* frame ) frame->RecalculateConnections(); // Have to pick up the pointer again because it may have been changed by SchematicCleanUp - bool actionCancelled = false; - Bus = dynamic_cast( selTool->SelectPoint( pos, SCH_COLLECTOR::AllItemsButPins, - &actionCancelled ) ); + KICAD_T busType[] = { SCH_LINE_LOCATE_BUS_T, EOT }; + SELECTION& selection = selTool->RequestSelection( busType ); + Bus = (SCH_LINE*) selection.Front(); wxASSERT( Bus ); } diff --git a/eeschema/sch_edit_frame.cpp b/eeschema/sch_edit_frame.cpp index c76199ddde..d6d06a803d 100644 --- a/eeschema/sch_edit_frame.cpp +++ b/eeschema/sch_edit_frame.cpp @@ -245,7 +245,7 @@ BEGIN_EVENT_TABLE( SCH_EDIT_FRAME, EDA_DRAW_FRAME ) EVT_TOOL( ID_RUN_CVPCB, SCH_EDIT_FRAME::OnOpenCvpcb ) EVT_TOOL( ID_SHEET_SET, EDA_DRAW_FRAME::Process_PageSettings ) - EVT_TOOL( ID_HIERARCHY, SCH_EDIT_FRAME::Process_Special_Functions ) + EVT_TOOL( ID_HIERARCHY, SCH_EDIT_FRAME::InstallHierarchyFrame ) EVT_TOOL( wxID_UNDO, SCH_EDIT_FRAME::GetSchematicFromUndoList ) EVT_TOOL( wxID_REDO, SCH_EDIT_FRAME::GetSchematicFromRedoList ) EVT_TOOL( ID_GET_ANNOTATE, SCH_EDIT_FRAME::OnAnnotate ) @@ -270,8 +270,6 @@ BEGIN_EVENT_TABLE( SCH_EDIT_FRAME, EDA_DRAW_FRAME ) EVT_TOOL( ID_SIM_SHOW, SCH_EDIT_FRAME::OnSimulate ) #endif /* KICAD_SPICE */ - EVT_MENU_RANGE( ID_POPUP_START_RANGE, ID_POPUP_END_RANGE, - SCH_EDIT_FRAME::Process_Special_Functions ) EVT_MENU( ID_SCH_UNFOLD_BUS, SCH_EDIT_FRAME::OnUnfoldBusHotkey ) EVT_MENU( ID_MENU_CANVAS_CAIRO, SCH_EDIT_FRAME::OnSwitchCanvas ) @@ -281,8 +279,6 @@ BEGIN_EVENT_TABLE( SCH_EDIT_FRAME, EDA_DRAW_FRAME ) EVT_TOOL( ID_TB_OPTIONS_HIDDEN_PINS, SCH_EDIT_FRAME::OnSelectOptionToolbar ) EVT_TOOL( ID_TB_OPTIONS_BUS_WIRES_ORIENT, SCH_EDIT_FRAME::OnSelectOptionToolbar ) - EVT_MENU_RANGE( ID_POPUP_GENERAL_START_RANGE, ID_POPUP_GENERAL_END_RANGE, - SCH_EDIT_FRAME::Process_Special_Functions ) EVT_MENU_RANGE( ID_POPUP_SCH_SELECT_UNIT1, ID_POPUP_SCH_SELECT_UNIT_CMP_MAX, SCH_EDIT_FRAME::OnSelectUnit ) diff --git a/eeschema/sch_edit_frame.h b/eeschema/sch_edit_frame.h index 80a9d5848d..8506fc9e92 100644 --- a/eeschema/sch_edit_frame.h +++ b/eeschema/sch_edit_frame.h @@ -874,7 +874,7 @@ private: void NormalizeSchematicOnFirstLoad(); // Hierarchical Sheet & PinSheet - void InstallHierarchyFrame( wxPoint& pos ); + void InstallHierarchyFrame( wxCommandEvent& event ); public: /** diff --git a/eeschema/schedit.cpp b/eeschema/schedit.cpp deleted file mode 100644 index 654e4a8e56..0000000000 --- a/eeschema/schedit.cpp +++ /dev/null @@ -1,156 +0,0 @@ - /* - * This program source code file is part of KiCad, a free EDA CAD application. - * - * Copyright (C) 2019 Jean-Pierre Charras, jp.charras at wanadoo.fr - * Copyright (C) 2008-2017 Wayne Stambaugh - * Copyright (C) 2004-2019 KiCad Developers, see AUTHORS.txt for contributors. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, you may find one here: - * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html - * or you may search the http://www.gnu.org website for the version 2 license, - * or you may write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -void SCH_EDIT_FRAME::Process_Special_Functions( wxCommandEvent& event ) -{ - if( event.GetId() == ID_HIERARCHY ) - { - wxPoint pos = wxGetMousePosition(); - pos.y += 20; - - SetNoToolSelected(); - InstallHierarchyFrame( pos ); - SetRepeatItem( NULL ); - } -} - - -void SCH_EDIT_FRAME::OnUnfoldBus( wxCommandEvent& event ) -{ - wxMenuItem* item = static_cast( event.GetEventUserData() ); - wxString net = item->GetItemLabelText(); - - GetToolManager()->RunAction( SCH_ACTIONS::unfoldBus, true, &net ); - - // Now that we have handled the chosen bus unfold, disconnect all the events so they can be - // recreated with updated data on the next unfold - Unbind( wxEVT_COMMAND_MENU_SELECTED, &SCH_EDIT_FRAME::OnUnfoldBus, this ); -} - - -void SCH_EDIT_FRAME::OnUnfoldBusHotkey( wxCommandEvent& aEvent ) -{ - SCH_SELECTION_TOOL* selTool = GetToolManager()->GetTool(); - EDA_HOTKEY_CLIENT_DATA* data = (EDA_HOTKEY_CLIENT_DATA*) aEvent.GetClientObject(); - SCH_ITEM* item = GetScreen()->GetCurItem(); - - wxCHECK_RET( data != NULL, wxT( "Invalid hot key client object." ) ); - - if( item == NULL ) - { - // If we didn't get here by a hot key, then something has gone wrong. - if( aEvent.GetInt() == 0 ) - return; - - item = selTool->SelectPoint( data->GetPosition(), SCH_COLLECTOR::EditableItems ); - - // Exit if no item found at the current location or the item is already being edited. - if( item == NULL || item->GetEditFlags() != 0 ) - return; - } - - if( item->Type() != SCH_LINE_T ) - return; - - wxMenu* bus_unfold_menu = GetUnfoldBusMenu( static_cast( item ) ); - - if( bus_unfold_menu ) - { - auto controls = GetCanvas()->GetViewControls(); - auto vmp = controls->GetMousePosition( false ); - wxPoint mouse_pos( (int) vmp.x, (int) vmp.y ); - - GetGalCanvas()->PopupMenu( bus_unfold_menu, mouse_pos ); - } -} - - -wxMenu* SCH_EDIT_FRAME::GetUnfoldBusMenu( SCH_LINE* aBus ) -{ - auto connection = aBus->Connection( *g_CurrentSheet ); - - if( !connection || !connection->IsBus() || connection->Members().empty() ) - return nullptr; - - int idx = 0; - wxMenu* bus_unfolding_menu = new wxMenu; - - for( const auto& member : connection->Members() ) - { - int id = ID_POPUP_SCH_UNFOLD_BUS + ( idx++ ); - wxString name = member->Name( true ); - - if( member->Type() == CONNECTION_BUS ) - { - wxMenu* submenu = new wxMenu; - bus_unfolding_menu->AppendSubMenu( submenu, _( name ) ); - - for( const auto& sub_member : member->Members() ) - { - id = ID_POPUP_SCH_UNFOLD_BUS + ( idx++ ); - - submenu->Append( id, sub_member->Name( true ), wxEmptyString ); - - // See comment in else clause below - auto sub_item_clone = new wxMenuItem(); - sub_item_clone->SetItemLabel( sub_member->Name( true ) ); - - Bind( wxEVT_COMMAND_MENU_SELECTED, &SCH_EDIT_FRAME::OnUnfoldBus, this, id, id, - sub_item_clone ); - } - } - else - { - bus_unfolding_menu->Append( id, name, wxEmptyString ); - - // Because Bind() takes ownership of the user data item, we - // make a new menu item here and set its label. Why create a - // menu item instead of just a wxString or something? Because - // Bind() requires a pointer to wxObject rather than a void - // pointer. Maybe at some point I'll think of a better way... - auto item_clone = new wxMenuItem(); - item_clone->SetItemLabel( name ); - - Bind( wxEVT_COMMAND_MENU_SELECTED, &SCH_EDIT_FRAME::OnUnfoldBus, this, id, id, - item_clone ); - } - } - - return bus_unfolding_menu; -} diff --git a/eeschema/tools/sch_actions.cpp b/eeschema/tools/sch_actions.cpp index 8272b0745e..622baeeb6a 100644 --- a/eeschema/tools/sch_actions.cpp +++ b/eeschema/tools/sch_actions.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -288,6 +289,7 @@ void SCH_ACTIONS::RegisterAllTools( TOOL_MANAGER* aToolManager ) aToolManager->RegisterTool( new SCH_EDITOR_CONTROL ); aToolManager->RegisterTool( new SCH_PICKER_TOOL ); aToolManager->RegisterTool( new SCH_DRAWING_TOOL ); + aToolManager->RegisterTool( new SCH_LINE_DRAWING_TOOL ); aToolManager->RegisterTool( new SCH_EDIT_TOOL ); aToolManager->RegisterTool( new SCH_INSPECTION_TOOL ); } diff --git a/eeschema/tools/sch_drawing_tool.cpp b/eeschema/tools/sch_drawing_tool.cpp index a24c59c8df..371fc55a2e 100644 --- a/eeschema/tools/sch_drawing_tool.cpp +++ b/eeschema/tools/sch_drawing_tool.cpp @@ -40,7 +40,6 @@ #include #include #include -#include #include #include #include @@ -59,29 +58,6 @@ TOOL_ACTION SCH_ACTIONS::placePower( "eeschema.InteractiveDrawing.placePowerPort _( "Add Power" ), _( "Add a power port" ), add_power_xpm, AF_ACTIVATE ); -TOOL_ACTION SCH_ACTIONS::startWire( "eeschema.InteractiveDrawing.startWire", - AS_GLOBAL, 0, - _( "Start Wire" ), _( "Start drawing a wire" ), - add_line_xpm, AF_ACTIVATE ); - -TOOL_ACTION SCH_ACTIONS::drawWire( "eeschema.InteractiveDrawing.drawWire", - AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_BEGIN_WIRE ), - _( "Add Wire" ), _( "Add a wire" ), - add_line_xpm, AF_ACTIVATE ); - -TOOL_ACTION SCH_ACTIONS::startBus( "eeschema.InteractiveDrawing.startBus", - AS_GLOBAL, 0, - _( "Start Bus" ), _( "Start drawing a bus" ), - add_bus_xpm, AF_ACTIVATE ); - -TOOL_ACTION SCH_ACTIONS::drawBus( "eeschema.InteractiveDrawing.drawBus", - AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_BEGIN_BUS ), - _( "Add Bus" ), _( "Add a bus" ), - add_bus_xpm, AF_ACTIVATE ); - -TOOL_ACTION SCH_ACTIONS::unfoldBus( "eeschema.InteractiveDrawing.unfoldBus", - AS_GLOBAL, 0, "", "", nullptr, AF_ACTIVATE ); - TOOL_ACTION SCH_ACTIONS::placeNoConnect( "eeschema.InteractiveDrawing.placeNoConnect", AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_ADD_NOCONN_FLAG ), _( "Add No Connect Flag" ), _( "Add a no-connection flag" ), @@ -139,36 +115,10 @@ TOOL_ACTION SCH_ACTIONS::placeSchematicText( "eeschema.InteractiveDrawing.placeS _( "Add Text" ), _( "Add text" ), text_xpm, AF_ACTIVATE ); -TOOL_ACTION SCH_ACTIONS::startLines( "eeschema.InteractiveDrawing.startLines", - AS_GLOBAL, 0, _( "Begin Lines" ), _( "Start drawing connected graphic lines" ), - add_line_xpm, AF_ACTIVATE ); - -TOOL_ACTION SCH_ACTIONS::drawLines( "eeschema.InteractiveDrawing.drawLines", - AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_ADD_GRAPHIC_POLYLINE ), - _( "Add Lines" ), _( "Add connected graphic lines" ), - add_graphical_segments_xpm, AF_ACTIVATE ); - TOOL_ACTION SCH_ACTIONS::placeImage( "eeschema.InteractiveDrawing.placeImage", AS_GLOBAL, 0, _( "Add Image" ), _( "Add bitmap image" ), image_xpm, AF_ACTIVATE ); -TOOL_ACTION SCH_ACTIONS::finishLineWireOrBus( "eeschema.InteractiveDrawing.finishLineWireOrBus", - AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_END_CURR_LINEWIREBUS ), - _( "Finish Wire or Bus" ), _( "Complete drawing at current segment" ), - checked_ok_xpm, AF_NONE ); - -TOOL_ACTION SCH_ACTIONS::finishWire( "eeschema.InteractiveDrawing.finishWire", - AS_GLOBAL, 0, _( "Finish Wire" ), _( "Complete wire with current segment" ), - checked_ok_xpm, AF_NONE ); - -TOOL_ACTION SCH_ACTIONS::finishBus( "eeschema.InteractiveDrawing.finishBus", - AS_GLOBAL, 0, _( "Finish Bus" ), _( "Complete bus with current segment" ), - checked_ok_xpm, AF_NONE ); - -TOOL_ACTION SCH_ACTIONS::finishLine( "eeschema.InteractiveDrawing.finishLine", - AS_GLOBAL, 0, _( "Finish Lines" ), _( "Complete connected lines with current segment" ), - checked_ok_xpm, AF_NONE ); - TOOL_ACTION SCH_ACTIONS::finishSheet( "eeschema.InteractiveDrawing.finishSheet", AS_GLOBAL, 0, _( "Finish Sheet" ), _( "Finish drawing sheet" ), checked_ok_xpm, AF_NONE ); @@ -198,7 +148,6 @@ SCH_DRAWING_TOOL::SCH_DRAWING_TOOL() : m_frame( nullptr ), m_menu( *this ) { - m_busUnfold = {}; }; @@ -209,8 +158,6 @@ SCH_DRAWING_TOOL::~SCH_DRAWING_TOOL() bool SCH_DRAWING_TOOL::Init() { - static KICAD_T wireOrBusTypes[] = { SCH_LINE_LOCATE_WIRE_T, SCH_LINE_LOCATE_BUS_T, EOT }; - m_frame = getEditFrame(); m_selectionTool = m_toolMgr->GetTool(); @@ -218,24 +165,10 @@ bool SCH_DRAWING_TOOL::Init() return ( m_frame->GetToolId() != ID_NO_TOOL_SELECTED ); }; - auto wireOrBusTool = [ this ] ( const SELECTION& aSel ) { - return ( m_frame->GetToolId() == ID_WIRE_BUTT || m_frame->GetToolId() == ID_BUS_BUTT ); - }; - - auto lineTool = [ this ] ( const SELECTION& aSel ) { - return ( m_frame->GetToolId() == ID_LINE_COMMENT_BUTT ); - }; - auto sheetTool = [ this ] ( const SELECTION& aSel ) { return ( m_frame->GetToolId() == ID_SHEET_SYMBOL_BUTT ); }; - auto wireOrBusSelectionCondition = SELECTION_CONDITIONS::MoreThan( 0 ) - && SELECTION_CONDITIONS::OnlyTypes( wireOrBusTypes ); - - auto singleSheetCondition = SELECTION_CONDITIONS::Count( 1 ) - && SELECTION_CONDITIONS::OnlyType( SCH_SHEET_T ); - auto& ctxMenu = m_menu.GetMenu(); // @@ -243,44 +176,11 @@ bool SCH_DRAWING_TOOL::Init() // ctxMenu.AddItem( ACTIONS::cancelInteractive, activeTool, 1 ); - ctxMenu.AddItem( SCH_ACTIONS::startWire, wireOrBusTool && SCH_CONDITIONS::Idle, 1 ); - ctxMenu.AddItem( SCH_ACTIONS::startBus, wireOrBusTool && SCH_CONDITIONS::Idle, 1 ); - ctxMenu.AddItem( SCH_ACTIONS::startLines, lineTool && SCH_CONDITIONS::Idle, 1 ); - ctxMenu.AddItem( SCH_ACTIONS::finishWire, IsDrawingWire, 1 ); - ctxMenu.AddItem( SCH_ACTIONS::finishBus, IsDrawingBus, 1 ); - ctxMenu.AddItem( SCH_ACTIONS::finishLine, IsDrawingLine, 1 ); - // TODO(JE): add menu access to unfold bus... - ctxMenu.AddItem( SCH_ACTIONS::resizeSheet, sheetTool && SCH_CONDITIONS::Idle, 1 ); - ctxMenu.AddSeparator( wireOrBusTool && SCH_CONDITIONS::Idle, 100 ); - ctxMenu.AddItem( SCH_ACTIONS::addJunction, wireOrBusTool && SCH_CONDITIONS::Idle, 100 ); - ctxMenu.AddItem( SCH_ACTIONS::addLabel, wireOrBusTool && SCH_CONDITIONS::Idle, 100 ); - ctxMenu.AddItem( SCH_ACTIONS::addGlobalLabel, wireOrBusTool && SCH_CONDITIONS::Idle, 100 ); - ctxMenu.AddItem( SCH_ACTIONS::addHierLabel, wireOrBusTool && SCH_CONDITIONS::Idle, 100 ); - ctxMenu.AddSeparator( activeTool, 1000 ); m_menu.AddStandardSubMenus( m_frame ); - // - // Add drawing actions to the selection tool menu - // - CONDITIONAL_MENU& selToolMenu = m_selectionTool->GetToolMenu().GetMenu(); - - // TODO(JE): add menu access to unfold bus on busSelectionCondition... - selToolMenu.AddItem( SCH_ACTIONS::resizeSheet, singleSheetCondition, 1 ); - - selToolMenu.AddItem( SCH_ACTIONS::startWire, SCH_CONDITIONS::Empty, 1 ); - selToolMenu.AddItem( SCH_ACTIONS::startBus, SCH_CONDITIONS::Empty, 1 ); - selToolMenu.AddItem( SCH_ACTIONS::finishWire, IsDrawingWire, 1 ); - selToolMenu.AddItem( SCH_ACTIONS::finishBus, IsDrawingBus, 1 ); - - selToolMenu.AddItem( SCH_ACTIONS::addJunction, wireOrBusSelectionCondition, 100 ); - selToolMenu.AddItem( SCH_ACTIONS::addLabel, wireOrBusSelectionCondition, 100 ); - selToolMenu.AddItem( SCH_ACTIONS::addGlobalLabel, wireOrBusSelectionCondition, 100 ); - selToolMenu.AddItem( SCH_ACTIONS::addHierLabel, wireOrBusSelectionCondition, 100 ); - selToolMenu.AddItem( SCH_ACTIONS::importSheetPin, singleSheetCondition, 100 ); - return true; } @@ -294,42 +194,6 @@ void SCH_DRAWING_TOOL::Reset( RESET_REASON aReason ) } -static bool isNewSegment( SCH_ITEM* aItem ) -{ - return aItem && aItem->IsNew() && aItem->Type() == SCH_LINE_T; -} - - -bool SCH_DRAWING_TOOL::IsDrawingLine( const SELECTION& aSelection ) -{ - static KICAD_T wireOrBusTypes[] = { SCH_LINE_LOCATE_WIRE_T, SCH_LINE_LOCATE_BUS_T, EOT }; - return IsDrawingLineWireOrBus( aSelection ) && !aSelection.Front()->IsType( wireOrBusTypes ); -} - - -bool SCH_DRAWING_TOOL::IsDrawingWire( const SELECTION& aSelection ) -{ - static KICAD_T wireType[] = { SCH_LINE_LOCATE_WIRE_T, EOT }; - return IsDrawingLineWireOrBus( aSelection ) && aSelection.Front()->IsType( wireType ); -} - - -bool SCH_DRAWING_TOOL::IsDrawingBus( const SELECTION& aSelection ) -{ - static KICAD_T busType[] = { SCH_LINE_LOCATE_BUS_T, EOT }; - return IsDrawingLineWireOrBus( aSelection ) && aSelection.Front()->IsType( busType ); -} - - -bool SCH_DRAWING_TOOL::IsDrawingLineWireOrBus( const SELECTION& aSelection ) -{ - // NOTE: for immediate hotkeys, it is NOT required that the line, wire or bus tool - // be selected - SCH_ITEM* item = (SCH_ITEM*) aSelection.Front(); - return isNewSegment( item ); -} - - int SCH_DRAWING_TOOL::AddJunction( const TOOL_EVENT& aEvent ) { m_toolMgr->RunAction( SCH_ACTIONS::clearSelection, true ); @@ -890,592 +754,6 @@ int SCH_DRAWING_TOOL::doTwoClickPlace( KICAD_T aType ) } -int SCH_DRAWING_TOOL::StartWire( const TOOL_EVENT& aEvent ) -{ - m_toolMgr->RunAction( SCH_ACTIONS::clearSelection, true ); - - m_frame->GetCanvas()->MoveCursorToCrossHair(); - SCH_LINE* segment = startSegments( LAYER_WIRE, m_frame->GetCrossHairPosition() ); - return doDrawSegments( LAYER_WIRE, segment ); -} - - -int SCH_DRAWING_TOOL::DrawWire( const TOOL_EVENT& aEvent ) -{ - if( m_frame->GetToolId() == ID_WIRE_BUTT ) - return StartWire( aEvent ); - else - { - m_frame->SetToolID( ID_WIRE_BUTT, wxCURSOR_PENCIL, _( "Add wire" ) ); - m_toolMgr->RunAction( SCH_ACTIONS::clearSelection, true ); - - return doDrawSegments( LAYER_WIRE, nullptr ); - } -} - - -int SCH_DRAWING_TOOL::StartBus( const TOOL_EVENT& aEvent ) -{ - m_toolMgr->RunAction( SCH_ACTIONS::clearSelection, true ); - - m_frame->GetCanvas()->MoveCursorToCrossHair(); - SCH_LINE* segment = startSegments( LAYER_BUS, m_frame->GetCrossHairPosition() ); - return doDrawSegments( LAYER_BUS, segment ); -} - - -int SCH_DRAWING_TOOL::DrawBus( const TOOL_EVENT& aEvent ) -{ - if( m_frame->GetToolId() == ID_BUS_BUTT ) - return StartBus( aEvent ); - else - { - m_frame->SetToolID( ID_BUS_BUTT, wxCURSOR_PENCIL, _( "Add bus" ) ); - m_toolMgr->RunAction( SCH_ACTIONS::clearSelection, true ); - - return doDrawSegments( LAYER_BUS, nullptr ); - } -} - - -int SCH_DRAWING_TOOL::UnfoldBus( const TOOL_EVENT& aEvent ) -{ - wxString net = *aEvent.Parameter(); - wxPoint pos = m_frame->GetCrossHairPosition(); - - /** - * Unfolding a bus consists of the following user inputs: - * 1) User selects a bus to unfold (see AddMenusForBus()) - * We land in this event handler. - * - * 2) User clicks to set the net label location (handled by BeginSegment()) - * Before this first click, the posture of the bus entry follows the - * mouse cursor in X and Y (handled by DrawSegment()) - * - * 3) User is now in normal wiring mode and can exit in any normal way. - */ - - wxASSERT( !m_busUnfold.in_progress ); - - m_busUnfold.entry = new SCH_BUS_WIRE_ENTRY( pos, '\\' ); - m_busUnfold.entry->SetParent( m_frame->GetScreen() ); - m_frame->AddToScreen( m_busUnfold.entry ); - - m_busUnfold.label = new SCH_LABEL( m_busUnfold.entry->m_End(), net ); - m_busUnfold.label->SetTextSize( wxSize( GetDefaultTextSize(), GetDefaultTextSize() ) ); - m_busUnfold.label->SetLabelSpinStyle( 0 ); - m_busUnfold.label->SetParent( m_frame->GetScreen() ); - - m_busUnfold.in_progress = true; - m_busUnfold.origin = pos; - m_busUnfold.net_name = net; - - m_frame->SetToolID( ID_WIRE_BUTT, wxCURSOR_PENCIL, _( "Add wire" ) ); - - m_frame->SetCrossHairPosition( m_busUnfold.entry->m_End() ); - - SCH_LINE* segment = startSegments( LAYER_WIRE, m_busUnfold.entry->m_End() ); - return doDrawSegments( LAYER_WIRE, segment ); -} - - -int SCH_DRAWING_TOOL::StartLines( const TOOL_EVENT& aEvent) -{ - m_toolMgr->RunAction( SCH_ACTIONS::clearSelection, true ); - - m_frame->GetCanvas()->MoveCursorToCrossHair(); - SCH_LINE* segment = startSegments( LAYER_NOTES, m_frame->GetCrossHairPosition() ); - return doDrawSegments( LAYER_BUS, segment ); -} - - -int SCH_DRAWING_TOOL::DrawLines( const TOOL_EVENT& aEvent) -{ - if( m_frame->GetToolId() == ID_LINE_COMMENT_BUTT ) - return StartLines( aEvent ); - else - { - m_frame->SetToolID( ID_LINE_COMMENT_BUTT, wxCURSOR_PENCIL, _( "Add lines" ) ); - m_toolMgr->RunAction( SCH_ACTIONS::clearSelection, true ); - - return doDrawSegments( LAYER_NOTES, nullptr ); - } -} - - -// Storage for the line segments while drawing -static DLIST s_wires; - - -/** - * A helper function to find any sheet pins at the specified position. - */ -static const SCH_SHEET_PIN* getSheetPin( SCH_SCREEN* aScreen, const wxPoint& aPosition ) -{ - for( SCH_ITEM* item = aScreen->GetDrawItems(); item; item = item->Next() ) - { - if( item->Type() == SCH_SHEET_T ) - { - SCH_SHEET* sheet = (SCH_SHEET*) item; - - for( const SCH_SHEET_PIN& pin : sheet->GetPins() ) - { - if( pin.GetPosition() == aPosition ) - return &pin; - } - } - } - - return nullptr; -} - - -/** - * Function ComputeBreakPoint - * computes the middle coordinate for 2 segments from the start point to \a aPosition - * with the segments kept in the horizontal or vertical axis only. - * - * @param aSegment A pointer to a #SCH_LINE object containing the first line break point - * to compute. - * @param aPosition A reference to a wxPoint object containing the coordinates of the - * position used to calculate the line break point. - */ -static void computeBreakPoint( SCH_SCREEN* aScreen, SCH_LINE* aSegment, wxPoint& aPosition ) -{ - wxCHECK_RET( aSegment != nullptr, wxT( "Cannot compute break point of NULL line segment." ) ); - - SCH_LINE* nextSegment = aSegment->Next(); - - wxPoint midPoint; - int iDx = aSegment->GetEndPoint().x - aSegment->GetStartPoint().x; - int iDy = aSegment->GetEndPoint().y - aSegment->GetStartPoint().y; - - const SCH_SHEET_PIN* connectedPin = getSheetPin( aScreen, aSegment->GetStartPoint() ); - auto force = connectedPin ? connectedPin->GetEdge() : SCH_SHEET_PIN::SHEET_UNDEFINED_SIDE; - - if( force == SCH_SHEET_PIN::SHEET_LEFT_SIDE || force == SCH_SHEET_PIN::SHEET_RIGHT_SIDE ) - { - if( aPosition.x == connectedPin->GetPosition().x ) // push outside sheet boundary - { - int direction = ( force == SCH_SHEET_PIN::SHEET_LEFT_SIDE ) ? -1 : 1; - aPosition.x += int( aScreen->GetGridSize().x * direction ); - } - - midPoint.x = aPosition.x; - midPoint.y = aSegment->GetStartPoint().y; // force horizontal - } - else if( iDy != 0 ) // keep the first segment orientation (vertical) - { - midPoint.x = aSegment->GetStartPoint().x; - midPoint.y = aPosition.y; - } - else if( iDx != 0 ) // keep the first segment orientation (horizontal) - { - midPoint.x = aPosition.x; - midPoint.y = aSegment->GetStartPoint().y; - } - else - { - if( std::abs( aPosition.x - aSegment->GetStartPoint().x ) < - std::abs( aPosition.y - aSegment->GetStartPoint().y ) ) - { - midPoint.x = aSegment->GetStartPoint().x; - midPoint.y = aPosition.y; - } - else - { - midPoint.x = aPosition.x; - midPoint.y = aSegment->GetStartPoint().y; - } - } - - aSegment->SetEndPoint( midPoint ); - nextSegment->SetStartPoint( midPoint ); - nextSegment->SetEndPoint( aPosition ); -} - - -int SCH_DRAWING_TOOL::doDrawSegments( int aType, SCH_LINE* aSegment ) -{ - bool forceHV = m_frame->GetForceHVLines(); - SCH_SCREEN* screen = m_frame->GetScreen(); - - m_controls->ShowCursor( true ); - - Activate(); - - // Main loop: keep receiving events - while( OPT_TOOL_EVENT evt = Wait() ) - { - wxPoint cursorPos = (wxPoint)m_controls->GetCursorPosition( !evt->Modifier( MD_ALT ) ); - - if( TOOL_EVT_UTILS::IsCancelInteractive( evt.get() ) ) - { - if( aSegment || m_busUnfold.in_progress ) - { - m_toolMgr->RunAction( SCH_ACTIONS::clearSelection, true ); - - aSegment = nullptr; - s_wires.DeleteAll(); - - if( m_busUnfold.entry ) - m_frame->RemoveFromScreen( m_busUnfold.entry ); - - if( m_busUnfold.label && m_busUnfold.label_placed ) - m_frame->RemoveFromScreen( m_busUnfold.label ); - - delete m_busUnfold.entry; - delete m_busUnfold.label; - m_busUnfold = {}; - - m_view->ClearPreview(); - m_view->ShowPreview( false ); - - // Clear flags used in edit functions. - screen->ClearDrawingState(); - screen->SetCurItem( nullptr ); - - if( !evt->IsActivate() ) - continue; - } - - if( evt->IsAction( &SCH_ACTIONS::drawWire ) && aType == LAYER_WIRE ) - ; // don't cancel tool; we're going to re-enter - else if( evt->IsAction( &SCH_ACTIONS::drawBus ) && aType == LAYER_BUS ) - ; // don't cancel tool; we're going to re-enter - else - m_frame->SetNoToolSelected(); - - break; - } - else if( evt->IsAction( &SCH_ACTIONS::finishLineWireOrBus ) - || evt->IsAction( &SCH_ACTIONS::finishWire ) - || evt->IsAction( &SCH_ACTIONS::finishBus ) - || evt->IsAction( &SCH_ACTIONS::finishLine ) ) - { - if( aSegment || m_busUnfold.in_progress ) - { - finishSegments(); - aSegment = nullptr; - } - - if( m_frame->GetToolId() == ID_NO_TOOL_SELECTED ) - break; - } - else if( evt->IsClick( BUT_RIGHT ) ) - { - // Warp after context menu only if dragging... - if( !aSegment ) - m_toolMgr->VetoContextMenuMouseWarp(); - - m_menu.ShowContextMenu( m_selectionTool->GetSelection() ); - } - else if( evt->IsClick( BUT_LEFT ) || ( aSegment && evt->IsDblClick( BUT_LEFT ) ) ) - { - // First click when unfolding places the label and wire-to-bus entry - if( m_busUnfold.in_progress && !m_busUnfold.label_placed ) - { - wxASSERT( aType == LAYER_WIRE ); - - m_frame->AddToScreen( m_busUnfold.label ); - m_busUnfold.label_placed = true; - } - - if( !aSegment ) - { - aSegment = startSegments( aType, cursorPos ); - } - // Create a new segment if we're out of previously-created ones - else if( !aSegment->IsNull() || ( forceHV && !aSegment->Back()->IsNull() ) ) - { - // Terminate the command if the end point is on a pin, junction, or another - // wire or bus. - if( !m_busUnfold.in_progress - && screen->IsTerminalPoint( cursorPos, aSegment->GetLayer() ) ) - { - finishSegments(); - aSegment = nullptr; - - if( m_frame->GetToolId() == ID_NO_TOOL_SELECTED ) - break; - } - else - { - aSegment->SetEndPoint( cursorPos ); - - // Create a new segment, and chain it after the current segment. - aSegment = new SCH_LINE( *aSegment ); - aSegment->SetFlags( IS_NEW | IS_MOVED ); - aSegment->SetStartPoint( cursorPos ); - s_wires.PushBack( aSegment ); - m_selectionTool->AddItemToSel( aSegment, true /*quiet mode*/ ); - screen->SetCurItem( aSegment ); - } - } - - if( evt->IsDblClick( BUT_LEFT ) ) - { - finishSegments(); - aSegment = nullptr; - - if( m_frame->GetToolId() == ID_NO_TOOL_SELECTED ) - break; - } - } - else if( evt->IsMotion() ) - { - m_view->ClearPreview(); - - // Update the bus unfold posture based on the mouse movement - if( m_busUnfold.in_progress && !m_busUnfold.label_placed ) - { - wxPoint cursor_delta = cursorPos - m_busUnfold.origin; - SCH_BUS_WIRE_ENTRY* entry = m_busUnfold.entry; - - bool offset = ( cursor_delta.x < 0 ); - char shape = ( offset ? ( ( cursor_delta.y >= 0 ) ? '/' : '\\' ) - : ( ( cursor_delta.y >= 0 ) ? '\\' : '/' ) ); - - // Erase and redraw if necessary - if( shape != entry->GetBusEntryShape() || offset != m_busUnfold.offset ) - { - entry->SetBusEntryShape( shape ); - wxPoint entry_pos = m_busUnfold.origin; - - if( offset ) - entry_pos -= entry->GetSize(); - - entry->SetPosition( entry_pos ); - m_busUnfold.offset = offset; - - m_frame->RefreshItem( entry ); - - wxPoint wire_start = offset ? entry->GetPosition() : entry->m_End(); - s_wires.begin()->SetStartPoint( wire_start ); - } - - // Update the label "ghost" position - m_busUnfold.label->SetPosition( cursorPos ); - m_view->AddToPreview( m_busUnfold.label->Clone() ); - } - - if( aSegment ) - { - // Coerce the line to vertical or horizontal if necessary - if( forceHV ) - computeBreakPoint( screen, aSegment->Back(), cursorPos ); - else - aSegment->SetEndPoint( cursorPos ); - } - - for( auto seg = s_wires.begin(); seg; seg = seg->Next() ) - { - if( !seg->IsNull() ) // Add to preview if segment length != 0 - m_view->AddToPreview( seg->Clone() ); - } - } - - // Enable autopanning and cursor capture only when there is a segment to be placed - m_controls->SetAutoPan( !!aSegment ); - m_controls->CaptureCursor( !!aSegment ); - } - - return 0; -} - - -SCH_LINE* SCH_DRAWING_TOOL::startSegments( int aType, const wxPoint& aPos ) -{ - SCH_LINE* segment = nullptr; - bool forceHV = m_frame->GetForceHVLines(); - - switch( aType ) - { - default: segment = new SCH_LINE( aPos, LAYER_NOTES ); break; - case LAYER_WIRE: segment = new SCH_LINE( aPos, LAYER_WIRE ); break; - case LAYER_BUS: segment = new SCH_LINE( aPos, LAYER_BUS ); break; - } - - segment->SetFlags( IS_NEW | IS_MOVED ); - s_wires.PushBack( segment ); - m_selectionTool->AddItemToSel( segment, true /*quiet mode*/ ); - m_frame->GetScreen()->SetCurItem( segment ); - - // We need 2 segments to go from a given start pin to an end point when the - // horizontal and vertical lines only switch is on. - if( forceHV ) - { - segment = new SCH_LINE( *segment ); - segment->SetFlags( IS_NEW | IS_MOVED ); - s_wires.PushBack( segment ); - m_selectionTool->AddItemToSel( segment, true /*quiet mode*/ ); - m_frame->GetScreen()->SetCurItem( segment ); - } - - return segment; -} - - -/** - * In a contiguous list of wires, remove wires that backtrack over the previous - * wire. Example: - * - * Wire is added: - * ----------------------------------------> - * - * A second wire backtracks over it: - * -------------------<====================> - * - * RemoveBacktracks is called: - * -------------------> - */ -static void removeBacktracks( DLIST& aWires ) -{ - SCH_LINE* next = nullptr; - std::vector last_lines; - - for( SCH_LINE* line = aWires.GetFirst(); line; line = next ) - { - next = line->Next(); - - if( line->IsNull() ) - { - delete s_wires.Remove( line ); - continue; - } - - if( !last_lines.empty() ) - { - SCH_LINE* last_line = last_lines[last_lines.size() - 1]; - bool contiguous = ( last_line->GetEndPoint() == line->GetStartPoint() ); - bool backtracks = IsPointOnSegment( last_line->GetStartPoint(), - last_line->GetEndPoint(), line->GetEndPoint() ); - bool total_backtrack = ( last_line->GetStartPoint() == line->GetEndPoint() ); - - if( contiguous && backtracks ) - { - if( total_backtrack ) - { - delete s_wires.Remove( last_line ); - delete s_wires.Remove( line ); - last_lines.pop_back(); - } - else - { - last_line->SetEndPoint( line->GetEndPoint() ); - delete s_wires.Remove( line ); - } - } - else - { - last_lines.push_back( line ); - } - } - else - { - last_lines.push_back( line ); - } - } -} - - -void SCH_DRAWING_TOOL::finishSegments() -{ - // Clear selection when done so that a new wire can be started. - // NOTE: this must be done before RemoveBacktracks is called or we might end up with - // freed selected items. - m_toolMgr->RunAction( SCH_ACTIONS::clearSelection, true ); - - PICKED_ITEMS_LIST itemList; - - // Remove segments backtracking over others - removeBacktracks( s_wires ); - - // Collect the possible connection points for the new lines - std::vector< wxPoint > connections; - std::vector< wxPoint > new_ends; - m_frame->GetSchematicConnections( connections ); - - // Check each new segment for possible junctions and add/split if needed - for( SCH_LINE* wire = s_wires.GetFirst(); wire; wire = wire->Next() ) - { - if( wire->GetFlags() & SKIP_STRUCT ) - continue; - - wire->GetConnectionPoints( new_ends ); - - for( auto i : connections ) - { - if( IsPointOnSegment( wire->GetStartPoint(), wire->GetEndPoint(), i ) ) - new_ends.push_back( i ); - } - itemList.PushItem( ITEM_PICKER( wire, UR_NEW ) ); - } - - if( m_busUnfold.in_progress && m_busUnfold.label_placed ) - { - wxASSERT( m_busUnfold.entry && m_busUnfold.label ); - - itemList.PushItem( ITEM_PICKER( m_busUnfold.entry, UR_NEW ) ); - itemList.PushItem( ITEM_PICKER( m_busUnfold.label, UR_NEW ) ); - } - - // Get the last non-null wire (this is the last created segment). - m_frame->SetRepeatItem( s_wires.GetLast() ); - - // Add the new wires - while( s_wires.GetFirst() ) - { - s_wires.GetFirst()->ClearFlags( IS_NEW | IS_MOVED ); - m_frame->AddToScreen( s_wires.PopFront() ); - } - - m_view->ClearPreview(); - m_view->ShowPreview( false ); - - m_controls->CaptureCursor( false ); - m_controls->SetAutoPan( false ); - - m_frame->SaveCopyInUndoList( itemList, UR_NEW ); - - // Correct and remove segments that need to be merged. - m_frame->SchematicCleanUp( true ); - - for( auto item = m_frame->GetScreen()->GetDrawItems(); item; item = item->Next() ) - { - if( item->Type() != SCH_COMPONENT_T ) - continue; - - std::vector< wxPoint > pts; - item->GetConnectionPoints( pts ); - - if( pts.size() > 2 ) - continue; - - for( auto i = pts.begin(); i != pts.end(); i++ ) - { - for( auto j = i + 1; j != pts.end(); j++ ) - m_frame->TrimWire( *i, *j, true ); - } - } - - for( auto i : new_ends ) - { - if( m_frame->GetScreen()->IsJunctionNeeded( i, true ) ) - m_frame->AddJunction( i, true, false ); - } - - if( m_busUnfold.in_progress ) - m_busUnfold = {}; - - m_frame->TestDanglingEnds(); - - m_frame->GetScreen()->ClearDrawingState(); - m_frame->GetScreen()->SetCurItem( nullptr ); - m_frame->OnModify(); -} - - int SCH_DRAWING_TOOL::DrawSheet( const TOOL_EVENT& aEvent ) { m_frame->SetToolID( ID_SHEET_SYMBOL_BUTT, wxCURSOR_PENCIL, _( "Add sheet" ) ); @@ -1642,9 +920,6 @@ void SCH_DRAWING_TOOL::setTransitions() { Go( &SCH_DRAWING_TOOL::PlaceSymbol, SCH_ACTIONS::placeSymbol.MakeEvent() ); Go( &SCH_DRAWING_TOOL::PlacePower, SCH_ACTIONS::placePower.MakeEvent() ); - Go( &SCH_DRAWING_TOOL::DrawWire, SCH_ACTIONS::drawWire.MakeEvent() ); - Go( &SCH_DRAWING_TOOL::DrawBus, SCH_ACTIONS::drawBus.MakeEvent() ); - Go( &SCH_DRAWING_TOOL::UnfoldBus, SCH_ACTIONS::unfoldBus.MakeEvent() ); Go( &SCH_DRAWING_TOOL::PlaceNoConnect, SCH_ACTIONS::placeNoConnect.MakeEvent() ); Go( &SCH_DRAWING_TOOL::PlaceJunction, SCH_ACTIONS::placeJunction.MakeEvent() ); Go( &SCH_DRAWING_TOOL::PlaceBusWireEntry, SCH_ACTIONS::placeBusWireEntry.MakeEvent() ); @@ -1657,12 +932,8 @@ void SCH_DRAWING_TOOL::setTransitions() Go( &SCH_DRAWING_TOOL::PlaceSheetPin, SCH_ACTIONS::placeSheetPin.MakeEvent() ); Go( &SCH_DRAWING_TOOL::ImportSheetPin, SCH_ACTIONS::importSheetPin.MakeEvent() ); Go( &SCH_DRAWING_TOOL::PlaceSchematicText, SCH_ACTIONS::placeSchematicText.MakeEvent() ); - Go( &SCH_DRAWING_TOOL::DrawLines, SCH_ACTIONS::drawLines.MakeEvent() ); Go( &SCH_DRAWING_TOOL::PlaceImage, SCH_ACTIONS::placeImage.MakeEvent() ); - Go( &SCH_DRAWING_TOOL::StartWire, SCH_ACTIONS::startWire.MakeEvent() ); - Go( &SCH_DRAWING_TOOL::StartBus, SCH_ACTIONS::startBus.MakeEvent() ); - Go( &SCH_DRAWING_TOOL::StartLines, SCH_ACTIONS::startLines.MakeEvent() ); Go( &SCH_DRAWING_TOOL::AddJunction, SCH_ACTIONS::addJunction.MakeEvent() ); Go( &SCH_DRAWING_TOOL::AddLabel, SCH_ACTIONS::addLabel.MakeEvent() ); Go( &SCH_DRAWING_TOOL::AddLabel, SCH_ACTIONS::addGlobalLabel.MakeEvent() ); diff --git a/eeschema/tools/sch_drawing_tool.h b/eeschema/tools/sch_drawing_tool.h index 5c73385bdb..d0575da93e 100644 --- a/eeschema/tools/sch_drawing_tool.h +++ b/eeschema/tools/sch_drawing_tool.h @@ -38,21 +38,6 @@ class SCH_EDIT_FRAME; class SCH_SELECTION_TOOL; -/// Collection of data related to the bus unfolding tool -struct BUS_UNFOLDING_T -{ - bool in_progress; ///< True if bus unfold operation is running - bool offset; ///< True if the bus entry should be offset from origin - bool label_placed; ///< True if user has placed the net label - - wxPoint origin; ///< Origin (on the bus) of the unfold - wxString net_name; ///< Net label for the unfolding operation - - SCH_BUS_WIRE_ENTRY* entry; - SCH_LABEL* label; -}; - - /** * Class SCH_DRAWING_TOOL * @@ -74,17 +59,11 @@ public: ///> Get the SCH_DRAWING_TOOL top-level context menu inline TOOL_MENU& GetToolMenu() { return m_menu; } - int StartWire( const TOOL_EVENT& aEvent ); - int StartBus( const TOOL_EVENT& aEvent ); - int StartLines( const TOOL_EVENT& aEvent ); int AddJunction( const TOOL_EVENT& aEvent ); int AddLabel( const TOOL_EVENT& aEvent ); int PlaceSymbol( const TOOL_EVENT& aEvent ); int PlacePower( const TOOL_EVENT& aEvent ); - int DrawWire( const TOOL_EVENT& aEvent ); - int DrawBus( const TOOL_EVENT& aEvent ); - int UnfoldBus( const TOOL_EVENT& aEvent ); int PlaceNoConnect( const TOOL_EVENT& aEvent ); int PlaceJunction( const TOOL_EVENT& aEvent ); int PlaceBusWireEntry( const TOOL_EVENT& aEvent ); @@ -97,19 +76,10 @@ public: int PlaceSheetPin( const TOOL_EVENT& aEvent ); int ImportSheetPin( const TOOL_EVENT& aEvent ); int PlaceSchematicText( const TOOL_EVENT& aEvent ); - int DrawLines( const TOOL_EVENT& aEvent ); int PlaceImage( const TOOL_EVENT& aEvent ); - // SELECTION_CONDITIONs: - static bool IsDrawingLine( const SELECTION& aSelection ); - static bool IsDrawingWire( const SELECTION& aSelection ); - static bool IsDrawingBus( const SELECTION& aSelection ); - static bool IsDrawingLineWireOrBus( const SELECTION& aSelection ); - private: - int doAddItem( KICAD_T aType ); - int doPlaceComponent( SCH_COMPONENT* aComponent, SCHLIB_FILTER* aFilter, SCH_BASE_FRAME::HISTORY_LIST aHistoryList ); @@ -117,10 +87,6 @@ private: int doTwoClickPlace( KICAD_T aType ); - int doDrawSegments( int aType, SCH_LINE* aSegment ); - SCH_LINE* startSegments( int aType, const wxPoint& aPos ); - void finishSegments(); - int doDrawSheet( SCH_SHEET* aSheet ); void sizeSheet( SCH_SHEET* aSheet, VECTOR2I aPos ); @@ -133,9 +99,6 @@ private: KIGFX::VIEW_CONTROLS* m_controls; SCH_EDIT_FRAME* m_frame; - /// Data related to bus unfolding tool. - BUS_UNFOLDING_T m_busUnfold; - TOOL_MENU m_menu; }; diff --git a/eeschema/tools/sch_edit_tool.cpp b/eeschema/tools/sch_edit_tool.cpp index 73be0d728e..588b5759df 100644 --- a/eeschema/tools/sch_edit_tool.cpp +++ b/eeschema/tools/sch_edit_tool.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -208,7 +209,7 @@ bool SCH_EDIT_TOOL::Init() if( aSel.Empty() ) return false; - if( SCH_DRAWING_TOOL::IsDrawingLineWireOrBus( aSel ) ) + if( SCH_LINE_DRAWING_TOOL::IsDrawingLineWireOrBus( aSel ) ) return false; return true; @@ -218,7 +219,7 @@ bool SCH_EDIT_TOOL::Init() if( aSel.Empty() ) return false; - if( SCH_DRAWING_TOOL::IsDrawingLineWireOrBus( aSel ) ) + if( SCH_LINE_DRAWING_TOOL::IsDrawingLineWireOrBus( aSel ) ) return false; SCH_ITEM* item = (SCH_ITEM*) aSel.Front(); @@ -334,7 +335,7 @@ bool SCH_EDIT_TOOL::Init() // CONDITIONAL_MENU& drawingMenu = drawingTool->GetToolMenu().GetMenu(); - ctxMenu.AddSeparator( SCH_CONDITIONS::NotEmpty, 200 ); + drawingMenu.AddSeparator( SCH_CONDITIONS::NotEmpty, 200 ); drawingMenu.AddItem( SCH_ACTIONS::rotateCCW, orientCondition, 200 ); drawingMenu.AddItem( SCH_ACTIONS::rotateCW, orientCondition, 200 ); drawingMenu.AddItem( SCH_ACTIONS::mirrorX, orientCondition, 200 ); @@ -352,10 +353,6 @@ bool SCH_EDIT_TOOL::Init() drawingMenu.AddItem( SCH_ACTIONS::toHLabel, toHLabelCondition, 200 ); drawingMenu.AddItem( SCH_ACTIONS::toGLabel, toGLabelCondition, 200 ); drawingMenu.AddItem( SCH_ACTIONS::toText, toTextlCondition, 200 ); - drawingMenu.AddItem( SCH_ACTIONS::selectNode, SCH_CONDITIONS::Idle && wireOrBusTool, 200 ); - drawingMenu.AddItem( SCH_ACTIONS::selectConnection, SCH_CONDITIONS::Idle && wireOrBusTool, 200 ); - drawingMenu.AddItem( SCH_ACTIONS::breakWire, SCH_CONDITIONS::Idle && wireOrBusTool, 200 ); - drawingMenu.AddItem( SCH_ACTIONS::breakBus, SCH_CONDITIONS::Idle && wireOrBusTool, 200 ); // // Add editing actions to the selection tool menu @@ -380,12 +377,10 @@ bool SCH_EDIT_TOOL::Init() // JEY TODO: add menu access for changing symbol unit selToolMenu.AddItem( SCH_ACTIONS::toShapeSlash, entryCondition, 200 ); selToolMenu.AddItem( SCH_ACTIONS::toShapeBackslash, entryCondition, 200 ); - selToolMenu.AddItem( SCH_ACTIONS::toLabel, toLabelCondition, 200 ); - selToolMenu.AddItem( SCH_ACTIONS::toHLabel, toHLabelCondition, 200 ); - selToolMenu.AddItem( SCH_ACTIONS::toGLabel, toGLabelCondition, 200 ); - selToolMenu.AddItem( SCH_ACTIONS::toText, toTextlCondition, 200 ); - selToolMenu.AddItem( SCH_ACTIONS::breakWire, wireSelectionCondition, 200 ); - selToolMenu.AddItem( SCH_ACTIONS::breakBus, busSelectionCondition, 200 ); + selToolMenu.AddItem( SCH_ACTIONS::toLabel, toLabelCondition, 200 ); + selToolMenu.AddItem( SCH_ACTIONS::toHLabel, toHLabelCondition, 200 ); + selToolMenu.AddItem( SCH_ACTIONS::toGLabel, toGLabelCondition, 200 ); + selToolMenu.AddItem( SCH_ACTIONS::toText, toTextlCondition, 200 ); selToolMenu.AddItem( SCH_ACTIONS::cleanupSheetPins, singleSheetCondition, 200 ); selToolMenu.AddSeparator( SCH_CONDITIONS::Idle, 200 ); diff --git a/eeschema/tools/sch_inspection_tool.cpp b/eeschema/tools/sch_inspection_tool.cpp index 48c149a319..9f611a945a 100644 --- a/eeschema/tools/sch_inspection_tool.cpp +++ b/eeschema/tools/sch_inspection_tool.cpp @@ -73,8 +73,8 @@ bool SCH_INSPECTION_TOOL::Init() // CONDITIONAL_MENU& selToolMenu = m_selectionTool->GetToolMenu().GetMenu(); - selToolMenu.AddItem( SCH_ACTIONS::showDatasheet, SCH_CONDITIONS::SingleSymbol, 400 ); - selToolMenu.AddItem( SCH_ACTIONS::showMarkerInfo, singleMarkerCondition, 400 ); + selToolMenu.AddItem( SCH_ACTIONS::showDatasheet, SCH_CONDITIONS::SingleSymbol && SCH_CONDITIONS::Idle, 400 ); + selToolMenu.AddItem( SCH_ACTIONS::showMarkerInfo, singleMarkerCondition && SCH_CONDITIONS::Idle, 400 ); return true; } diff --git a/eeschema/tools/sch_line_drawing_tool.cpp b/eeschema/tools/sch_line_drawing_tool.cpp new file mode 100644 index 0000000000..2409ae43b5 --- /dev/null +++ b/eeschema/tools/sch_line_drawing_tool.cpp @@ -0,0 +1,806 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2019 KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +TOOL_ACTION SCH_ACTIONS::startWire( "eeschema.InteractiveLineDrawing.startWire", + AS_GLOBAL, 0, + _( "Start Wire" ), _( "Start drawing a wire" ), + add_line_xpm, AF_ACTIVATE ); + +TOOL_ACTION SCH_ACTIONS::drawWire( "eeschema.InteractiveLineDrawing.drawWire", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_BEGIN_WIRE ), + _( "Add Wire" ), _( "Add a wire" ), + add_line_xpm, AF_ACTIVATE ); + +TOOL_ACTION SCH_ACTIONS::startBus( "eeschema.InteractiveLineDrawing.startBus", + AS_GLOBAL, 0, + _( "Start Bus" ), _( "Start drawing a bus" ), + add_bus_xpm, AF_ACTIVATE ); + +TOOL_ACTION SCH_ACTIONS::drawBus( "eeschema.InteractiveLineDrawing.drawBus", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_BEGIN_BUS ), + _( "Add Bus" ), _( "Add a bus" ), + add_bus_xpm, AF_ACTIVATE ); + +TOOL_ACTION SCH_ACTIONS::unfoldBus( "eeschema.InteractiveLineDrawing.unfoldBus", + AS_GLOBAL, 0, "", "", nullptr, AF_ACTIVATE ); + +TOOL_ACTION SCH_ACTIONS::startLines( "eeschema.InteractiveLineDrawing.startLines", + AS_GLOBAL, 0, _( "Begin Lines" ), _( "Start drawing connected graphic lines" ), + add_line_xpm, AF_ACTIVATE ); + +TOOL_ACTION SCH_ACTIONS::drawLines( "eeschema.InteractiveLineDrawing.drawLines", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_ADD_GRAPHIC_POLYLINE ), + _( "Add Lines" ), _( "Add connected graphic lines" ), + add_graphical_segments_xpm, AF_ACTIVATE ); + +TOOL_ACTION SCH_ACTIONS::finishLineWireOrBus( "eeschema.InteractiveLineDrawing.finishLineWireOrBus", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_END_CURR_LINEWIREBUS ), + _( "Finish Wire or Bus" ), _( "Complete drawing at current segment" ), + checked_ok_xpm, AF_NONE ); + +TOOL_ACTION SCH_ACTIONS::finishWire( "eeschema.InteractiveLineDrawing.finishWire", + AS_GLOBAL, 0, _( "Finish Wire" ), _( "Complete wire with current segment" ), + checked_ok_xpm, AF_NONE ); + +TOOL_ACTION SCH_ACTIONS::finishBus( "eeschema.InteractiveLineDrawing.finishBus", + AS_GLOBAL, 0, _( "Finish Bus" ), _( "Complete bus with current segment" ), + checked_ok_xpm, AF_NONE ); + +TOOL_ACTION SCH_ACTIONS::finishLine( "eeschema.InteractiveLineDrawing.finishLine", + AS_GLOBAL, 0, _( "Finish Lines" ), _( "Complete connected lines with current segment" ), + checked_ok_xpm, AF_NONE ); + + + +SCH_LINE_DRAWING_TOOL::SCH_LINE_DRAWING_TOOL() : + TOOL_INTERACTIVE( "eeschema.InteractiveLineDrawing" ), + m_selectionTool( nullptr ), + m_view( nullptr ), + m_controls( nullptr ), + m_frame( nullptr ), + m_menu( *this ) +{ + m_busUnfold = {}; +}; + + +SCH_LINE_DRAWING_TOOL::~SCH_LINE_DRAWING_TOOL() +{ +} + + +bool SCH_LINE_DRAWING_TOOL::Init() +{ + m_frame = getEditFrame(); + m_selectionTool = m_toolMgr->GetTool(); + + auto activeTool = [ this ] ( const SELECTION& aSel ) { + return ( m_frame->GetToolId() != ID_NO_TOOL_SELECTED ); + }; + + auto wireOrBusTool = [ this ] ( const SELECTION& aSel ) { + return ( m_frame->GetToolId() == ID_WIRE_BUTT || m_frame->GetToolId() == ID_BUS_BUTT ); + }; + + auto lineTool = [ this ] ( const SELECTION& aSel ) { + return ( m_frame->GetToolId() == ID_LINE_COMMENT_BUTT ); + }; + + auto& ctxMenu = m_menu.GetMenu(); + + // + // Build the tool menu + // + ctxMenu.AddItem( ACTIONS::cancelInteractive, activeTool, 1 ); + + ctxMenu.AddItem( SCH_ACTIONS::startWire, wireOrBusTool && SCH_CONDITIONS::Idle, 1 ); + ctxMenu.AddItem( SCH_ACTIONS::startBus, wireOrBusTool && SCH_CONDITIONS::Idle, 1 ); + ctxMenu.AddItem( SCH_ACTIONS::startLines, lineTool && SCH_CONDITIONS::Idle, 1 ); + ctxMenu.AddItem( SCH_ACTIONS::finishWire, IsDrawingWire, 1 ); + ctxMenu.AddItem( SCH_ACTIONS::finishBus, IsDrawingBus, 1 ); + ctxMenu.AddItem( SCH_ACTIONS::finishLine, IsDrawingLine, 1 ); + + // TODO(JE): add menu access to unfold bus... + + ctxMenu.AddSeparator( wireOrBusTool && SCH_CONDITIONS::Idle, 100 ); + ctxMenu.AddItem( SCH_ACTIONS::addJunction, wireOrBusTool && SCH_CONDITIONS::Idle, 100 ); + ctxMenu.AddItem( SCH_ACTIONS::addLabel, wireOrBusTool && SCH_CONDITIONS::Idle, 100 ); + ctxMenu.AddItem( SCH_ACTIONS::addGlobalLabel, wireOrBusTool && SCH_CONDITIONS::Idle, 100 ); + ctxMenu.AddItem( SCH_ACTIONS::addHierLabel, wireOrBusTool && SCH_CONDITIONS::Idle, 100 ); + ctxMenu.AddItem( SCH_ACTIONS::breakWire, wireOrBusTool && SCH_CONDITIONS::Idle, 100 ); + ctxMenu.AddItem( SCH_ACTIONS::breakBus, wireOrBusTool && SCH_CONDITIONS::Idle, 100 ); + + ctxMenu.AddSeparator( wireOrBusTool && SCH_CONDITIONS::Idle, 200 ); + ctxMenu.AddItem( SCH_ACTIONS::selectNode, wireOrBusTool && SCH_CONDITIONS::Idle, 200 ); + ctxMenu.AddItem( SCH_ACTIONS::selectConnection, wireOrBusTool && SCH_CONDITIONS::Idle, 200 ); + + ctxMenu.AddSeparator( activeTool, 1000 ); + m_menu.AddStandardSubMenus( m_frame ); + + return true; +} + + +void SCH_LINE_DRAWING_TOOL::Reset( RESET_REASON aReason ) +{ + // Init variables used by every drawing tool + m_view = static_cast( getView() ); + m_controls = getViewControls(); + m_frame = getEditFrame(); +} + + +static bool isNewSegment( SCH_ITEM* aItem ) +{ + return aItem && aItem->IsNew() && aItem->Type() == SCH_LINE_T; +} + + +bool SCH_LINE_DRAWING_TOOL::IsDrawingLine( const SELECTION& aSelection ) +{ + static KICAD_T wireOrBusTypes[] = { SCH_LINE_LOCATE_WIRE_T, SCH_LINE_LOCATE_BUS_T, EOT }; + return IsDrawingLineWireOrBus( aSelection ) && !aSelection.Front()->IsType( wireOrBusTypes ); +} + + +bool SCH_LINE_DRAWING_TOOL::IsDrawingWire( const SELECTION& aSelection ) +{ + static KICAD_T wireType[] = { SCH_LINE_LOCATE_WIRE_T, EOT }; + return IsDrawingLineWireOrBus( aSelection ) && aSelection.Front()->IsType( wireType ); +} + + +bool SCH_LINE_DRAWING_TOOL::IsDrawingBus( const SELECTION& aSelection ) +{ + static KICAD_T busType[] = { SCH_LINE_LOCATE_BUS_T, EOT }; + return IsDrawingLineWireOrBus( aSelection ) && aSelection.Front()->IsType( busType ); +} + + +bool SCH_LINE_DRAWING_TOOL::IsDrawingLineWireOrBus( const SELECTION& aSelection ) +{ + // NOTE: for immediate hotkeys, it is NOT required that the line, wire or bus tool + // be selected + SCH_ITEM* item = (SCH_ITEM*) aSelection.Front(); + return isNewSegment( item ); +} + + +int SCH_LINE_DRAWING_TOOL::StartWire( const TOOL_EVENT& aEvent ) +{ + m_toolMgr->RunAction( SCH_ACTIONS::clearSelection, true ); + + m_frame->GetCanvas()->MoveCursorToCrossHair(); + SCH_LINE* segment = startSegments( LAYER_WIRE, m_frame->GetCrossHairPosition() ); + return doDrawSegments( LAYER_WIRE, segment ); +} + + +int SCH_LINE_DRAWING_TOOL::DrawWire( const TOOL_EVENT& aEvent ) +{ + if( m_frame->GetToolId() == ID_WIRE_BUTT ) + return StartWire( aEvent ); + else + { + m_frame->SetToolID( ID_WIRE_BUTT, wxCURSOR_PENCIL, _( "Add wire" ) ); + m_toolMgr->RunAction( SCH_ACTIONS::clearSelection, true ); + + return doDrawSegments( LAYER_WIRE, nullptr ); + } +} + + +int SCH_LINE_DRAWING_TOOL::StartBus( const TOOL_EVENT& aEvent ) +{ + m_toolMgr->RunAction( SCH_ACTIONS::clearSelection, true ); + + m_frame->GetCanvas()->MoveCursorToCrossHair(); + SCH_LINE* segment = startSegments( LAYER_BUS, m_frame->GetCrossHairPosition() ); + return doDrawSegments( LAYER_BUS, segment ); +} + + +int SCH_LINE_DRAWING_TOOL::DrawBus( const TOOL_EVENT& aEvent ) +{ + if( m_frame->GetToolId() == ID_BUS_BUTT ) + return StartBus( aEvent ); + else + { + m_frame->SetToolID( ID_BUS_BUTT, wxCURSOR_PENCIL, _( "Add bus" ) ); + m_toolMgr->RunAction( SCH_ACTIONS::clearSelection, true ); + + return doDrawSegments( LAYER_BUS, nullptr ); + } +} + + +int SCH_LINE_DRAWING_TOOL::UnfoldBus( const TOOL_EVENT& aEvent ) +{ + wxString net = *aEvent.Parameter(); + wxPoint pos = m_frame->GetCrossHairPosition(); + + /** + * Unfolding a bus consists of the following user inputs: + * 1) User selects a bus to unfold (see AddMenusForBus()) + * We land in this event handler. + * + * 2) User clicks to set the net label location (handled by BeginSegment()) + * Before this first click, the posture of the bus entry follows the + * mouse cursor in X and Y (handled by DrawSegment()) + * + * 3) User is now in normal wiring mode and can exit in any normal way. + */ + + wxASSERT( !m_busUnfold.in_progress ); + + m_busUnfold.entry = new SCH_BUS_WIRE_ENTRY( pos, '\\' ); + m_busUnfold.entry->SetParent( m_frame->GetScreen() ); + m_frame->AddToScreen( m_busUnfold.entry ); + + m_busUnfold.label = new SCH_LABEL( m_busUnfold.entry->m_End(), net ); + m_busUnfold.label->SetTextSize( wxSize( GetDefaultTextSize(), GetDefaultTextSize() ) ); + m_busUnfold.label->SetLabelSpinStyle( 0 ); + m_busUnfold.label->SetParent( m_frame->GetScreen() ); + + m_busUnfold.in_progress = true; + m_busUnfold.origin = pos; + m_busUnfold.net_name = net; + + m_frame->SetToolID( ID_WIRE_BUTT, wxCURSOR_PENCIL, _( "Add wire" ) ); + + m_frame->SetCrossHairPosition( m_busUnfold.entry->m_End() ); + + SCH_LINE* segment = startSegments( LAYER_WIRE, m_busUnfold.entry->m_End() ); + return doDrawSegments( LAYER_WIRE, segment ); +} + + +int SCH_LINE_DRAWING_TOOL::StartLines( const TOOL_EVENT& aEvent) +{ + m_toolMgr->RunAction( SCH_ACTIONS::clearSelection, true ); + + m_frame->GetCanvas()->MoveCursorToCrossHair(); + SCH_LINE* segment = startSegments( LAYER_NOTES, m_frame->GetCrossHairPosition() ); + return doDrawSegments( LAYER_BUS, segment ); +} + + +int SCH_LINE_DRAWING_TOOL::DrawLines( const TOOL_EVENT& aEvent) +{ + if( m_frame->GetToolId() == ID_LINE_COMMENT_BUTT ) + return StartLines( aEvent ); + else + { + m_frame->SetToolID( ID_LINE_COMMENT_BUTT, wxCURSOR_PENCIL, _( "Add lines" ) ); + m_toolMgr->RunAction( SCH_ACTIONS::clearSelection, true ); + + return doDrawSegments( LAYER_NOTES, nullptr ); + } +} + + +// Storage for the line segments while drawing +static DLIST s_wires; + + +/** + * A helper function to find any sheet pins at the specified position. + */ +static const SCH_SHEET_PIN* getSheetPin( SCH_SCREEN* aScreen, const wxPoint& aPosition ) +{ + for( SCH_ITEM* item = aScreen->GetDrawItems(); item; item = item->Next() ) + { + if( item->Type() == SCH_SHEET_T ) + { + SCH_SHEET* sheet = (SCH_SHEET*) item; + + for( const SCH_SHEET_PIN& pin : sheet->GetPins() ) + { + if( pin.GetPosition() == aPosition ) + return &pin; + } + } + } + + return nullptr; +} + + +/** + * Function ComputeBreakPoint + * computes the middle coordinate for 2 segments from the start point to \a aPosition + * with the segments kept in the horizontal or vertical axis only. + * + * @param aSegment A pointer to a #SCH_LINE object containing the first line break point + * to compute. + * @param aPosition A reference to a wxPoint object containing the coordinates of the + * position used to calculate the line break point. + */ +static void computeBreakPoint( SCH_SCREEN* aScreen, SCH_LINE* aSegment, wxPoint& aPosition ) +{ + wxCHECK_RET( aSegment != nullptr, wxT( "Cannot compute break point of NULL line segment." ) ); + + SCH_LINE* nextSegment = aSegment->Next(); + + wxPoint midPoint; + int iDx = aSegment->GetEndPoint().x - aSegment->GetStartPoint().x; + int iDy = aSegment->GetEndPoint().y - aSegment->GetStartPoint().y; + + const SCH_SHEET_PIN* connectedPin = getSheetPin( aScreen, aSegment->GetStartPoint() ); + auto force = connectedPin ? connectedPin->GetEdge() : SCH_SHEET_PIN::SHEET_UNDEFINED_SIDE; + + if( force == SCH_SHEET_PIN::SHEET_LEFT_SIDE || force == SCH_SHEET_PIN::SHEET_RIGHT_SIDE ) + { + if( aPosition.x == connectedPin->GetPosition().x ) // push outside sheet boundary + { + int direction = ( force == SCH_SHEET_PIN::SHEET_LEFT_SIDE ) ? -1 : 1; + aPosition.x += int( aScreen->GetGridSize().x * direction ); + } + + midPoint.x = aPosition.x; + midPoint.y = aSegment->GetStartPoint().y; // force horizontal + } + else if( iDy != 0 ) // keep the first segment orientation (vertical) + { + midPoint.x = aSegment->GetStartPoint().x; + midPoint.y = aPosition.y; + } + else if( iDx != 0 ) // keep the first segment orientation (horizontal) + { + midPoint.x = aPosition.x; + midPoint.y = aSegment->GetStartPoint().y; + } + else + { + if( std::abs( aPosition.x - aSegment->GetStartPoint().x ) < + std::abs( aPosition.y - aSegment->GetStartPoint().y ) ) + { + midPoint.x = aSegment->GetStartPoint().x; + midPoint.y = aPosition.y; + } + else + { + midPoint.x = aPosition.x; + midPoint.y = aSegment->GetStartPoint().y; + } + } + + aSegment->SetEndPoint( midPoint ); + nextSegment->SetStartPoint( midPoint ); + nextSegment->SetEndPoint( aPosition ); +} + + +int SCH_LINE_DRAWING_TOOL::doDrawSegments( int aType, SCH_LINE* aSegment ) +{ + bool forceHV = m_frame->GetForceHVLines(); + SCH_SCREEN* screen = m_frame->GetScreen(); + + m_controls->ShowCursor( true ); + + Activate(); + + // Main loop: keep receiving events + while( OPT_TOOL_EVENT evt = Wait() ) + { + wxPoint cursorPos = (wxPoint)m_controls->GetCursorPosition( !evt->Modifier( MD_ALT ) ); + + if( TOOL_EVT_UTILS::IsCancelInteractive( evt.get() ) ) + { + if( aSegment || m_busUnfold.in_progress ) + { + m_toolMgr->RunAction( SCH_ACTIONS::clearSelection, true ); + + aSegment = nullptr; + s_wires.DeleteAll(); + + if( m_busUnfold.entry ) + m_frame->RemoveFromScreen( m_busUnfold.entry ); + + if( m_busUnfold.label && m_busUnfold.label_placed ) + m_frame->RemoveFromScreen( m_busUnfold.label ); + + delete m_busUnfold.entry; + delete m_busUnfold.label; + m_busUnfold = {}; + + m_view->ClearPreview(); + m_view->ShowPreview( false ); + + // Clear flags used in edit functions. + screen->ClearDrawingState(); + screen->SetCurItem( nullptr ); + + if( !evt->IsActivate() ) + continue; + } + + if( evt->IsAction( &SCH_ACTIONS::drawWire ) && aType == LAYER_WIRE ) + ; // don't cancel tool; we're going to re-enter + else if( evt->IsAction( &SCH_ACTIONS::drawBus ) && aType == LAYER_BUS ) + ; // don't cancel tool; we're going to re-enter + else + m_frame->SetNoToolSelected(); + + break; + } + else if( evt->IsAction( &SCH_ACTIONS::finishLineWireOrBus ) + || evt->IsAction( &SCH_ACTIONS::finishWire ) + || evt->IsAction( &SCH_ACTIONS::finishBus ) + || evt->IsAction( &SCH_ACTIONS::finishLine ) ) + { + if( aSegment || m_busUnfold.in_progress ) + { + finishSegments(); + aSegment = nullptr; + } + + if( m_frame->GetToolId() == ID_NO_TOOL_SELECTED ) + break; + } + else if( evt->IsClick( BUT_RIGHT ) ) + { + // Warp after context menu only if dragging... + if( !aSegment ) + m_toolMgr->VetoContextMenuMouseWarp(); + + m_menu.ShowContextMenu( m_selectionTool->GetSelection() ); + } + else if( evt->IsClick( BUT_LEFT ) || ( aSegment && evt->IsDblClick( BUT_LEFT ) ) ) + { + // First click when unfolding places the label and wire-to-bus entry + if( m_busUnfold.in_progress && !m_busUnfold.label_placed ) + { + wxASSERT( aType == LAYER_WIRE ); + + m_frame->AddToScreen( m_busUnfold.label ); + m_busUnfold.label_placed = true; + } + + if( !aSegment ) + { + aSegment = startSegments( aType, cursorPos ); + } + // Create a new segment if we're out of previously-created ones + else if( !aSegment->IsNull() || ( forceHV && !aSegment->Back()->IsNull() ) ) + { + // Terminate the command if the end point is on a pin, junction, or another + // wire or bus. + if( !m_busUnfold.in_progress + && screen->IsTerminalPoint( cursorPos, aSegment->GetLayer() ) ) + { + finishSegments(); + aSegment = nullptr; + + if( m_frame->GetToolId() == ID_NO_TOOL_SELECTED ) + break; + } + else + { + aSegment->SetEndPoint( cursorPos ); + + // Create a new segment, and chain it after the current segment. + aSegment = new SCH_LINE( *aSegment ); + aSegment->SetFlags( IS_NEW | IS_MOVED ); + aSegment->SetStartPoint( cursorPos ); + s_wires.PushBack( aSegment ); + m_selectionTool->AddItemToSel( aSegment, true /*quiet mode*/ ); + screen->SetCurItem( aSegment ); + } + } + + if( evt->IsDblClick( BUT_LEFT ) ) + { + finishSegments(); + aSegment = nullptr; + + if( m_frame->GetToolId() == ID_NO_TOOL_SELECTED ) + break; + } + } + else if( evt->IsMotion() ) + { + m_view->ClearPreview(); + + // Update the bus unfold posture based on the mouse movement + if( m_busUnfold.in_progress && !m_busUnfold.label_placed ) + { + wxPoint cursor_delta = cursorPos - m_busUnfold.origin; + SCH_BUS_WIRE_ENTRY* entry = m_busUnfold.entry; + + bool offset = ( cursor_delta.x < 0 ); + char shape = ( offset ? ( ( cursor_delta.y >= 0 ) ? '/' : '\\' ) + : ( ( cursor_delta.y >= 0 ) ? '\\' : '/' ) ); + + // Erase and redraw if necessary + if( shape != entry->GetBusEntryShape() || offset != m_busUnfold.offset ) + { + entry->SetBusEntryShape( shape ); + wxPoint entry_pos = m_busUnfold.origin; + + if( offset ) + entry_pos -= entry->GetSize(); + + entry->SetPosition( entry_pos ); + m_busUnfold.offset = offset; + + m_frame->RefreshItem( entry ); + + wxPoint wire_start = offset ? entry->GetPosition() : entry->m_End(); + s_wires.begin()->SetStartPoint( wire_start ); + } + + // Update the label "ghost" position + m_busUnfold.label->SetPosition( cursorPos ); + m_view->AddToPreview( m_busUnfold.label->Clone() ); + } + + if( aSegment ) + { + // Coerce the line to vertical or horizontal if necessary + if( forceHV ) + computeBreakPoint( screen, aSegment->Back(), cursorPos ); + else + aSegment->SetEndPoint( cursorPos ); + } + + for( auto seg = s_wires.begin(); seg; seg = seg->Next() ) + { + if( !seg->IsNull() ) // Add to preview if segment length != 0 + m_view->AddToPreview( seg->Clone() ); + } + } + + // Enable autopanning and cursor capture only when there is a segment to be placed + m_controls->SetAutoPan( !!aSegment ); + m_controls->CaptureCursor( !!aSegment ); + } + + return 0; +} + + +SCH_LINE* SCH_LINE_DRAWING_TOOL::startSegments( int aType, const wxPoint& aPos ) +{ + SCH_LINE* segment = nullptr; + bool forceHV = m_frame->GetForceHVLines(); + + switch( aType ) + { + default: segment = new SCH_LINE( aPos, LAYER_NOTES ); break; + case LAYER_WIRE: segment = new SCH_LINE( aPos, LAYER_WIRE ); break; + case LAYER_BUS: segment = new SCH_LINE( aPos, LAYER_BUS ); break; + } + + segment->SetFlags( IS_NEW | IS_MOVED ); + s_wires.PushBack( segment ); + m_selectionTool->AddItemToSel( segment, true /*quiet mode*/ ); + m_frame->GetScreen()->SetCurItem( segment ); + + // We need 2 segments to go from a given start pin to an end point when the + // horizontal and vertical lines only switch is on. + if( forceHV ) + { + segment = new SCH_LINE( *segment ); + segment->SetFlags( IS_NEW | IS_MOVED ); + s_wires.PushBack( segment ); + m_selectionTool->AddItemToSel( segment, true /*quiet mode*/ ); + m_frame->GetScreen()->SetCurItem( segment ); + } + + return segment; +} + + +/** + * In a contiguous list of wires, remove wires that backtrack over the previous + * wire. Example: + * + * Wire is added: + * ----------------------------------------> + * + * A second wire backtracks over it: + * -------------------<====================> + * + * RemoveBacktracks is called: + * -------------------> + */ +static void removeBacktracks( DLIST& aWires ) +{ + SCH_LINE* next = nullptr; + std::vector last_lines; + + for( SCH_LINE* line = aWires.GetFirst(); line; line = next ) + { + next = line->Next(); + + if( line->IsNull() ) + { + delete s_wires.Remove( line ); + continue; + } + + if( !last_lines.empty() ) + { + SCH_LINE* last_line = last_lines[last_lines.size() - 1]; + bool contiguous = ( last_line->GetEndPoint() == line->GetStartPoint() ); + bool backtracks = IsPointOnSegment( last_line->GetStartPoint(), + last_line->GetEndPoint(), line->GetEndPoint() ); + bool total_backtrack = ( last_line->GetStartPoint() == line->GetEndPoint() ); + + if( contiguous && backtracks ) + { + if( total_backtrack ) + { + delete s_wires.Remove( last_line ); + delete s_wires.Remove( line ); + last_lines.pop_back(); + } + else + { + last_line->SetEndPoint( line->GetEndPoint() ); + delete s_wires.Remove( line ); + } + } + else + { + last_lines.push_back( line ); + } + } + else + { + last_lines.push_back( line ); + } + } +} + + +void SCH_LINE_DRAWING_TOOL::finishSegments() +{ + // Clear selection when done so that a new wire can be started. + // NOTE: this must be done before RemoveBacktracks is called or we might end up with + // freed selected items. + m_toolMgr->RunAction( SCH_ACTIONS::clearSelection, true ); + + PICKED_ITEMS_LIST itemList; + + // Remove segments backtracking over others + removeBacktracks( s_wires ); + + // Collect the possible connection points for the new lines + std::vector< wxPoint > connections; + std::vector< wxPoint > new_ends; + m_frame->GetSchematicConnections( connections ); + + // Check each new segment for possible junctions and add/split if needed + for( SCH_LINE* wire = s_wires.GetFirst(); wire; wire = wire->Next() ) + { + if( wire->GetFlags() & SKIP_STRUCT ) + continue; + + wire->GetConnectionPoints( new_ends ); + + for( auto i : connections ) + { + if( IsPointOnSegment( wire->GetStartPoint(), wire->GetEndPoint(), i ) ) + new_ends.push_back( i ); + } + itemList.PushItem( ITEM_PICKER( wire, UR_NEW ) ); + } + + if( m_busUnfold.in_progress && m_busUnfold.label_placed ) + { + wxASSERT( m_busUnfold.entry && m_busUnfold.label ); + + itemList.PushItem( ITEM_PICKER( m_busUnfold.entry, UR_NEW ) ); + itemList.PushItem( ITEM_PICKER( m_busUnfold.label, UR_NEW ) ); + } + + // Get the last non-null wire (this is the last created segment). + m_frame->SetRepeatItem( s_wires.GetLast() ); + + // Add the new wires + while( s_wires.GetFirst() ) + { + s_wires.GetFirst()->ClearFlags( IS_NEW | IS_MOVED ); + m_frame->AddToScreen( s_wires.PopFront() ); + } + + m_view->ClearPreview(); + m_view->ShowPreview( false ); + + m_controls->CaptureCursor( false ); + m_controls->SetAutoPan( false ); + + m_frame->SaveCopyInUndoList( itemList, UR_NEW ); + + // Correct and remove segments that need to be merged. + m_frame->SchematicCleanUp( true ); + + for( auto item = m_frame->GetScreen()->GetDrawItems(); item; item = item->Next() ) + { + if( item->Type() != SCH_COMPONENT_T ) + continue; + + std::vector< wxPoint > pts; + item->GetConnectionPoints( pts ); + + if( pts.size() > 2 ) + continue; + + for( auto i = pts.begin(); i != pts.end(); i++ ) + { + for( auto j = i + 1; j != pts.end(); j++ ) + m_frame->TrimWire( *i, *j, true ); + } + } + + for( auto i : new_ends ) + { + if( m_frame->GetScreen()->IsJunctionNeeded( i, true ) ) + m_frame->AddJunction( i, true, false ); + } + + if( m_busUnfold.in_progress ) + m_busUnfold = {}; + + m_frame->TestDanglingEnds(); + + m_frame->GetScreen()->ClearDrawingState(); + m_frame->GetScreen()->SetCurItem( nullptr ); + m_frame->OnModify(); +} + + +void SCH_LINE_DRAWING_TOOL::setTransitions() +{ + Go( &SCH_LINE_DRAWING_TOOL::DrawWire, SCH_ACTIONS::drawWire.MakeEvent() ); + Go( &SCH_LINE_DRAWING_TOOL::DrawBus, SCH_ACTIONS::drawBus.MakeEvent() ); + Go( &SCH_LINE_DRAWING_TOOL::UnfoldBus, SCH_ACTIONS::unfoldBus.MakeEvent() ); + Go( &SCH_LINE_DRAWING_TOOL::DrawLines, SCH_ACTIONS::drawLines.MakeEvent() ); + + Go( &SCH_LINE_DRAWING_TOOL::StartWire, SCH_ACTIONS::startWire.MakeEvent() ); + Go( &SCH_LINE_DRAWING_TOOL::StartBus, SCH_ACTIONS::startBus.MakeEvent() ); + Go( &SCH_LINE_DRAWING_TOOL::StartLines, SCH_ACTIONS::startLines.MakeEvent() ); +} diff --git a/eeschema/tools/sch_line_drawing_tool.h b/eeschema/tools/sch_line_drawing_tool.h new file mode 100644 index 0000000000..578efc59f8 --- /dev/null +++ b/eeschema/tools/sch_line_drawing_tool.h @@ -0,0 +1,116 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2019 KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef SCH_LINE_DRAWING_TOOL_H +#define SCH_LINE_DRAWING_TOOL_H + +#include +#include +#include +#include + + +class SCH_COMPONENT; +class SCH_BUS_WIRE_ENTRY; +class SCH_LABEL; +class SCHLIB_FILTER; +class SCH_EDIT_FRAME; +class SCH_SELECTION_TOOL; + + +/// Collection of data related to the bus unfolding tool +struct BUS_UNFOLDING_T +{ + bool in_progress; ///< True if bus unfold operation is running + bool offset; ///< True if the bus entry should be offset from origin + bool label_placed; ///< True if user has placed the net label + + wxPoint origin; ///< Origin (on the bus) of the unfold + wxString net_name; ///< Net label for the unfolding operation + + SCH_BUS_WIRE_ENTRY* entry; + SCH_LABEL* label; +}; + + +/** + * Class SCH_LINE_DRAWING_TOOL + * + * Tool responsible for drawing/placing items (symbols, wires, busses, labels, etc.) + */ + +class SCH_LINE_DRAWING_TOOL : public TOOL_INTERACTIVE +{ +public: + SCH_LINE_DRAWING_TOOL(); + ~SCH_LINE_DRAWING_TOOL(); + + /// @copydoc TOOL_INTERACTIVE::Init() + bool Init() override; + + /// @copydoc TOOL_INTERACTIVE::Reset() + void Reset( RESET_REASON aReason ) override; + + ///> Get the SCH_LINE_DRAWING_TOOL top-level context menu + inline TOOL_MENU& GetToolMenu() { return m_menu; } + + int StartWire( const TOOL_EVENT& aEvent ); + int StartBus( const TOOL_EVENT& aEvent ); + int StartLines( const TOOL_EVENT& aEvent ); + int AddJunction( const TOOL_EVENT& aEvent ); + int AddLabel( const TOOL_EVENT& aEvent ); + + int DrawWire( const TOOL_EVENT& aEvent ); + int DrawBus( const TOOL_EVENT& aEvent ); + int DrawLines( const TOOL_EVENT& aEvent ); + + int UnfoldBus( const TOOL_EVENT& aEvent ); + + // SELECTION_CONDITIONs: + static bool IsDrawingLine( const SELECTION& aSelection ); + static bool IsDrawingWire( const SELECTION& aSelection ); + static bool IsDrawingBus( const SELECTION& aSelection ); + static bool IsDrawingLineWireOrBus( const SELECTION& aSelection ); + +private: + + int doDrawSegments( int aType, SCH_LINE* aSegment ); + SCH_LINE* startSegments( int aType, const wxPoint& aPos ); + void finishSegments(); + + ///> Sets up handlers for various events. + void setTransitions() override; + +private: + SCH_SELECTION_TOOL* m_selectionTool; + KIGFX::SCH_VIEW* m_view; + KIGFX::VIEW_CONTROLS* m_controls; + SCH_EDIT_FRAME* m_frame; + + /// Data related to bus unfolding tool. + BUS_UNFOLDING_T m_busUnfold; + + TOOL_MENU m_menu; +}; + +#endif /* SCH_LINE_DRAWING_TOOL_H */ diff --git a/eeschema/tools/sch_selection_tool.cpp b/eeschema/tools/sch_selection_tool.cpp index e295ab2231..647a6b97da 100644 --- a/eeschema/tools/sch_selection_tool.cpp +++ b/eeschema/tools/sch_selection_tool.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -150,14 +151,40 @@ bool SCH_SELECTION_TOOL::Init() m_frame = getEditFrame(); static KICAD_T wireOrBusTypes[] = { SCH_LINE_LOCATE_WIRE_T, SCH_LINE_LOCATE_BUS_T, EOT }; + + auto wireSelection = SCH_CONDITIONS::MoreThan( 0 ) + && SCH_CONDITIONS::OnlyType( SCH_LINE_LOCATE_WIRE_T ); + + auto busSelection = SCH_CONDITIONS::MoreThan( 0 ) + && SCH_CONDITIONS::OnlyType( SCH_LINE_LOCATE_BUS_T ); + auto wireOrBusSelection = SCH_CONDITIONS::MoreThan( 0 ) && SCH_CONDITIONS::OnlyTypes( wireOrBusTypes ); - auto& ctxMenu = m_menu.GetMenu(); + auto singleSheetCondition = SELECTION_CONDITIONS::Count( 1 ) + && SELECTION_CONDITIONS::OnlyType( SCH_SHEET_T ); - ctxMenu.AddItem( SCH_ACTIONS::selectConnection, wireOrBusSelection && SCH_CONDITIONS::Idle, 200 ); + auto& menu = m_menu.GetMenu(); - ctxMenu.AddSeparator( SELECTION_CONDITIONS::ShowAlways, 1000 ); + // TODO(JE): add menu access to unfold bus on busSelectionCondition... + menu.AddItem( SCH_ACTIONS::resizeSheet, singleSheetCondition && SCH_CONDITIONS::Idle, 1 ); + + menu.AddItem( SCH_ACTIONS::startWire, SCH_CONDITIONS::Empty, 1 ); + menu.AddItem( SCH_ACTIONS::startBus, SCH_CONDITIONS::Empty, 1 ); + menu.AddItem( SCH_ACTIONS::finishWire, SCH_LINE_DRAWING_TOOL::IsDrawingWire, 1 ); + menu.AddItem( SCH_ACTIONS::finishBus, SCH_LINE_DRAWING_TOOL::IsDrawingBus, 1 ); + + menu.AddSeparator( SCH_CONDITIONS::NotEmpty, 200 ); + menu.AddItem( SCH_ACTIONS::selectConnection, wireOrBusSelection && SCH_CONDITIONS::Idle, 200 ); + menu.AddItem( SCH_ACTIONS::addJunction, wireOrBusSelection && SCH_CONDITIONS::Idle, 200 ); + menu.AddItem( SCH_ACTIONS::addLabel, wireOrBusSelection && SCH_CONDITIONS::Idle, 200 ); + menu.AddItem( SCH_ACTIONS::addGlobalLabel, wireOrBusSelection && SCH_CONDITIONS::Idle, 200 ); + menu.AddItem( SCH_ACTIONS::addHierLabel, wireOrBusSelection && SCH_CONDITIONS::Idle, 200 ); + menu.AddItem( SCH_ACTIONS::breakWire, wireSelection && SCH_CONDITIONS::Idle, 200 ); + menu.AddItem( SCH_ACTIONS::breakBus, busSelection && SCH_CONDITIONS::Idle, 200 ); + menu.AddItem( SCH_ACTIONS::importSheetPin, singleSheetCondition && SCH_CONDITIONS::Idle, 200 ); + + menu.AddSeparator( SELECTION_CONDITIONS::ShowAlways, 1000 ); m_menu.AddStandardSubMenus( m_frame ); return true;