diff --git a/common/tool/actions.cpp b/common/tool/actions.cpp index 41ce66221c..7fe108111e 100644 --- a/common/tool/actions.cpp +++ b/common/tool/actions.cpp @@ -139,3 +139,16 @@ TOOL_ACTION ACTIONS::gridResetOrigin( "common.Control.gridResetOrigin", TOOL_ACTION ACTIONS::gridPreset( "common.Control.gridPreset", AS_GLOBAL, 0, "", "" ); + + +// System-wide selection Events + +///> Event sent after an item is selected. +const TOOL_EVENT EVENTS::SelectedEvent( TC_MESSAGE, TA_ACTION, "common.Interactive.selected" ); + +///> Event sent after an item is unselected. +const TOOL_EVENT EVENTS::UnselectedEvent( TC_MESSAGE, TA_ACTION, "common.Interactive.unselected" ); + +///> Event sent after selection is cleared. +const TOOL_EVENT EVENTS::ClearedEvent( TC_MESSAGE, TA_ACTION, "common.Interactive.cleared" ); + diff --git a/eeschema/CMakeLists.txt b/eeschema/CMakeLists.txt index 7a2c2e89aa..a6c238c858 100644 --- a/eeschema/CMakeLists.txt +++ b/eeschema/CMakeLists.txt @@ -164,7 +164,6 @@ set( EESCHEMA_SRCS generate_alias_info.cpp getpart.cpp hierarch.cpp - tools/sch_editor_control.cpp hotkeys.cpp lib_arc.cpp lib_bezier.cpp @@ -244,6 +243,8 @@ set( EESCHEMA_SRCS tools/sch_actions.cpp tools/sch_drawing_tool.cpp + tools/sch_edit_tool.cpp + tools/sch_editor_control.cpp tools/sch_picker_tool.cpp tools/sch_selection_tool.cpp tools/selection.cpp diff --git a/eeschema/autoplace_fields.cpp b/eeschema/autoplace_fields.cpp index 2509b5fbf3..ff66450c48 100644 --- a/eeschema/autoplace_fields.cpp +++ b/eeschema/autoplace_fields.cpp @@ -63,6 +63,8 @@ #include #include #include +#include +#include #define FIELD_PADDING 10 // arbitrarily chosen for aesthetics #define FIELD_PADDING_ALIGNED 18 // aligns 50 mil text to a 100 mil grid @@ -683,8 +685,9 @@ const AUTOPLACER::SIDE AUTOPLACER::SIDE_RIGHT( 1, 0 ); void SCH_EDIT_FRAME::OnAutoplaceFields( wxCommandEvent& aEvent ) { - SCH_SCREEN* screen = GetScreen(); - SCH_ITEM* item = screen->GetCurItem(); + SCH_SELECTION_TOOL* selTool = GetToolManager()->GetTool(); + SCH_SCREEN* screen = GetScreen(); + SCH_ITEM* item = screen->GetCurItem(); // Get the item under cursor if we're not currently moving something if( !item ) @@ -692,9 +695,8 @@ void SCH_EDIT_FRAME::OnAutoplaceFields( wxCommandEvent& aEvent ) if( aEvent.GetInt() == 0 ) return; - EDA_HOTKEY_CLIENT_DATA& data = dynamic_cast( - *aEvent.GetClientObject() ); - item = LocateItem( data.GetPosition(), SCH_COLLECTOR::MovableItems, aEvent.GetInt() ); + auto& data = dynamic_cast( *aEvent.GetClientObject() ); + item = selTool->SelectPoint( data.GetPosition(), SCH_COLLECTOR::MovableItems ); screen->SetCurItem( NULL ); if( !item || item->GetEditFlags() ) diff --git a/eeschema/controle.cpp b/eeschema/controle.cpp index 7916eeb320..8749a161f3 100644 --- a/eeschema/controle.cpp +++ b/eeschema/controle.cpp @@ -23,224 +23,16 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ -/** - * eeschema/controle.cpp - */ - #include -#include #include -#include #include -#include -#include -#include - #include -#include #include #include #include -#include -#include -#include -#include -#include -#include #include -SCH_ITEM* SCH_EDIT_FRAME::LocateAndShowItem( const wxPoint& aPosition, const KICAD_T aFilterList[], - int aHotKeyCommandId, - bool* aClarificationMenuCancelled ) -{ - SCH_ITEM* item; - LIB_PIN* Pin = NULL; - SCH_COMPONENT* component = NULL; - wxPoint gridPosition = GetNearestGridPosition( aPosition ); - - // Check the on grid position first. There is more likely to be multiple items on - // grid than off grid. - m_canvas->SetAbortRequest( false ); // be sure a old abort request in not pending - item = LocateItem( gridPosition, aFilterList, aHotKeyCommandId ); - - // If the user aborted the clarification context menu, don't show it again at the - // off grid position. - if( !item && m_canvas->GetAbortRequest() ) - { - if( aClarificationMenuCancelled ) - *aClarificationMenuCancelled = true; - - m_canvas->SetAbortRequest( false ); - return NULL; - } - - if( !item && (aPosition != gridPosition) ) - item = LocateItem( aPosition, aFilterList, aHotKeyCommandId ); - - if( !item ) - { - if( aClarificationMenuCancelled ) - *aClarificationMenuCancelled = m_canvas->GetAbortRequest(); - - m_canvas->SetAbortRequest( false ); // Just in case the user aborted the context menu. - return NULL; - } - - // Cross probing to Pcbnew if a pin or a component is found - switch( item->Type() ) - { - case SCH_FIELD_T: - case LIB_FIELD_T: - component = (SCH_COMPONENT*) item->GetParent(); - SendMessageToPCBNEW( item, component ); - break; - - case SCH_COMPONENT_T: - component = (SCH_COMPONENT*) item; - SendMessageToPCBNEW( item, component ); - break; - - case LIB_PIN_T: - Pin = (LIB_PIN*) item; - component = (SCH_COMPONENT*) LocateItem( aPosition, SCH_COLLECTOR::ComponentsOnly ); - break; - - /* case SCH_SHEET_T: */ - /* // This may lag on larger projects */ - /* SendMessageToPCBNEW( item, nullptr ); */ - /* break; */ - default: - ; - } - - if( Pin ) - { - // Force display pin information (the previous display could be a component info) - MSG_PANEL_ITEMS items; - - Pin->GetMsgPanelInfo( m_UserUnits, items, component ); - - SetMsgPanel( items ); - - // Cross probing:2 - pin found, and send a locate pin command to Pcbnew (highlight net) - SendMessageToPCBNEW( Pin, component ); - } - - return item; -} - - -SCH_ITEM* SCH_EDIT_FRAME::LocateItem( const wxPoint& aPosition, const KICAD_T aFilterList[], - int aHotKeyCommandId ) -{ - SCH_ITEM* item = NULL; - - m_collectedItems.Collect( GetScreen()->GetDrawItems(), aFilterList, aPosition ); - - if( m_collectedItems.GetCount() == 0 ) - { - ClearMsgPanel(); - } - else if( m_collectedItems.GetCount() == 1 ) - { - item = m_collectedItems[0]; - } - else - { - // There are certain parent/child and enclosure combinations that can be handled - // automatically. Since schematics are meant to be human-readable we don't have - // all the various overlap and coverage issues that we do in Pcbnew. - if( m_collectedItems.GetCount() == 2 ) - { - SCH_ITEM* a = m_collectedItems[ 0 ]; - SCH_ITEM* b = m_collectedItems[ 1 ]; - - if( a->GetParent() == b ) - item = a; - else if( a == b->GetParent() ) - item = b; - else if( a->Type() == SCH_SHEET_T && b->Type() != SCH_SHEET_T ) - item = b; - else if( b->Type() == SCH_SHEET_T && a->Type() != SCH_SHEET_T ) - item = a; - } - - // There are certain combinations of items that do not need clarification such as - // a corner were two lines meet or all the items form a junction. - if( aHotKeyCommandId ) - { - switch( aHotKeyCommandId ) - { - case HK_DRAG: - if( m_collectedItems.IsCorner() || m_collectedItems.IsNode( false ) - || m_collectedItems.IsDraggableJunction() ) - { - item = m_collectedItems[0]; - } - break; - - case HK_MOVE_COMPONENT_OR_ITEM: - if( m_collectedItems.GetCount() == 2 && - dynamic_cast< SCH_SHEET_PIN * >( m_collectedItems[0] ) && - dynamic_cast< SCH_SHEET * >( m_collectedItems[1] ) ) - { - item = m_collectedItems[0]; - } - break; - - default: - ; - } - } - - if( item == NULL ) - { - wxASSERT_MSG( m_collectedItems.GetCount() <= MAX_SELECT_ITEM_IDS, - wxT( "Select item clarification context menu size limit exceeded." ) ); - - wxMenu selectMenu; - - AddMenuItem( &selectMenu, wxID_NONE, _( "Clarify Selection" ), KiBitmap( info_xpm ) ); - selectMenu.AppendSeparator(); - - for( int i = 0; i < m_collectedItems.GetCount() && i < MAX_SELECT_ITEM_IDS; i++ ) - { - wxString text = m_collectedItems[i]->GetSelectMenuText( m_UserUnits ); - BITMAP_DEF xpm = m_collectedItems[i]->GetMenuImage(); - AddMenuItem( &selectMenu, ID_SELECT_ITEM_START + i, text, KiBitmap( xpm ) ); - } - - // Set to NULL in case the user aborts the clarification context menu. - GetScreen()->SetCurItem( NULL ); - m_canvas->SetAbortRequest( true ); // Changed to false if an item is selected - PopupMenu( &selectMenu ); - - if( !m_canvas->GetAbortRequest() ) - { - m_canvas->MoveCursorToCrossHair(); - item = GetScreen()->GetCurItem(); - } - } - } - - GetScreen()->SetCurItem( item ); - - if( item ) - { - MSG_PANEL_ITEMS items; - item->GetMsgPanelInfo( m_UserUnits, items ); - SetMsgPanel( items ); - } - else - { - ClearMsgPanel(); - } - - return item; -} - - bool SCH_EDIT_FRAME::GeneralControl( wxDC* aDC, const wxPoint& aPosition, EDA_KEY aHotKey ) { // Filter out the 'fake' mouse motion after a keyboard movement diff --git a/eeschema/cross-probing.cpp b/eeschema/cross-probing.cpp index b9332f9fa3..6eaf273ac0 100644 --- a/eeschema/cross-probing.cpp +++ b/eeschema/cross-probing.cpp @@ -143,22 +143,29 @@ void SCH_EDIT_FRAME::ExecuteRemoteCommand( const char* cmdline ) } -std::string FormatProbeItem( EDA_ITEM* aItem, SCH_COMPONENT* aPart ) +std::string FormatProbeItem( EDA_ITEM* aItem, SCH_COMPONENT* aComp ) { // This is a keyword followed by a quoted string. // Cross probing to Pcbnew if a pin or a component is found switch( aItem->Type() ) { - case SCH_FIELD_T: + case LIB_PIN_T: + wxFAIL_MSG( "What are we doing with LIB_* items here?" ); + break; + case LIB_FIELD_T: - if( aPart ) - return StrPrintf( "$PART: \"%s\"", TO_UTF8( aPart->GetField( REFERENCE )->GetText() ) ); + wxFAIL_MSG( "What are we doing with LIB_* items here?" ); + // fall through to SCH_FIELD_T: + + case SCH_FIELD_T: + if( aComp ) + return StrPrintf( "$PART: \"%s\"", TO_UTF8( aComp->GetField( REFERENCE )->GetText() ) ); break; case SCH_COMPONENT_T: - aPart = (SCH_COMPONENT*) aItem; - return StrPrintf( "$PART: \"%s\"", TO_UTF8( aPart->GetField( REFERENCE )->GetText() ) ); + aComp = (SCH_COMPONENT*) aItem; + return StrPrintf( "$PART: \"%s\"", TO_UTF8( aComp->GetField( REFERENCE )->GetText() ) ); case SCH_SHEET_T: { @@ -166,24 +173,23 @@ std::string FormatProbeItem( EDA_ITEM* aItem, SCH_COMPONENT* aPart ) return StrPrintf( "$SHEET: \"%8.8lX\"", (unsigned long) sheet->GetTimeStamp() ); } - case LIB_PIN_T: + case SCH_PIN_T: { - if( !aPart ) - break; - - LIB_PIN* pin = (LIB_PIN*) aItem; + SCH_PIN* pin = (SCH_PIN*) aItem; + aComp = pin->GetParentComponent(); if( !pin->GetNumber().IsEmpty() ) { - return StrPrintf( "$PIN: \"%s\" $PART: \"%s\"", TO_UTF8( pin->GetNumber() ), - TO_UTF8( aPart->GetField( REFERENCE )->GetText() ) ); + return StrPrintf( "$PIN: \"%s\" $PART: \"%s\"", + TO_UTF8( pin->GetNumber() ), + TO_UTF8( aComp->GetField( REFERENCE )->GetText() ) ); } else { - return StrPrintf( "$PART: \"%s\"", TO_UTF8( aPart->GetField( REFERENCE )->GetText() ) ); + return StrPrintf( "$PART: \"%s\"", + TO_UTF8( aComp->GetField( REFERENCE )->GetText() ) ); } } - break; default: break; diff --git a/eeschema/hotkeys.cpp b/eeschema/hotkeys.cpp index 912d0d48ba..064fe53d0d 100644 --- a/eeschema/hotkeys.cpp +++ b/eeschema/hotkeys.cpp @@ -39,6 +39,8 @@ #include #include +#include +#include // Remark: the hotkey message info is used as keyword in hotkey config files and // as comments in help windows, therefore translated only when displayed @@ -610,7 +612,8 @@ bool SCH_EDIT_FRAME::OnHotKey( wxDC* aDC, int aHotKey, const wxPoint& aPosition, if( aItem == NULL ) { - aItem = LocateAndShowItem( aPosition, SCH_COLLECTOR::CopyableItems ); + SCH_SELECTION_TOOL* selTool = GetToolManager()->GetTool(); + aItem = selTool->SelectPoint( aPosition, SCH_COLLECTOR::CopyableItems ); if( aItem == NULL ) break; diff --git a/eeschema/lib_collectors.h b/eeschema/lib_collectors.h index 57ce5a09c6..06c46a3cff 100644 --- a/eeschema/lib_collectors.h +++ b/eeschema/lib_collectors.h @@ -99,7 +99,7 @@ public: * @param aIndex The index into the list. * @return LIB_ITEM* at \a aIndex or NULL. */ - LIB_ITEM* operator[]( int aIndex ) const + LIB_ITEM* operator[]( int aIndex ) const override { if( (unsigned)aIndex < (unsigned)GetCount() ) return (LIB_ITEM*) m_List[ aIndex ]; diff --git a/eeschema/onleftclick.cpp b/eeschema/onleftclick.cpp index b266128aaa..65183d1910 100644 --- a/eeschema/onleftclick.cpp +++ b/eeschema/onleftclick.cpp @@ -34,10 +34,13 @@ #include #include #include +#include +#include void SCH_EDIT_FRAME::OnLeftClick( wxDC* aDC, const wxPoint& aPosition ) { SCH_ITEM* item = GetScreen()->GetCurItem(); + SCH_SELECTION_TOOL* selTool = GetToolManager()->GetTool(); if( GetToolId() == ID_NO_TOOL_SELECTED ) { @@ -76,7 +79,7 @@ void SCH_EDIT_FRAME::OnLeftClick( wxDC* aDC, const wxPoint& aPosition ) } else { - item = LocateAndShowItem( aPosition ); + item = selTool->SelectPoint( aPosition ); } } @@ -96,7 +99,7 @@ void SCH_EDIT_FRAME::OnLeftClick( wxDC* aDC, const wxPoint& aPosition ) SCH_COMPONENT_T, SCH_SHEET_PIN_T, EOT }; - item = LocateAndShowItem( aPosition, wiresAndComponents ); + item = selTool->SelectPoint( aPosition, wiresAndComponents ); if( !item ) break; @@ -121,7 +124,7 @@ void SCH_EDIT_FRAME::OnLeftClick( wxDC* aDC, const wxPoint& aPosition ) case ID_SIM_TUNE: { constexpr KICAD_T fieldsAndComponents[] = { SCH_COMPONENT_T, SCH_FIELD_T, EOT }; - item = LocateAndShowItem( aPosition, fieldsAndComponents ); + item = selTool->SelectPoint( aPosition, fieldsAndComponents ); if( !item ) return; @@ -159,13 +162,14 @@ void SCH_EDIT_FRAME::OnLeftClick( wxDC* aDC, const wxPoint& aPosition ) void SCH_EDIT_FRAME::OnLeftDClick( wxDC* aDC, const wxPoint& aPosition ) { - EDA_ITEM* item = GetScreen()->GetCurItem(); + SCH_SELECTION_TOOL* selTool = GetToolManager()->GetTool(); + EDA_ITEM* item = GetScreen()->GetCurItem(); switch( GetToolId() ) { case ID_NO_TOOL_SELECTED: if( item == NULL || item->GetEditFlags() == 0 ) - item = LocateAndShowItem( aPosition, SCH_COLLECTOR::DoubleClickItems ); + item = selTool->SelectPoint( aPosition, SCH_COLLECTOR::DoubleClickItems ); if( item == NULL || item->GetEditFlags() != 0 ) break; diff --git a/eeschema/onrightclick.cpp b/eeschema/onrightclick.cpp index 2330576e9c..3f7bc7450e 100644 --- a/eeschema/onrightclick.cpp +++ b/eeschema/onrightclick.cpp @@ -54,7 +54,9 @@ #include #include +#include #include +#include static void AddMenusForBlock( wxMenu* PopMenu, SCH_EDIT_FRAME* frame ); static void AddMenusForWire( wxMenu* PopMenu, SCH_LINE* Wire, SCH_EDIT_FRAME* frame ); @@ -77,9 +79,10 @@ static void AddMenusForBusEntry( wxMenu* aPopMenu, SCH_BUS_ENTRY_BASE * aBusEntr bool SCH_EDIT_FRAME::OnRightClick( const wxPoint& aPosition, wxMenu* PopMenu ) { - SCH_ITEM* item = GetScreen()->GetCurItem(); - bool blockActive = GetScreen()->IsBlockActive(); - wxString msg; + SCH_SELECTION_TOOL* selTool = GetToolManager()->GetTool(); + SCH_ITEM* item = GetScreen()->GetCurItem(); + bool blockActive = GetScreen()->IsBlockActive(); + wxString msg; // Ugly hack, clear any highligthed symbol, because the HIGHLIGHT flag create issues when creating menus // Will be fixed later @@ -147,7 +150,7 @@ bool SCH_EDIT_FRAME::OnRightClick( const wxPoint& aPosition, wxMenu* PopMenu ) if( item == NULL || item->GetEditFlags() == 0 ) { bool actionCancelled = false; - item = LocateAndShowItem( aPosition, SCH_COLLECTOR::AllItemsButPins, 0, &actionCancelled ); + item = selTool->SelectPoint( aPosition, SCH_COLLECTOR::AllItemsButPins, &actionCancelled ); // If the clarify item selection context menu is aborted, don't show the context menu. if( item == NULL && actionCancelled ) @@ -744,8 +747,9 @@ void AddMenusForWire( wxMenu* PopMenu, SCH_LINE* Wire, SCH_EDIT_FRAME* frame ) void AddMenusForBus( wxMenu* PopMenu, SCH_LINE* Bus, SCH_EDIT_FRAME* frame ) { - wxPoint pos = frame->GetCrossHairPosition(); - wxString msg; + SCH_SELECTION_TOOL* selTool = frame->GetToolManager()->GetTool(); + wxPoint pos = frame->GetCrossHairPosition(); + wxString msg; if( Bus == NULL ) { @@ -773,8 +777,8 @@ void AddMenusForBus( wxMenu* PopMenu, SCH_LINE* Bus, SCH_EDIT_FRAME* frame ) // Have to pick up the pointer again because it may have been changed by SchematicCleanUp bool actionCancelled = false; - Bus = dynamic_cast( frame->LocateAndShowItem( pos, SCH_COLLECTOR::AllItemsButPins, - 0, &actionCancelled ) ); + Bus = dynamic_cast( selTool->SelectPoint( pos, SCH_COLLECTOR::AllItemsButPins, + &actionCancelled ) ); wxASSERT( Bus ); } diff --git a/eeschema/sch_base_frame.h b/eeschema/sch_base_frame.h index 351802e1ee..8ef593dd4f 100644 --- a/eeschema/sch_base_frame.h +++ b/eeschema/sch_base_frame.h @@ -110,6 +110,11 @@ public: KIGFX::SCH_RENDER_SETTINGS* GetRenderSettings(); + /** + * Allow some frames to show/hide hidden pins. The default impl shows all pins. + */ + virtual bool GetShowAllPins() const { return true; } + /** * switches currently used canvas ( Cairo / OpenGL). */ diff --git a/eeschema/sch_collectors.cpp b/eeschema/sch_collectors.cpp index 70914265b1..993ec6b342 100644 --- a/eeschema/sch_collectors.cpp +++ b/eeschema/sch_collectors.cpp @@ -51,7 +51,7 @@ const KICAD_T SCH_COLLECTOR::AllItems[] = { SCH_HIER_LABEL_T, SCH_FIELD_T, SCH_COMPONENT_T, - LIB_PIN_T, + SCH_PIN_T, SCH_SHEET_PIN_T, SCH_SHEET_T, EOT @@ -238,43 +238,21 @@ const KICAD_T SCH_COLLECTOR::DoubleClickItems[] = { SEARCH_RESULT SCH_COLLECTOR::Inspect( EDA_ITEM* aItem, void* aTestData ) { - if( aItem->Type() != LIB_PIN_T && !aItem->HitTest( m_RefPos ) ) - return SEARCH_CONTINUE; - - // Pins have special hit testing requirements that are relative to their parent - // SCH_COMPONENT item. - if( aItem->Type() == LIB_PIN_T ) - { - wxCHECK_MSG( aTestData && ( (EDA_ITEM*) aTestData )->Type() == SCH_COMPONENT_T, - SEARCH_CONTINUE, wxT( "Cannot inspect invalid data. Bad programmer!" ) ); - - // Pin hit testing is relative to the components position and orientation in the - // schematic. The hit test position must be converted to library coordinates. - SCH_COMPONENT* component = (SCH_COMPONENT*) aTestData; - TRANSFORM transform = component->GetTransform().InverseTransform(); - wxPoint position = transform.TransformCoordinate( m_RefPos - component->GetPosition() ); - - position.y *= -1; // Y axis polarity in schematic is inverted from library. - - if( !aItem->HitTest( position ) ) - return SEARCH_CONTINUE; - } - - Append( aItem ); + if( aItem->HitTest( m_RefPos ) ) + Append( aItem ); return SEARCH_CONTINUE; } -void SCH_COLLECTOR::Collect( SCH_ITEM* aItem, const KICAD_T aFilterList[], - const wxPoint& aPosition ) +void SCH_COLLECTOR::Collect( SCH_ITEM* aItem, const KICAD_T aFilterList[], const wxPoint& aPos ) { Empty(); // empty the collection just in case SetScanTypes( aFilterList ); // remember where the snapshot was taken from and pass refPos to the Inspect() function. - SetRefPos( aPosition ); + SetRefPos( aPos ); EDA_ITEM::IterateForward( aItem, m_inspector, NULL, m_ScanTypes ); } @@ -319,7 +297,7 @@ bool SCH_COLLECTOR::IsNode( bool aIncludePins ) const continue; } - if( type == LIB_PIN_T ) + if( type == SCH_PIN_T ) { if( !aIncludePins ) return false; @@ -559,7 +537,7 @@ SEARCH_RESULT SCH_FIND_COLLECTOR::Inspect( EDA_ITEM* aItem, void* aTestData ) if( aItem->Matches( m_findReplaceData, m_currentSheetPath, &position ) ) { - if( aItem->Type() == LIB_PIN_T ) + if( aItem->Type() == SCH_PIN_T ) { wxCHECK_MSG( aTestData && ( (EDA_ITEM*) aTestData )->Type() == SCH_COMPONENT_T, SEARCH_CONTINUE, wxT( "Cannot inspect invalid data. Bad programmer!" ) ); diff --git a/eeschema/sch_collectors.h b/eeschema/sch_collectors.h index 8533a51e7f..0893c43eaf 100644 --- a/eeschema/sch_collectors.h +++ b/eeschema/sch_collectors.h @@ -142,7 +142,7 @@ public: * @param aIndex The index into the list. * @return SCH_ITEM* at \a aIndex or NULL. */ - SCH_ITEM* operator[]( int aIndex ) const + SCH_ITEM* operator[]( int aIndex ) const override { if( (unsigned)aIndex < (unsigned)GetCount() ) return (SCH_ITEM*) m_List[ aIndex ]; @@ -159,9 +159,9 @@ public: * @param aFilterList A list of #KICAD_T types with a terminating #EOT, that determines * what is to be collected and the priority order of the resulting * collection. - * @param aPosition A wxPoint to use in hit-testing. + * @param aPos A wxPoint to use in hit-testing. */ - void Collect( SCH_ITEM* aItem, const KICAD_T aFilterList[], const wxPoint& aPosition ); + void Collect( SCH_ITEM* aItem, const KICAD_T aFilterList[], const wxPoint& aPos ); /** * Function IsCorner @@ -289,7 +289,7 @@ public: } SCH_ITEM* GetItem( int ndx ) const; - SCH_ITEM* operator[]( int ndx ) const; + SCH_ITEM* operator[]( int ndx ) const override; void SetForceSearch( bool doSearch = true ) { m_forceSearch = doSearch; } @@ -405,8 +405,7 @@ class SCH_TYPE_COLLECTOR : public SCH_COLLECTOR public: /** * Function Inspect - * is the examining function within the INSPECTOR which is passed to the - * Iterate function. + * is the examining function within the INSPECTOR which is passed to the Iterate function. * * @param testItem An EDA_ITEM to examine. * @param testData is not used in this class. @@ -417,12 +416,11 @@ public: /** * Function Collect - * scans a BOARD_ITEM using this class's Inspector method, which does - * the collection. - * @param aBoard The BOARD_ITEM to scan. + * scans a DLIST using this class's Inspector method, which does the collection. + * @param aItem The head of a DLIST to scan. * @param aScanList The KICAD_Ts to gather up. */ - void Collect( SCH_ITEM* aBoard, const KICAD_T aScanList[] ); + void Collect( SCH_ITEM* aItem, const KICAD_T aScanList[] ); }; diff --git a/eeschema/sch_component.cpp b/eeschema/sch_component.cpp index 3c5162675f..f7ddcac93e 100644 --- a/eeschema/sch_component.cpp +++ b/eeschema/sch_component.cpp @@ -1612,18 +1612,11 @@ SEARCH_RESULT SCH_COMPONENT::Visit( INSPECTOR aInspector, void* aTestData, return SEARCH_QUIT; break; - case LIB_PIN_T: - if( PART_SPTR part = m_part.lock() ) + case SCH_PIN_T: + for( SCH_PIN& pin : m_pins ) { - LIB_PINS pins; - - part->GetPins( pins, m_unit, m_convert ); - - for( size_t i = 0; i < pins.size(); i++ ) - { - if( SEARCH_QUIT == aInspector( pins[ i ], (void*) this ) ) - return SEARCH_QUIT; - } + if( SEARCH_QUIT == aInspector( &pin, (void*) this ) ) + return SEARCH_QUIT; } break; @@ -1728,7 +1721,7 @@ bool SCH_COMPONENT::operator!=( const SCH_COMPONENT& aComponent ) const } -SCH_ITEM& SCH_COMPONENT::operator=( const SCH_ITEM& aItem ) +SCH_COMPONENT& SCH_COMPONENT::operator=( const SCH_ITEM& aItem ) { wxCHECK_MSG( Type() == aItem.Type(), *this, wxT( "Cannot assign object type " ) + aItem.GetClass() + wxT( " to type " ) + diff --git a/eeschema/sch_component.h b/eeschema/sch_component.h index f4a33f4bc5..588c2b791a 100644 --- a/eeschema/sch_component.h +++ b/eeschema/sch_component.h @@ -606,7 +606,7 @@ public: bool operator==( const SCH_COMPONENT& aComponent) const; bool operator!=( const SCH_COMPONENT& aComponent) const; - SCH_ITEM& operator=( const SCH_ITEM& aItem ); + SCH_COMPONENT& operator=( const SCH_ITEM& aItem ); bool IsReplaceable() const override { return true; } diff --git a/eeschema/sch_edit_frame.cpp b/eeschema/sch_edit_frame.cpp index 7b54df19ef..87041d61dc 100644 --- a/eeschema/sch_edit_frame.cpp +++ b/eeschema/sch_edit_frame.cpp @@ -62,6 +62,7 @@ #include #include #include +#include #include #include @@ -1107,7 +1108,8 @@ void SCH_EDIT_FRAME::OnOpenCvpcb( wxCommandEvent& event ) void SCH_EDIT_FRAME::OnOpenLibraryEditor( wxCommandEvent& event ) { - SCH_COMPONENT* component = NULL; + SCH_SELECTION_TOOL* selTool = GetToolManager()->GetTool(); + SCH_COMPONENT* component = NULL; if( event.GetId() == ID_POPUP_SCH_CALL_LIBEDIT_AND_LOAD_CMP ) { @@ -1127,7 +1129,7 @@ void SCH_EDIT_FRAME::OnOpenLibraryEditor( wxCommandEvent& event ) // Set the locat filter, according to the edit command const KICAD_T* filterList = SCH_COLLECTOR::ComponentsOnly; - item = LocateAndShowItem( data->GetPosition(), filterList, event.GetInt() ); + item = selTool->SelectPoint( data->GetPosition(), filterList ); // Exit if no item found at the current location or the item is already being edited. if( item == NULL || item->GetEditFlags() != 0 ) diff --git a/eeschema/sch_edit_frame.h b/eeschema/sch_edit_frame.h index f40d4da0d9..617a8aa09d 100644 --- a/eeschema/sch_edit_frame.h +++ b/eeschema/sch_edit_frame.h @@ -225,7 +225,7 @@ public: bool GetForceHVLines() const { return m_forceHVLines; } void SetForceHVLines( bool aForceHVdirection ) { m_forceHVLines = aForceHVdirection; } - bool GetShowAllPins() const { return m_showAllPins; } + bool GetShowAllPins() const override { return m_showAllPins; } void SetShowAllPins( bool aEnable ) { m_showAllPins = aEnable; } bool GetShowFootprintPreviews() const { return m_footprintPreview; } @@ -383,48 +383,6 @@ public: */ void AddItemToScreen( SCH_ITEM* aItem ); - /** - * Check the schematic at \a aPosition in logical (drawing) units for a item - * matching the types in \a aFilterList. - *

- * The search is first performed at the nearest grid position to \a aPosition. If no - * item if found on grid, then \a aPosition is tested for any items. If the item found - * can be cross probed, a message is send to Pcbnew and the selected item is highlighted - * in PCB editor. - *

- * - * @param aPosition The wxPoint on the schematic to search. - * @param aFilterList A list of #KICAD_T types to to filter. - * @param aHotKeyCommandId A hot key command ID for performing additional tests when - * multiple items are found at \a aPosition. - * @param aClarifySelectionMenuCancelled is a pointer to a bool to handle a cancel command - * from user when the user cancels the locate menu disambiguation (selection between located items) - * @return A SCH_ITEM pointer of the item found or NULL if no item found - */ - SCH_ITEM* LocateAndShowItem( const wxPoint& aPosition, - const KICAD_T aFilterList[] = SCH_COLLECTOR::AllItems, - int aHotKeyCommandId = 0, - bool* aClarifySelectionMenuCancelled = nullptr ); - - /** - * Check for items at \a aPosition matching the types in \a aFilterList. - *

- * If multiple items are located at \a aPosition, a context menu is displayed to clarify - * which item the user intended to select. If the user aborts the context menu, NULL is - * returned and the abort request flag will be set to true. Make sure to clear this flag - * before attempting to display any other context menus. - *

- * - * @param aPosition The wxPoint location where to search. - * @param aFilterList A list of #KICAD_T types to to filter. - * @param aHotKeyCommandId A hot key command ID for performing additional tests when - * multiple items are found at \a aPosition. - * @return The SCH_ITEM pointer of the item found or NULL if no item found. - */ - SCH_ITEM* LocateItem( const wxPoint& aPosition, - const KICAD_T aFilterList[] = SCH_COLLECTOR::AllItems, - int aHotKeyCommandId = 0 ); - /** * Delete the item found under the cross hair. If multiple items are found at the * cross hair position, a context menu is displayed to clarify which item to delete. diff --git a/eeschema/sch_item_struct.h b/eeschema/sch_item_struct.h index 2193d318bc..8302f8971f 100644 --- a/eeschema/sch_item_struct.h +++ b/eeschema/sch_item_struct.h @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2004 Jean-Pierre Charras, jaen-pierre.charras@gipsa-lab.inpg.com - * Copyright (C) 2004-2017 KiCad Developers, see change_log.txt for contributors. + * Copyright (C) 2004-2019 KiCad Developers, see change_log.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 @@ -171,6 +171,17 @@ public: wxPoint& GetStoredPos() { return m_storedPos; } void SetStoredPos( wxPoint aPos ) { m_storedPos = aPos; } + /** + * Function IsLocked + * @return bool - true if the object is locked, else false + */ + virtual bool IsLocked() const { return false; } + + /** + * Function SetLocked + * modifies 'lock' status for of the item. + */ + virtual void SetLocked( bool aLocked ) {} /** * Function GetLayer diff --git a/eeschema/schedit.cpp b/eeschema/schedit.cpp index 15cb128eb2..91eda78938 100644 --- a/eeschema/schedit.cpp +++ b/eeschema/schedit.cpp @@ -46,6 +46,7 @@ #include #include #include +#include void SCH_EDIT_FRAME::Process_Special_Functions( wxCommandEvent& event ) { @@ -364,8 +365,9 @@ void SCH_EDIT_FRAME::OnDuplicateItem( wxCommandEvent& event ) void SCH_EDIT_FRAME::OnMoveItem( wxCommandEvent& aEvent ) { - SCH_SCREEN* screen = GetScreen(); - SCH_ITEM* item = screen->GetCurItem(); + SCH_SELECTION_TOOL* selTool = GetToolManager()->GetTool(); + SCH_SCREEN* screen = GetScreen(); + SCH_ITEM* item = screen->GetCurItem(); // trying to move an item when there is a block at the same time is not acceptable if( screen->m_BlockLocate.GetState() != STATE_NO_BLOCK ) @@ -381,8 +383,7 @@ void SCH_EDIT_FRAME::OnMoveItem( wxCommandEvent& aEvent ) wxCHECK_RET( data != NULL, wxT( "Invalid hot key client object." ) ); - item = LocateAndShowItem( data->GetPosition(), SCH_COLLECTOR::MovableItems, - aEvent.GetInt() ); + item = selTool->SelectPoint( data->GetPosition(), SCH_COLLECTOR::MovableItems ); // Exit if no item found at the current location or the item is already being edited. if( item == NULL || item->GetEditFlags() != 0 ) @@ -526,8 +527,10 @@ void SCH_EDIT_FRAME::DeleteConnection( bool aFullConnection ) bool SCH_EDIT_FRAME::DeleteItemAtCrossHair() { - SCH_SCREEN* screen = GetScreen(); - SCH_ITEM* item = LocateItem( GetCrossHairPosition(), SCH_COLLECTOR::ParentItems ); + SCH_SELECTION_TOOL* selTool = GetToolManager()->GetTool(); + SCH_SCREEN* screen = GetScreen(); + + SCH_ITEM* item = selTool->SelectPoint( GetCrossHairPosition(), SCH_COLLECTOR::ParentItems ); if( item ) { @@ -693,12 +696,13 @@ void SCH_EDIT_FRAME::PrepareMoveItem( SCH_ITEM* aItem ) void SCH_EDIT_FRAME::SelectAllFromSheet( wxCommandEvent& aEvent ) { - SCH_SCREEN* screen = GetScreen(); - SCH_ITEM* item = screen->GetCurItem(); + SCH_SELECTION_TOOL* selTool = GetToolManager()->GetTool(); + SCH_SCREEN* screen = GetScreen(); + SCH_ITEM* item = screen->GetCurItem(); if( item != NULL ) { - item = LocateAndShowItem( item->GetPosition() ); + item = selTool->SelectPoint( item->GetPosition() ); SendMessageToPCBNEW( item, NULL ); } else @@ -711,7 +715,7 @@ void SCH_EDIT_FRAME::SelectAllFromSheet( wxCommandEvent& aEvent ) wxCHECK_RET( data != NULL, wxT( "Invalid hot key client object." ) ); - item = LocateAndShowItem( data->GetPosition() ); + item = selTool->SelectPoint( data->GetPosition() ); SendMessageToPCBNEW( item, NULL ); } } @@ -719,9 +723,10 @@ void SCH_EDIT_FRAME::SelectAllFromSheet( wxCommandEvent& aEvent ) void SCH_EDIT_FRAME::OnRotate( wxCommandEvent& aEvent ) { - SCH_SCREEN* screen = GetScreen(); - SCH_ITEM* item = screen->GetCurItem(); - BLOCK_SELECTOR& block = screen->m_BlockLocate; + SCH_SELECTION_TOOL* selTool = GetToolManager()->GetTool(); + SCH_SCREEN* screen = GetScreen(); + SCH_ITEM* item = screen->GetCurItem(); + BLOCK_SELECTOR& block = screen->m_BlockLocate; // Allows block rotate operation on hot key. if( block.GetState() != STATE_NO_BLOCK ) @@ -752,8 +757,7 @@ void SCH_EDIT_FRAME::OnRotate( wxCommandEvent& aEvent ) wxCHECK_RET( data != NULL, wxT( "Invalid hot key client object." ) ); - item = LocateAndShowItem( data->GetPosition(), SCH_COLLECTOR::RotatableItems, - aEvent.GetInt() ); + item = selTool->SelectPoint( data->GetPosition(), SCH_COLLECTOR::RotatableItems ); // Exit if no item found at the current location or the item is already being edited. if( item == NULL || item->GetEditFlags() != 0 ) @@ -852,8 +856,9 @@ void SCH_EDIT_FRAME::OnRotate( wxCommandEvent& aEvent ) void SCH_EDIT_FRAME::OnEditItem( wxCommandEvent& aEvent ) { - SCH_SCREEN* screen = GetScreen(); - SCH_ITEM* item = screen->GetCurItem(); + SCH_SELECTION_TOOL* selTool = GetToolManager()->GetTool(); + SCH_SCREEN* screen = GetScreen(); + SCH_ITEM* item = screen->GetCurItem(); if( item == NULL ) { @@ -894,11 +899,11 @@ void SCH_EDIT_FRAME::OnEditItem( wxCommandEvent& aEvent ) break; } - item = LocateAndShowItem( data->GetPosition(), filterList, aEvent.GetInt() ); + item = selTool->SelectPoint( data->GetPosition(), filterList ); // If no item found, and if an auxiliary filter exists, try to use it if( !item && filterListAux ) - item = LocateAndShowItem( data->GetPosition(), filterListAux, aEvent.GetInt() ); + item = selTool->SelectPoint( data->GetPosition(), filterListAux ); // Exit if no item found at the current location or the item is already being edited. if( item == NULL || item->GetEditFlags() != 0 ) @@ -1014,8 +1019,9 @@ void SCH_EDIT_FRAME::OnEditItem( wxCommandEvent& aEvent ) void SCH_EDIT_FRAME::OnDragItem( wxCommandEvent& aEvent ) { - SCH_SCREEN* screen = GetScreen(); - SCH_ITEM* item = screen->GetCurItem(); + SCH_SELECTION_TOOL* selTool = GetToolManager()->GetTool(); + SCH_SCREEN* screen = GetScreen(); + SCH_ITEM* item = screen->GetCurItem(); // The easiest way to handle a menu or a hot key drag command // is to simulate a block drag command @@ -1036,8 +1042,7 @@ void SCH_EDIT_FRAME::OnDragItem( wxCommandEvent& aEvent ) wxCHECK_RET( data != NULL, wxT( "Invalid hot key client object." ) ); - item = LocateAndShowItem( data->GetPosition(), SCH_COLLECTOR::DraggableItems, - aEvent.GetInt() ); + item = selTool->SelectPoint( data->GetPosition(), SCH_COLLECTOR::DraggableItems ); // Exit if no item found at the current location or the item is already being edited. if( item == NULL || item->GetEditFlags() != 0 ) @@ -1086,9 +1091,10 @@ void SCH_EDIT_FRAME::OnDragItem( wxCommandEvent& aEvent ) void SCH_EDIT_FRAME::OnOrient( wxCommandEvent& aEvent ) { - SCH_SCREEN* screen = GetScreen(); - SCH_ITEM* item = screen->GetCurItem(); - BLOCK_SELECTOR& block = screen->m_BlockLocate; + SCH_SELECTION_TOOL* selTool = GetToolManager()->GetTool(); + SCH_SCREEN* screen = GetScreen(); + SCH_ITEM* item = screen->GetCurItem(); + BLOCK_SELECTOR& block = screen->m_BlockLocate; // Allows block rotate operation on hot key. if( block.GetState() != STATE_NO_BLOCK ) @@ -1149,8 +1155,7 @@ void SCH_EDIT_FRAME::OnOrient( wxCommandEvent& aEvent ) wxCHECK_RET( data != NULL, wxT( "Invalid hot key client object." ) ); - item = LocateAndShowItem( data->GetPosition(), SCH_COLLECTOR::OrientableItems, - aEvent.GetInt() ); + item = selTool->SelectPoint( data->GetPosition(), SCH_COLLECTOR::OrientableItems ); // Exit if no item found at the current location or the item is already being edited. if( item == NULL || item->GetEditFlags() != 0 ) @@ -1231,8 +1236,9 @@ void SCH_EDIT_FRAME::OnOrient( wxCommandEvent& aEvent ) void SCH_EDIT_FRAME::OnUnfoldBusHotkey( wxCommandEvent& aEvent ) { - auto data = (EDA_HOTKEY_CLIENT_DATA*) aEvent.GetClientObject(); - auto item = GetScreen()->GetCurItem(); + 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." ) ); @@ -1242,8 +1248,7 @@ void SCH_EDIT_FRAME::OnUnfoldBusHotkey( wxCommandEvent& aEvent ) if( aEvent.GetInt() == 0 ) return; - item = LocateAndShowItem( data->GetPosition(), SCH_COLLECTOR::EditableItems, - aEvent.GetInt() ); + 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 ) diff --git a/eeschema/tools/sch_actions.cpp b/eeschema/tools/sch_actions.cpp index 9d14e036c7..f0cb66a5aa 100644 --- a/eeschema/tools/sch_actions.cpp +++ b/eeschema/tools/sch_actions.cpp @@ -28,8 +28,8 @@ #include #include #include - -#include +#include +#include #include OPT SCH_ACTIONS::TranslateLegacyId( int aId ) @@ -180,6 +180,7 @@ void SCH_ACTIONS::RegisterAllTools( TOOL_MANAGER* aToolManager ) { aToolManager->RegisterTool( new COMMON_TOOLS ); aToolManager->RegisterTool( new ZOOM_TOOL ); + aToolManager->RegisterTool( new SCH_SELECTION_TOOL ); aToolManager->RegisterTool( new SCH_EDITOR_CONTROL ); aToolManager->RegisterTool( new SCH_PICKER_TOOL ); aToolManager->RegisterTool( new SCH_DRAWING_TOOL ); diff --git a/eeschema/tools/sch_actions.h b/eeschema/tools/sch_actions.h index 8e7d19aaf3..16c93f8c43 100644 --- a/eeschema/tools/sch_actions.h +++ b/eeschema/tools/sch_actions.h @@ -112,6 +112,10 @@ public: static TOOL_ACTION finishDrawing; // Editing + static TOOL_ACTION editActivate; + static TOOL_ACTION move; + static TOOL_ACTION duplicate; + static TOOL_ACTION rotate; static TOOL_ACTION properties; static TOOL_ACTION addJunction; static TOOL_ACTION addLabel; @@ -131,6 +135,7 @@ public: // Net highlighting static TOOL_ACTION highlightNet; + static TOOL_ACTION clearHighlight; static TOOL_ACTION highlightNetSelection; static TOOL_ACTION highlightNetCursor; diff --git a/eeschema/tools/sch_drawing_tool.cpp b/eeschema/tools/sch_drawing_tool.cpp index 74e801361b..2e32c6275d 100644 --- a/eeschema/tools/sch_drawing_tool.cpp +++ b/eeschema/tools/sch_drawing_tool.cpp @@ -22,6 +22,7 @@ */ #include "sch_drawing_tool.h" +#include "sch_selection_tool.h" #include #include @@ -513,6 +514,7 @@ int SCH_DRAWING_TOOL::PlaceImage( const TOOL_EVENT& aEvent ) int SCH_DRAWING_TOOL::doTwoClickPlace( KICAD_T aType ) { + SCH_SELECTION_TOOL* selTool = m_toolMgr->GetTool(); VECTOR2I cursorPos = m_controls->GetCursorPosition(); SCH_ITEM* item = nullptr; @@ -569,8 +571,7 @@ int SCH_DRAWING_TOOL::doTwoClickPlace( KICAD_T aType ) item = m_frame->CreateNewImage(); break; case SCH_SHEET_PIN_T: - item = m_frame->LocateAndShowItem( (wxPoint)cursorPos, - SCH_COLLECTOR::SheetsAndSheetLabels ); + item = selTool->SelectPoint( cursorPos, SCH_COLLECTOR::SheetsAndSheetLabels ); if( item ) { if( m_frame->GetToolId() == ID_IMPORT_HLABEL_BUTT ) diff --git a/eeschema/tools/sch_edit_tool.cpp b/eeschema/tools/sch_edit_tool.cpp new file mode 100644 index 0000000000..f3368cf6cd --- /dev/null +++ b/eeschema/tools/sch_edit_tool.cpp @@ -0,0 +1,50 @@ +/* + * 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 "sch_edit_tool.h" +#include +#include +#include + + +TOOL_ACTION SCH_ACTIONS::editActivate( "eeschema.InteractiveEdit", + AS_GLOBAL, 0, + _( "Edit Activate" ), "", move_xpm, AF_ACTIVATE ); + +TOOL_ACTION SCH_ACTIONS::move( "eeschema.InteractiveEdit.move", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_MOVE_COMPONENT_OR_ITEM ), + _( "Move" ), _( "Moves the selected item(s)" ), move_xpm, AF_ACTIVATE ); + +TOOL_ACTION SCH_ACTIONS::duplicate( "eeschema.InteractiveEdit.duplicate", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_DUPLICATE_ITEM ), + _( "Duplicate" ), _( "Duplicates the selected item(s)" ), duplicate_xpm ); + +TOOL_ACTION SCH_ACTIONS::rotate( "eeschema.InteractiveEdit.rotate", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_ROTATE ), + _( "Rotate" ), _( "Rotates selected item(s)" ), + rotate_ccw_xpm, AF_NONE ); + +TOOL_ACTION SCH_ACTIONS::properties( "eeschema.InteractiveEdit.properties", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_EDIT ), + _( "Properties..." ), _( "Displays item properties dialog" ), config_xpm ); + diff --git a/eeschema/tools/sch_edit_tool.h b/eeschema/tools/sch_edit_tool.h new file mode 100644 index 0000000000..aedeb9c011 --- /dev/null +++ b/eeschema/tools/sch_edit_tool.h @@ -0,0 +1,34 @@ +/* + * 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 KICAD_SCH_EDIT_TOOL_H +#define KICAD_SCH_EDIT_TOOL_H + +#include + + +class SCH_EDIT_TOOL : public TOOL_INTERACTIVE +{ +}; + +#endif //KICAD_SCH_EDIT_TOOL_H diff --git a/eeschema/tools/sch_editor_control.cpp b/eeschema/tools/sch_editor_control.cpp index eed7e5f9a7..b9b83212d6 100644 --- a/eeschema/tools/sch_editor_control.cpp +++ b/eeschema/tools/sch_editor_control.cpp @@ -34,8 +34,9 @@ #include #include #include -#include #include +#include +#include #include #include @@ -45,6 +46,9 @@ TOOL_ACTION SCH_ACTIONS::refreshPreview( "eeschema.EditorControl.refreshPreview" TOOL_ACTION SCH_ACTIONS::highlightNet( "eeschema.EditorControl.highlightNet", AS_GLOBAL, 0, "", "" ); +TOOL_ACTION SCH_ACTIONS::clearHighlight( "eeschema.EditorControl.clearHighlight", + AS_GLOBAL, 0, "", "" ); + TOOL_ACTION SCH_ACTIONS::highlightNetSelection( "eeschema.EditorControl.highlightNetSelection", AS_GLOBAL, 0, "", "" ); @@ -115,6 +119,59 @@ bool SCH_EDITOR_CONTROL::Init() } +int SCH_EDITOR_CONTROL::CrossProbeSchToPcb( const TOOL_EVENT& aEvent ) +{ + // Don't get in an infinite loop SCH -> PCB -> SCH -> PCB -> SCH -> ... + if( m_probingSchToPcb ) + { + m_probingSchToPcb = false; + return 0; + } + + SCH_SELECTION_TOOL* selTool = m_toolMgr->GetTool(); + const SELECTION& selection = selTool->GetSelection(); + + if( selection.Size() == 1 ) + { + SCH_ITEM* item = static_cast( selection.Front() ); + SCH_COMPONENT* component; + + switch( item->Type() ) + { + case SCH_FIELD_T: + case LIB_FIELD_T: + component = (SCH_COMPONENT*) item->GetParent(); + m_frame->SendMessageToPCBNEW( item, component ); + break; + + case SCH_COMPONENT_T: + component = (SCH_COMPONENT*) item; + m_frame->SendMessageToPCBNEW( item, component ); + break; + + case SCH_PIN_T: + component = (SCH_COMPONENT*) item->GetParent(); + m_frame->SendMessageToPCBNEW( static_cast( item ), component ); + break; + +#if 0 // This is too slow on larger projects + case SCH_SHEET_T: + SendMessageToPCBNEW( item, nullptr ); + break; +#endif + default: + ; + } + } + + return 0; +} + + +// A magic cookie token for clearing the highlight +static VECTOR2D CLEAR; + + // TODO(JE) Probably use netcode rather than connection name here eventually static bool highlightNet( TOOL_MANAGER* aToolMgr, const VECTOR2D& aPosition ) { @@ -123,7 +180,7 @@ static bool highlightNet( TOOL_MANAGER* aToolMgr, const VECTOR2D& aPosition ) EDA_ITEMS nodeList; bool retVal = true; - if( editFrame->GetScreen()->GetNode( wxPoint( aPosition.x, aPosition.y ), nodeList ) ) + if( aPosition != CLEAR && editFrame->GetScreen()->GetNode( (wxPoint) aPosition, nodeList ) ) { if( TestDuplicateSheetNames( false ) > 0 ) { @@ -156,7 +213,7 @@ static bool highlightNet( TOOL_MANAGER* aToolMgr, const VECTOR2D& aPosition ) int SCH_EDITOR_CONTROL::HighlightNet( const TOOL_EVENT& aEvent ) { KIGFX::VIEW_CONTROLS* controls = getViewControls(); - VECTOR2I gridPosition = controls->GetCursorPosition( true ); + VECTOR2D gridPosition = controls->GetCursorPosition( true ); highlightNet( m_toolMgr, gridPosition ); @@ -164,6 +221,14 @@ int SCH_EDITOR_CONTROL::HighlightNet( const TOOL_EVENT& aEvent ) } +int SCH_EDITOR_CONTROL::ClearHighlight( const TOOL_EVENT& aEvent ) +{ + highlightNet( m_toolMgr, CLEAR ); + + return 0; +} + + int SCH_EDITOR_CONTROL::HighlightNetSelection( const TOOL_EVENT& aEvent ) { SCH_SCREEN* screen = g_CurrentSheet->LastScreen(); @@ -269,14 +334,15 @@ void SCH_EDITOR_CONTROL::setTransitions() Go( &SCH_EDITOR_CONTROL::UnlockSelected, SCH_ACTIONS::unlock.MakeEvent() ); */ + Go( &SCH_EDITOR_CONTROL::CrossProbeSchToPcb, EVENTS::SelectedEvent ); + Go( &SCH_EDITOR_CONTROL::CrossProbeSchToPcb, EVENTS::UnselectedEvent ); + Go( &SCH_EDITOR_CONTROL::CrossProbeSchToPcb, EVENTS::ClearedEvent ); /* - Go( &SCH_EDITOR_CONTROL::CrossProbeSchToPcb, SELECTION_TOOL::SelectedEvent ); - Go( &SCH_EDITOR_CONTROL::CrossProbeSchToPcb, SELECTION_TOOL::UnselectedEvent ); - Go( &SCH_EDITOR_CONTROL::CrossProbeSchToPcb, SELECTION_TOOL::ClearedEvent ); Go( &SCH_EDITOR_CONTROL::CrossProbePcbToSch, SCH_ACTIONS::crossProbeSchToPcb.MakeEvent() ); */ Go( &SCH_EDITOR_CONTROL::HighlightNet, SCH_ACTIONS::highlightNet.MakeEvent() ); + Go( &SCH_EDITOR_CONTROL::ClearHighlight, SCH_ACTIONS::clearHighlight.MakeEvent() ); Go( &SCH_EDITOR_CONTROL::HighlightNetCursor, SCH_ACTIONS::highlightNetCursor.MakeEvent() ); Go( &SCH_EDITOR_CONTROL::HighlightNetSelection, SCH_ACTIONS::highlightNetSelection.MakeEvent() ); } diff --git a/eeschema/tools/sch_editor_control.h b/eeschema/tools/sch_editor_control.h index 7800713fe7..623a76970e 100644 --- a/eeschema/tools/sch_editor_control.h +++ b/eeschema/tools/sch_editor_control.h @@ -62,6 +62,9 @@ public: ///> Highlights net under the cursor. int HighlightNet( const TOOL_EVENT& aEvent ); + ///> Removes any net highlighting + int ClearHighlight( const TOOL_EVENT& aEvent ); + ///> Highlights frame's SelectedNetName. int HighlightNetSelection( const TOOL_EVENT& aEvent ); @@ -73,8 +76,9 @@ private: ///> Sets up handlers for various events. void setTransitions() override; - ///> Pointer to the currently used edit frame. - SCH_EDIT_FRAME* m_frame; + SCH_EDIT_FRAME* m_frame; ///> Pointer to the currently used edit frame + + bool m_probingSchToPcb; ///> Recursion guard when cross-probing to PCBNew /// Menu model displayed by the tool. TOOL_MENU m_menu; diff --git a/eeschema/tools/sch_selection_tool.cpp b/eeschema/tools/sch_selection_tool.cpp index 39572500d0..d8c3464a34 100644 --- a/eeschema/tools/sch_selection_tool.cpp +++ b/eeschema/tools/sch_selection_tool.cpp @@ -23,34 +23,706 @@ #include - +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // Selection tool actions TOOL_ACTION SCH_ACTIONS::selectionActivate( "eeschema.InteractiveSelection", - AS_GLOBAL, 0, - "", "", NULL, AF_ACTIVATE ); // No description, it is not supposed to be shown anywhere + AS_GLOBAL, 0, "", "", NULL, AF_ACTIVATE ); // No description, not shown anywhere TOOL_ACTION SCH_ACTIONS::selectionCursor( "eeschema.InteractiveSelection.Cursor", - AS_GLOBAL, 0, - "", "" ); // No description, it is not supposed to be shown anywhere + AS_GLOBAL, 0, "", "" ); // No description, it is not supposed to be shown anywhere + +TOOL_ACTION SCH_ACTIONS::selectionMenu( "eeschema.InteractiveSelection.SelectionMenu", + AS_GLOBAL, 0, "", "" ); // No description, it is not supposed to be shown anywhere TOOL_ACTION SCH_ACTIONS::selectItem( "eeschema.InteractiveSelection.SelectItem", - AS_GLOBAL, 0, - "", "" ); // No description, it is not supposed to be shown anywhere + AS_GLOBAL, 0, "", "" ); // No description, it is not supposed to be shown anywhere TOOL_ACTION SCH_ACTIONS::selectItems( "eeschema.InteractiveSelection.SelectItems", - AS_GLOBAL, 0, - "", "" ); // No description, it is not supposed to be shown anywhere + AS_GLOBAL, 0, "", "" ); // No description, it is not supposed to be shown anywhere TOOL_ACTION SCH_ACTIONS::unselectItem( "eeschema.InteractiveSelection.UnselectItem", - AS_GLOBAL, 0, - "", "" ); // No description, it is not supposed to be shown anywhere + AS_GLOBAL, 0, "", "" ); // No description, it is not supposed to be shown anywhere TOOL_ACTION SCH_ACTIONS::unselectItems( "eeschema.InteractiveSelection.UnselectItems", - AS_GLOBAL, 0, - "", "" ); // No description, it is not supposed to be shown anywhere + AS_GLOBAL, 0, "", "" ); // No description, it is not supposed to be shown anywhere TOOL_ACTION SCH_ACTIONS::selectionClear( "eeschema.InteractiveSelection.Clear", - AS_GLOBAL, 0, - "", "" ); // No description, it is not supposed to be shown anywhere + AS_GLOBAL, 0, "", "" ); // No description, it is not supposed to be shown anywhere + + +SCH_SELECTION_TOOL::SCH_SELECTION_TOOL() : + TOOL_INTERACTIVE( "eeschema.InteractiveSelection" ), + m_frame( NULL ), + m_additive( false ), + m_subtractive( false ), + m_multiple( false ), + m_skip_heuristics( false ), + m_locked( true ), + m_menu( *this ) +{ +} + + +SCH_SELECTION_TOOL::~SCH_SELECTION_TOOL() +{ + getView()->Remove( &m_selection ); +} + + +bool SCH_SELECTION_TOOL::Init() +{ + auto frame = getEditFrame(); + + if( frame ) + m_menu.AddStandardSubMenus( *frame ); + + return true; +} + + +void SCH_SELECTION_TOOL::Reset( RESET_REASON aReason ) +{ + m_frame = getEditFrame(); + m_locked = 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 sheet 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 ); + getView()->Add( &m_selection ); +} + + +int SCH_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 ); + + // Should selected items be REMOVED from the current selection? + // This will be ignored if the SHIFT modifier is pressed + m_subtractive = !m_additive && evt->Modifier( MD_CTRL ); + + // Is the user requesting that the selection list include all possible + // items without removing less likely selection candidates + m_skip_heuristics = !!evt->Modifier( MD_ALT ); + + // Single click? Select single object + if( evt->IsClick( BUT_LEFT ) ) + { + if( evt->Modifier( MD_CTRL ) && dynamic_cast( m_frame ) ) + { + m_toolMgr->RunAction( SCH_ACTIONS::highlightNet, true ); + } + else + { + // If no modifier keys are pressed, clear the selection + 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 selectionCancelled = false; + + if( m_selection.Empty() ) + { + SelectPoint( evt->Position(), SCH_COLLECTOR::AllItems, &selectionCancelled ); + m_selection.SetIsHover( true ); + } + + if( !selectionCancelled ) + m_menu.ShowContextMenu( m_selection ); + } + + // double click? Display the properties window + else if( evt->IsDblClick( BUT_LEFT ) ) + { + if( m_selection.Empty() ) + SelectPoint( evt->Position()); + + m_toolMgr->RunAction( SCH_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_subtractive || m_selection.Empty() ) + { + // JEY TODO: move block selection to SCH_SELECTION_TOOL + //selectMultiple(); + } + + 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( "eeschema.InteractiveEdit" ); + } + else + { + // No -> clear the selection list + clearSelection(); + } + } + } + + else if( evt->IsCancel() || evt->Action() == TA_UNDO_REDO_PRE ) + { + clearSelection(); + + if( evt->IsCancel() && dynamic_cast( m_frame ) ) + m_toolMgr->RunAction( SCH_ACTIONS::clearHighlight, true ); + } + + else if( evt->Action() == TA_CONTEXT_MENU_CLOSED ) + { + m_menu.CloseContextMenu( evt ); + } + } + + // This tool is supposed to be active forever + assert( false ); + + return 0; +} + + +SELECTION& SCH_SELECTION_TOOL::GetSelection() +{ + return m_selection; +} + + +SCH_ITEM* SCH_SELECTION_TOOL::SelectPoint( const VECTOR2I& aWhere, const KICAD_T* aFilterList, + bool* aSelectionCancelledFlag, bool aCheckLocked ) +{ + SCH_COLLECTOR collector; + + collector.Collect( m_frame->GetScreen()->GetDrawItems(), aFilterList, (wxPoint) aWhere ); + + bool anyCollected = collector.GetCount() != 0; + + // Remove unselectable items + for( int i = collector.GetCount() - 1; i >= 0; --i ) + { + if( !selectable( collector[ i ] ) ) + collector.Remove( i ); + + if( aCheckLocked && collector[ i ]->IsLocked() ) + collector.Remove( i ); + } + + m_selection.ClearReferencePoint(); + + // Apply some ugly heuristics to avoid disambiguation menus whenever possible + if( collector.GetCount() > 1 && !m_skip_heuristics ) + { + guessSelectionCandidates( collector, aWhere ); + } + + // If still more than one item we're going to have to ask the user. + if( collector.GetCount() > 1 ) + { + if( !doSelectionMenu( &collector, _( "Clarify Selection" ) ) ) + { + if( aSelectionCancelledFlag ) + *aSelectionCancelledFlag = true; + + m_frame->GetScreen()->SetCurItem( nullptr ); + return nullptr; + } + } + + if( collector.GetCount() == 1 ) + { + SCH_ITEM* item = collector[ 0 ]; + + toggleSelection( item ); + + MSG_PANEL_ITEMS msgItems; + item->GetMsgPanelInfo( m_frame->GetUserUnits(), msgItems ); + m_frame->SetMsgPanel( msgItems ); + + return item; + } + + if( !m_additive && anyCollected ) + clearSelection(); + + m_frame->ClearMsgPanel(); + + return nullptr; +} + + +void SCH_SELECTION_TOOL::guessSelectionCandidates( SCH_COLLECTOR& collector, + const VECTOR2I& aWhere ) +{ + // There are certain parent/child and enclosure combinations that can be handled + // automatically. Since schematics are meant to be human-readable we don't have + // all the various overlap and coverage issues that we do in Pcbnew. + if( collector.GetCount() == 2 ) + { + SCH_ITEM* a = collector[ 0 ]; + SCH_ITEM* b = collector[ 1 ]; + + if( a->GetParent() == b ) + collector.Remove( b ); + else if( a == b->GetParent() ) + collector.Remove( a ); + else if( a->Type() == SCH_SHEET_T && b->Type() != SCH_SHEET_T ) + collector.Remove( a ); + else if( b->Type() == SCH_SHEET_T && a->Type() != SCH_SHEET_T ) + collector.Remove( b ); + } +} + + +bool SCH_SELECTION_TOOL::selectCursor( const KICAD_T aFilterList[], bool aForceSelect ) +{ + if( aForceSelect || m_selection.Empty() ) + { + VECTOR2D cursorPos = getViewControls()->GetCursorPosition( false ); + + clearSelection(); + SelectPoint( cursorPos, aFilterList ); + } + + return !m_selection.Empty(); +} + + +int SCH_SELECTION_TOOL::SelectItems( const TOOL_EVENT& aEvent ) +{ + std::vector* items = aEvent.Parameter*>(); + + if( items ) + { + // Perform individual selection of each item before processing the event. + for( auto item : *items ) + select( item ); + + m_toolMgr->ProcessEvent( EVENTS::SelectedEvent ); + } + + return 0; +} + + +int SCH_SELECTION_TOOL::SelectItem( const TOOL_EVENT& aEvent ) +{ + // Check if there is an item to be selected + SCH_ITEM* item = aEvent.Parameter(); + + if( item ) + { + select( item ); + + // Inform other potentially interested tools + m_toolMgr->ProcessEvent( EVENTS::SelectedEvent ); + } + + return 0; +} + + +int SCH_SELECTION_TOOL::UnselectItems( const TOOL_EVENT& aEvent ) +{ + std::vector* items = aEvent.Parameter*>(); + + if( items ) + { + // Perform individual unselection of each item before processing the event + for( auto item : *items ) + unselect( item ); + + m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent ); + } + + return 0; +} + + +int SCH_SELECTION_TOOL::UnselectItem( const TOOL_EVENT& aEvent ) +{ + // Check if there is an item to be selected + SCH_ITEM* item = aEvent.Parameter(); + + if( item ) + { + unselect( item ); + + // Inform other potentially interested tools + m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent ); + } + + return 0; +} + + +int SCH_SELECTION_TOOL::ClearSelection( const TOOL_EVENT& aEvent ) +{ + clearSelection(); + + return 0; +} + + +int SCH_SELECTION_TOOL::SelectionMenu( const TOOL_EVENT& aEvent ) +{ + SCH_COLLECTOR* collector = aEvent.Parameter(); + doSelectionMenu( collector, wxEmptyString ); + + return 0; +} + + +bool SCH_SELECTION_TOOL::doSelectionMenu( SCH_COLLECTOR* aCollector, const wxString& aTitle ) +{ + SCH_ITEM* current = nullptr; + CONTEXT_MENU menu; + + int limit = std::min( MAX_SELECT_ITEM_IDS, aCollector->GetCount() ); + + for( int i = 0; i < limit; ++i ) + { + wxString text; + SCH_ITEM* item = ( *aCollector )[i]; + text = item->GetSelectMenuText( m_frame->GetUserUnits() ); + + wxString menuText = wxString::Format("&%d. %s", i + 1, text ); + menu.Add( menuText, i + 1, item->GetMenuImage() ); + } + + if( aTitle.Length() ) + menu.SetTitle( aTitle ); + + menu.SetIcon( info_xpm ); + menu.DisplayTitle( true ); + SetContextMenu( &menu, CMENU_NOW ); + + while( OPT_TOOL_EVENT evt = Wait() ) + { + if( evt->Action() == TA_CONTEXT_MENU_UPDATE ) + { + if( current ) + unhighlight( current, BRIGHTENED ); + + 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]; + highlight( current, BRIGHTENED ); + } + else + { + current = NULL; + } + } + else if( evt->Action() == TA_CONTEXT_MENU_CHOICE ) + { + if( current ) + unhighlight( current, BRIGHTENED ); + + OPT 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; + } + } + + if( current ) + { + unhighlight( current, BRIGHTENED ); + + toggleSelection( current ); + + aCollector->Empty(); + aCollector->Append( current ); + return true; + } + + return false; +} + + +bool SCH_SELECTION_TOOL::selectable( const SCH_ITEM* aItem, bool checkVisibilityOnly ) const +{ + // NOTE: in the future this is where eeschema layer/itemtype visibility will be handled + + switch( aItem->Type() ) + { + case SCH_PIN_T: + if( !static_cast( aItem )->IsVisible() && !m_frame->GetShowAllPins() ) + return false; + break; + + case LIB_PART_T: // In libedit we do not want to select the symbol itself. + return false; + + case SCH_MARKER_T: // Always selectable + return true; + + default: // Suppress warnings + break; + } + + return true; +} + + +void SCH_SELECTION_TOOL::clearSelection() +{ + if( m_selection.Empty() ) + return; + + while( m_selection.GetSize() ) + unhighlight( static_cast( m_selection.Front() ), SELECTED, &m_selection ); + + getView()->Update( &m_selection ); + + m_selection.SetIsHover( false ); + m_selection.ClearReferencePoint(); + + if( m_frame ) + m_frame->GetScreen()->SetCurItem( nullptr ); + + m_locked = true; + + // Inform other potentially interested tools + m_toolMgr->ProcessEvent( EVENTS::ClearedEvent ); +} + + +void SCH_SELECTION_TOOL::toggleSelection( SCH_ITEM* aItem, bool aForce ) +{ + if( aItem->IsSelected() ) + { + unselect( aItem ); + + // Inform other potentially interested tools + m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent ); + } + else + { + if( !m_additive ) + clearSelection(); + + // Prevent selection of invisible or inactive items + if( aForce || selectable( aItem ) ) + { + select( aItem ); + + // Inform other potentially interested tools + m_toolMgr->ProcessEvent( EVENTS::SelectedEvent ); + } + } + + if( m_frame ) + m_frame->GetGalCanvas()->ForceRefresh(); +} + + +void SCH_SELECTION_TOOL::select( SCH_ITEM* aItem ) +{ + if( aItem->IsSelected() ) + return; + + highlight( aItem, SELECTED, &m_selection ); + getView()->Update( &m_selection ); + + if( m_frame ) + { + if( m_selection.Size() == 1 ) + { + // Set as the current item, so the information about selection is displayed + m_frame->GetScreen()->SetCurItem( aItem ); + } + 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->GetScreen()->SetCurItem( nullptr ); + } + } +} + + +void SCH_SELECTION_TOOL::unselect( SCH_ITEM* aItem ) +{ + unhighlight( aItem, SELECTED, &m_selection ); + getView()->Update( &m_selection ); + + if( m_frame && m_frame->GetScreen()->GetCurItem() == aItem ) + m_frame->GetScreen()->SetCurItem( nullptr ); + + if( m_selection.Empty() ) + m_locked = true; +} + + +void SCH_SELECTION_TOOL::highlight( SCH_ITEM* aItem, int aMode, SELECTION* aGroup ) +{ + if( aMode == SELECTED ) + aItem->SetSelected(); + else if( aMode == BRIGHTENED ) + aItem->SetBrightened(); + + if( aGroup ) + aGroup->Add( aItem ); + + // Highlight pins and fields. (All the other component children are currently only + // represented in the LIB_PART.) + if( aItem->Type() == SCH_COMPONENT_T ) + { + SCH_PINS pins = static_cast( aItem )->GetPins(); + + for( SCH_PIN& pin : pins ) + { + if( aMode == SELECTED ) + pin.SetSelected(); + else if( aMode == BRIGHTENED ) + pin.SetBrightened(); + } + + std::vector fields; + static_cast( aItem )->GetFields( fields, false ); + + for( auto field : fields ) + { + if( aMode == SELECTED ) + field->SetSelected(); + else if( aMode == BRIGHTENED ) + field->SetBrightened(); + + // JEY TODO: do these need hiding from view and adding to aGroup? + } + } + + // JEY TODO: Sheets and sheet pins? + + // Many selections are very temporal and updating the display each time just + // creates noise. + if( aMode == BRIGHTENED ) + getView()->MarkTargetDirty( KIGFX::TARGET_OVERLAY ); +} + + +void SCH_SELECTION_TOOL::unhighlight( SCH_ITEM* aItem, int aMode, SELECTION* aGroup ) +{ + if( aMode == SELECTED ) + aItem->ClearSelected(); + else if( aMode == BRIGHTENED ) + aItem->ClearBrightened(); + + if( aGroup ) + aGroup->Remove( aItem ); + + // Unhighlight pins and fields. (All the other component children are currently only + // represented in the LIB_PART.) + if( aItem->Type() == SCH_COMPONENT_T ) + { + SCH_PINS pins = static_cast( aItem )->GetPins(); + + for( SCH_PIN& pin : pins ) + { + if( aMode == SELECTED ) + pin.ClearSelected(); + else if( aMode == BRIGHTENED ) + pin.ClearBrightened(); + } + + std::vector fields; + static_cast( aItem )->GetFields( fields, false ); + + for( auto field : fields ) + { + if( aMode == SELECTED ) + field->ClearSelected(); + else if( aMode == BRIGHTENED ) + field->ClearBrightened(); + + // JEY TODO: do these need showing and updating? + } + } + + // JEY TODO: Sheets and sheet pins? + + // Many selections are very temporal and updating the display each time just + // creates noise. + if( aMode == BRIGHTENED ) + getView()->MarkTargetDirty( KIGFX::TARGET_OVERLAY ); +} + + +bool SCH_SELECTION_TOOL::selectionContains( const VECTOR2I& aPoint ) const +{ + const unsigned GRIP_MARGIN = 20; + VECTOR2I margin = getView()->ToWorld( VECTOR2I( GRIP_MARGIN, GRIP_MARGIN ), false ); + + // Check if the point is located within any of the currently selected items bounding boxes + for( auto item : m_selection ) + { + 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; +} + + +void SCH_SELECTION_TOOL::setTransitions() +{ + Go( &SCH_SELECTION_TOOL::Main, SCH_ACTIONS::selectionActivate.MakeEvent() ); + Go( &SCH_SELECTION_TOOL::ClearSelection, SCH_ACTIONS::selectionClear.MakeEvent() ); + Go( &SCH_SELECTION_TOOL::SelectItem, SCH_ACTIONS::selectItem.MakeEvent() ); + Go( &SCH_SELECTION_TOOL::SelectItems, SCH_ACTIONS::selectItems.MakeEvent() ); + Go( &SCH_SELECTION_TOOL::UnselectItem, SCH_ACTIONS::unselectItem.MakeEvent() ); + Go( &SCH_SELECTION_TOOL::UnselectItems, SCH_ACTIONS::unselectItems.MakeEvent() ); + Go( &SCH_SELECTION_TOOL::SelectionMenu, SCH_ACTIONS::selectionMenu.MakeEvent() ); +} + diff --git a/eeschema/tools/sch_selection_tool.h b/eeschema/tools/sch_selection_tool.h new file mode 100644 index 0000000000..4172d091b2 --- /dev/null +++ b/eeschema/tools/sch_selection_tool.h @@ -0,0 +1,212 @@ +/* + * 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 KICAD_SCH_SELECTION_TOOL_H +#define KICAD_SCH_SELECTION_TOOL_H + +#include +#include +#include +#include +#include + +class SCH_BASE_FRAME; +class SCH_ITEM; +class SCH_COLLECTOR; + +namespace KIGFX +{ + class GAL; +} + + +class SCH_SELECTION_TOOL : public TOOL_INTERACTIVE +{ +public: + SCH_SELECTION_TOOL(); + ~SCH_SELECTION_TOOL(); + + /// @copydoc TOOL_BASE::Init() + bool Init() override; + + /// @copydoc TOOL_BASE::Reset() + void Reset( RESET_REASON aReason ) override; + + /** + * Function Main() + * + * The main loop. + */ + int Main( const TOOL_EVENT& aEvent ); + + /** + * Function GetSelection() + * + * Returns the set of currently selected items. + */ + SELECTION& GetSelection(); + + /** + * 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 one to choose the item. + * + * @param aWhere is the place where the item should be selected. + * @param aSelectionCancelledFlag allows the function to inform its caller that a selection + * was cancelled (for instance, by clicking outside of the disambiguation menu). + * @param aCheckLocked indicates if locked items should be excluded + */ + SCH_ITEM* SelectPoint( const VECTOR2I& aWhere, + const KICAD_T* aFilterList = SCH_COLLECTOR::AllItems, + bool* aSelectionCancelledFlag = NULL, bool aCheckLocked = false ); + + ///> Item selection event handler. + int SelectItem( const TOOL_EVENT& aEvent ); + + ///> Multiple item selection event handler + int SelectItems( const TOOL_EVENT& aEvent ); + + ///> Item unselection event handler. + int UnselectItem( const TOOL_EVENT& aEvent ); + + ///> Multiple item unselection event handler + int UnselectItems( const TOOL_EVENT& aEvent ); + + ///> Clear current selection event handler. + int ClearSelection( const TOOL_EVENT& aEvent ); + + /** + * Function SelectionMenu() + * Allows the selection of a single item from a list of items via a popup menu. The + * list is passed as aEvent's parameter. + */ + int SelectionMenu( const TOOL_EVENT& aEvent ); + +private: + /** + * Function selectCursor() + * Selects an item under the cursor unless there is something already selected or aForceSelect + * is true. + * @param aForceSelect forces to select an item even if there is an item already selected. + * @param aClientFilter allows the client to perform tool- or action-specific filtering. + * @return true if eventually there is an item selected, false otherwise. + */ + bool selectCursor( const KICAD_T aFilterList[], bool aForceSelect = false ); + + /** + * Apply heuristics to try and determine a single object when multiple are found under the + * cursor. + */ + void guessSelectionCandidates( SCH_COLLECTOR& collector, const VECTOR2I& aWhere ); + + /** + * Allows the selection of a single item from a list via pop-up menu. The items are + * highlighted on the canvas when hovered in the menu. The collector is trimmed to + * the picked item. + * @param aTitle (optional) Allows the menu to be titled (ie: "Clarify Selection"). + * @return true if an item was picked + */ + bool doSelectionMenu( SCH_COLLECTOR* aItems, const wxString& aTitle ); + + /** + * Function clearSelection() + * Clears the current selection. + */ + void clearSelection(); + + /** + * Function toggleSelection() + * Changes selection status of a given item. + * + * @param aItem is the item to have selection status changed. + * @param aForce causes the toggle to happen without checking selectability + */ + void toggleSelection( SCH_ITEM* aItem, bool aForce = false ); + + /** + * Function selectable() + * Checks conditions for an item to be selected. + * + * @return True if the item fulfills conditions to be selected. + */ + bool selectable( const SCH_ITEM* aItem, bool checkVisibilityOnly = false ) const; + + /** + * Function select() + * Takes necessary action mark an item as selected. + * + * @param aItem is an item to be selected. + */ + void select( SCH_ITEM* aItem ); + + /** + * Function unselect() + * Takes necessary action mark an item as unselected. + * + * @param aItem is an item to be unselected. + */ + void unselect( SCH_ITEM* aItem ); + + /** + * Function highlight() + * Highlights the item visually. + * @param aItem is an item to be be highlighted. + * @param aHighlightMode should be either SELECTED or BRIGHTENED + * @param aGroup is the group to add the item to in the BRIGHTENED mode. + */ + void highlight( SCH_ITEM* aItem, int aHighlightMode, SELECTION* aGroup = nullptr ); + + /** + * Function unhighlight() + * Unhighlights the item visually. + * @param aItem is an item to be be highlighted. + * @param aHighlightMode should be either SELECTED or BRIGHTENED + * @param aGroup is the group to remove the item from. + */ + void unhighlight( SCH_ITEM* aItem, int aHighlightMode, SELECTION* aGroup = nullptr ); + + /** + * 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; + + ///> Sets up handlers for various events. + void setTransitions() override; + +private: + SCH_BASE_FRAME* m_frame; // Pointer to the parent frame + SELECTION m_selection; // Current state of selection + + bool m_additive; // Items should be added to selection (instead of replacing) + bool m_subtractive; // Items should be removed from selection + bool m_multiple; // Multiple selection mode is active + bool m_skip_heuristics; // Heuristics are not allowed when choosing item under cursor + bool m_locked; // Other tools are not allowed to modify locked items + + TOOL_MENU m_menu; +}; + +#endif //KICAD_SCH_SELECTION_TOOL_H diff --git a/gerbview/gerber_collectors.h b/gerbview/gerber_collectors.h index 53e2cbe024..b5061ec6eb 100644 --- a/gerbview/gerber_collectors.h +++ b/gerbview/gerber_collectors.h @@ -92,7 +92,7 @@ public: * @param ndx The index into the list. * @return EDA_ITEM* - or something derived from it, or NULL. */ - EDA_ITEM* operator[]( int ndx ) const + EDA_ITEM* operator[]( int ndx ) const override { if( (unsigned)ndx < (unsigned)GetCount() ) return (EDA_ITEM*) m_List[ ndx ]; diff --git a/gerbview/tools/selection_tool.cpp b/gerbview/tools/selection_tool.cpp index 1a0b49b310..d04bcf0b08 100644 --- a/gerbview/tools/selection_tool.cpp +++ b/gerbview/tools/selection_tool.cpp @@ -267,7 +267,7 @@ void GERBVIEW_SELECTION_TOOL::toggleSelection( EDA_ITEM* aItem ) unselect( aItem ); // Inform other potentially interested tools - m_toolMgr->ProcessEvent( UnselectedEvent ); + m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent ); } else { @@ -280,7 +280,7 @@ void GERBVIEW_SELECTION_TOOL::toggleSelection( EDA_ITEM* aItem ) select( aItem ); // Inform other potentially interested tools - m_toolMgr->ProcessEvent( SelectedEvent ); + m_toolMgr->ProcessEvent( EVENTS::SelectedEvent ); } } @@ -455,7 +455,7 @@ bool GERBVIEW_SELECTION_TOOL::selectMultiple() // Inform other potentially interested tools if( !m_selection.Empty() ) - m_toolMgr->ProcessEvent( SelectedEvent ); + m_toolMgr->ProcessEvent( EVENTS::SelectedEvent ); break; // Stop waiting for events } @@ -516,7 +516,7 @@ int GERBVIEW_SELECTION_TOOL::SelectItems( const TOOL_EVENT& aEvent ) select( item ); } - m_toolMgr->ProcessEvent( SelectedEvent ); + m_toolMgr->ProcessEvent( EVENTS::SelectedEvent ); } return 0; @@ -533,7 +533,7 @@ int GERBVIEW_SELECTION_TOOL::SelectItem( const TOOL_EVENT& aEvent ) select( item ); // Inform other potentially interested tools - m_toolMgr->ProcessEvent( SelectedEvent ); + m_toolMgr->ProcessEvent( EVENTS::SelectedEvent ); } return 0; @@ -553,7 +553,7 @@ int GERBVIEW_SELECTION_TOOL::UnselectItems( const TOOL_EVENT& aEvent ) unselect( item ); } - m_toolMgr->ProcessEvent( UnselectedEvent ); + m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent ); } return 0; @@ -570,7 +570,7 @@ int GERBVIEW_SELECTION_TOOL::UnselectItem( const TOOL_EVENT& aEvent ) unselect( item ); // Inform other potentially interested tools - m_toolMgr->ProcessEvent( UnselectedEvent ); + m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent ); } return 0; @@ -590,7 +590,7 @@ void GERBVIEW_SELECTION_TOOL::clearSelection() m_frame->SetCurItem( NULL ); // Inform other potentially interested tools - m_toolMgr->ProcessEvent( ClearedEvent ); + m_toolMgr->ProcessEvent( EVENTS::ClearedEvent ); } @@ -950,9 +950,3 @@ const KIGFX::VIEW_GROUP::ITEMS SELECTION::updateDrawList() const return items; } - - - -const TOOL_EVENT GERBVIEW_SELECTION_TOOL::SelectedEvent( TC_MESSAGE, TA_ACTION, "gerbview.InteractiveSelection.selected" ); -const TOOL_EVENT GERBVIEW_SELECTION_TOOL::UnselectedEvent( TC_MESSAGE, TA_ACTION, "gerbview.InteractiveSelection.unselected" ); -const TOOL_EVENT GERBVIEW_SELECTION_TOOL::ClearedEvent( TC_MESSAGE, TA_ACTION, "gerbview.InteractiveSelection.cleared" ); diff --git a/gerbview/tools/selection_tool.h b/gerbview/tools/selection_tool.h index bafd6248f5..b8b83fd7f6 100644 --- a/gerbview/tools/selection_tool.h +++ b/gerbview/tools/selection_tool.h @@ -98,15 +98,6 @@ public: ///> Launches a tool to measure between points int MeasureTool( 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() override; diff --git a/include/collector.h b/include/collector.h index 3872e08522..6ec895b67d 100644 --- a/include/collector.h +++ b/include/collector.h @@ -168,7 +168,7 @@ public: * @param aIndex The index into the list. * @return EDA_ITEM* - or something derived from it, or NULL. */ - EDA_ITEM* operator[]( int aIndex ) const + virtual EDA_ITEM* operator[]( int aIndex ) const { if( (unsigned)aIndex < (unsigned)GetCount() ) // (unsigned) excludes aIndex<0 also return m_List[ aIndex ]; diff --git a/include/tool/actions.h b/include/tool/actions.h index ffa7c64109..2ddf8ccf08 100644 --- a/include/tool/actions.h +++ b/include/tool/actions.h @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2013-2016 CERN - * Copyright (C) 2016 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2016-2019 KiCad Developers, see AUTHORS.txt for contributors. * @author Maciej Suminski * * This program is free software; you can redistribute it and/or @@ -108,4 +108,20 @@ public: enum class REMOVE_FLAGS { NORMAL = 0x00, ALT = 0x01, CUT = 0x02 }; }; -#endif + +/** + * Class EVENTS + * + * Gathers all the events that are shared by tools. + */ +class EVENTS +{ +public: + const static TOOL_EVENT SelectedEvent; + const static TOOL_EVENT UnselectedEvent; + const static TOOL_EVENT ClearedEvent; +}; + +#endif // __ACTIONS_H + + diff --git a/pcbnew/board_connected_item.h b/pcbnew/board_connected_item.h index dbba3d818c..fc013d2f39 100644 --- a/pcbnew/board_connected_item.h +++ b/pcbnew/board_connected_item.h @@ -286,7 +286,7 @@ public: return (BOARD_ITEM*) &myItems[aIndex]; } - BOARD_ITEM* operator[]( int aIndex ) const + BOARD_ITEM* operator[]( int aIndex ) const override { return At( aIndex ); } diff --git a/pcbnew/collectors.h b/pcbnew/collectors.h index c2b8ae18e6..b4f6d777c3 100644 --- a/pcbnew/collectors.h +++ b/pcbnew/collectors.h @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2007-2008 SoftPLC Corporation, Dick Hollenbeck - * Copyright (C) 2004-2018 KiCad Developers, see AUTHORS.txt for contributors. + * 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 @@ -22,10 +22,6 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ -/** - * @file collectors.h - */ - #ifndef COLLECTORS_H #define COLLECTORS_H @@ -38,10 +34,9 @@ #include #include // LAYER_COUNT, layer defs #include +#include -class BOARD_ITEM; - /** * An abstract base class whose derivatives may be passed to a GENERAL_COLLECTOR, @@ -223,7 +218,7 @@ public: * @param ndx The index into the list. * @return BOARD_ITEM* - or something derived from it, or NULL. */ - BOARD_ITEM* operator[]( int ndx ) const + BOARD_ITEM* operator[]( int ndx ) const override { if( (unsigned)ndx < (unsigned)GetCount() ) return (BOARD_ITEM*) m_List[ ndx ]; diff --git a/pcbnew/tools/pcb_editor_control.cpp b/pcbnew/tools/pcb_editor_control.cpp index 57450c1c18..c213da22d5 100644 --- a/pcbnew/tools/pcb_editor_control.cpp +++ b/pcbnew/tools/pcb_editor_control.cpp @@ -25,10 +25,11 @@ #include #include #include - +#include #include "pcb_editor_control.h" #include "pcb_actions.h" #include +#include #include #include "edit_tool.h" @@ -57,12 +58,8 @@ #include #include #include - #include -#include - -#include using namespace std::placeholders; @@ -836,6 +833,7 @@ int PCB_EDITOR_CONTROL::ZoneDuplicate( const TOOL_EVENT& aEvent ) int PCB_EDITOR_CONTROL::CrossProbePcbToSch( const TOOL_EVENT& aEvent ) { + // Don't get in an infinite loop PCB -> SCH -> PCB -> SCH -> ... if( m_probingSchToPcb ) { m_probingSchToPcb = false; @@ -1287,9 +1285,9 @@ void PCB_EDITOR_CONTROL::setTransitions() Go( &PCB_EDITOR_CONTROL::ToggleLockSelected, PCB_ACTIONS::toggleLock.MakeEvent() ); Go( &PCB_EDITOR_CONTROL::LockSelected, PCB_ACTIONS::lock.MakeEvent() ); Go( &PCB_EDITOR_CONTROL::UnlockSelected, PCB_ACTIONS::unlock.MakeEvent() ); - Go( &PCB_EDITOR_CONTROL::CrossProbePcbToSch, SELECTION_TOOL::SelectedEvent ); - Go( &PCB_EDITOR_CONTROL::CrossProbePcbToSch, SELECTION_TOOL::UnselectedEvent ); - Go( &PCB_EDITOR_CONTROL::CrossProbePcbToSch, SELECTION_TOOL::ClearedEvent ); + Go( &PCB_EDITOR_CONTROL::CrossProbePcbToSch, EVENTS::SelectedEvent ); + Go( &PCB_EDITOR_CONTROL::CrossProbePcbToSch, EVENTS::UnselectedEvent ); + Go( &PCB_EDITOR_CONTROL::CrossProbePcbToSch, EVENTS::ClearedEvent ); Go( &PCB_EDITOR_CONTROL::CrossProbeSchToPcb, PCB_ACTIONS::crossProbeSchToPcb.MakeEvent() ); Go( &PCB_EDITOR_CONTROL::DrillOrigin, PCB_ACTIONS::drillOrigin.MakeEvent() ); Go( &PCB_EDITOR_CONTROL::HighlightNet, PCB_ACTIONS::highlightNet.MakeEvent() ); diff --git a/pcbnew/tools/pcb_editor_control.h b/pcbnew/tools/pcb_editor_control.h index f93261957f..380cc64735 100644 --- a/pcbnew/tools/pcb_editor_control.h +++ b/pcbnew/tools/pcb_editor_control.h @@ -135,17 +135,11 @@ private: /// Menu model displayed by the tool. TOOL_MENU m_menu; - ///> Place & drill origin marker. - std::unique_ptr m_placeOrigin; + std::unique_ptr m_placeOrigin; ///> Place & drill origin marker - ///> Flag to ignore a single crossprobe message from eeschema. - bool m_probingSchToPcb; - - ///> Flag to indicate whether the current selection ratsnest is slow to calculate. - bool m_slowRatsnest; - - ///> Timer that start ratsnest calculation when it is slow to compute. - wxTimer m_ratsnestTimer; + bool m_probingSchToPcb; ///> Recursion guard when cross-probing to EESchema + bool m_slowRatsnest; ///> Indicates current selection ratsnest will be slow to calculate + wxTimer m_ratsnestTimer; ///> Timer to initiate lazy ratsnest calculation (ie: when slow) ///> How to modify a property for selected items. enum MODIFY_MODE { ON, OFF, TOGGLE }; diff --git a/pcbnew/tools/pcbnew_control.cpp b/pcbnew/tools/pcbnew_control.cpp index 609d28344b..6bdf10ebc5 100644 --- a/pcbnew/tools/pcbnew_control.cpp +++ b/pcbnew/tools/pcbnew_control.cpp @@ -899,7 +899,7 @@ int PCBNEW_CONTROL::placeBoardItems( std::vector& aItems, bool aIsN selection.SetReferencePoint( VECTOR2I( 0, 0 ) ); - m_toolMgr->ProcessEvent( SELECTION_TOOL::SelectedEvent ); + m_toolMgr->ProcessEvent( EVENTS::SelectedEvent ); m_toolMgr->RunAction( PCB_ACTIONS::move, true ); return 0; diff --git a/pcbnew/tools/point_editor.cpp b/pcbnew/tools/point_editor.cpp index 0b23083cba..05d7ba7a25 100644 --- a/pcbnew/tools/point_editor.cpp +++ b/pcbnew/tools/point_editor.cpp @@ -345,9 +345,9 @@ int POINT_EDITOR::OnSelectionChange( const TOOL_EVENT& aEvent ) controls->SetSnapping( !evt->Modifier( MD_ALT ) ); if( !m_editPoints || - evt->Matches( m_selectionTool->ClearedEvent ) || - evt->Matches( m_selectionTool->UnselectedEvent ) || - evt->Matches( m_selectionTool->SelectedEvent ) ) + evt->Matches( EVENTS::ClearedEvent ) || + evt->Matches( EVENTS::UnselectedEvent ) || + evt->Matches( EVENTS::SelectedEvent ) ) { break; } @@ -905,8 +905,8 @@ void POINT_EDITOR::setTransitions() Go( &POINT_EDITOR::addCorner, PCB_ACTIONS::pointEditorAddCorner.MakeEvent() ); Go( &POINT_EDITOR::removeCorner, PCB_ACTIONS::pointEditorRemoveCorner.MakeEvent() ); Go( &POINT_EDITOR::modifiedSelection, PCB_ACTIONS::selectionModified.MakeEvent() ); - Go( &POINT_EDITOR::OnSelectionChange, SELECTION_TOOL::SelectedEvent ); - Go( &POINT_EDITOR::OnSelectionChange, SELECTION_TOOL::UnselectedEvent ); + Go( &POINT_EDITOR::OnSelectionChange, EVENTS::SelectedEvent ); + Go( &POINT_EDITOR::OnSelectionChange, EVENTS::UnselectedEvent ); } diff --git a/pcbnew/tools/selection_tool.cpp b/pcbnew/tools/selection_tool.cpp index e3747bf408..16a68c8a7e 100644 --- a/pcbnew/tools/selection_tool.cpp +++ b/pcbnew/tools/selection_tool.cpp @@ -65,44 +65,40 @@ using namespace std::placeholders; // Selection tool actions TOOL_ACTION PCB_ACTIONS::selectionActivate( "pcbnew.InteractiveSelection", - AS_GLOBAL, 0, - "", "", NULL, AF_ACTIVATE ); // No description, it is not supposed to be shown anywhere + AS_GLOBAL, 0, "", "", NULL, AF_ACTIVATE ); // No description, not shown anywhere TOOL_ACTION PCB_ACTIONS::selectionCursor( "pcbnew.InteractiveSelection.Cursor", - AS_GLOBAL, 0, - "", "" ); // No description, it is not supposed to be shown anywhere + AS_GLOBAL, 0, "", "" ); // No description, it is not supposed to be shown anywhere TOOL_ACTION PCB_ACTIONS::selectItem( "pcbnew.InteractiveSelection.SelectItem", - AS_GLOBAL, 0, - "", "" ); // No description, it is not supposed to be shown anywhere + AS_GLOBAL, 0, "", "" ); // No description, it is not supposed to be shown anywhere TOOL_ACTION PCB_ACTIONS::selectItems( "pcbnew.InteractiveSelection.SelectItems", - AS_GLOBAL, 0, - "", "" ); // No description, it is not supposed to be shown anywhere + AS_GLOBAL, 0, "", "" ); // No description, it is not supposed to be shown anywhere TOOL_ACTION PCB_ACTIONS::unselectItem( "pcbnew.InteractiveSelection.UnselectItem", - AS_GLOBAL, 0, - "", "" ); // No description, it is not supposed to be shown anywhere + AS_GLOBAL, 0, "", "" ); // No description, it is not supposed to be shown anywhere TOOL_ACTION PCB_ACTIONS::unselectItems( "pcbnew.InteractiveSelection.UnselectItems", - AS_GLOBAL, 0, - "", "" ); // No description, it is not supposed to be shown anywhere + AS_GLOBAL, 0, "", "" ); // No description, it is not supposed to be shown anywhere TOOL_ACTION PCB_ACTIONS::selectionClear( "pcbnew.InteractiveSelection.Clear", - AS_GLOBAL, 0, - "", "" ); // No description, it is not supposed to be shown anywhere + AS_GLOBAL, 0, "", "" ); // No description, it is not supposed to be shown anywhere TOOL_ACTION PCB_ACTIONS::selectionMenu( "pcbnew.InteractiveSelection.SelectionMenu", - AS_GLOBAL, 0, - "", "" ); // No description, it is not supposed to be shown anywhere + AS_GLOBAL, 0, "", "" ); // No description, it is not supposed to be shown anywhere TOOL_ACTION PCB_ACTIONS::selectConnection( "pcbnew.InteractiveSelection.SelectConnection", AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_SEL_TRIVIAL_CONNECTION ), - _( "Single Track" ), _( "Selects all track segments & vias between two junctions." ), add_tracks_xpm ); + _( "Single Track" ), + _( "Selects all track segments & vias between two junctions." ), + add_tracks_xpm ); TOOL_ACTION PCB_ACTIONS::selectCopper( "pcbnew.InteractiveSelection.SelectCopper", AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_SEL_COPPER_CONNECTION ), - _( "Connected Tracks" ), _( "Selects all connected tracks & vias." ), net_highlight_xpm ); + _( "Connected Tracks" ), + _( "Selects all connected tracks & vias." ), + net_highlight_xpm ); TOOL_ACTION PCB_ACTIONS::expandSelectedConnection( "pcbnew.InteractiveSelection.ExpandConnection", AS_GLOBAL, 0, @@ -111,20 +107,27 @@ TOOL_ACTION PCB_ACTIONS::expandSelectedConnection( "pcbnew.InteractiveSelection. TOOL_ACTION PCB_ACTIONS::selectNet( "pcbnew.InteractiveSelection.SelectNet", AS_GLOBAL, 0, - _( "All Tracks in Net" ), _( "Selects all tracks & vias belonging to the same net." ), mode_track_xpm ); + _( "All Tracks in Net" ), + _( "Selects all tracks & vias belonging to the same net." ), + mode_track_xpm ); TOOL_ACTION PCB_ACTIONS::selectOnSheetFromEeschema( "pcbnew.InteractiveSelection.SelectOnSheet", AS_GLOBAL, 0, - _( "Sheet" ), _( "Selects all modules and tracks in the schematic sheet" ), select_same_sheet_xpm ); + _( "Sheet" ), + _( "Selects all modules and tracks in the schematic sheet" ), + select_same_sheet_xpm ); TOOL_ACTION PCB_ACTIONS::selectSameSheet( "pcbnew.InteractiveSelection.SelectSameSheet", AS_GLOBAL, 0, _( "Items in Same Hierarchical Sheet" ), - _( "Selects all modules and tracks in the same schematic sheet" ), select_same_sheet_xpm ); + _( "Selects all modules and tracks in the same schematic sheet" ), + select_same_sheet_xpm ); TOOL_ACTION PCB_ACTIONS::find( "pcbnew.InteractiveSelection.Find", AS_GLOBAL, 0, //TOOL_ACTION::LegacyHotKey( HK_FIND_ITEM ), // handled by wxWidgets - _( "Find Item..." ),_( "Searches the document for an item" ), find_xpm ); + _( "Find Item..." ), + _( "Searches the document for an item" ), + find_xpm ); TOOL_ACTION PCB_ACTIONS::findMove( "pcbnew.InteractiveSelection.FindMove", AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_GET_AND_MOVE_FOOTPRINT ), @@ -466,7 +469,7 @@ void SELECTION_TOOL::toggleSelection( BOARD_ITEM* aItem, bool aForce ) unselect( aItem ); // Inform other potentially interested tools - m_toolMgr->ProcessEvent( UnselectedEvent ); + m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent ); } else { @@ -479,7 +482,7 @@ void SELECTION_TOOL::toggleSelection( BOARD_ITEM* aItem, bool aForce ) select( aItem ); // Inform other potentially interested tools - m_toolMgr->ProcessEvent( SelectedEvent ); + m_toolMgr->ProcessEvent( EVENTS::SelectedEvent ); } } @@ -542,60 +545,47 @@ bool SELECTION_TOOL::selectPoint( const VECTOR2I& aWhere, bool aOnDrag, if( aClientFilter ) aClientFilter( aWhere, collector ); - if( collector.GetCount() == 0 ) + // Apply some ugly heuristics to avoid disambiguation menus whenever possible + if( collector.GetCount() > 1 && !m_skip_heuristics ) { - if( !m_additive && anyCollected ) + guessSelectionCandidates( collector, aWhere ); + } + + // If still more than one item we're going to have to ask the user. + if( collector.GetCount() > 1 ) + { + if( aOnDrag ) { - clearSelection(); + Wait( TOOL_EVENT( TC_ANY, TA_MOUSE_UP, BUT_LEFT ) ); + } + + if( !doSelectionMenu( &collector, _( "Clarify Selection" ) ) ) + { + if( aSelectionCancelledFlag ) + *aSelectionCancelledFlag = true; + + return false; } - return false; } if( collector.GetCount() == 1 ) { - toggleSelection( collector[0] ); + BOARD_ITEM* item = collector[ 0 ]; + + toggleSelection( item ); return true; } - // Apply some ugly heuristics to avoid disambiguation menus whenever possible - if( !m_skip_heuristics ) - { - guessSelectionCandidates( collector, aWhere ); - - if( collector.GetCount() == 1 ) - { - toggleSelection( collector[0] ); - return true; - } - } - - // Still more than one item. We're going to have to ask the user. - if( aOnDrag ) - { - Wait( TOOL_EVENT( TC_ANY, TA_MOUSE_UP, BUT_LEFT ) ); - } - - BOARD_ITEM* item = doSelectionMenu( &collector, _( "Clarify Selection" ) ); - - if( item ) - { - return true; - } - else - { - if( aSelectionCancelledFlag ) - *aSelectionCancelledFlag = true; - - return false; - } + if( !m_additive && anyCollected ) + clearSelection(); return false; } -bool SELECTION_TOOL::selectCursor( bool aSelectAlways, CLIENT_SELECTION_FILTER aClientFilter ) +bool SELECTION_TOOL::selectCursor( bool aForceSelect, CLIENT_SELECTION_FILTER aClientFilter ) { - if( aSelectAlways || m_selection.Empty() ) + if( aForceSelect || m_selection.Empty() ) { clearSelection(); selectPoint( getViewControls()->GetCursorPosition( false ), false, NULL, aClientFilter ); @@ -710,7 +700,7 @@ bool SELECTION_TOOL::selectMultiple() // Inform other potentially interested tools if( !m_selection.Empty() ) - m_toolMgr->ProcessEvent( SelectedEvent ); + m_toolMgr->ProcessEvent( EVENTS::SelectedEvent ); break; // Stop waiting for events } @@ -819,14 +809,11 @@ int SELECTION_TOOL::SelectItems( const TOOL_EVENT& aEvent ) if( items ) { - // Perform individual selection of each item - // before processing the event. + // Perform individual selection of each item before processing the event. for( auto item : *items ) - { select( item ); - } - m_toolMgr->ProcessEvent( SelectedEvent ); + m_toolMgr->ProcessEvent( EVENTS::SelectedEvent ); } return 0; @@ -843,7 +830,7 @@ int SELECTION_TOOL::SelectItem( const TOOL_EVENT& aEvent ) select( item ); // Inform other potentially interested tools - m_toolMgr->ProcessEvent( SelectedEvent ); + m_toolMgr->ProcessEvent( EVENTS::SelectedEvent ); } return 0; @@ -856,14 +843,11 @@ int SELECTION_TOOL::UnselectItems( const TOOL_EVENT& aEvent ) if( items ) { - // Perform individual unselection of each item - // before processing the event + // Perform individual unselection of each item before processing the event for( auto item : *items ) - { unselect( item ); - } - m_toolMgr->ProcessEvent( UnselectedEvent ); + m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent ); } return 0; @@ -880,7 +864,7 @@ int SELECTION_TOOL::UnselectItem( const TOOL_EVENT& aEvent ) unselect( item ); // Inform other potentially interested tools - m_toolMgr->ProcessEvent( UnselectedEvent ); + m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent ); } return 0; @@ -935,7 +919,7 @@ int SELECTION_TOOL::expandSelectedConnection( const TOOL_EVENT& aEvent ) // Inform other potentially interested tools if( m_selection.Size() > 0 ) - m_toolMgr->ProcessEvent( SelectedEvent ); + m_toolMgr->ProcessEvent( EVENTS::SelectedEvent ); return 0; } @@ -987,7 +971,7 @@ int SELECTION_TOOL::selectCopper( const TOOL_EVENT& aEvent ) // Inform other potentially interested tools if( m_selection.Size() > 0 ) - m_toolMgr->ProcessEvent( SelectedEvent ); + m_toolMgr->ProcessEvent( EVENTS::SelectedEvent ); return 0; } @@ -1056,7 +1040,7 @@ int SELECTION_TOOL::selectNet( const TOOL_EVENT& aEvent ) // Inform other potentially interested tools if( m_selection.Size() > 0 ) - m_toolMgr->ProcessEvent( SelectedEvent ); + m_toolMgr->ProcessEvent( EVENTS::SelectedEvent ); return 0; } @@ -1201,7 +1185,7 @@ int SELECTION_TOOL::selectOnSheetFromEeschema( const TOOL_EVENT& aEvent ) zoomFitSelection(); if( m_selection.Size() > 0 ) - m_toolMgr->ProcessEvent( SelectedEvent ); + m_toolMgr->ProcessEvent( EVENTS::SelectedEvent ); return 0; } @@ -1235,7 +1219,7 @@ int SELECTION_TOOL::selectSameSheet( const TOOL_EVENT& aEvent ) // Inform other potentially interested tools if( m_selection.Size() > 0 ) - m_toolMgr->ProcessEvent( SelectedEvent ); + m_toolMgr->ProcessEvent( EVENTS::SelectedEvent ); return 0; } @@ -1251,7 +1235,7 @@ void SELECTION_TOOL::findCallback( BOARD_ITEM* aItem ) getView()->SetCenter( aItem->GetPosition() ); // Inform other potentially interested tools - m_toolMgr->ProcessEvent( SelectedEvent ); + m_toolMgr->ProcessEvent( EVENTS::SelectedEvent ); } m_frame->GetGalCanvas()->ForceRefresh(); @@ -1430,14 +1414,12 @@ void SELECTION_TOOL::clearSelection() m_selection.ClearReferencePoint(); if( m_frame ) - { m_frame->SetCurItem( NULL ); - } m_locked = true; // Inform other potentially interested tools - m_toolMgr->ProcessEvent( ClearedEvent ); + m_toolMgr->ProcessEvent( EVENTS::ClearedEvent ); m_toolMgr->RunAction( PCB_ACTIONS::hideLocalRatsnest, true ); } @@ -1451,10 +1433,9 @@ int SELECTION_TOOL::SelectionMenu( const TOOL_EVENT& aEvent ) } -BOARD_ITEM* SELECTION_TOOL::doSelectionMenu( GENERAL_COLLECTOR* aCollector, - const wxString& aTitle ) +bool SELECTION_TOOL::doSelectionMenu( GENERAL_COLLECTOR* aCollector, const wxString& aTitle ) { - BOARD_ITEM* current = NULL; + BOARD_ITEM* current = nullptr; SELECTION highlightGroup; CONTEXT_MENU menu; @@ -1475,6 +1456,7 @@ BOARD_ITEM* SELECTION_TOOL::doSelectionMenu( GENERAL_COLLECTOR* aCollector, if( aTitle.Length() ) menu.SetTitle( aTitle ); + menu.SetIcon( info_xpm ); menu.DisplayTitle( true ); SetContextMenu( &menu, CMENU_NOW ); @@ -1518,9 +1500,14 @@ BOARD_ITEM* SELECTION_TOOL::doSelectionMenu( GENERAL_COLLECTOR* aCollector, getView()->Remove( &highlightGroup ); if( current ) + { toggleSelection( current ); + aCollector->Empty(); + aCollector->Append( current ); + return true; + } - return current; + return false; } @@ -1849,8 +1836,8 @@ void SELECTION_TOOL::highlight( BOARD_ITEM* aItem, int aMode, SELECTION& aGroup aGroup.Add( aItem ); - // 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 + // Modules are treated in a special way - when they are highlighted, we have to + // highlight all the parts that make the module, not the module itself if( aItem->Type() == PCB_MODULE_T ) { static_cast( aItem )->RunOnChildren( [&] ( BOARD_ITEM* item ) @@ -1886,8 +1873,8 @@ void SELECTION_TOOL::unhighlight( BOARD_ITEM* aItem, int aMode, SELECTION& aGrou view()->Hide( aItem, false ); view()->Update( aItem ); - // 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 + // Modules are treated in a special way - when they are highlighted, we have to + // highlight all the parts that make the module, not the module itself if( aItem->Type() == PCB_MODULE_T ) { static_cast( aItem )->RunOnChildren( [&] ( BOARD_ITEM* item ) @@ -2301,6 +2288,3 @@ int SELECTION_TOOL::updateSelection( const TOOL_EVENT& aEvent ) } -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 index 02de875e5c..0137f34897 100644 --- a/pcbnew/tools/selection_tool.h +++ b/pcbnew/tools/selection_tool.h @@ -4,7 +4,7 @@ * Copyright (C) 2013-2017 CERN * @author Tomasz Wlostowski * @author Maciej Suminski - * Copyright (C) 2017 KiCad Developers, see CHANGELOG.TXT for contributors. + * Copyright (C) 2017-2019 KiCad Developers, see CHANGELOG.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 @@ -132,15 +132,6 @@ public: */ int SelectionMenu( 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() override; @@ -168,11 +159,11 @@ private: * 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. + * @param aForceSelect forces to select an item even if there is an item already selected. * @param aClientFilter allows the client to perform tool- or action-specific filtering. * @return true if eventually there is an item selected, false otherwise. */ - bool selectCursor( bool aSelectAlways = false, + bool selectCursor( bool aForceSelect = false, CLIENT_SELECTION_FILTER aClientFilter = NULL ); /** @@ -186,10 +177,12 @@ private: /** * Allows the selection of a single item from a list via pop-up menu. The items are - * highlighted on the canvas when hovered in the menu. + * highlighted on the canvas when hovered in the menu. The collector is trimmed to + * the picked item. * @param aTitle (optional) Allows the menu to be titled (ie: "Clarify Selection"). + * @return true if an item was picked */ - BOARD_ITEM* doSelectionMenu( GENERAL_COLLECTOR* aItems, const wxString& aTitle ); + bool doSelectionMenu( GENERAL_COLLECTOR* aItems, const wxString& aTitle ); ///> Selects a trivial connection (between two junctions) of items in selection int selectConnection( const TOOL_EVENT& aEvent ); @@ -295,7 +288,7 @@ private: void unselect( BOARD_ITEM* aItem ); /** - * Function selectVisually() + * Function highlight() * Highlights the item visually. * @param aItem is an item to be be highlighted. * @param aHighlightMode should be either SELECTED or BRIGHTENED @@ -304,7 +297,7 @@ private: void highlight( BOARD_ITEM* aItem, int aHighlightMode, SELECTION& aGroup ); /** - * Function unselectVisually() + * Function unhighlight() * Unhighlights the item visually. * @param aItem is an item to be be highlighted. * @param aHighlightMode should be either SELECTED or BRIGHTENED @@ -336,28 +329,16 @@ private: const GENERAL_COLLECTORS_GUIDE getCollectorsGuide() const; - /// Pointer to the parent frame. - PCB_BASE_FRAME* m_frame; +private: + PCB_BASE_FRAME* m_frame; // Pointer to the parent frame + SELECTION m_selection; // Current state of selection - /// Current state of selection. - SELECTION m_selection; + bool m_additive; // Items should be added to selection (instead of replacing) + bool m_subtractive; // Items should be removed from selection + bool m_multiple; // Multiple selection mode is active + bool m_skip_heuristics; // Heuristics are not allowed when choosing item under cursor + bool m_locked; // Other tools are not allowed to modify locked items - /// Flag saying if items should be added to the current selection or rather replace it. - bool m_additive; - - /// Flag saying if items should be removed from the current selection - bool m_subtractive; - - /// Flag saying if multiple selection mode is active. - bool m_multiple; - - /// Flag saying that heuristics should be skipped while choosing selection - bool m_skip_heuristics; - - /// Can other tools modify locked items. - bool m_locked; - - /// Menu model displayed by the tool. TOOL_MENU m_menu; /// Private state (opaque pointer/compilation firewall)