← Back to team overview

kicad-developers team mailing list archive

Re: [FEATURE] Component table viewer

 

I have attached two more patches that address issues raised by Tom and JP:

_005:
- Adds OK/CANCEL dialog
- Removes onClose event
- Moves UI updates into onUpdateUI event

_006:
- Fixes editing of "duplicate" components (e.g. components on sheets that
are referenced multiple times)
- Editing one of these components will edit ALL instances of that component
in the table

I will next try to work out a sensible way of grouping the UNDO actions
per-sheet. In the mean time if anyone wants to provide feedback on the
latest patches, I would appreciate that.

Regards,
Oliver

On Tue, Apr 18, 2017 at 11:05 PM, Wayne Stambaugh <stambaughw@xxxxxxxxx>
wrote:

> On 4/18/2017 5:01 AM, Oliver Walters wrote:
> > Wayne,
> >
> > With this in mind, I am unsure how to determine (given a list of
> > components) which sheet they originate in.
>
> All of this information is in the SCH_REFERENCE object.  You will have
> to cross reference the SCH_SHEET_PATH to find the appropriate
> SCH_SCREEN for each object.  SCH_SCREEN (derived from BASE_SCREEN) is
> where the undo/redo stacks reside.
>
> >
> > I need a SCH_EDIT_FRAME* for each component, to work out where to push
> > each undo operation.
>
> You shouldn't need the SCH_EDIT_FRAME to find the undo/redo stack
> objects if you have the SCH_REFERENCE objects.  See above.
>
> >
> > I have a list of SCH_REFERENCE objects, is there a way of determining
> > where each object originates? Could you point me in the right direction?
> >
> > Regards,
> > Oliver
> >
> > On Tue, Apr 18, 2017 at 6:30 AM, Wayne Stambaugh <stambaughw@xxxxxxxxx
> > <mailto:stambaughw@xxxxxxxxx>> wrote:
> >
> >     On 4/17/2017 4:18 PM, Oliver Walters wrote:
> >     > So how do we proceed here? Is there a 'global' undo stack? If not:
> >
> >     Unfortunately there is no global undo stack.  Undo stacks are
> maintained
> >     for each unique SCH_SCREEN (schematic file) object.
> >
> >     >
> >     > A) don't allow changes made in the component table viewer to be
> undone
> >     > B) Make an undo entry for each sheet that has changed symbols
> >     >
> >     > A) is easier but the user would need to quit-without-save to undo
> changes
> >
> >     This is less than desirable
> >
> >     >
> >     > B) is more difficult and doesn't solve the undo operations getting
> out
> >     > of order either, as the user could inject another operation on a
> given
> >     > sheet.
> >
> >     This would be my preference.  Out of order operations are already an
> >     issue so this solution doesn't make that issue any worse.  Undo/redo
> is
> >     only available for the current sheet so the user would have to change
> >     sheets in order to undo anything changed in the component properties
> >     table.
> >
> >     >
> >     > Suggestions?
> >     >
> >     > On 18 Apr 2017 01:26, "Wayne Stambaugh" <stambaughw@xxxxxxxxx
> <mailto:stambaughw@xxxxxxxxx>
> >     > <mailto:stambaughw@xxxxxxxxx <mailto:stambaughw@xxxxxxxxx>>>
> wrote:
> >     >
> >     >     On 4/17/2017 10:21 AM, jp charras wrote:
> >     >     > Le 17/04/2017 à 04:11, Oliver Walters a écrit :
> >     >     >> JP, others,
> >     >     >>
> >     >     >> After further investigation, I have worked out why the
> components
> >     >     with duplicated references were
> >     >     >> displaying incorrectly.
> >     >     >>
> >     >     >> Patch_004 is attached, Thomas can you confirm that it fixes
> the
> >     >     display for you?
> >     >     >>
> >     >     >> Kind Regards,
> >     >     >> Oliver
> >     >     >>
> >     >     >> On Mon, Apr 17, 2017 at 7:53 AM, Oliver Walters
> >     >     <oliver.henry.walters@xxxxxxxxx
> >     <mailto:oliver.henry.walters@xxxxxxxxx>
> >     <mailto:oliver.henry.walters@xxxxxxxxx
> >     <mailto:oliver.henry.walters@xxxxxxxxx>>
> >     >     >
> >     >     > Good work, Oliver!
> >     >     >
> >     >     > I found 2 issues (tested on W7)
> >     >     >
> >     >     > 1 - m_reloadTableButton is not correctly enabled/disabled.
> >     >     > This is due to the way events are managed, and this is OS
> >     dependent.
> >     >     > To avoid this issue, enable/disable it inside a
> wxUpdateUIEvent
> >     >     attached to this button.
> >     >     >
> >     >     > 2 - ESC key and ENTER keys do not dismiss the dialog.
> >     >     > This is due to the fact you do not have a
> >     wxStdDialogButtonSizer,
> >     >     and no OK and Cancel button.
> >     >     > Please, add it and use the OK button (as usual in a dialog)
> to
> >     >     transfer changes to schematic (do not
> >     >     > use a wxCloseEvent to manage that), and obviously Cancel just
> >     >     closes the dialog.
> >     >     > To do this transfer, just  override
> >     TransferDataFromWindow(), that
> >     >     is called by wxWidgets when
> >     >     > closing a dialog by the OK button.
> >     >     >
> >     >     > About other things, undo/redo lists should manage only
> changes
> >     >     made inside the corresponding sheet,
> >     >     > not in other sheets, to avoid inconsistencies and therefore
> >     crashes.
> >     >     >
> >     >
> >     >     This is one of the reasons I've been reluctant to accept code
> that
> >     >     attempts to change the state of a SCH_SCREEN object other than
> the
> >     >     current SCH_SCREEN object.  It exposes a known flaw in our
> >     schematic
> >     >     undo/redo design and I have yet to see anyone update the
> undo/redo
> >     >     SCH_SCREEN stacks correctly.  I see the potential for serious
> >     issues if
> >     >     you do not keep the undo/redo stacks properly synced.  Once
> >     you allow
> >     >     the modification of information in the SCH_SCREEN object other
> >     than the
> >     >     current one, you need to update the undo/redo stack for the
> >     appropriate
> >     >     SCH_SCREEN object.  Otherwise, you wont be able to undo all of
> the
> >     >     changes correctly.
> >     >
> >     >     _______________________________________________
> >     >     Mailing list: https://launchpad.net/~kicad-developers
> >     <https://launchpad.net/~kicad-developers>
> >     >     <https://launchpad.net/~kicad-developers
> >     <https://launchpad.net/~kicad-developers>>
> >     >     Post to     : kicad-developers@xxxxxxxxxxxxxxxxxxx
> >     <mailto:kicad-developers@xxxxxxxxxxxxxxxxxxx>
> >     >     <mailto:kicad-developers@xxxxxxxxxxxxxxxxxxx
> >     <mailto:kicad-developers@xxxxxxxxxxxxxxxxxxx>>
> >     >     Unsubscribe : https://launchpad.net/~kicad-developers
> >     <https://launchpad.net/~kicad-developers>
> >     >     <https://launchpad.net/~kicad-developers
> >     <https://launchpad.net/~kicad-developers>>
> >     >     More help   : https://help.launchpad.net/ListHelp
> >     <https://help.launchpad.net/ListHelp>
> >     >     <https://help.launchpad.net/ListHelp
> >     <https://help.launchpad.net/ListHelp>>
> >     >
> >
> >
>
From 2eddd5057bedc7b7ff70c857510b3a692a4221c7 Mon Sep 17 00:00:00 2001
From: Oliver Walters <oliver.henry.walters@xxxxxxxxx>
Date: Tue, 18 Apr 2017 19:25:02 +1000
Subject: [PATCH 1/2] Reworked UI

- Buttons are enabled/disabled within wxUpdateUI events
- Save/Cancel dialog used to close window and apply (or reject) changes
---
 eeschema/dialogs/dialog_bom_editor.cpp      | 28 +++++++++--------
 eeschema/dialogs/dialog_bom_editor.h        |  8 +++--
 eeschema/dialogs/dialog_bom_editor_base.cpp | 19 ++++++++++--
 eeschema/dialogs/dialog_bom_editor_base.fbp | 47 +++++++++++++++++++++++++++--
 eeschema/dialogs/dialog_bom_editor_base.h   | 11 ++++---
 5 files changed, 90 insertions(+), 23 deletions(-)

diff --git a/eeschema/dialogs/dialog_bom_editor.cpp b/eeschema/dialogs/dialog_bom_editor.cpp
index f4b73b4..19b0c79 100644
--- a/eeschema/dialogs/dialog_bom_editor.cpp
+++ b/eeschema/dialogs/dialog_bom_editor.cpp
@@ -85,7 +85,7 @@ DIALOG_BOM_EDITOR::DIALOG_BOM_EDITOR( SCH_EDIT_FRAME* parent ) :
 
     m_bom->ReloadTable();
 
-    UpdateTitle();
+    Update();
 }
 
 DIALOG_BOM_EDITOR::~DIALOG_BOM_EDITOR()
@@ -98,7 +98,7 @@ DIALOG_BOM_EDITOR::~DIALOG_BOM_EDITOR()
  * work out if we need to save any changed.
  * If so, capture those changes and push them to the undo stack.
  */
-void DIALOG_BOM_EDITOR::OnBomEditorClosed( wxCloseEvent& event )
+bool DIALOG_BOM_EDITOR::TransferDataFromWindow()
 {
     bool saveChanges = false;
 
@@ -115,11 +115,10 @@ void DIALOG_BOM_EDITOR::OnBomEditorClosed( wxCloseEvent& event )
             break;
         // Cancel (do not exit)
         case wxID_CANCEL:
-            event.Veto();
-            return;
+            return false;
         // Do not save, exit
         default:
-            break;
+            return true;
         }
     }
 
@@ -157,7 +156,7 @@ void DIALOG_BOM_EDITOR::OnBomEditorClosed( wxCloseEvent& event )
         m_parent->OnModify();
     }
 
-    Destroy();
+    return true;
 }
 
 /**
@@ -285,7 +284,14 @@ void DIALOG_BOM_EDITOR::OnGroupComponentsToggled( wxCommandEvent& event )
     m_bom->SetColumnGrouping( group );
     m_bom->ReloadTable();
 
-    m_regroupComponentsButton->Enable( group );
+    Update();
+}
+
+void DIALOG_BOM_EDITOR::OnUpdateUI( wxUpdateUIEvent& event )
+{
+    m_regroupComponentsButton->Enable( m_bom->GetColumnGrouping() );
+
+    m_reloadTableButton->Enable( m_bom->HaveFieldsChanged() );
 
     UpdateTitle();
 }
@@ -417,14 +423,13 @@ void DIALOG_BOM_EDITOR::OnExportBOM( wxCommandEvent& event )
 
 void DIALOG_BOM_EDITOR::OnTableValueChanged( wxDataViewEvent& event )
 {
-    m_reloadTableButton->Enable( m_bom->HaveFieldsChanged() );
-
-    UpdateTitle();
+    Update();
 }
 
 void DIALOG_BOM_EDITOR::OnRegroupComponents( wxCommandEvent& event )
 {
     m_bom->ReloadTable();
+    Update();
 }
 
 void DIALOG_BOM_EDITOR::OnRevertFieldChanges( wxCommandEvent& event )
@@ -434,8 +439,7 @@ void DIALOG_BOM_EDITOR::OnRevertFieldChanges( wxCommandEvent& event )
         if( IsOK( this, _( "Revert all component table changes?" ) ) )
         {
             m_bom->RevertFieldChanges();
-            m_reloadTableButton->Enable( m_bom->HaveFieldsChanged() );
-            UpdateTitle();
+            Update();
         }
     }
 }
diff --git a/eeschema/dialogs/dialog_bom_editor.h b/eeschema/dialogs/dialog_bom_editor.h
index 9269134..0f79e98 100644
--- a/eeschema/dialogs/dialog_bom_editor.h
+++ b/eeschema/dialogs/dialog_bom_editor.h
@@ -62,6 +62,9 @@ private:
     void LoadColumnNames( void );
     void ReloadColumns( void );
 
+    // Called when the OK button is pressed
+    virtual bool TransferDataFromWindow() override;
+
     // Checkbox event callbacks
     virtual void OnColumnItemToggled( wxDataViewEvent& event ) override;
     virtual void OnGroupComponentsToggled( wxCommandEvent& event ) override;
@@ -75,9 +78,10 @@ private:
     // Called after a value in the table has changed
     virtual void OnTableValueChanged( wxDataViewEvent& event ) override;
 
-    virtual void OnBomEditorClosed( wxCloseEvent& event ) override;
-
     void UpdateTitle( void );
+
+    virtual void OnUpdateUI( wxUpdateUIEvent& event ) override;
+
 };
 
 #endif /* EESCHEMA_DIALOGS_DIALOG_BOM_EDITOR_H_ */
diff --git a/eeschema/dialogs/dialog_bom_editor_base.cpp b/eeschema/dialogs/dialog_bom_editor_base.cpp
index d5e07a4..d83d34d 100644
--- a/eeschema/dialogs/dialog_bom_editor_base.cpp
+++ b/eeschema/dialogs/dialog_bom_editor_base.cpp
@@ -1,5 +1,5 @@
 ///////////////////////////////////////////////////////////////////////////
-// C++ code generated with wxFormBuilder (version Apr  1 2017)
+// C++ code generated with wxFormBuilder (version Mar 22 2017)
 // http://www.wxformbuilder.org/
 //
 // PLEASE DO "NOT" EDIT THIS FILE!
@@ -10,7 +10,7 @@
 ///////////////////////////////////////////////////////////////////////////
 
 BEGIN_EVENT_TABLE( DIALOG_BOM_EDITOR_BASE, DIALOG_SHIM )
-	EVT_CLOSE( DIALOG_BOM_EDITOR_BASE::_wxFB_OnBomEditorClosed )
+	EVT_UPDATE_UI( wxID_ANY, DIALOG_BOM_EDITOR_BASE::_wxFB_OnUpdateUI )
 	EVT_CHECKBOX( OPT_GROUP_COMPONENTS, DIALOG_BOM_EDITOR_BASE::_wxFB_OnGroupComponentsToggled )
 	EVT_BUTTON( ID_BUTTON_REGROUP, DIALOG_BOM_EDITOR_BASE::_wxFB_OnRegroupComponents )
 	EVT_BUTTON( ID_BUTTON_REVERT_CHANGES, DIALOG_BOM_EDITOR_BASE::_wxFB_OnRevertFieldChanges )
@@ -112,6 +112,21 @@ DIALOG_BOM_EDITOR_BASE::DIALOG_BOM_EDITOR_BASE( wxWindow* parent, wxWindowID id,
 	
 	bSizer6->Add( sbSizer2, 0, wxEXPAND, 5 );
 	
+	wxStaticBoxSizer* sbSizer4;
+	sbSizer4 = new wxStaticBoxSizer( new wxStaticBox( m_leftPanel, wxID_ANY, _("Apply changes") ), wxVERTICAL );
+	
+	m_sdbSizer1 = new wxStdDialogButtonSizer();
+	m_sdbSizer1OK = new wxButton( sbSizer4->GetStaticBox(), wxID_OK );
+	m_sdbSizer1->AddButton( m_sdbSizer1OK );
+	m_sdbSizer1Cancel = new wxButton( sbSizer4->GetStaticBox(), wxID_CANCEL );
+	m_sdbSizer1->AddButton( m_sdbSizer1Cancel );
+	m_sdbSizer1->Realize();
+	
+	sbSizer4->Add( m_sdbSizer1, 1, wxEXPAND, 5 );
+	
+	
+	bSizer6->Add( sbSizer4, 1, wxEXPAND, 5 );
+	
 	
 	m_leftPanel->SetSizer( bSizer6 );
 	m_leftPanel->Layout();
diff --git a/eeschema/dialogs/dialog_bom_editor_base.fbp b/eeschema/dialogs/dialog_bom_editor_base.fbp
index 9d1cf28..2fcc929 100644
--- a/eeschema/dialogs/dialog_bom_editor_base.fbp
+++ b/eeschema/dialogs/dialog_bom_editor_base.fbp
@@ -45,7 +45,7 @@
             <property name="name">DIALOG_BOM_EDITOR_BASE</property>
             <property name="pos"></property>
             <property name="size">1047,649</property>
-            <property name="style">wxDEFAULT_DIALOG_STYLE|wxMAXIMIZE_BOX|wxRESIZE_BORDER</property>
+            <property name="style">wxCAPTION|wxMAXIMIZE_BOX|wxRESIZE_BORDER|wxSTAY_ON_TOP</property>
             <property name="subclass">DIALOG_SHIM; dialog_shim.h</property>
             <property name="title">BOM editor</property>
             <property name="tooltip"></property>
@@ -61,7 +61,7 @@
             <event name="OnAuiPaneRestore"></event>
             <event name="OnAuiRender"></event>
             <event name="OnChar"></event>
-            <event name="OnClose">OnBomEditorClosed</event>
+            <event name="OnClose"></event>
             <event name="OnEnterWindow"></event>
             <event name="OnEraseBackground"></event>
             <event name="OnHibernate"></event>
@@ -87,7 +87,7 @@
             <event name="OnRightUp"></event>
             <event name="OnSetFocus"></event>
             <event name="OnSize"></event>
-            <event name="OnUpdateUI"></event>
+            <event name="OnUpdateUI">OnUpdateUI</event>
             <object class="wxBoxSizer" expanded="1">
                 <property name="minimum_size"></property>
                 <property name="name">bHorizontalSizer</property>
@@ -1037,6 +1037,47 @@
                                                         </object>
                                                     </object>
                                                 </object>
+                                                <object class="sizeritem" expanded="1">
+                                                    <property name="border">5</property>
+                                                    <property name="flag">wxEXPAND</property>
+                                                    <property name="proportion">1</property>
+                                                    <object class="wxStaticBoxSizer" expanded="1">
+                                                        <property name="id">wxID_ANY</property>
+                                                        <property name="label">Apply changes</property>
+                                                        <property name="minimum_size"></property>
+                                                        <property name="name">sbSizer4</property>
+                                                        <property name="orient">wxVERTICAL</property>
+                                                        <property name="parent">1</property>
+                                                        <property name="permission">none</property>
+                                                        <event name="OnUpdateUI"></event>
+                                                        <object class="sizeritem" expanded="1">
+                                                            <property name="border">5</property>
+                                                            <property name="flag">wxEXPAND</property>
+                                                            <property name="proportion">1</property>
+                                                            <object class="wxStdDialogButtonSizer" expanded="1">
+                                                                <property name="Apply">0</property>
+                                                                <property name="Cancel">1</property>
+                                                                <property name="ContextHelp">0</property>
+                                                                <property name="Help">0</property>
+                                                                <property name="No">0</property>
+                                                                <property name="OK">1</property>
+                                                                <property name="Save">0</property>
+                                                                <property name="Yes">0</property>
+                                                                <property name="minimum_size"></property>
+                                                                <property name="name">m_sdbSizer1</property>
+                                                                <property name="permission">protected</property>
+                                                                <event name="OnApplyButtonClick"></event>
+                                                                <event name="OnCancelButtonClick"></event>
+                                                                <event name="OnContextHelpButtonClick"></event>
+                                                                <event name="OnHelpButtonClick"></event>
+                                                                <event name="OnNoButtonClick"></event>
+                                                                <event name="OnOKButtonClick"></event>
+                                                                <event name="OnSaveButtonClick"></event>
+                                                                <event name="OnYesButtonClick"></event>
+                                                            </object>
+                                                        </object>
+                                                    </object>
+                                                </object>
                                             </object>
                                         </object>
                                     </object>
diff --git a/eeschema/dialogs/dialog_bom_editor_base.h b/eeschema/dialogs/dialog_bom_editor_base.h
index 277bcff..87d70ef 100644
--- a/eeschema/dialogs/dialog_bom_editor_base.h
+++ b/eeschema/dialogs/dialog_bom_editor_base.h
@@ -1,5 +1,5 @@
 ///////////////////////////////////////////////////////////////////////////
-// C++ code generated with wxFormBuilder (version Apr  1 2017)
+// C++ code generated with wxFormBuilder (version Mar 22 2017)
 // http://www.wxformbuilder.org/
 //
 // PLEASE DO "NOT" EDIT THIS FILE!
@@ -46,7 +46,7 @@ class DIALOG_BOM_EDITOR_BASE : public DIALOG_SHIM
 	private:
 		
 		// Private event handlers
-		void _wxFB_OnBomEditorClosed( wxCloseEvent& event ){ OnBomEditorClosed( event ); }
+		void _wxFB_OnUpdateUI( wxUpdateUIEvent& event ){ OnUpdateUI( event ); }
 		void _wxFB_OnGroupComponentsToggled( wxCommandEvent& event ){ OnGroupComponentsToggled( event ); }
 		void _wxFB_OnRegroupComponents( wxCommandEvent& event ){ OnRegroupComponents( event ); }
 		void _wxFB_OnRevertFieldChanges( wxCommandEvent& event ){ OnRevertFieldChanges( event ); }
@@ -69,11 +69,14 @@ class DIALOG_BOM_EDITOR_BASE : public DIALOG_SHIM
 		wxCheckBox* m_includeProjectData;
 		wxCheckBox* m_showRowNumbers;
 		wxButton* m_exportButton;
+		wxStdDialogButtonSizer* m_sdbSizer1;
+		wxButton* m_sdbSizer1OK;
+		wxButton* m_sdbSizer1Cancel;
 		wxPanel* m_panel4;
 		wxDataViewCtrl* m_bomView;
 		
 		// Virtual event handlers, overide them in your derived class
-		virtual void OnBomEditorClosed( wxCloseEvent& event ) { event.Skip(); }
+		virtual void OnUpdateUI( wxUpdateUIEvent& event ) { event.Skip(); }
 		virtual void OnGroupComponentsToggled( wxCommandEvent& event ) { event.Skip(); }
 		virtual void OnRegroupComponents( wxCommandEvent& event ) { event.Skip(); }
 		virtual void OnRevertFieldChanges( wxCommandEvent& event ) { event.Skip(); }
@@ -87,7 +90,7 @@ class DIALOG_BOM_EDITOR_BASE : public DIALOG_SHIM
 	
 	public:
 		
-		DIALOG_BOM_EDITOR_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("BOM editor"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 1047,649 ), long style = wxDEFAULT_DIALOG_STYLE|wxMAXIMIZE_BOX|wxRESIZE_BORDER ); 
+		DIALOG_BOM_EDITOR_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("BOM editor"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 1047,649 ), long style = wxCAPTION|wxMAXIMIZE_BOX|wxRESIZE_BORDER|wxSTAY_ON_TOP ); 
 		~DIALOG_BOM_EDITOR_BASE();
 		
 		void m_splitter1OnIdle( wxIdleEvent& )
-- 
2.7.4

From 17664cd72287417c5c7e5eb35c8911ed67de68e4 Mon Sep 17 00:00:00 2001
From: Oliver Walters <oliver.henry.walters@xxxxxxxxx>
Date: Tue, 18 Apr 2017 22:43:40 +1000
Subject: [PATCH 2/2] Reworked field association

- Allow duplicate components to access same field data
---
 eeschema/bom_table_model.cpp | 163 ++++++++++++++++++++++++++++++++++---------
 eeschema/bom_table_model.h   |  54 ++++++++++++--
 2 files changed, 178 insertions(+), 39 deletions(-)

diff --git a/eeschema/bom_table_model.cpp b/eeschema/bom_table_model.cpp
index 5301e95..6e8a0f0 100644
--- a/eeschema/bom_table_model.cpp
+++ b/eeschema/bom_table_model.cpp
@@ -53,6 +53,86 @@ static BOM_TABLE_ROW const* ItemToRow( wxDataViewItem aItem )
     }
 }
 
+BOM_FIELD_VALUES::BOM_FIELD_VALUES( wxString aRefDes ) : m_refDes( aRefDes )
+{
+}
+
+/**
+ * Return the current value for the provided field ID
+ */
+bool BOM_FIELD_VALUES::GetFieldValue( unsigned int aFieldId, wxString& aValue ) const
+{
+    auto search = m_currentValues.find( aFieldId );
+
+    if( search == m_currentValues.end() )
+        return false;
+
+    aValue = search->second;
+
+    return true;
+}
+
+/**
+ * Return the backup value for the provided field ID
+ */
+bool BOM_FIELD_VALUES::GetBackupValue( unsigned int aFieldId, wxString& aValue ) const
+{
+    auto search = m_backupValues.find( aFieldId );
+
+    if( search == m_backupValues.end() )
+        return false;
+
+    aValue = search->second;
+
+    return true;
+}
+
+/**
+ * Set the value for the provided field ID
+ * Field value is set under any of the following conditions:
+ * - param aOverwrite is true
+ * - There is no current value
+ * - The current value is empty
+ */
+void BOM_FIELD_VALUES::SetFieldValue( unsigned int aFieldId, wxString aValue, bool aOverwrite )
+{
+    if( aOverwrite || m_currentValues.count( aFieldId ) == 0 || m_currentValues[aFieldId].IsEmpty() )
+    {
+        m_currentValues[aFieldId] = aValue;
+    }
+}
+
+/**
+ * Set the backup value for the provided field ID
+ * If the backup value is already set, new value is ignored
+ */
+void BOM_FIELD_VALUES::SetBackupValue( unsigned int aFieldId, wxString aValue )
+{
+    if( m_backupValues.count( aFieldId ) == 0 || m_backupValues[aFieldId].IsEmpty() )
+    {
+        m_backupValues[aFieldId] = aValue;
+    }
+}
+
+bool BOM_FIELD_VALUES::HasValueChanged( unsigned int aFieldId) const
+{
+    wxString currentValue, backupValue;
+
+    GetFieldValue( aFieldId, currentValue );
+    GetBackupValue( aFieldId, backupValue );
+
+    return currentValue.Cmp( backupValue ) != 0;
+}
+
+void BOM_FIELD_VALUES::RevertChanges( unsigned int aFieldId )
+{
+    wxString backupValue;
+
+    GetBackupValue( aFieldId, backupValue );
+
+    SetFieldValue( aFieldId, backupValue, true );
+}
+
 BOM_TABLE_ROW::BOM_TABLE_ROW() : m_columnList( nullptr )
 {
 }
@@ -416,10 +496,12 @@ int BOM_TABLE_GROUP::SortValues( const wxString& aFirst, const wxString& aSecond
  * Each COMPONENT row is associated with a single component item.
  */
 BOM_TABLE_COMPONENT::BOM_TABLE_COMPONENT( BOM_TABLE_GROUP* aParent,
-                                          BOM_COLUMN_LIST* aColumnList)
+                                          BOM_COLUMN_LIST* aColumnList,
+                                          BOM_FIELD_VALUES* aFieldValues )
 {
     m_parent = aParent;
     m_columnList = aColumnList;
+    m_fieldValues = aFieldValues;
 }
 
 /**
@@ -471,10 +553,8 @@ bool BOM_TABLE_COMPONENT::AddUnit( SCH_REFERENCE aUnit )
                 break;
             }
 
-            SetFieldValue( column->Id(), value );
-
-            // Add the value to the fallback map
-            m_fallbackData[column->Id()] = value;
+            m_fieldValues->SetFieldValue( column->Id(), value );
+            m_fieldValues->SetBackupValue( column->Id(), value );
         }
 
         return true;
@@ -489,16 +569,22 @@ bool BOM_TABLE_COMPONENT::AddUnit( SCH_REFERENCE aUnit )
  */
 wxString BOM_TABLE_COMPONENT::GetFieldValue( unsigned int aFieldId ) const
 {
-    auto search = m_fieldData.find( aFieldId );
+    wxString value;
 
-    if( search != m_fieldData.end()  )
-    {
-        return search->second;
-    }
-    else
+    switch ( aFieldId )
     {
+    case BOM_COL_ID_QUANTITY:
         return wxEmptyString;
+    case BOM_COL_ID_REFERENCE:
+        return GetReference();
+    default:
+        break;
     }
+
+    if( m_fieldValues )
+        m_fieldValues->GetFieldValue( aFieldId, value );
+
+    return value;
 }
 
 /**
@@ -509,9 +595,9 @@ wxString BOM_TABLE_COMPONENT::GetFieldValue( unsigned int aFieldId ) const
  */
 bool BOM_TABLE_COMPONENT::SetFieldValue( unsigned int aFieldId, const wxString aValue, bool aOverwrite )
 {
-    if( aOverwrite || m_fieldData.count( aFieldId ) == 0 || m_fieldData[aFieldId].IsEmpty() )
+    if( m_fieldValues )
     {
-        m_fieldData[aFieldId] = aValue;
+        m_fieldValues->SetFieldValue( aFieldId, aValue, aOverwrite );
         return true;
     }
 
@@ -550,13 +636,7 @@ bool BOM_TABLE_COMPONENT::HasValueChanged( BOM_COLUMN* aField ) const
         return false;
     }
 
-    auto value = m_fieldData.find( aField->Id() );
-    auto backup = m_fallbackData.find( aField->Id() );
-
-    wxString currentValue = value == m_fieldData.end() ? wxString() : value->second;
-    wxString backupValue = backup == m_fallbackData.end() ? wxString() : backup->second;
-
-    return currentValue.Cmp( backupValue ) != 0;
+    return m_fieldValues->HasValueChanged( aField->Id() );
 }
 
 /**
@@ -634,17 +714,7 @@ void BOM_TABLE_COMPONENT::RevertFieldChanges()
             break;
         }
 
-        if( column && HasValueChanged( column ) )
-        {
-            if( m_fallbackData.count( column->Id() ) > 0 )
-            {
-                m_fieldData[column->Id()] = m_fallbackData[column->Id()];
-            }
-            else
-            {
-                m_fieldData[column->Id()] = wxEmptyString;
-            }
-        }
+        m_fieldValues->RevertChanges( column->Id() );
     }
 }
 
@@ -931,6 +1001,7 @@ void BOM_TABLE_MODEL::SetComponents( SCH_REFERENCE_LIST aRefs )
 
     // Group multi-unit components together
     m_components.clear();
+    m_fieldValues.clear();
 
     for( unsigned int ii=0; ii<aRefs.GetCount(); ii++ )
     {
@@ -949,7 +1020,30 @@ void BOM_TABLE_MODEL::SetComponents( SCH_REFERENCE_LIST aRefs )
 
         if( !found )
         {
-            auto* newComponent = new BOM_TABLE_COMPONENT( nullptr, &ColumnList );
+            // Find the field:value map associated with this component
+            wxString refDes = ref.GetComp()->GetField( REFERENCE )->GetText();
+
+            bool dataFound = false;
+
+            BOM_FIELD_VALUES* values;
+
+            for( auto& data : m_fieldValues )
+            {
+                // Look for a match based on RefDes
+                if( data->GetReference().Cmp( refDes ) == 0 )
+                {
+                    dataFound = true;
+                    values = &*data;
+                }
+            }
+
+            if( !dataFound )
+            {
+                values = new BOM_FIELD_VALUES( refDes );
+                m_fieldValues.push_back( std::unique_ptr<BOM_FIELD_VALUES>( values ) );
+            }
+
+            auto* newComponent = new BOM_TABLE_COMPONENT( nullptr, &ColumnList, values );
             newComponent->AddUnit( ref );
 
             m_components.push_back( std::unique_ptr<BOM_TABLE_COMPONENT>( newComponent ) );
@@ -1090,6 +1184,11 @@ bool BOM_TABLE_MODEL::SetValue(
             }
         }
 
+        if( m_widget )
+        {
+            m_widget->Update();
+        }
+
         return result;
     }
 
diff --git a/eeschema/bom_table_model.h b/eeschema/bom_table_model.h
index 8cf3898..7b160bb 100644
--- a/eeschema/bom_table_model.h
+++ b/eeschema/bom_table_model.h
@@ -43,6 +43,46 @@ class BOM_TABLE_ROW;        // Base-class for table row data model
 class BOM_TABLE_GROUP;      // Class for displaying a group of components
 class BOM_TABLE_COMPONENT;  // Class for displaying a single component
 
+// Map column IDs to field values (for quick lookup)
+typedef std::map<unsigned int, wxString> FIELD_VALUE_MAP;
+
+
+/**
+ * The BOM_FIELD_VALUES class provides quick lookup of component values
+ * (based on Field ID)
+ * This is done for the following reasons:
+ * - Increase lookup speed for table values
+ * - Allow field values to be reverted to original values
+ * - Allow duplicate components to reference the same data
+ */
+class BOM_FIELD_VALUES
+{
+public:
+    BOM_FIELD_VALUES(wxString aRefDes);
+
+    bool GetFieldValue( unsigned int aFieldId, wxString& aValue ) const;
+    bool GetBackupValue( unsigned int aFieldId, wxString& aValue ) const;
+
+    void SetFieldValue( unsigned int aFieldId, wxString aValue, bool aOverwrite = false );
+    void SetBackupValue( unsigned int aFieldId, wxString aValue );
+
+    wxString GetReference() const { return m_refDes; }
+
+    bool HasValueChanged( unsigned int aFieldId ) const;
+
+    void RevertChanges( unsigned int aFieldId );
+
+protected:
+    //! The RefDes to which these values correspond
+    wxString m_refDes;
+
+    //! Current values for each column
+    FIELD_VALUE_MAP m_currentValues;
+
+    //! Backup values for each column
+    FIELD_VALUE_MAP m_backupValues;
+};
+
 /**
  * Virtual base class determining how a row is displayed
  * There are three types of rows:
@@ -144,7 +184,7 @@ public:
     // List of units associated with this component
     std::vector<SCH_REFERENCE> Units;
 
-    BOM_TABLE_COMPONENT( BOM_TABLE_GROUP* aParent, BOM_COLUMN_LIST* aColumnList );
+    BOM_TABLE_COMPONENT( BOM_TABLE_GROUP* aParent, BOM_COLUMN_LIST* aColumnList, BOM_FIELD_VALUES* aValues );
 
     bool AddUnit( SCH_REFERENCE aUnit );
 
@@ -168,13 +208,9 @@ public:
     virtual BOM_TABLE_ROW* GetParent() const override { return m_parent; }
 
 protected:
-    // Initial data for reverting component values
-    std::map<unsigned int, wxString> m_fallbackData;
-
-    // Data as it is updated
-    std::map<unsigned int, wxString> m_fieldData;
-
     BOM_TABLE_GROUP* m_parent;
+
+    BOM_FIELD_VALUES* m_fieldValues;
 };
 
 /**
@@ -189,8 +225,12 @@ class BOM_TABLE_MODEL : public wxDataViewModel
 protected:
     BOM_TABLE_MODEL();
 
+    // Vector of unique component rows
     std::vector<std::unique_ptr<BOM_TABLE_COMPONENT>> m_components;
 
+    // Vector of field values mapped to field IDs
+    std::vector<std::unique_ptr<BOM_FIELD_VALUES>> m_fieldValues;
+
     // BOM Preferences
     //! Group components based on values
     bool m_groupColumns = true;
-- 
2.7.4


References