← Back to team overview

kicad-developers team mailing list archive

Re: [PATCH] Option in eeschema to annotate keeping multi-unit parts grouped

 

Here is a fixed version of the patch. It handled perfectly any complex hierarchies I could throw at it. Got anything worse to torture it with?

Chris

On Sat, Mar 21, 2015 at 04:44:43PM +0100, jp charras wrote:
Le 14/03/2015 00:11, Chris Pavlina a écrit :
Hello,

Here is a patch for eeschema that adds "Reset existing annotation, but
do not swap any units" to eeschema. This first compiles a list of
multi-unit parts and all of the components that comprise them, and then
on annotation, annotates all of them as a group. For example, if
components were originally R3B and R3C, they can be annotated to R5B and
R5C, but never R5B and R6C, or R5A and R5D.

I wanted this feature so that I could re-annotate a project to tidy up
the references, while not losing symbol->footprint mappings that already
exist in a routed PCB.

Perhaps, I'll follow up in a few days with a patch to follow through the
annotations to the other files in the project. (Currently I'm using a
hacked-together Python script for this...)


Chris

Thank for your contribution.
This is a very interesting feature.

I tested it on different schematic projects.

It works fine only for simple hierarchies.
Unfortunately, it does not work on complex hierarchies.
These hierarchies uses more than once a given sheet: there is one file,
one schematic, and several instances.
the sheet which is used more than once stores only one drawing, but n
different references when it is used n times.

For instance demos/complex_hierarchy project.

Be *extremely" careful with complex hierarchies.
Complex hierarchies are always a very tricky case, and are never easy to
handle.


--
Jean-Pierre CHARRAS

_______________________________________________
Mailing list: https://launchpad.net/~kicad-developers
Post to     : kicad-developers@xxxxxxxxxxxxxxxxxxx
Unsubscribe : https://launchpad.net/~kicad-developers
More help   : https://help.launchpad.net/ListHelp
diff --git a/eeschema/annotate.cpp b/eeschema/annotate.cpp
index 3fccb8e..f041fe3 100644
--- a/eeschema/annotate.cpp
+++ b/eeschema/annotate.cpp
@@ -38,6 +38,7 @@
 #include <sch_component.h>
 #include <lib_pin.h>
 
+#include <boost/foreach.hpp>
 
 void SCH_EDIT_FRAME::DeleteAnnotation( bool aCurrentSheetOnly )
 {
@@ -62,7 +63,8 @@ void SCH_EDIT_FRAME::AnnotateComponents( bool              aAnnotateSchematic,
                                          ANNOTATE_ORDER_T  aSortOption,
                                          ANNOTATE_OPTION_T aAlgoOption,
                                          bool              aResetAnnotation,
-                                         bool              aRepairTimestamps )
+                                         bool              aRepairTimestamps,
+                                         bool              aLockUnits )
 {
     SCH_REFERENCE_LIST references;
 
@@ -73,6 +75,9 @@ void SCH_EDIT_FRAME::AnnotateComponents( bool              aAnnotateSchematic,
     // Build the sheet list.
     SCH_SHEET_LIST sheets;
 
+    // Map of locked components
+    SCH_MULTI_UNIT_REFERENCE_MAP lockedComponents;
+
     // Test for and replace duplicate time stamps in components and sheets.  Duplicate
     // time stamps can happen with old schematics, schematic conversions, or manual
     // editing of files.
@@ -88,6 +93,19 @@ void SCH_EDIT_FRAME::AnnotateComponents( bool              aAnnotateSchematic,
         }
     }
 
+    // If units must be locked, collect all the sets that must be annotated together.
+    if( aLockUnits )
+    {
+        if( aAnnotateSchematic )
+        {
+            sheets.GetMultiUnitComponents( Prj().SchLibs(), lockedComponents );
+        }
+        else
+        {
+            m_CurrentSheet->GetMultiUnitComponents( Prj().SchLibs(), lockedComponents );
+        }
+    }
+
     // If it is an annotation for all the components, reset previous annotation.
     if( aResetAnnotation )
         DeleteAnnotation( !aAnnotateSchematic );
@@ -141,7 +159,7 @@ void SCH_EDIT_FRAME::AnnotateComponents( bool              aAnnotateSchematic,
     }
 
     // Recalculate and update reference numbers in schematic
-    references.Annotate( useSheetNum, idStep );
+    references.Annotate( useSheetNum, idStep, lockedComponents );
     references.UpdateAnnotation();
 
     wxArrayString errors;
diff --git a/eeschema/component_references_lister.cpp b/eeschema/component_references_lister.cpp
index 15eb9e6..6796ef3 100644
--- a/eeschema/component_references_lister.cpp
+++ b/eeschema/component_references_lister.cpp
@@ -39,6 +39,8 @@
 #include <sch_reference_list.h>
 #include <sch_component.h>
 
+#include <boost/foreach.hpp>
+
 
 //#define USE_OLD_ALGO
 
@@ -283,7 +285,8 @@ int SCH_REFERENCE_LIST::CreateFirstFreeRefId( std::vector<int>& aIdList, int aFi
 }
 
 
-void SCH_REFERENCE_LIST::Annotate( bool aUseSheetNum, int aSheetIntervalId  )
+void SCH_REFERENCE_LIST::Annotate( bool aUseSheetNum, int aSheetIntervalId,
+      SCH_MULTI_UNIT_REFERENCE_MAP aLockedUnitMap )
 {
     if ( componentFlatList.size() == 0 )
         return;
@@ -327,6 +330,24 @@ void SCH_REFERENCE_LIST::Annotate( bool aUseSheetNum, int aSheetIntervalId  )
         if( componentFlatList[ii].m_Flag )
             continue;
 
+        // Check whether this component is in aLockedUnitMap.
+        SCH_REFERENCE_LIST* lockedList = NULL;
+        BOOST_FOREACH( SCH_MULTI_UNIT_REFERENCE_MAP::value_type& pair, aLockedUnitMap )
+        {
+            unsigned n_refs = pair.second.GetCount();
+            for( unsigned thisRefI = 0; thisRefI < n_refs; ++thisRefI )
+            {
+                SCH_REFERENCE &thisRef = pair.second[thisRefI];
+
+                if( thisRef.IsSameInstance( componentFlatList[ii] ) )
+                {
+                    lockedList = &pair.second;
+                    break;
+                }
+            }
+            if( lockedList != NULL ) break;
+        }
+
         if(  ( componentFlatList[first].CompareRef( componentFlatList[ii] ) != 0 )
           || ( aUseSheetNum && ( componentFlatList[first].m_SheetNum != componentFlatList[ii].m_SheetNum ) )  )
         {
@@ -388,47 +409,80 @@ void SCH_REFERENCE_LIST::Annotate( bool aUseSheetNum, int aSheetIntervalId  )
             componentFlatList[ii].m_Flag = 1;
         }
 
-        /* search for others units of this component.
-         * we search for others parts that have the same value and the same
-         * reference prefix (ref without ref number)
-         */
-        for( Unit = 1; Unit <= NumberOfUnits; Unit++ )
+        // If this component is in aLockedUnitMap, copy the annotation to all
+        // components that are not it
+        if( lockedList != NULL )
         {
-            if( componentFlatList[ii].m_Unit == Unit )
-                continue;
+            unsigned n_refs = lockedList->GetCount();
+            for( unsigned thisRefI = 0; thisRefI < n_refs; ++thisRefI )
+            {
+                SCH_REFERENCE &thisRef = (*lockedList)[thisRefI];
+                if( thisRef.IsSameInstance( componentFlatList[ii] ) )
+                {
+                    // This is the component we're currently annotating. Hold the unit!
+                    componentFlatList[ii].m_Unit = thisRef.m_Unit;
+                }
 
-            int found = FindUnit( ii, Unit );
+                if( thisRef.CompareValue( componentFlatList[ii] ) != 0 ) continue;
+                if( thisRef.CompareLibName( componentFlatList[ii] ) != 0 ) continue;
 
-            if( found >= 0 )
-                continue; // this unit exists for this reference (unit already annotated)
+                // Find the matching component
+                for( unsigned jj = ii + 1; jj < componentFlatList.size(); jj++ )
+                {
+                    if( ! thisRef.IsSameInstance( componentFlatList[jj] ) ) continue;
+                    componentFlatList[jj].m_NumRef = componentFlatList[ii].m_NumRef;
+                    componentFlatList[jj].m_Unit = thisRef.m_Unit;
+                    componentFlatList[jj].m_IsNew = false;
+                    componentFlatList[jj].m_Flag = 1;
+                    break;
+                }
+            }
+        }
 
-            // Search a component to annotate ( same prefix, same value, not annotated)
-            for( unsigned jj = ii + 1; jj < componentFlatList.size(); jj++ )
+        else
+        {
+            /* search for others units of this component.
+            * we search for others parts that have the same value and the same
+            * reference prefix (ref without ref number)
+            */
+            for( Unit = 1; Unit <= NumberOfUnits; Unit++ )
             {
-                if( componentFlatList[jj].m_Flag )    // already tested
-                    continue;
-
-                if( componentFlatList[ii].CompareRef( componentFlatList[jj] ) != 0 )
+                if( componentFlatList[ii].m_Unit == Unit )
                     continue;
 
-                if( componentFlatList[jj].CompareValue( componentFlatList[ii] ) != 0 )
-                    continue;
+                int found = FindUnit( ii, Unit );
 
-                if( componentFlatList[jj].CompareLibName( componentFlatList[ii] ) != 0 )
-                    continue;
+                if( found >= 0 )
+                    continue; // this unit exists for this reference (unit already annotated)
 
-                if( !componentFlatList[jj].m_IsNew )
-                    continue;
-
-                // Component without reference number found, annotate it if possible
-                if( !componentFlatList[jj].IsUnitsLocked()
-                    || ( componentFlatList[jj].m_Unit == Unit ) )
+                // Search a component to annotate ( same prefix, same value, not annotated)
+                for( unsigned jj = ii + 1; jj < componentFlatList.size(); jj++ )
                 {
-                    componentFlatList[jj].m_NumRef = componentFlatList[ii].m_NumRef;
-                    componentFlatList[jj].m_Unit   = Unit;
-                    componentFlatList[jj].m_Flag   = 1;
-                    componentFlatList[jj].m_IsNew  = false;
-                    break;
+                    if( componentFlatList[jj].m_Flag )    // already tested
+                        continue;
+
+                    if( componentFlatList[ii].CompareRef( componentFlatList[jj] ) != 0 )
+                        continue;
+
+                    if( componentFlatList[jj].CompareValue( componentFlatList[ii] ) != 0 )
+                        continue;
+
+                    if( componentFlatList[jj].CompareLibName( componentFlatList[ii] ) != 0 )
+                        continue;
+
+                    if( !componentFlatList[jj].m_IsNew )
+                        continue;
+
+                    // Component without reference number found, annotate it if possible
+                    if( !componentFlatList[jj].IsUnitsLocked()
+                        || ( componentFlatList[jj].m_Unit == Unit ) )
+                    {
+                        componentFlatList[jj].m_NumRef = componentFlatList[ii].m_NumRef;
+                        componentFlatList[jj].m_Unit   = Unit;
+                        componentFlatList[jj].m_Flag   = 1;
+                        componentFlatList[jj].m_IsNew  = false;
+                        break;
+                    }
                 }
             }
         }
diff --git a/eeschema/dialogs/dialog_annotate.cpp b/eeschema/dialogs/dialog_annotate.cpp
index 8cf2c1d..55ab3a4 100644
--- a/eeschema/dialogs/dialog_annotate.cpp
+++ b/eeschema/dialogs/dialog_annotate.cpp
@@ -67,6 +67,7 @@ private:
     // User functions:
     bool GetLevel();
     bool GetResetItems();
+    bool GetLockUnits();
 
     /**
      * Function GetSortOrder
@@ -212,7 +213,7 @@ void DIALOG_ANNOTATE::OnApplyClick( wxCommandEvent& event )
 
     m_Parent->AnnotateComponents( GetLevel(), (ANNOTATE_ORDER_T) GetSortOrder(),
                                   (ANNOTATE_OPTION_T) GetAnnotateAlgo(),
-                                  GetResetItems() , true );
+                                  GetResetItems() , true, GetLockUnits() );
     m_Parent->GetCanvas()->Refresh();
 
     m_btnClear->Enable();
@@ -273,9 +274,13 @@ bool DIALOG_ANNOTATE::GetLevel()
 
 bool DIALOG_ANNOTATE::GetResetItems()
 {
-    return m_rbResetAnnotation->GetValue();
+    return m_rbResetAnnotation->GetValue() || m_rbResetButLock->GetValue();
 }
 
+bool DIALOG_ANNOTATE::GetLockUnits()
+{
+    return m_rbResetButLock->GetValue();
+}
 
 int DIALOG_ANNOTATE::GetSortOrder()
 {
diff --git a/eeschema/dialogs/dialog_annotate_base.cpp b/eeschema/dialogs/dialog_annotate_base.cpp
index 83fb59b..a911ca7 100644
--- a/eeschema/dialogs/dialog_annotate_base.cpp
+++ b/eeschema/dialogs/dialog_annotate_base.cpp
@@ -1,5 +1,5 @@
 ///////////////////////////////////////////////////////////////////////////
-// C++ code generated with wxFormBuilder (version Jun  6 2014)
+// C++ code generated with wxFormBuilder (version Mar 13 2015)
 // http://www.wxformbuilder.org/
 //
 // PLEASE DO "NOT" EDIT THIS FILE!
@@ -43,6 +43,9 @@ DIALOG_ANNOTATE_BASE::DIALOG_ANNOTATE_BASE( wxWindow* parent, wxWindowID id, con
 	m_rbResetAnnotation = new wxRadioButton( this, ID_RESET_ANNOTATION, _("&Reset existing annotation"), wxDefaultPosition, wxDefaultSize, 0 );
 	bscopeOptSizer->Add( m_rbResetAnnotation, 0, wxALL, 3 );
 	
+	m_rbResetButLock = new wxRadioButton( this, ID_RESET_BUT_LOCK, _("R&eset existing annotation, but do not swap any units"), wxDefaultPosition, wxDefaultSize, 0 );
+	bscopeOptSizer->Add( m_rbResetButLock, 0, wxALL, 3 );
+	
 	
 	bupperSizer->Add( bscopeOptSizer, 0, wxEXPAND|wxLEFT|wxALIGN_RIGHT, 25 );
 	
diff --git a/eeschema/dialogs/dialog_annotate_base.fbp b/eeschema/dialogs/dialog_annotate_base.fbp
index ebf4f0f..f99d1a1 100644
--- a/eeschema/dialogs/dialog_annotate_base.fbp
+++ b/eeschema/dialogs/dialog_annotate_base.fbp
@@ -627,6 +627,94 @@
                                         <event name="OnUpdateUI"></event>
                                     </object>
                                 </object>
+                                <object class="sizeritem" expanded="1">
+                                    <property name="border">3</property>
+                                    <property name="flag">wxALL</property>
+                                    <property name="proportion">0</property>
+                                    <object class="wxRadioButton" expanded="1">
+                                        <property name="BottomDockable">1</property>
+                                        <property name="LeftDockable">1</property>
+                                        <property name="RightDockable">1</property>
+                                        <property name="TopDockable">1</property>
+                                        <property name="aui_layer"></property>
+                                        <property name="aui_name"></property>
+                                        <property name="aui_position"></property>
+                                        <property name="aui_row"></property>
+                                        <property name="best_size"></property>
+                                        <property name="bg"></property>
+                                        <property name="caption"></property>
+                                        <property name="caption_visible">1</property>
+                                        <property name="center_pane">0</property>
+                                        <property name="close_button">1</property>
+                                        <property name="context_help"></property>
+                                        <property name="context_menu">1</property>
+                                        <property name="default_pane">0</property>
+                                        <property name="dock">Dock</property>
+                                        <property name="dock_fixed">0</property>
+                                        <property name="docking">Left</property>
+                                        <property name="enabled">1</property>
+                                        <property name="fg"></property>
+                                        <property name="floatable">1</property>
+                                        <property name="font"></property>
+                                        <property name="gripper">0</property>
+                                        <property name="hidden">0</property>
+                                        <property name="id">ID_RESET_BUT_LOCK</property>
+                                        <property name="label">R&amp;eset existing annotation, but do not swap any units</property>
+                                        <property name="max_size"></property>
+                                        <property name="maximize_button">0</property>
+                                        <property name="maximum_size"></property>
+                                        <property name="min_size"></property>
+                                        <property name="minimize_button">0</property>
+                                        <property name="minimum_size"></property>
+                                        <property name="moveable">1</property>
+                                        <property name="name">m_rbResetButLock</property>
+                                        <property name="pane_border">1</property>
+                                        <property name="pane_position"></property>
+                                        <property name="pane_size"></property>
+                                        <property name="permission">protected</property>
+                                        <property name="pin_button">1</property>
+                                        <property name="pos"></property>
+                                        <property name="resize">Resizable</property>
+                                        <property name="show">1</property>
+                                        <property name="size"></property>
+                                        <property name="style"></property>
+                                        <property name="subclass"></property>
+                                        <property name="toolbar_pane">0</property>
+                                        <property name="tooltip"></property>
+                                        <property name="validator_data_type"></property>
+                                        <property name="validator_style">wxFILTER_NONE</property>
+                                        <property name="validator_type">wxDefaultValidator</property>
+                                        <property name="validator_variable"></property>
+                                        <property name="value">0</property>
+                                        <property name="window_extra_style"></property>
+                                        <property name="window_name"></property>
+                                        <property name="window_style"></property>
+                                        <event name="OnChar"></event>
+                                        <event name="OnEnterWindow"></event>
+                                        <event name="OnEraseBackground"></event>
+                                        <event name="OnKeyDown"></event>
+                                        <event name="OnKeyUp"></event>
+                                        <event name="OnKillFocus"></event>
+                                        <event name="OnLeaveWindow"></event>
+                                        <event name="OnLeftDClick"></event>
+                                        <event name="OnLeftDown"></event>
+                                        <event name="OnLeftUp"></event>
+                                        <event name="OnMiddleDClick"></event>
+                                        <event name="OnMiddleDown"></event>
+                                        <event name="OnMiddleUp"></event>
+                                        <event name="OnMotion"></event>
+                                        <event name="OnMouseEvents"></event>
+                                        <event name="OnMouseWheel"></event>
+                                        <event name="OnPaint"></event>
+                                        <event name="OnRadioButton"></event>
+                                        <event name="OnRightDClick"></event>
+                                        <event name="OnRightDown"></event>
+                                        <event name="OnRightUp"></event>
+                                        <event name="OnSetFocus"></event>
+                                        <event name="OnSize"></event>
+                                        <event name="OnUpdateUI"></event>
+                                    </object>
+                                </object>
                             </object>
                         </object>
                         <object class="sizeritem" expanded="1">
diff --git a/eeschema/dialogs/dialog_annotate_base.h b/eeschema/dialogs/dialog_annotate_base.h
index a6161ee..3fce47b 100644
--- a/eeschema/dialogs/dialog_annotate_base.h
+++ b/eeschema/dialogs/dialog_annotate_base.h
@@ -1,5 +1,5 @@
 ///////////////////////////////////////////////////////////////////////////
-// C++ code generated with wxFormBuilder (version Jun  6 2014)
+// C++ code generated with wxFormBuilder (version Mar 13 2015)
 // http://www.wxformbuilder.org/
 //
 // PLEASE DO "NOT" EDIT THIS FILE!
@@ -37,9 +37,10 @@ class DIALOG_SHIM;
 #define ID_CURRENT_PAGE 1001
 #define ID_KEEP_ANNOTATION 1002
 #define ID_RESET_ANNOTATION 1003
-#define ID_SORT_BY_X_POSITION 1004
-#define ID_SORT_BY_Y_POSITION 1005
-#define ID_CLEAR_ANNOTATION_CMP 1006
+#define ID_RESET_BUT_LOCK 1004
+#define ID_SORT_BY_X_POSITION 1005
+#define ID_SORT_BY_Y_POSITION 1006
+#define ID_CLEAR_ANNOTATION_CMP 1007
 
 ///////////////////////////////////////////////////////////////////////////////
 /// Class DIALOG_ANNOTATE_BASE
@@ -55,6 +56,7 @@ class DIALOG_ANNOTATE_BASE : public DIALOG_SHIM
 		wxStaticLine* m_staticline1;
 		wxRadioButton* m_rbKeepAnnotation;
 		wxRadioButton* m_rbResetAnnotation;
+		wxRadioButton* m_rbResetButLock;
 		wxStaticLine* m_staticline2;
 		wxStaticText* m_staticTextOrder;
 		wxRadioButton* m_rbSortBy_X_Position;
diff --git a/eeschema/sch_reference_list.h b/eeschema/sch_reference_list.h
index 7133d4b..31e8157 100644
--- a/eeschema/sch_reference_list.h
+++ b/eeschema/sch_reference_list.h
@@ -39,6 +39,10 @@
 #include <sch_component.h>
 #include <sch_text.h>
 
+#include <map>
+
+class SCH_REFERENCE;
+class SCH_REFERENCE_LIST;
 
 /**
  * Class SCH_REFERENCE
@@ -136,6 +140,17 @@ public:
         return m_Ref.c_str();
     }
 
+    /**
+     * Function GetRefAndPath
+     * a SCH_REFERENCE_AND_PATH pair is used to identify a complete component
+     * instance, including reference designator and sheet path.
+     */
+    SCH_REFERENCE_AND_PATH GetRefAndPath() const
+    {
+        SCH_REFERENCE_AND_PATH refAndPath( GetRef(), GetSheetPath().Path() );
+        return refAndPath;
+    }
+
     int CompareValue( const SCH_REFERENCE& item ) const
     {
         return Cmp_KEEPCASE( m_Value->GetText(), item.m_Value->GetText() );
@@ -151,6 +166,16 @@ public:
         return Cmp_KEEPCASE( m_RootCmp->GetPartName(), item.m_RootCmp->GetPartName() );
     }
 
+    /**
+     * Function IsSameInstance
+     * returns whether this reference refers to the same component instance
+     * (component and sheet) as another.
+     */
+    bool IsSameInstance( const SCH_REFERENCE& other ) const
+    {
+        return GetComp() == other.GetComp() && GetSheetPath().Path() == other.GetSheetPath().Path();
+    }
+
     bool IsUnitsLocked()
     {
         return m_Entry->UnitsLocked();
@@ -272,6 +297,9 @@ public:
      * @param aUseSheetNum Set to true to start annotation for each sheet at the sheet number
      *                     times \a aSheetIntervalId.  Otherwise annotate incrementally.
      * @param aSheetIntervalId The per sheet reference designator multiplier.
+     * @param aLockedUnitMap A SCH_MULTI_UNIT_REFERENCE_MAP of reference designator wxStrings
+     *      to SCH_REFERENCE_LISTs. May be an empty map. If not empty, any multi-unit parts
+     *      found in this map will be annotated as a group rather than individually.
      * <p>
      * If a the sheet number is 2 and \a aSheetIntervalId is 100, then the first reference
      * designator would be 201 and the last reference designator would be 299 when no overlap
@@ -279,7 +307,7 @@ public:
      * referenced U201 to U351, and items in sheet 3 start from U352
      * </p>
      */
-    void Annotate( bool aUseSheetNum, int aSheetIntervalId );
+    void Annotate( bool aUseSheetNum, int aSheetIntervalId, SCH_MULTI_UNIT_REFERENCE_MAP aLockedUnitMap );
 
     /**
      * Function CheckAnnotation
@@ -449,5 +477,4 @@ private:
     int CreateFirstFreeRefId( std::vector<int>& aIdList, int aFirstValue );
 };
 
-
 #endif    // _SCH_REFERENCE_LIST_H_
diff --git a/eeschema/sch_sheet_path.cpp b/eeschema/sch_sheet_path.cpp
index 28900e4..d719ad5 100644
--- a/eeschema/sch_sheet_path.cpp
+++ b/eeschema/sch_sheet_path.cpp
@@ -44,6 +44,8 @@
 
 #include <dialogs/dialog_schematic_find.h>
 
+#include <boost/foreach.hpp>
+
 
 SCH_SHEET_PATH::SCH_SHEET_PATH()
 {
@@ -299,11 +301,12 @@ void SCH_SHEET_PATH::GetComponents( PART_LIBS* aLibs, SCH_REFERENCE_LIST& aRefer
             SCH_COMPONENT* component = (SCH_COMPONENT*) item;
 
             // Skip pseudo components, which have a reference starting with #.  This mainly
-            // effects power symbols.
+            // affects power symbols.
             if( !aIncludePowerSymbols && component->GetRef( this )[0] == wxT( '#' ) )
                 continue;
 
-            if( LIB_PART* part = aLibs->FindLibPart( component->GetPartName() ) )
+            LIB_PART* part = aLibs->FindLibPart( component->GetPartName() );
+            if( part )
             {
                 SCH_REFERENCE reference = SCH_REFERENCE( component, part, *this );
                 reference.SetSheetNumber( sheetnumber );
@@ -313,6 +316,40 @@ void SCH_SHEET_PATH::GetComponents( PART_LIBS* aLibs, SCH_REFERENCE_LIST& aRefer
     }
 }
 
+void SCH_SHEET_PATH::GetMultiUnitComponents( PART_LIBS* aLibs, SCH_MULTI_UNIT_REFERENCE_MAP &aRefList,
+       bool aIncludePowerSymbols )
+{
+    // Find sheet path number
+    int sheetnumber = 1; // 1 = root
+
+    SCH_SHEET_LIST sheetList;
+
+    for( SCH_SHEET_PATH* path = sheetList.GetFirst(); path; path = sheetList.GetNext(), sheetnumber++ )
+    {
+        if( Cmp( *path ) == 0 )
+            break;
+    }
+
+    for( SCH_ITEM* item = LastDrawList(); item; item = item->Next() )
+    {
+        if( item->Type() != SCH_COMPONENT_T ) continue;
+        SCH_COMPONENT* component = (SCH_COMPONENT*) item;
+
+        // Skip pseudo components, which have a reference starting with #.  This mainly
+        // affects power symbols.
+        if( !aIncludePowerSymbols && component->GetRef( this )[0] == wxT( '#' ) )
+            continue;
+
+        LIB_PART* part = aLibs->FindLibPart( component->GetPartName() );
+        if( part && part->GetUnitCount() > 1 )
+        {
+            SCH_REFERENCE reference = SCH_REFERENCE( component, part, *this );
+            reference.SetSheetNumber( sheetnumber );
+            aRefList[reference.GetRefAndPath()].AddItem( reference );
+        }
+    }
+}
+
 
 SCH_ITEM* SCH_SHEET_PATH::FindNextItem( KICAD_T aType, SCH_ITEM* aLastItem, bool aWrap ) const
 {
@@ -606,6 +643,25 @@ void SCH_SHEET_LIST::GetComponents( PART_LIBS* aLibs, SCH_REFERENCE_LIST& aRefer
         path->GetComponents( aLibs, aReferences, aIncludePowerSymbols );
 }
 
+void SCH_SHEET_LIST::GetMultiUnitComponents( PART_LIBS* aLibs,
+        SCH_MULTI_UNIT_REFERENCE_MAP &aRefList, bool aIncludePowerSymbols )
+{
+    for( SCH_SHEET_PATH* path = GetFirst(); path; path = GetNext() )
+    {
+        SCH_MULTI_UNIT_REFERENCE_MAP tempMap;
+        path->GetMultiUnitComponents( aLibs, tempMap );
+        BOOST_FOREACH( SCH_MULTI_UNIT_REFERENCE_MAP::value_type& pair, tempMap )
+        {
+            // Merge this list into the main one
+            unsigned n_refs = pair.second.GetCount();
+            for( unsigned thisRef = 0; thisRef < n_refs; ++thisRef )
+            {
+                aRefList[pair.first].AddItem( pair.second[thisRef] );
+            }
+        }
+    }
+}
+
 
 SCH_ITEM* SCH_SHEET_LIST::FindNextItem( KICAD_T aType, SCH_SHEET_PATH** aSheetFoundIn,
                                         SCH_ITEM* aLastItem, bool aWrap )
diff --git a/eeschema/sch_sheet_path.h b/eeschema/sch_sheet_path.h
index d5bb231..23d6164 100644
--- a/eeschema/sch_sheet_path.h
+++ b/eeschema/sch_sheet_path.h
@@ -85,6 +85,18 @@ class SCH_ITEM;
 class SCH_REFERENCE_LIST;
 class PART_LIBS;
 
+/**
+ * Type SCH_REFERENCE_AND_PATH
+ * holds a pair of wxStrings to identify a component instance including
+ * reference designator and sheet path.
+ */
+typedef std::pair<wxString, wxString> SCH_REFERENCE_AND_PATH;
+
+/**
+ * Type SCH_MULTI_UNIT_REFERENCE_MAP
+ * is used to create a map of reference designators for multi-unit parts.
+ */
+typedef std::map<SCH_REFERENCE_AND_PATH, SCH_REFERENCE_LIST> SCH_MULTI_UNIT_REFERENCE_MAP;
 
 /**
  * Class SCH_SHEET_PATH
@@ -230,6 +242,18 @@ public:
                         bool aIncludePowerSymbols = true  );
 
     /**
+     * Function GetMultiUnitComponents
+     * adds a SCH_REFERENCE_LIST object to \a aRefList for each same-reference set of
+     * multi-unit parts in the sheet. The map key for each element will be the
+     * reference designator.
+     * @param aLibs the library list to use
+     * @param aRefList Map of reference designators to reference lists
+     * @param aIncludePowerSymbols : false to only get normal components.
+     */
+    void GetMultiUnitComponents( PART_LIBS* aLibs, SCH_MULTI_UNIT_REFERENCE_MAP &aRefList,
+                                 bool aIncludePowerSymbols = true );
+
+    /**
      * Function SetFootprintField
      * searches last sheet in the path for a component with \a aReference and set the footprint
      * field to \a aFootPrint if found.
@@ -415,6 +439,18 @@ public:
     void GetComponents( PART_LIBS* aLibs, SCH_REFERENCE_LIST& aReferences, bool aIncludePowerSymbols = true  );
 
     /**
+     * Function GetMultiUnitComponents
+     * adds a SCH_REFERENCE_LIST object to \a aRefList for each same-reference set of
+     * multi-unit parts in the list of sheets. The map key for each element will be the
+     * reference designator.
+     * @param aLibs the library list to use
+     * @param aRefList Map of reference designators to reference lists
+     * @param aIncludePowerSymbols Set to false to only get normal components.
+     */
+    void GetMultiUnitComponents( PART_LIBS* aLibs, SCH_MULTI_UNIT_REFERENCE_MAP &aRefList,
+            bool aIncludePowerSymbols = true );
+
+    /**
      * Function FindNextItem
      * searches the entire schematic for the next schematic object.
      *
diff --git a/eeschema/schframe.h b/eeschema/schframe.h
index c09f0c3..d1702e0 100644
--- a/eeschema/schframe.h
+++ b/eeschema/schframe.h
@@ -30,7 +30,6 @@
 #ifndef  WX_EESCHEMA_STRUCT_H
 #define  WX_EESCHEMA_STRUCT_H
 
-
 #include <sch_base_frame.h>
 #include <config_params.h>
 #include <class_undoredo_container.h>
@@ -522,8 +521,8 @@ public:
     /**
      * Function DeleteAnnotation
      * clears the current component annotation.
-     * @param aCurrentSheetOnly Clear the entire schematic annotation if true.  Otherwise
-     *                          only clear the annotation for the current sheet.
+     * @param aCurrentSheetOnly Clear only the annotation for the current sheet if true.
+     *                          Otherwise clear the entire schematic annotation.
      */
     void DeleteAnnotation( bool aCurrentSheetOnly );
 
@@ -542,6 +541,14 @@ public:
      *                          Otherwise, keep the existing time stamps.  This option
      *                          could change previous annotation because time stamps are
      *                          used to handle annotation in complex hierarchies.
+     * @param aLockUnits    When both aLockUnits and aResetAnnotation are true, all unit
+     *                          associations should be kept when reannotating. That is, if
+     *                          two components were R8A and R8B, they may become R3A and R3B,
+     *                          but not R3A and R3C or R3C and R4D.
+     *                          When aResetAnnotation is true but aLockUnits is false, the
+     *                          usual behavior of annotating each part individually is
+     *                          performed.
+     *                          When aResetAnnotation is false, this option has no effect.
      *
      * When the sheet number is used in annotation, each sheet annotation starts from sheet
      * number * 100.  In other words the first sheet uses 100 to 199, the second sheet uses
@@ -549,7 +556,7 @@ public:
      */
     void AnnotateComponents( bool aAnnotateSchematic, ANNOTATE_ORDER_T aSortOption,
                              ANNOTATE_OPTION_T aAlgoOption, bool aResetAnnotation,
-                             bool aRepairTimestamps );
+                             bool aRepairTimestamps, bool aLockUnits );
 
     /**
      * Function CheckAnnotate

Follow ups

References