← Back to team overview

kicad-developers team mailing list archive

Group selection idea

 

Hello!

I hacked together a group selection concept looking like this: https://youtu.be/eJp-aJ8i0H4

It can assign BOARD_ITEM to a specific group for easier selection and group manipulation. I am open to suggestions on changes, this is surely
not an optimal implementation.

Useful when you may want to keep the relative position of something on the board like maybe a RF layout etc.

It cannot currently save the group assignments between sessions, since that would require some changes to the file format. That would need some agreement that this is indeed wanted.

it also doesnt work on zones right now.

ps: I do not now the best way to attach a patch file.
I added my feature
commit
git pull
fix conflict
commit

Anyone have any steps on how to get one patch file for this, now I got one patch file, and a merge.

- Kristoffer
>From 2980b0146ab2a195bc9b82bdb8797a5911299740 Mon Sep 17 00:00:00 2001
From: totalkrill <kristoffer.odmark90@xxxxxxxxx>
Date: Wed, 11 Jan 2017 19:49:05 +0100
Subject: [PATCH 2/2] Added concept of selectiongroups.     Groups context menu
 Added     Modified SELECT_MENU and the select_tool     Added getters to BOARD
 class     Added std::string groupId to BOARD_ITEM class

---
 include/class_board_item.h          |   2 +
 pcbnew/class_board.cpp              |  88 +++++++++++++++++++++
 pcbnew/class_board.h                |  22 ++++++
 pcbnew/tools/common_actions.cpp     |  20 +++++
 pcbnew/tools/common_actions.h       |  15 ++++
 pcbnew/tools/pcb_editor_control.cpp | 147 +++++++++++++++++++++++++++++++++++-
 pcbnew/tools/pcb_editor_control.h   |  11 +++
 pcbnew/tools/selection_tool.cpp     |  98 +++++++++++++++++++++++-
 pcbnew/tools/selection_tool.h       |   6 ++
 9 files changed, 406 insertions(+), 3 deletions(-)

diff --git a/include/class_board_item.h b/include/class_board_item.h
index 34c275c14..d78951ac7 100644
--- a/include/class_board_item.h
+++ b/include/class_board_item.h
@@ -84,6 +84,8 @@ protected:
 
 public:
 
+    std::string groupId = "";
+
     BOARD_ITEM( BOARD_ITEM* aParent, KICAD_T idtype ) :
         EDA_ITEM( aParent, idtype ), m_Layer( F_Cu )
     {
diff --git a/pcbnew/class_board.cpp b/pcbnew/class_board.cpp
index 6608c3ce0..b292ddd66 100644
--- a/pcbnew/class_board.cpp
+++ b/pcbnew/class_board.cpp
@@ -1128,6 +1128,94 @@ EDA_RECT BOARD::ComputeBoundingBox( bool aBoardEdgesOnly )
     return area;
 }
 
+std::list<BOARD_ITEM*> BOARD::GetAllItems()
+{
+    std::list<BOARD_ITEM*> itemsList;
+
+    auto tracks = m_Track.GetFirst();
+    auto drawings = m_Drawings.GetFirst();
+    auto modules = m_Modules.GetFirst();
+    auto zones = m_Zone.GetFirst();
+
+    for( BOARD_ITEM* item = tracks; item; item = item->Next() )
+    {
+        if ( item != NULL )
+            itemsList.push_back( item );
+    }
+    for( BOARD_ITEM* item = drawings; item; item = item->Next() )
+    {
+        if ( item != NULL )
+            itemsList.push_back( item );
+    }
+    for( BOARD_ITEM* item = modules; item; item = item->Next() )
+    {
+        if ( item != NULL )
+            itemsList.push_back( item );
+    }
+    for( BOARD_ITEM* item = zones; item; item = item->Next() )
+    {
+        if ( item != NULL )
+            itemsList.push_back( item );
+    }
+
+    return itemsList;
+}
+
+std::list<BOARD_ITEM*> BOARD::GetAllGroupItems()
+{
+    std::list<BOARD_ITEM*> itemsList;
+
+    auto tracks = m_Track.GetFirst();
+    auto drawings = m_Drawings.GetFirst();
+    auto modules = m_Modules.GetFirst();
+    auto zones = m_Zone.GetFirst();
+
+    for( BOARD_ITEM* item = tracks; item; item = item->Next() )
+    {
+        if ( item != NULL && item->groupId != "" )
+            itemsList.push_back( item );
+    }
+    for( BOARD_ITEM* item = drawings; item; item = item->Next() )
+    {
+        if ( item != NULL && item->groupId != "" )
+            itemsList.push_back( item );
+    }
+    for( BOARD_ITEM* item = modules; item; item = item->Next() )
+    {
+        if ( item != NULL && item->groupId != "" )
+            itemsList.push_back( item );
+    }
+    for( BOARD_ITEM* item = zones; item; item = item->Next() )
+    {
+        if ( item != NULL && item->groupId != "" )
+            itemsList.push_back( item );
+    }
+
+    return itemsList;
+}
+
+std::list<std::string> BOARD::GetGroups()
+{
+
+    std::list<std::string> existingGroups;
+
+    std::list<BOARD_ITEM*> itemsList = GetAllGroupItems();
+
+    for( BOARD_ITEM* i : itemsList )
+    {
+        bool groupSeen = false;
+
+        for( std::string s : existingGroups )
+        {
+            if( s == i->groupId )
+                groupSeen = true;
+        }
+        if( !groupSeen  && i->groupId != "" )
+            existingGroups.push_back( i->groupId );
+    }
+    return existingGroups;
+}
+
 
 // virtual, see pcbstruct.h
 void BOARD::GetMsgPanelInfo( std::vector< MSG_PANEL_ITEM >& aList )
diff --git a/pcbnew/class_board.h b/pcbnew/class_board.h
index 0ad5e9d89..210b7065d 100644
--- a/pcbnew/class_board.h
+++ b/pcbnew/class_board.h
@@ -278,6 +278,28 @@ public:
     ///> @copydoc BOARD_ITEM_CONTAINER::Remove()
     void Remove( BOARD_ITEM* aBoardItem ) override;
 
+    /**
+     * Function GetAllItems()
+     * returns a list of ALL items on board.
+     * @return list of BOARD_ITEMS* representing all BOARD_ITEMS currently on the board.
+     */
+    std::list<BOARD_ITEM*> GetAllItems();
+
+    /**
+     * Function GetAllGroupItems()
+     * returns a list of ALL items in a group on the board.
+     * @return list of BOARD_ITEMS* representing all BOARD_ITEMS in a group currently on the board.
+     */
+    std::list<BOARD_ITEM*> GetAllGroupItems();
+
+    /**
+     * Function GetGroups()
+     * returns a list of groups .
+     * @return list of std::string representing all the different groups on the board.
+     */
+    std::list<std::string> GetGroups();
+
+
     BOARD_ITEM* Duplicate( const BOARD_ITEM* aItem, bool aAddToBoard = false );
 
     /**
diff --git a/pcbnew/tools/common_actions.cpp b/pcbnew/tools/common_actions.cpp
index b9af35915..e303e8575 100644
--- a/pcbnew/tools/common_actions.cpp
+++ b/pcbnew/tools/common_actions.cpp
@@ -66,6 +66,26 @@ TOOL_ACTION COMMON_ACTIONS::selectNet( "pcbnew.InteractiveSelection.SelectNet",
         AS_GLOBAL, 0,
         _( "whole net" ), _( "Selects all tracks & vias belonging to the same net." ) );
 
+TOOL_ACTION COMMON_ACTIONS::selectGroup( "pcbnew.InteractiveSelection.SelectGroup",
+        AS_GLOBAL, 0,
+        _( "group" ), _( "Selects items in specified group." ) );
+
+TOOL_ACTION COMMON_ACTIONS::selectSameGroup( "pcbnew.InteractiveSelection.SelectSameGroup",
+        AS_GLOBAL, 'G',
+        _( "same group" ), _( "Selects items in the same group." ) );
+
+TOOL_ACTION COMMON_ACTIONS::assignGroup( "pcbnew.EditorControl.AssignGroup",
+        AS_GLOBAL, 0,
+        _( "Assign to existing group" ), _( "Assign selection to group." ) );
+
+TOOL_ACTION COMMON_ACTIONS::assignNewGroup( "pcbnew.EditorControl.AssignNewGroup",
+        AS_GLOBAL, 0,
+        _( "Assign to new group" ), _( "Assign selection to group." ) );
+
+TOOL_ACTION COMMON_ACTIONS::clearGroup( "pcbnew.EditorControl.ClearGroup",
+        AS_GLOBAL, 0,
+        _( "Clear group belonging" ), _( "remove group belonging from selection." ) );
+
 TOOL_ACTION COMMON_ACTIONS::find( "pcbnew.InteractiveSelection.Find",
         AS_GLOBAL, 0, //TOOL_ACTION::LegacyHotKey( HK_FIND_ITEM ), // handled by wxWidgets
         _( "Find an item" ), _( "Searches the document for an item" ), find_xpm );
diff --git a/pcbnew/tools/common_actions.h b/pcbnew/tools/common_actions.h
index 962b6b436..8fe45bf63 100644
--- a/pcbnew/tools/common_actions.h
+++ b/pcbnew/tools/common_actions.h
@@ -66,6 +66,21 @@ public:
     /// Selects all connections belonging to a single net.
     static TOOL_ACTION selectNet;
 
+    /// Selects all items belonging the same group
+    static TOOL_ACTION selectGroup;
+
+    /// Selects all items belonging the same group
+    static TOOL_ACTION selectSameGroup;
+
+    /// Assign selected items to group
+    static TOOL_ACTION assignGroup;
+
+    /// Assign selected items to new group
+    static TOOL_ACTION assignNewGroup;
+
+    /// Clear selected items from group
+    static TOOL_ACTION clearGroup;
+
     // Edit Tool
     /// Activation of the edit tool
     static TOOL_ACTION editActivate;
diff --git a/pcbnew/tools/pcb_editor_control.cpp b/pcbnew/tools/pcb_editor_control.cpp
index 74e2761ea..ad65fd541 100644
--- a/pcbnew/tools/pcb_editor_control.cpp
+++ b/pcbnew/tools/pcb_editor_control.cpp
@@ -95,10 +95,21 @@ public:
     }
 };
 
+class GROUP_CONTEXT_MENU : public CONTEXT_MENU
+{
+public:
+    GROUP_CONTEXT_MENU()
+    {
+        Add( COMMON_ACTIONS::assignGroup );
+        Add( COMMON_ACTIONS::assignNewGroup );
+        Add( COMMON_ACTIONS::clearGroup );
+    }
+};
+
 
 PCB_EDITOR_CONTROL::PCB_EDITOR_CONTROL() :
     TOOL_INTERACTIVE( "pcbnew.EditorControl" ),
-    m_frame( NULL ), m_zoneMenu( NULL ), m_lockMenu( NULL )
+    m_frame( NULL ), m_zoneMenu( NULL ), m_lockMenu( NULL ), m_groupMenu ( NULL )
 {
     m_placeOrigin = new KIGFX::ORIGIN_VIEWITEM( KIGFX::COLOR4D( 0.8, 0.0, 0.0, 1.0 ),
                                                 KIGFX::ORIGIN_VIEWITEM::CIRCLE_CROSS );
@@ -144,6 +155,11 @@ bool PCB_EDITOR_CONTROL::Init()
         m_lockMenu->SetTool( this );
         selTool->GetMenu().AddMenu( m_lockMenu, _( "Locking" ), false,
                                     SELECTION_CONDITIONS::OnlyTypes( GENERAL_COLLECTOR::Tracks ) );
+
+        m_groupMenu = new GROUP_CONTEXT_MENU;
+        m_groupMenu->SetTool( this );
+        selTool->GetMenu().AddMenu( m_groupMenu, _( "Groups" ), false ,
+                                    SELECTION_CONDITIONS::MoreThan( 0 ) );
     }
 
     return true;
@@ -825,6 +841,129 @@ int PCB_EDITOR_CONTROL::HighlightNetCursor( const TOOL_EVENT& aEvent )
     return 0;
 }
 
+int PCB_EDITOR_CONTROL::AssignGroup( const TOOL_EVENT& aEvent )
+{
+    SELECTION_TOOL* selTool = m_toolMgr->GetTool<SELECTION_TOOL>();
+    const SELECTION& selection = selTool->GetSelection();
+
+    if( selection.Empty() )
+        m_toolMgr->RunAction( COMMON_ACTIONS::selectionCursor, true );
+
+    bool modified = false;
+
+    wxString group;
+    wxArrayString existingGroups;
+
+    BOARD* board = getModel<BOARD>();
+    std::list<BOARD_ITEM*> itemsList = board->GetAllGroupItems();
+
+    for( BOARD_ITEM* i : itemsList )
+    {
+        bool groupSeen = false;
+
+        if( i->groupId == "")
+            continue;
+
+        for( wxString s : existingGroups )
+        {
+            if( s == i->groupId )
+                groupSeen = true;
+        }
+        if( !groupSeen  && i->groupId != "" )
+            existingGroups.Add( i->groupId );
+    }
+
+    wxSingleChoiceDialog dlg( NULL,  _( "Groups:" ), wxEmptyString, existingGroups);
+
+    if ( dlg.ShowModal() == wxID_OK )
+    {
+        // We can be certain that this string contains letters only.
+        group = dlg.GetStringSelection();
+    }
+    else
+    {
+        return 0;
+    }
+
+    for( auto item : selection )
+    {
+
+        item->groupId = group.ToStdString();
+
+        // Check if we really modified an item
+        modified = true;
+    }
+
+    if( modified )
+        m_frame->OnModify();
+
+    return 0;
+}
+
+int PCB_EDITOR_CONTROL::AssignNewGroup( const TOOL_EVENT& aEvent )
+{
+    SELECTION_TOOL* selTool = m_toolMgr->GetTool<SELECTION_TOOL>();
+    const SELECTION& selection = selTool->GetSelection();
+
+    if( selection.Empty() )
+        m_toolMgr->RunAction( COMMON_ACTIONS::selectionCursor, true );
+
+    bool modified = false;
+
+    wxString group;
+
+    wxTextEntryDialog dlg( NULL, _( "Group:" ), wxEmptyString, group );
+
+    if ( dlg.ShowModal() == wxID_OK )
+    {
+        // We can be certain that this string contains letters only.
+        group = dlg.GetValue();
+    }
+    else
+    {
+        return 0;
+    }
+
+    for( auto item : selection )
+    {
+
+        item->groupId = group.ToStdString();
+
+        // Check if we really modified an item
+        modified = true;
+    }
+
+    if( modified )
+        m_frame->OnModify();
+
+    return 0;
+}
+
+int PCB_EDITOR_CONTROL::ClearGroup( const TOOL_EVENT& aEvent )
+{
+    SELECTION_TOOL* selTool = m_toolMgr->GetTool<SELECTION_TOOL>();
+    const SELECTION& selection = selTool->GetSelection();
+
+    if( selection.Empty() )
+        m_toolMgr->RunAction( COMMON_ACTIONS::selectionCursor, true );
+
+    bool modified = false;
+
+    for( auto item : selection )
+    {
+
+        item->groupId = "";
+
+        // Check if we really modified an item
+        modified = true;
+    }
+
+    if( modified )
+        m_frame->OnModify();
+
+    return 0;
+}
+
 
 void PCB_EDITOR_CONTROL::SetTransitions()
 {
@@ -854,6 +993,12 @@ void PCB_EDITOR_CONTROL::SetTransitions()
     Go( &PCB_EDITOR_CONTROL::DrillOrigin,         COMMON_ACTIONS::drillOrigin.MakeEvent() );
     Go( &PCB_EDITOR_CONTROL::HighlightNet,        COMMON_ACTIONS::highlightNet.MakeEvent() );
     Go( &PCB_EDITOR_CONTROL::HighlightNetCursor,  COMMON_ACTIONS::highlightNetCursor.MakeEvent() );
+
+    //Group actions
+    Go( &PCB_EDITOR_CONTROL::AssignGroup,  COMMON_ACTIONS::assignGroup.MakeEvent() );
+    Go( &PCB_EDITOR_CONTROL::AssignNewGroup,  COMMON_ACTIONS::assignNewGroup.MakeEvent() );
+    Go( &PCB_EDITOR_CONTROL::ClearGroup,  COMMON_ACTIONS::clearGroup.MakeEvent() );
+
 }
 
 
diff --git a/pcbnew/tools/pcb_editor_control.h b/pcbnew/tools/pcb_editor_control.h
index bbf99e724..62f7e69c4 100644
--- a/pcbnew/tools/pcb_editor_control.h
+++ b/pcbnew/tools/pcb_editor_control.h
@@ -34,6 +34,7 @@ namespace KIGFX {
 class PCB_EDIT_FRAME;
 class ZONE_CONTEXT_MENU;
 class LOCK_CONTEXT_MENU;
+class GROUP_CONTEXT_MENU;
 
 /**
  * Class PCB_EDITOR_CONTROL
@@ -98,6 +99,15 @@ public:
     ///> Highlights net belonging to the item under the cursor.
     int HighlightNet( const TOOL_EVENT& aEvent );
 
+    ///> Assigns group to the current selected items.
+    int AssignGroup( const TOOL_EVENT& aEvent );
+
+    ///> Assign selected items to a new group.
+    int AssignNewGroup( const TOOL_EVENT& aEvent );
+
+    ///> removes group from the current selected items.
+    int ClearGroup( const TOOL_EVENT& aEvent );
+
     ///> Launches a tool to pick the item whose net is going to be highlighted.
     int HighlightNetCursor( const TOOL_EVENT& aEvent );
 
@@ -124,6 +134,7 @@ private:
 
     ZONE_CONTEXT_MENU* m_zoneMenu;
     LOCK_CONTEXT_MENU* m_lockMenu;
+    GROUP_CONTEXT_MENU* m_groupMenu;
 };
 
 #endif
diff --git a/pcbnew/tools/selection_tool.cpp b/pcbnew/tools/selection_tool.cpp
index d5950f842..92208171d 100644
--- a/pcbnew/tools/selection_tool.cpp
+++ b/pcbnew/tools/selection_tool.cpp
@@ -60,9 +60,30 @@ class SELECT_MENU: public CONTEXT_MENU
 public:
     SELECT_MENU()
     {
+        SetUpdateHandler( std::bind( &SELECT_MENU::update, this ) );
         Add( COMMON_ACTIONS::selectConnection );
         Add( COMMON_ACTIONS::selectCopper );
         Add( COMMON_ACTIONS::selectNet );
+        Add( COMMON_ACTIONS::selectSameGroup );
+        Add( COMMON_ACTIONS::selectGroup );
+    }
+private:
+    void update()
+    {
+        SELECTION_TOOL* selTool = getToolManager()->GetTool<SELECTION_TOOL>();
+
+        // lines like this make me really think about a better name for SELECTION_CONDITIONS class
+        bool selEnabled =  ( SELECTION_CONDITIONS::OnlyType( PCB_VIA_T )
+                || SELECTION_CONDITIONS::OnlyType( PCB_TRACE_T ) )
+                              ( selTool->GetSelection() );
+
+        bool groupSelEnabled = ( SELECTION_CONDITIONS::Count( 1 ) )
+                              ( selTool->GetSelection() );
+
+        Enable( getMenuId( COMMON_ACTIONS::selectNet ), selEnabled );
+        Enable( getMenuId( COMMON_ACTIONS::selectCopper ), selEnabled );
+        Enable( getMenuId( COMMON_ACTIONS::selectConnection ), selEnabled );
+        Enable( getMenuId( COMMON_ACTIONS::selectSameGroup ), groupSelEnabled );
     }
 };
 
@@ -95,8 +116,8 @@ bool SELECTION_TOOL::Init()
     m_selectMenu->SetTool( this );
 
     m_menu.AddMenu( m_selectMenu, _( "Select..." ), false,
-            ( SELECTION_CONDITIONS::OnlyType( PCB_VIA_T ) || SELECTION_CONDITIONS::OnlyType( PCB_TRACE_T ) ) &&
-            SELECTION_CONDITIONS::Count( 1 ) );
+                                    SELECTION_CONDITIONS::ShowAlways );
+    //m_menu.AddItem( COMMON_ACTIONS::selectGroup, SELECTION_CONDITIONS::Count( 1 ));
 
     m_menu.AddSeparator( SELECTION_CONDITIONS::ShowAlways, 1000 );
 
@@ -275,6 +296,16 @@ int SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
             selectCopper( *evt );
         }
 
+        else if( evt->IsAction( &COMMON_ACTIONS::selectGroup ) )
+        {
+            selectGroup( *evt );
+        }
+
+        else if( evt->IsAction( &COMMON_ACTIONS::selectSameGroup ) )
+        {
+            selectSameGroup( *evt );
+        }
+
         else if( evt->IsAction( &COMMON_ACTIONS::selectNet ) )
         {
             selectNet( *evt );
@@ -511,6 +542,8 @@ void SELECTION_TOOL::SetTransitions()
     Go( &SELECTION_TOOL::findMove, COMMON_ACTIONS::findMove.MakeEvent() );
     Go( &SELECTION_TOOL::selectConnection, COMMON_ACTIONS::selectConnection.MakeEvent() );
     Go( &SELECTION_TOOL::selectCopper, COMMON_ACTIONS::selectCopper.MakeEvent() );
+    Go( &SELECTION_TOOL::selectGroup, COMMON_ACTIONS::selectGroup.MakeEvent() );
+    Go( &SELECTION_TOOL::selectSameGroup, COMMON_ACTIONS::selectSameGroup.MakeEvent() );
     Go( &SELECTION_TOOL::selectNet, COMMON_ACTIONS::selectNet.MakeEvent() );
 }
 
@@ -667,6 +700,67 @@ int SELECTION_TOOL::selectCopper( const TOOL_EVENT& aEvent )
     return 0;
 }
 
+int SELECTION_TOOL::selectSameGroup( const TOOL_EVENT& aEvent )
+{
+    if( !selectCursor( true ) )
+        return 0;
+
+    auto item = static_cast<BOARD_ITEM*> ( m_selection.Front() );
+
+    if( item->groupId == "" )
+        return 0;
+
+    clearSelection();
+
+    std::list<BOARD_ITEM*> itemsList = board()->GetAllGroupItems();
+
+    for( BOARD_ITEM* i : itemsList )
+        if( i->groupId == item->groupId )
+            select( i );
+
+    // Inform other potentially interested tools
+    if( itemsList.size() > 0 )
+        m_toolMgr->ProcessEvent( SelectedEvent );
+
+    return 0;
+}
+
+int SELECTION_TOOL::selectGroup( const TOOL_EVENT& aEvent )
+{
+    std::list<std::string> groupList = board()->GetGroups();
+
+    wxArrayString existingGroups;
+
+    for(std::string s : groupList )
+    {
+        existingGroups.Add( s );
+    }
+
+    wxSingleChoiceDialog dlg( NULL,  _( "Groups:" ), wxEmptyString, existingGroups);
+
+    if( dlg.ShowModal() != wxID_OK )
+        return 0;
+
+
+    clearSelection();
+
+    std::list<BOARD_ITEM*> itemsList = board()->GetAllGroupItems();
+
+    for( BOARD_ITEM* i : itemsList )
+    {
+        if( i->groupId == dlg.GetStringSelection() )
+        {
+            select( i );
+        }
+    }
+
+    // Inform other potentially interested tools
+    if( itemsList.size() > 0 )
+        m_toolMgr->ProcessEvent( SelectedEvent );
+
+    return 0;
+}
+
 
 int SELECTION_TOOL::selectNet( const TOOL_EVENT& aEvent )
 {
diff --git a/pcbnew/tools/selection_tool.h b/pcbnew/tools/selection_tool.h
index 054b99441..435129013 100644
--- a/pcbnew/tools/selection_tool.h
+++ b/pcbnew/tools/selection_tool.h
@@ -268,6 +268,12 @@ private:
     ///> Selects a continuous copper connection.
     int selectCopper( const TOOL_EVENT& aEvent );
 
+    ///> Selects a group based on selected item.
+    int selectGroup( const TOOL_EVENT& aEvent );
+
+    ///> Selects all in group chosen from list.
+    int selectSameGroup( const TOOL_EVENT& aEvent );
+
     ///> Selects all copper connections belonging to a single net.
     int selectNet( const TOOL_EVENT& aEvent );
 
-- 
2.11.0


Follow ups