/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2020 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 #include #include #define WX_DATAVIEW_WINDOW_PADDING 6 BOARD_ITEM* DRC_TREE_MODEL::ToBoardItem( BOARD* aBoard, wxDataViewItem aItem ) { BOARD_ITEM* board_item = nullptr; const DRC_TREE_NODE* node = DRC_TREE_MODEL::ToNode( aItem ); if( node ) { const DRC_ITEM* drc_item = node->m_DrcItem; switch( node->m_Type ) { case DRC_TREE_NODE::MARKER: board_item = static_cast( drc_item->GetParent() ); break; case DRC_TREE_NODE::MAIN_ITEM: board_item = drc_item->GetMainItem( aBoard ); break; case DRC_TREE_NODE::AUX_ITEM: board_item = drc_item->GetAuxiliaryItem( aBoard ); break; } } return board_item; } DRC_TREE_MODEL::DRC_TREE_MODEL( PCB_BASE_FRAME* aParentFrame, wxDataViewCtrl* aView ) : m_parentFrame( aParentFrame ), m_view( aView ), m_drcItemsProvider( nullptr ) { m_view->GetMainWindow()->Connect( wxEVT_SIZE, wxSizeEventHandler( DRC_TREE_MODEL::onSizeView ), NULL, this ); } DRC_TREE_MODEL::~DRC_TREE_MODEL() { delete m_drcItemsProvider; // m_tree is all std::unique_ptr based, so it will look after itself } void DRC_TREE_MODEL::rebuildModel( DRC_ITEMS_PROVIDER* aProvider, int aSeverities ) { wxWindowUpdateLocker updateLock( m_view ); // Even with the updateLock, wxWidgets sometimes ties its knickers in // a knot when trying to run a wxdataview_selection_changed_callback() // on a row that has been deleted. if( m_view ) m_view->UnselectAll(); Cleared(); if( aProvider != m_drcItemsProvider ) { delete m_drcItemsProvider; m_drcItemsProvider = aProvider; } if( aSeverities != m_severities ) m_severities = aSeverities; if( m_drcItemsProvider ) m_drcItemsProvider->SetSeverities( m_severities ); m_tree.clear(); #define PUSH_NODE( p, item, type ) push_back( std::make_unique( p, item, type ) ) for( int i = 0; m_drcItemsProvider && i < m_drcItemsProvider->GetCount(); ++i ) { DRC_ITEM* drcItem = m_drcItemsProvider->GetItem( i ); m_tree.PUSH_NODE( nullptr, drcItem, DRC_TREE_NODE::MARKER ); DRC_TREE_NODE* node = m_tree.back().get(); node->m_Children.PUSH_NODE( node, drcItem, DRC_TREE_NODE::MAIN_ITEM ); if( drcItem->GetAuxItemID() != niluuid ) node->m_Children.PUSH_NODE( node, drcItem, DRC_TREE_NODE::AUX_ITEM ); } #ifdef __WXGTK__ // The fastest method to update wxDataViewCtrl is to rebuild from // scratch by calling Cleared(). Linux requires to reassociate model to // display data, but Windows will create multiple associations. // On MacOS, this crashes kicad. See https://gitlab.com/kicad/code/kicad/issues/3666 // and https://gitlab.com/kicad/code/kicad/issues/3653 m_view->AssociateModel( this ); #endif m_view->ClearColumns(); int width = m_view->GetMainWindow()->GetRect().GetWidth() - WX_DATAVIEW_WINDOW_PADDING; m_view->AppendTextColumn( wxEmptyString, 0, wxDATAVIEW_CELL_INERT, width ); ExpandAll(); } void DRC_TREE_MODEL::SetProvider( DRC_ITEMS_PROVIDER* aProvider ) { rebuildModel( aProvider, m_severities ); } void DRC_TREE_MODEL::SetSeverities( int aSeverities ) { rebuildModel( m_drcItemsProvider, aSeverities ); } void DRC_TREE_MODEL::ExpandAll() { for( std::unique_ptr& markerNode : m_tree ) m_view->Expand( ToItem( markerNode.get() ) ); } bool DRC_TREE_MODEL::IsContainer( wxDataViewItem const& aItem ) const { if( ToNode( aItem ) == nullptr ) // must be tree root... return true; else return ToNode( aItem )->m_Type == DRC_TREE_NODE::MARKER; } wxDataViewItem DRC_TREE_MODEL::GetParent( wxDataViewItem const& aItem ) const { return ToItem( ToNode( aItem)->m_Parent ); } unsigned int DRC_TREE_MODEL::GetChildren( wxDataViewItem const& aItem, wxDataViewItemArray& aChildren ) const { const DRC_TREE_NODE* p = ToNode( aItem ); const std::vector>& children = p ? p->m_Children : m_tree; for( const std::unique_ptr& child: children ) aChildren.Add( ToItem( child.get() ) ); return children.size(); } /** * Called by the wxDataView to fetch an item's value. */ void DRC_TREE_MODEL::GetValue( wxVariant& aVariant, wxDataViewItem const& aItem, unsigned int aCol ) const { const DRC_TREE_NODE* node = ToNode( aItem ); const DRC_ITEM* drcItem = node->m_DrcItem; switch( node->m_Type ) { case DRC_TREE_NODE::MARKER: { auto& bds = m_parentFrame->GetBoard()->GetDesignSettings(); bool excluded = drcItem->GetParent() && drcItem->GetParent()->IsExcluded(); bool error = bds.m_DRCSeverities[ drcItem->GetErrorCode() ] == RPT_SEVERITY_ERROR; wxString prefix = wxString::Format( wxT( "%s%s" ), excluded ? _( "Excluded " ) : wxString( "" ), error ? _( "Error: " ) : _( "Warning: " ) ); aVariant = prefix + drcItem->GetErrorText(); } break; case DRC_TREE_NODE::MAIN_ITEM: aVariant = drcItem->GetMainText(); break; case DRC_TREE_NODE::AUX_ITEM: aVariant = drcItem->GetAuxiliaryText(); break; } } /** * Called by the wxDataView to fetch an item's formatting. Return true iff the * item has non-default attributes. */ bool DRC_TREE_MODEL::GetAttr( wxDataViewItem const& aItem, unsigned int aCol, wxDataViewItemAttr& aAttr ) const { const DRC_TREE_NODE* node = ToNode( aItem ); wxASSERT( node ); bool ret = false; bool heading = node->m_Type == DRC_TREE_NODE::MARKER; if( heading ) { aAttr.SetBold( true ); ret = true; } if( node->m_DrcItem->GetParent() && node->m_DrcItem->GetParent()->IsExcluded() ) { wxColour textColour = wxSystemSettings::GetColour( wxSYS_COLOUR_LISTBOXTEXT ); if( KIGFX::COLOR4D( textColour ).GetBrightness() > 0.5 ) aAttr.SetColour( textColour.ChangeLightness( heading ? 30 : 35 ) ); else aAttr.SetColour( textColour.ChangeLightness( heading ? 170 : 165 ) ); aAttr.SetItalic( true ); // Strikethrough would be better, if wxWidgets supported it ret = true; } return ret; } void DRC_TREE_MODEL::ValueChanged( DRC_TREE_NODE* aNode ) { if( aNode->m_Type == DRC_TREE_NODE::MAIN_ITEM || aNode->m_Type == DRC_TREE_NODE::AUX_ITEM ) { ValueChanged( aNode->m_Parent ); } if( aNode->m_Type == DRC_TREE_NODE::MARKER ) { wxDataViewModel::ValueChanged( ToItem( aNode ), 0 ); for( auto & child : aNode->m_Children ) wxDataViewModel::ValueChanged( ToItem( child.get() ), 0 ); } } void DRC_TREE_MODEL::DeleteCurrentItem( bool aDeep ) { wxDataViewItem dataViewItem = m_view->GetCurrentItem(); DRC_TREE_NODE* tree_node = ToNode( dataViewItem ); const DRC_ITEM* drc_item = tree_node ? tree_node->m_DrcItem : nullptr; if( !drc_item ) { wxBell(); return; } for( int i = 0; i < m_drcItemsProvider->GetCount(); ++i ) { if( m_drcItemsProvider->GetItem( i ) == drc_item ) { m_drcItemsProvider->DeleteItem( i, aDeep ); m_tree.erase( m_tree.begin() + i ); ItemDeleted( ToItem( tree_node->m_Parent ), dataViewItem ); break; } } } void DRC_TREE_MODEL::DeleteAllItems() { if( m_drcItemsProvider ) { m_drcItemsProvider->DeleteAllItems(); m_tree.clear(); Cleared(); } } void DRC_TREE_MODEL::onSizeView( wxSizeEvent& aEvent ) { int width = m_view->GetMainWindow()->GetRect().GetWidth() - WX_DATAVIEW_WINDOW_PADDING; if( m_view->GetColumnCount() > 0 ) m_view->GetColumn( 0 )->SetWidth( width ); }