summaryrefslogtreecommitdiff
path: root/eeschema/dialogs/dialog_lib_edit_pin_table.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'eeschema/dialogs/dialog_lib_edit_pin_table.cpp')
-rw-r--r--eeschema/dialogs/dialog_lib_edit_pin_table.cpp584
1 files changed, 584 insertions, 0 deletions
diff --git a/eeschema/dialogs/dialog_lib_edit_pin_table.cpp b/eeschema/dialogs/dialog_lib_edit_pin_table.cpp
new file mode 100644
index 0000000..dc39b53
--- /dev/null
+++ b/eeschema/dialogs/dialog_lib_edit_pin_table.cpp
@@ -0,0 +1,584 @@
+#include "dialog_lib_edit_pin_table.h"
+
+#include "lib_pin.h"
+
+#include <boost/algorithm/string/join.hpp>
+#include <queue>
+
+/* Avoid wxWidgets bug #16906 -- http://trac.wxwidgets.org/ticket/16906
+ *
+ * If multiple elements live in the root of a wxDataViewCtrl, using
+ * ItemsAdded() can run into an assertion failure. To avoid this, we avoid
+ * notifying the widget of changes, but rather reinitialize it.
+ *
+ * When a fix for this exists in wxWidgets, this is the place to turn it
+ * off.
+ */
+#define REASSOCIATE_HACK
+
+class DIALOG_LIB_EDIT_PIN_TABLE::DataViewModel :
+ public wxDataViewModel
+{
+public:
+ DataViewModel( LIB_PART& aPart );
+
+ // wxDataViewModel
+ virtual unsigned int GetColumnCount() const;
+ virtual wxString GetColumnType( unsigned int col ) const;
+ virtual void GetValue( wxVariant&, const wxDataViewItem&, unsigned int ) const;
+ virtual bool SetValue( const wxVariant&, const wxDataViewItem&, unsigned int );
+ virtual wxDataViewItem GetParent( const wxDataViewItem& ) const;
+ virtual bool IsContainer( const wxDataViewItem& ) const;
+ virtual bool HasContainerColumns( const wxDataViewItem& ) const;
+ virtual unsigned int GetChildren( const wxDataViewItem&, wxDataViewItemArray& ) const;
+
+ virtual int Compare( const wxDataViewItem& lhs,
+ const wxDataViewItem& rhs,
+ unsigned int col,
+ bool ascending ) const;
+
+ void SetGroupingColumn( int aCol );
+ void CalculateGrouping();
+ void Refresh();
+
+#ifdef REASSOCIATE_HACK
+ void SetWidget( wxDataViewCtrl* aWidget ) { m_Widget = aWidget; }
+#endif
+
+ enum
+ {
+ NONE = -1,
+ PIN_NUMBER = 0,
+ PIN_NAME = 1,
+ PIN_TYPE = 2,
+ PIN_POSITION = 3
+ };
+
+private:
+ LIB_PART& m_Part;
+ LIB_PINS m_Backing;
+ int m_GroupingColumn;
+ int m_UnitCount;
+
+ class Item;
+ class Group;
+ class Pin;
+
+ mutable std::list<Pin> m_Pins;
+ mutable std::map<wxString, Group> m_Groups;
+
+#ifdef REASSOCIATE_HACK
+ wxDataViewCtrl* m_Widget;
+#endif
+};
+
+class DIALOG_LIB_EDIT_PIN_TABLE::DataViewModel::Item
+{
+public:
+ virtual void GetValue( wxVariant& aValue, unsigned int aCol ) const = 0;
+ virtual wxDataViewItem GetParent() const = 0;
+ virtual bool IsContainer() const = 0;
+ virtual unsigned int GetChildren( wxDataViewItemArray& ) const = 0;
+};
+
+class DIALOG_LIB_EDIT_PIN_TABLE::DataViewModel::Group :
+ public Item
+{
+public:
+ Group( unsigned int aGroupingColumn ) : m_GroupingColumn( aGroupingColumn ) {}
+
+ virtual void GetValue( wxVariant& aValue, unsigned int aCol ) const;
+
+ virtual wxDataViewItem GetParent() const { return wxDataViewItem(); }
+ virtual bool IsContainer() const { return true; }
+ virtual unsigned int GetChildren( wxDataViewItemArray& aItems ) const
+ {
+ /// @todo C++11
+ for( std::list<Pin*>::const_iterator i = m_Members.begin(); i != m_Members.end(); ++i )
+ aItems.push_back( wxDataViewItem( *i ) );
+
+ return aItems.size();
+ }
+
+ unsigned int GetCount() const { return m_Members.size(); }
+ void Add( Pin* aPin );
+
+private:
+ std::list<Pin*> m_Members;
+ unsigned int m_GroupingColumn;
+};
+
+class DIALOG_LIB_EDIT_PIN_TABLE::DataViewModel::Pin :
+ public Item
+{
+public:
+ Pin( DataViewModel& aModel,
+ LIB_PIN* aBacking ) : m_Model( aModel ), m_Backing( aBacking ), m_Group( 0 ) {}
+
+ virtual void GetValue( wxVariant& aValue, unsigned int aCol ) const;
+
+ virtual wxDataViewItem GetParent() const { return wxDataViewItem( m_Group ); }
+ virtual bool IsContainer() const { return false; }
+ virtual unsigned int GetChildren( wxDataViewItemArray& ) const { return 0; }
+
+ void SetGroup( Group* aGroup ) { m_Group = aGroup; }
+
+private:
+ DataViewModel& m_Model;
+ LIB_PIN* m_Backing;
+ Group* m_Group;
+};
+
+DIALOG_LIB_EDIT_PIN_TABLE::DIALOG_LIB_EDIT_PIN_TABLE( wxWindow* parent,
+ LIB_PART& aPart ) :
+ DIALOG_LIB_EDIT_PIN_TABLE_BASE( parent ),
+ m_Model( new DataViewModel( aPart ) )
+{
+#ifdef REASSOCIATE_HACK
+ m_Model->SetWidget( m_Pins );
+#endif
+ m_Pins->AssociateModel( m_Model.get() );
+
+ /// @todo wxFormBuilder bug #61 -- move to base once supported
+ wxDataViewTextRenderer* rend0 = new wxDataViewTextRenderer( wxT( "string" ), wxDATAVIEW_CELL_INERT );
+ wxDataViewColumn* col0 = new wxDataViewColumn( _( "Number" ),
+ rend0,
+ DataViewModel::PIN_NUMBER,
+ 100,
+ wxAlignment( wxALIGN_LEFT | wxALIGN_TOP ),
+ wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_SORTABLE );
+ wxDataViewTextRenderer* rend1 = new wxDataViewTextRenderer( wxT( "string" ), wxDATAVIEW_CELL_INERT );
+ wxDataViewColumn* col1 = new wxDataViewColumn( _( "Name" ),
+ rend1,
+ DataViewModel::PIN_NAME,
+ 100,
+ wxAlignment( wxALIGN_LEFT | wxALIGN_TOP ),
+ wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_SORTABLE );
+ wxDataViewTextRenderer* rend2 = new wxDataViewTextRenderer( wxT( "string" ), wxDATAVIEW_CELL_INERT );
+ wxDataViewColumn* col2 = new wxDataViewColumn( _( "Type" ),
+ rend2,
+ DataViewModel::PIN_TYPE,
+ 100,
+ wxAlignment( wxALIGN_LEFT | wxALIGN_TOP ),
+ wxDATAVIEW_COL_RESIZABLE );
+ wxDataViewTextRenderer* rend3 = new wxDataViewTextRenderer( wxT( "string" ), wxDATAVIEW_CELL_INERT );
+ wxDataViewColumn* col3 = new wxDataViewColumn( _( "Position" ),
+ rend3,
+ DataViewModel::PIN_POSITION,
+ 100,
+ wxAlignment( wxALIGN_LEFT | wxALIGN_TOP ),
+ wxDATAVIEW_COL_RESIZABLE );
+ m_Pins->AppendColumn( col0 );
+ m_Pins->SetExpanderColumn( col0 );
+ m_Pins->AppendColumn( col1 );
+ m_Pins->AppendColumn( col2 );
+ m_Pins->AppendColumn( col3 );
+
+ GetSizer()->SetSizeHints(this);
+ Centre();
+}
+
+
+DIALOG_LIB_EDIT_PIN_TABLE::~DIALOG_LIB_EDIT_PIN_TABLE()
+{
+}
+
+
+void DIALOG_LIB_EDIT_PIN_TABLE::OnColumnHeaderRightClicked( wxDataViewEvent& event )
+{
+ m_Model->SetGroupingColumn( event.GetDataViewColumn()->GetModelColumn() );
+ event.Skip();
+}
+
+
+DIALOG_LIB_EDIT_PIN_TABLE::DataViewModel::DataViewModel( LIB_PART& aPart ) :
+ m_Part( aPart ),
+ m_GroupingColumn( 1 ),
+ m_UnitCount( m_Part.GetUnitCount() )
+{
+#ifdef REASSOCIATE_HACK
+ m_Widget = NULL;
+#endif
+ aPart.GetPins( m_Backing );
+ /// @todo C++11
+ for( LIB_PINS::const_iterator i = m_Backing.begin(); i != m_Backing.end(); ++i )
+ m_Pins.push_back( Pin( *this, *i ) );
+
+ CalculateGrouping();
+}
+
+
+unsigned int DIALOG_LIB_EDIT_PIN_TABLE::DataViewModel::GetColumnCount() const
+{
+ return 4;
+}
+
+
+wxString DIALOG_LIB_EDIT_PIN_TABLE::DataViewModel::GetColumnType( unsigned int aCol ) const
+{
+ return wxT( "string" );
+}
+
+
+void DIALOG_LIB_EDIT_PIN_TABLE::DataViewModel::GetValue( wxVariant& aVal,
+ const wxDataViewItem& aItem,
+ unsigned int aCol ) const
+{
+ assert( aItem.IsOk() );
+
+ reinterpret_cast<Item const*>( aItem.GetID() )->GetValue( aVal, aCol );
+}
+
+
+bool DIALOG_LIB_EDIT_PIN_TABLE::DataViewModel::SetValue( const wxVariant&,
+ const wxDataViewItem&,
+ unsigned int )
+{
+ return false;
+}
+
+
+wxDataViewItem DIALOG_LIB_EDIT_PIN_TABLE::DataViewModel::GetParent( const wxDataViewItem& aItem )
+const
+{
+ assert( aItem.IsOk() );
+
+ return reinterpret_cast<Item const*>( aItem.GetID() )->GetParent();
+}
+
+
+bool DIALOG_LIB_EDIT_PIN_TABLE::DataViewModel::IsContainer( const wxDataViewItem& aItem ) const
+{
+ if( aItem.IsOk() )
+ return reinterpret_cast<Item const*>( aItem.GetID() )->IsContainer();
+ else
+ return true;
+}
+
+
+bool DIALOG_LIB_EDIT_PIN_TABLE::DataViewModel::HasContainerColumns( const wxDataViewItem& ) const
+{
+ return true;
+}
+
+
+unsigned int DIALOG_LIB_EDIT_PIN_TABLE::DataViewModel::GetChildren( const wxDataViewItem& aItem,
+ wxDataViewItemArray& aItems ) const
+{
+ if( !aItem.IsOk() )
+ {
+ for( std::map<wxString, Group>::iterator i = m_Groups.begin(); i != m_Groups.end(); ++i )
+ if( i->second.GetCount() > 1 )
+ aItems.push_back( wxDataViewItem( &i->second ) );
+
+ for( std::list<Pin>::iterator i = m_Pins.begin(); i != m_Pins.end(); ++i )
+ if( !i->GetParent().IsOk() )
+ aItems.push_back( wxDataViewItem( &*i ) );
+
+ return aItems.size();
+ }
+ else
+ return reinterpret_cast<Item const*>( aItem.GetID() )->GetChildren( aItems );
+}
+
+
+namespace {
+wxString GetNextComponent( const wxString& str, wxString::size_type& cursor )
+{
+ if( str.size() <= cursor )
+ return wxEmptyString;
+
+ wxString::size_type begin = cursor;
+
+ wxUniChar c = str[cursor];
+
+ if( isdigit( c ) || c == '+' || c == '-' )
+ {
+ // number, possibly with sign
+ while( ++cursor < str.size() )
+ {
+ c = str[cursor];
+
+ if( isdigit( c ) || c == 'v' || c == 'V' )
+ continue;
+ else
+ break;
+ }
+ }
+ else
+ {
+ while( ++cursor < str.size() )
+ {
+ c = str[cursor];
+
+ if( isdigit( c ) )
+ break;
+ else
+ continue;
+ }
+ }
+
+ return str.substr( begin, cursor - begin );
+}
+
+
+int ComparePinNames( const wxString& lhs, const wxString& rhs )
+{
+ wxString::size_type cursor1 = 0;
+ wxString::size_type cursor2 = 0;
+
+ wxString comp1, comp2;
+
+ for( ; ; )
+ {
+ comp1 = GetNextComponent( lhs, cursor1 );
+ comp2 = GetNextComponent( rhs, cursor2 );
+
+ if( comp1.empty() && comp2.empty() )
+ return 0;
+
+ if( comp1.empty() )
+ return -1;
+
+ if( comp2.empty() )
+ return 1;
+
+ wxUniChar c1 = comp1[0];
+ wxUniChar c2 = comp2[0];
+
+ if( isdigit( c1 ) || c1 == '-' || c1 == '+' )
+ {
+ if( isdigit( c2 ) || c2 == '-' || c2 == '+' )
+ {
+ // numeric comparison
+ wxString::size_type v1 = comp1.find_first_of( "vV" );
+
+ if( v1 != wxString::npos )
+ comp1[v1] = '.';
+
+ wxString::size_type v2 = comp2.find_first_of( "vV" );
+
+ if( v2 != wxString::npos )
+ comp2[v2] = '.';
+
+ double val1, val2;
+
+ comp1.ToDouble( &val1 );
+ comp2.ToDouble( &val2 );
+
+ if( val1 < val2 )
+ return -1;
+
+ if( val1 > val2 )
+ return 1;
+ }
+ else
+ return -1;
+ }
+ else
+ {
+ if( isdigit( c2 ) || c2 == '-' || c2 == '+' )
+ return 1;
+
+ int res = comp1.Cmp( comp2 );
+
+ if( res != 0 )
+ return res;
+ }
+ }
+}
+
+
+class CompareLess
+{
+public:
+ bool operator()( const wxString& lhs, const wxString& rhs )
+ {
+ return ComparePinNames( lhs, rhs ) == -1;
+ }
+};
+}
+
+int DIALOG_LIB_EDIT_PIN_TABLE::DataViewModel::Compare( const wxDataViewItem& aItem1,
+ const wxDataViewItem& aItem2,
+ unsigned int aCol,
+ bool aAscending ) const
+{
+ wxVariant var1;
+
+ GetValue( var1, aItem1, aCol );
+ wxString str1 = var1.GetString();
+
+ wxVariant var2;
+ GetValue( var2, aItem2, aCol );
+ wxString str2 = var2.GetString();
+
+ int res = ComparePinNames( str1, str2 );
+
+ if( res == 0 )
+ res = ( aItem1.GetID() < aItem2.GetID() ) ? -1 : 1;
+
+ return res * ( aAscending ? 1 : -1 );
+}
+
+
+void DIALOG_LIB_EDIT_PIN_TABLE::DataViewModel::SetGroupingColumn( int aCol )
+{
+ if( m_GroupingColumn == aCol )
+ return;
+
+ m_GroupingColumn = aCol;
+
+ Cleared();
+ CalculateGrouping();
+ Refresh();
+}
+
+
+void DIALOG_LIB_EDIT_PIN_TABLE::DataViewModel::CalculateGrouping()
+{
+ m_Groups.clear();
+
+ if( m_GroupingColumn != -1 )
+ {
+ wxVariant value;
+
+ for( std::list<Pin>::iterator i = m_Pins.begin(); i != m_Pins.end(); ++i )
+ {
+ i->GetValue( value, m_GroupingColumn );
+ wxString str = value.GetString();
+ std::map<wxString, Group>::iterator j = m_Groups.find( str );
+
+ if( j == m_Groups.end() )
+ j = m_Groups.insert( std::make_pair( str, m_GroupingColumn ) ).first;
+
+ j->second.Add( &*i );
+ }
+ }
+ else
+ {
+ for( std::list<Pin>::iterator i = m_Pins.begin(); i != m_Pins.end(); ++i )
+ i->SetGroup( 0 );
+ }
+}
+
+
+void DIALOG_LIB_EDIT_PIN_TABLE::DataViewModel::Refresh()
+{
+#ifdef REASSOCIATE_HACK
+ m_Widget->AssociateModel( this );
+#else
+ std::queue<wxDataViewItem> todo;
+ todo.push( wxDataViewItem() );
+
+ while( !todo.empty() )
+ {
+ wxDataViewItem current = todo.front();
+ wxDataViewItemArray items;
+
+ GetChildren( current, items );
+ ItemsAdded( current, items );
+
+ for( wxDataViewItemArray::const_iterator i = items.begin(); i != items.end(); ++i )
+ {
+ if( IsContainer( *i ) )
+ todo.push( *i );
+ }
+
+ todo.pop();
+ }
+
+#endif
+}
+
+
+void DIALOG_LIB_EDIT_PIN_TABLE::DataViewModel::Group::GetValue( wxVariant& aValue,
+ unsigned int aCol ) const
+{
+ if( aCol == m_GroupingColumn )
+ {
+ // shortcut
+ m_Members.front()->GetValue( aValue, aCol );
+ }
+ else
+ {
+ std::set<wxString, CompareLess> values;
+
+ for( std::list<Pin*>::const_iterator i = m_Members.begin(); i != m_Members.end(); ++i )
+ {
+ wxVariant value;
+ (*i)->GetValue( value, aCol );
+ values.insert( value.GetString() );
+ }
+
+ aValue = boost::algorithm::join( values, "," );
+ }
+}
+
+
+void DIALOG_LIB_EDIT_PIN_TABLE::DataViewModel::Group::Add( Pin* aPin )
+{
+ switch( GetCount() )
+ {
+ case 0:
+ aPin->SetGroup( 0 );
+ break;
+
+ case 1:
+ m_Members.front()->SetGroup( this );
+ // fall through
+
+ default:
+ aPin->SetGroup( this );
+ }
+
+ m_Members.push_back( aPin );
+}
+
+
+void DIALOG_LIB_EDIT_PIN_TABLE::DataViewModel::Pin::GetValue( wxVariant& aValue,
+ unsigned int aCol ) const
+{
+ switch( aCol )
+ {
+ case PIN_NUMBER:
+ aValue = m_Backing->GetNumberString();
+ break;
+
+ case PIN_NAME:
+ {
+ if( m_Model.m_UnitCount > 1 )
+ {
+ wxString name;
+ int unit = m_Backing->GetPartNumber();
+
+ if( unit )
+ name << unit;
+ else
+ name << "com";
+
+ name << ':';
+ name << m_Backing->GetName();
+ aValue = name;
+ }
+ else
+ {
+ aValue = m_Backing->GetName();
+ }
+ }
+ break;
+
+ case PIN_TYPE:
+ aValue = m_Backing->GetElectricalTypeName();
+ break;
+
+ case PIN_POSITION:
+ {
+ wxPoint position = m_Backing->GetPosition();
+ wxString value;
+ value << "(" << position.x << "," << position.y << ")";
+ aValue = value;
+ }
+ break;
+ }
+}