/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, you may find one here: * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * or you may search the http://www.gnu.org website for the version 2 license, * or you may write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include "dialog_lib_edit_pin_table.h" #include "lib_pin.h" #include "pin_number.h" #include #include /* 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(); PinNumbers GetAllPinNumbers(); #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 m_Pins; mutable std::map m_Groups; // like GetValue, but always returns a string wxString GetString( const wxDataViewItem&, unsigned int ) const; #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 wxString GetString( 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 wxString GetString( 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::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 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 wxString GetString( 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 ); wxDataViewIconTextRenderer* rend2 = new wxDataViewIconTextRenderer( wxT( "wxDataViewIconText" ), wxDATAVIEW_CELL_INERT ); wxDataViewColumn* col2 = new wxDataViewColumn( _( "Type" ), rend2, DataViewModel::PIN_TYPE, 100, wxAlignment( wxALIGN_LEFT | wxALIGN_TOP ), wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_SORTABLE ); 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 | wxDATAVIEW_COL_SORTABLE ); m_Pins->AppendColumn( col0 ); m_Pins->SetExpanderColumn( col0 ); m_Pins->AppendColumn( col1 ); m_Pins->AppendColumn( col2 ); m_Pins->AppendColumn( col3 ); UpdateSummary(); GetSizer()->SetSizeHints(this); Centre(); } DIALOG_LIB_EDIT_PIN_TABLE::~DIALOG_LIB_EDIT_PIN_TABLE() { } void DIALOG_LIB_EDIT_PIN_TABLE::UpdateSummary() { PinNumbers pins = m_Model->GetAllPinNumbers(); m_Summary->SetValue( pins.GetSummary() ); } 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 { switch( aCol ) { case PIN_NUMBER: return wxT( "string" ); case PIN_NAME: return wxT( "string" ); case PIN_TYPE: return wxT( "wxDataViewIconText" ); case PIN_POSITION: return wxT( "string" ); } assert( ! "Unhandled column" ); return wxT( "" ); } void DIALOG_LIB_EDIT_PIN_TABLE::DataViewModel::GetValue( wxVariant& aVal, const wxDataViewItem& aItem, unsigned int aCol ) const { assert( aItem.IsOk() ); reinterpret_cast( 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( aItem.GetID() )->GetParent(); } bool DIALOG_LIB_EDIT_PIN_TABLE::DataViewModel::IsContainer( const wxDataViewItem& aItem ) const { if( aItem.IsOk() ) return reinterpret_cast( 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::iterator i = m_Groups.begin(); i != m_Groups.end(); ++i ) if( i->second.GetCount() > 1 ) aItems.push_back( wxDataViewItem( &i->second ) ); for( std::list::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( aItem.GetID() )->GetChildren( aItems ); } int DIALOG_LIB_EDIT_PIN_TABLE::DataViewModel::Compare( const wxDataViewItem& aItem1, const wxDataViewItem& aItem2, unsigned int aCol, bool aAscending ) const { wxString str1 = GetString( aItem1, aCol ); wxString str2 = GetString( aItem2, aCol ); int res = PinNumbers::Compare( 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 ) m_GroupingColumn = NONE; else 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::iterator i = m_Pins.begin(); i != m_Pins.end(); ++i ) { wxString str = i->GetString( m_GroupingColumn ); std::map::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::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 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 } PinNumbers DIALOG_LIB_EDIT_PIN_TABLE::DataViewModel::GetAllPinNumbers() { PinNumbers ret; for( std::list::const_iterator i = m_Pins.begin(); i != m_Pins.end(); ++i ) ret.insert( i->GetString( PIN_NUMBER ) ); return ret; } wxString DIALOG_LIB_EDIT_PIN_TABLE::DataViewModel::GetString( const wxDataViewItem& aItem, unsigned int aCol ) const { assert( aItem.IsOk() ); return reinterpret_cast( aItem.GetID() )->GetString( aCol ); } 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 if( aCol != PIN_TYPE ) aValue = GetString( aCol ); else { PinNumbers values; for( std::list::const_iterator i = m_Members.begin(); i != m_Members.end(); ++i ) values.insert( (*i)->GetString( aCol ) ); if( values.size() > 1 ) aValue << wxDataViewIconText( boost::algorithm::join( values, "," ), wxNullIcon ); else m_Members.front()->GetValue( aValue, aCol ); } } wxString DIALOG_LIB_EDIT_PIN_TABLE::DataViewModel::Group::GetString( unsigned int aCol ) const { if( aCol == m_GroupingColumn ) return m_Members.front()->GetString( aCol ); PinNumbers values; for( std::list::const_iterator i = m_Members.begin(); i != m_Members.end(); ++i ) values.insert( (*i)->GetString( aCol ) ); if( values.size() > 1 ) return boost::algorithm::join( values, "," ); else return m_Members.front()->GetString( aCol ); } 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: case PIN_NAME: case PIN_POSITION: aValue = GetString( aCol ); break; case PIN_TYPE: { wxIcon icon; icon.CopyFromBitmap( KiBitmap ( GetBitmap( m_Backing->GetType() ) ) ); aValue << wxDataViewIconText( m_Backing->GetElectricalTypeName(), icon ); } break; } } wxString DIALOG_LIB_EDIT_PIN_TABLE::DataViewModel::Pin::GetString( unsigned int aCol ) const { switch( aCol ) { case PIN_NUMBER: return m_Backing->GetNumberString(); 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(); return name; } else { return m_Backing->GetName(); } case PIN_TYPE: return m_Backing->GetElectricalTypeName(); case PIN_POSITION: { wxPoint position = m_Backing->GetPosition(); wxString value; value << "(" << position.x << "," << position.y << ")"; return value; } } return wxEmptyString; }