KiCad PCB EDA Suite
rc_item.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, you may find one here:
18  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19  * or you may search the http://www.gnu.org website for the version 2 license,
20  * or you may write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22  */
23 
24 
25 #include <wx/wupdlock.h>
26 #include <wx/dataview.h>
27 #include <widgets/ui_common.h>
28 #include <marker_base.h>
29 #include <eda_draw_frame.h>
30 #include <rc_item.h>
31 #include <base_units.h>
32 
33 #define WX_DATAVIEW_WINDOW_PADDING 6
34 
35 
36 wxString RC_ITEM::GetErrorMessage() const
37 {
38  if( m_errorMessage.IsEmpty() )
39  return GetErrorText();
40  else
41  return m_errorMessage;
42 }
43 
44 
45 wxString RC_ITEM::ShowCoord( EDA_UNITS aUnits, const wxPoint& aPos )
46 {
47  return wxString::Format( "@(%s, %s)",
48  MessageTextFromValue( aUnits, aPos.x ),
49  MessageTextFromValue( aUnits, aPos.y ) );
50 }
51 
52 
53 wxString RC_ITEM::ShowReport( EDA_UNITS aUnits, const std::map<KIID, EDA_ITEM*>& aItemMap ) const
54 {
55  EDA_ITEM* mainItem = nullptr;
56  EDA_ITEM* auxItem = nullptr;
57 
58  if( m_mainItemUuid != niluuid )
59  mainItem = aItemMap.at( m_mainItemUuid );
60 
61  if( m_auxItemUuid != niluuid )
62  auxItem = aItemMap.at( m_auxItemUuid );
63 
64  if( mainItem && auxItem )
65  {
66  return wxString::Format( wxT( "ErrType(%d): %s\n %s: %s\n %s: %s\n" ),
67  GetErrorCode(),
69  ShowCoord( aUnits, mainItem->GetPosition() ),
70  mainItem->GetSelectMenuText( aUnits ),
71  ShowCoord( aUnits, auxItem->GetPosition() ),
72  auxItem->GetSelectMenuText( aUnits ) );
73  }
74  else if( mainItem )
75  {
76  return wxString::Format( wxT( "ErrType(%d): %s\n %s: %s\n" ),
77  GetErrorCode(),
79  ShowCoord( aUnits, mainItem->GetPosition() ),
80  mainItem->GetSelectMenuText( aUnits ) );
81  }
82  else
83  {
84  return wxString::Format( wxT( "ErrType(%d): %s\n" ),
85  GetErrorCode(),
86  GetErrorMessage() );
87  }
88 }
89 
90 
91 KIID RC_TREE_MODEL::ToUUID( wxDataViewItem aItem )
92 {
93  const RC_TREE_NODE* node = RC_TREE_MODEL::ToNode( aItem );
94 
95  if( node )
96  {
97  const RC_ITEM* rc_item = node->m_RcItem;
98 
99  switch( node->m_Type )
100  {
102  // rc_item->GetParent() can be null, if the parent is not existing
103  // when a RC item has no corresponding ERC/DRC marker
104  if( rc_item->GetParent() )
105  return rc_item->GetParent()->GetUUID();
106 
107  break;
108 
109  case RC_TREE_NODE::MAIN_ITEM: return rc_item->GetMainItemID();
110  case RC_TREE_NODE::AUX_ITEM: return rc_item->GetAuxItemID();
111  case RC_TREE_NODE::AUX_ITEM2: return rc_item->GetAuxItem2ID();
112  case RC_TREE_NODE::AUX_ITEM3: return rc_item->GetAuxItem3ID();
113  }
114  }
115 
116  return niluuid;
117 }
118 
119 
120 RC_TREE_MODEL::RC_TREE_MODEL( EDA_DRAW_FRAME* aParentFrame, wxDataViewCtrl* aView ) :
121  m_editFrame( aParentFrame ),
122  m_view( aView ),
123  m_severities( 0 ),
124  m_rcItemsProvider( nullptr )
125 {
126  m_view->GetMainWindow()->Connect( wxEVT_SIZE,
127  wxSizeEventHandler( RC_TREE_MODEL::onSizeView ),
128  NULL, this );
129 }
130 
131 
133 {
134  delete m_rcItemsProvider;
135 
136  for( RC_TREE_NODE* topLevelNode : m_tree )
137  delete topLevelNode;
138 }
139 
140 
141 void RC_TREE_MODEL::rebuildModel( RC_ITEMS_PROVIDER* aProvider, int aSeverities )
142 {
143  wxWindowUpdateLocker updateLock( m_view );
144 
145  RC_ITEM* selectedRcItem = nullptr;
146 
147  if( m_view )
148  {
149  RC_TREE_NODE* selectedNode = ToNode( m_view->GetSelection() );
150  selectedRcItem = selectedNode ? selectedNode->m_RcItem : nullptr;
151 
152  // Even with the updateLock, wxWidgets sometimes ties its knickers in a knot trying
153  // to run a wxdataview_selection_changed_callback() on a row that has been deleted.
154  m_view->UnselectAll();
155  }
156 
157  if( aProvider != m_rcItemsProvider )
158  {
159  delete m_rcItemsProvider;
160  m_rcItemsProvider = aProvider;
161  }
162 
163  if( aSeverities != m_severities )
164  m_severities = aSeverities;
165 
166  if( m_rcItemsProvider )
168 
169  m_tree.clear();
170 
171  for( int i = 0; m_rcItemsProvider && i < m_rcItemsProvider->GetCount(); ++i )
172  {
173  RC_ITEM* rcItem = m_rcItemsProvider->GetItem( i );
174 
175  m_tree.push_back( new RC_TREE_NODE( nullptr, rcItem, RC_TREE_NODE::MARKER ) );
176  RC_TREE_NODE* n = m_tree.back();
177 
178  if( rcItem->GetMainItemID() != niluuid )
179  n->m_Children.push_back( new RC_TREE_NODE( n, rcItem, RC_TREE_NODE::MAIN_ITEM ) );
180 
181  if( rcItem->GetAuxItemID() != niluuid )
182  n->m_Children.push_back( new RC_TREE_NODE( n, rcItem, RC_TREE_NODE::AUX_ITEM ) );
183 
184  if( rcItem->GetAuxItem2ID() != niluuid )
185  n->m_Children.push_back( new RC_TREE_NODE( n, rcItem, RC_TREE_NODE::AUX_ITEM2 ) );
186 
187  if( rcItem->GetAuxItem3ID() != niluuid )
188  n->m_Children.push_back( new RC_TREE_NODE( n, rcItem, RC_TREE_NODE::AUX_ITEM3 ) );
189  }
190 
191  // Must be called after a significant change of items to force the
192  // wxDataViewModel to reread all of them, repopulating itself entirely.
193  Cleared();
194 
195 #ifdef __WXGTK__
196  // The fastest method to update wxDataViewCtrl is to rebuild from
197  // scratch by calling Cleared(). Linux requires to reassociate model to
198  // display data, but Windows will create multiple associations.
199  // On MacOS, this crashes kicad. See https://gitlab.com/kicad/code/kicad/issues/3666
200  // and https://gitlab.com/kicad/code/kicad/issues/3653
201  m_view->AssociateModel( this );
202 #endif
203 
204  m_view->ClearColumns();
205  int width = m_view->GetMainWindow()->GetRect().GetWidth() - WX_DATAVIEW_WINDOW_PADDING;
206  m_view->AppendTextColumn( wxEmptyString, 0, wxDATAVIEW_CELL_INERT, width );
207 
208  ExpandAll();
209 
210  // Most annoyingly wxWidgets won't tell us the scroll position (and no, all the usual
211  // routines don't work), so we can only restore the scroll position based on a selection.
212  if( selectedRcItem )
213  {
214  for( RC_TREE_NODE* candidate : m_tree )
215  {
216  if( candidate->m_RcItem == selectedRcItem )
217  {
218  m_view->Select( ToItem( candidate ) );
219  m_view->EnsureVisible( ToItem( candidate ) );
220  break;
221  }
222  }
223  }
224 }
225 
226 
228 {
229  rebuildModel( aProvider, m_severities );
230 }
231 
232 
233 void RC_TREE_MODEL::SetSeverities( int aSeverities )
234 {
235  rebuildModel( m_rcItemsProvider, aSeverities );
236 }
237 
238 
240 {
241  for( RC_TREE_NODE* topLevelNode : m_tree )
242  m_view->Expand( ToItem( topLevelNode ) );
243 }
244 
245 
246 bool RC_TREE_MODEL::IsContainer( wxDataViewItem const& aItem ) const
247 {
248  if( ToNode( aItem ) == nullptr ) // must be tree root...
249  return true;
250  else
251  return ToNode( aItem )->m_Type == RC_TREE_NODE::MARKER;
252 }
253 
254 
255 wxDataViewItem RC_TREE_MODEL::GetParent( wxDataViewItem const& aItem ) const
256 {
257  return ToItem( ToNode( aItem)->m_Parent );
258 }
259 
260 
261 unsigned int RC_TREE_MODEL::GetChildren( wxDataViewItem const& aItem,
262  wxDataViewItemArray& aChildren ) const
263 {
264  const RC_TREE_NODE* node = ToNode( aItem );
265  const std::vector<RC_TREE_NODE*>& children = node ? node->m_Children : m_tree;
266 
267  for( const RC_TREE_NODE* child: children )
268  aChildren.push_back( ToItem( child ) );
269 
270  return children.size();
271 }
272 
273 
277 void RC_TREE_MODEL::GetValue( wxVariant& aVariant,
278  wxDataViewItem const& aItem,
279  unsigned int aCol ) const
280 {
281  const RC_TREE_NODE* node = ToNode( aItem );
282  const RC_ITEM* rcItem = node->m_RcItem;
283 
284  switch( node->m_Type )
285  {
287  {
288  wxString prefix;
289 
290  if( rcItem->GetParent() && rcItem->GetParent()->IsExcluded() )
291  prefix = _( "Excluded " );
292 
293  switch( m_editFrame->GetSeverity( rcItem->GetErrorCode() ) )
294  {
295  case RPT_SEVERITY_ERROR: prefix += _( "Error: " ); break;
296  case RPT_SEVERITY_WARNING: prefix += _( "Warning: " ); break;
297  }
298 
299  aVariant = prefix + rcItem->GetErrorMessage();
300  }
301  break;
302 
304  {
305  EDA_ITEM* item = m_editFrame->GetItem( rcItem->GetMainItemID() );
306  aVariant = item->GetSelectMenuText( m_editFrame->GetUserUnits() );
307  }
308  break;
309 
311  {
312  EDA_ITEM* item = m_editFrame->GetItem( rcItem->GetAuxItemID() );
313  aVariant = item->GetSelectMenuText( m_editFrame->GetUserUnits() );
314  }
315  break;
316 
318  {
319  EDA_ITEM* item = m_editFrame->GetItem( rcItem->GetAuxItem2ID() );
320  aVariant = item->GetSelectMenuText( m_editFrame->GetUserUnits() );
321  }
322  break;
323 
325  {
326  EDA_ITEM* item = m_editFrame->GetItem( rcItem->GetAuxItem3ID() );
327  aVariant = item->GetSelectMenuText( m_editFrame->GetUserUnits() );
328  }
329  break;
330  }
331 }
332 
333 
338 bool RC_TREE_MODEL::GetAttr( wxDataViewItem const& aItem,
339  unsigned int aCol,
340  wxDataViewItemAttr& aAttr ) const
341 {
342  const RC_TREE_NODE* node = ToNode( aItem );
343  wxASSERT( node );
344 
345  bool ret = false;
346  bool heading = node->m_Type == RC_TREE_NODE::MARKER;
347 
348  if( heading )
349  {
350  aAttr.SetBold( true );
351  ret = true;
352  }
353 
354  if( node->m_RcItem->GetParent() && node->m_RcItem->GetParent()->IsExcluded() )
355  {
356  wxColour textColour = wxSystemSettings::GetColour( wxSYS_COLOUR_LISTBOXTEXT );
357 
358  if( KIGFX::COLOR4D( textColour ).GetBrightness() > 0.5 )
359  aAttr.SetColour( textColour.ChangeLightness( heading ? 30 : 35 ) );
360  else
361  aAttr.SetColour( textColour.ChangeLightness( heading ? 170 : 165 ) );
362 
363  aAttr.SetItalic( true ); // Strikethrough would be better, if wxWidgets supported it
364  ret = true;
365  }
366 
367  return ret;
368 }
369 
370 
372 {
373  if( aNode->m_Type == RC_TREE_NODE::MAIN_ITEM || aNode->m_Type == RC_TREE_NODE::AUX_ITEM )
374  {
375  ValueChanged( aNode->m_Parent );
376  }
377 
378  if( aNode->m_Type == RC_TREE_NODE::MARKER )
379  {
380  wxDataViewModel::ValueChanged( ToItem( aNode ), 0 );
381 
382  for( RC_TREE_NODE* child : aNode->m_Children )
383  wxDataViewModel::ValueChanged( ToItem( child ), 0 );
384  }
385 }
386 
387 
389 {
390  DeleteItems( true, true, aDeep );
391 }
392 
393 
394 void RC_TREE_MODEL::DeleteItems( bool aCurrentOnly, bool aIncludeExclusions, bool aDeep )
395 {
396  RC_TREE_NODE* current_node = ToNode( m_view->GetCurrentItem() );
397  const RC_ITEM* current_item = current_node ? current_node->m_RcItem : nullptr;
398 
399  if( aCurrentOnly && !current_item )
400  {
401  wxBell();
402  return;
403  }
404 
405  for( int i = m_rcItemsProvider->GetCount() - 1; i >= 0; --i )
406  {
407  RC_ITEM* rcItem = m_rcItemsProvider->GetItem( i );
408  MARKER_BASE* marker = rcItem->GetParent();
409  bool excluded = marker ? marker->IsExcluded() : false;
410 
411  if( aCurrentOnly && rcItem != current_item )
412  continue;
413 
414  if( excluded && !aIncludeExclusions )
415  continue;
416 
417  wxDataViewItem markerItem = ToItem( m_tree[i] );
418  wxDataViewItemArray childItems;
419  wxDataViewItem parentItem = ToItem( m_tree[i]->m_Parent );
420 
421  for( RC_TREE_NODE* child : m_tree[i]->m_Children )
422  {
423  childItems.push_back( ToItem( child ) );
424  delete child;
425  }
426 
427  m_tree[i]->m_Children.clear();
428  ItemsDeleted( markerItem, childItems );
429 
430  delete m_tree[i];
431  m_tree.erase( m_tree.begin() + i );
432  ItemDeleted( parentItem, markerItem );
433 
434  // Only deep delete the current item here; others will be done through the
435  // DeleteAllItems() call below, which is more efficient.
436  m_rcItemsProvider->DeleteItem( i, aDeep && aCurrentOnly );
437  }
438 
439  if( !aCurrentOnly )
440  {
441  m_rcItemsProvider->DeleteAllItems( aIncludeExclusions, aDeep );
442  }
443 }
444 
445 
446 void RC_TREE_MODEL::onSizeView( wxSizeEvent& aEvent )
447 {
448  int width = m_view->GetMainWindow()->GetRect().GetWidth() - WX_DATAVIEW_WINDOW_PADDING;
449 
450  if( m_view->GetColumnCount() > 0 )
451  m_view->GetColumn( 0 )->SetWidth( width );
452 
453  // Pass size event to other widgets
454  aEvent.Skip();
455 }
EDA_UNITS
Definition: common.h:198
void DeleteItems(bool aCurrentOnly, bool aIncludeExclusions, bool aDeep)
Deletes the current item or all items.
Definition: rc_item.cpp:394
virtual EDA_ITEM * GetItem(const KIID &aId)
Fetch an item by KIID.
virtual void DeleteAllItems(bool aIncludeExclusions, bool aDeep)=0
KIID GetAuxItem2ID() const
Definition: rc_item.h:141
static wxDataViewItem ToItem(RC_TREE_NODE const *aNode)
Definition: rc_item.h:211
void SetProvider(RC_ITEMS_PROVIDER *aProvider)
Definition: rc_item.cpp:227
Implementation of conversion functions that require both schematic and board internal units.
RC_ITEM is a holder for a DRC (in Pcbnew) or ERC (in Eeschema) error item.
Definition: rc_item.h:73
bool GetAttr(wxDataViewItem const &aItem, unsigned int aCol, wxDataViewItemAttr &aAttr) const override
Called by the wxDataView to fetch an item's formatting.
Definition: rc_item.cpp:338
RC_TREE_NODE * m_Parent
Definition: rc_item.h:203
KIID m_auxItemUuid
Definition: rc_item.h:82
virtual int GetCount(int aSeverity=-1)=0
static KIID ToUUID(wxDataViewItem aItem)
Definition: rc_item.cpp:91
wxDataViewCtrl * m_view
Definition: rc_item.h:290
#define WX_DATAVIEW_WINDOW_PADDING
Definition: rc_item.cpp:33
virtual wxPoint GetPosition() const
Definition: base_struct.h:337
The base class for create windows for drawing purpose.
virtual RC_ITEM * GetItem(int aIndex)=0
Function GetItem retrieves a RC_ITEM by index.
void DeleteCurrentItem(bool aDeep)
Definition: rc_item.cpp:388
void SetSeverities(int aSeverities)
Definition: rc_item.cpp:233
Provide an abstract interface of a RC_ITEM* list manager.
Definition: rc_item.h:42
void GetValue(wxVariant &aVariant, wxDataViewItem const &aItem, unsigned int aCol) const override
Called by the wxDataView to fetch an item's value.
Definition: rc_item.cpp:277
wxString GetErrorText() const
Definition: rc_item.h:165
std::vector< RC_TREE_NODE * > m_Children
Definition: rc_item.h:204
RC_ITEMS_PROVIDER * m_rcItemsProvider
Definition: rc_item.h:292
Definition: common.h:68
PCB_EDIT_FRAME * m_editFrame
virtual wxString ShowReport(EDA_UNITS aUnits, const std::map< KIID, EDA_ITEM * > &aItemMap) const
Function ShowReport translates this object into a text string suitable for saving to disk in a report...
Definition: rc_item.cpp:53
#define NULL
void ExpandAll()
Definition: rc_item.cpp:239
void rebuildModel(RC_ITEMS_PROVIDER *aProvider, int aSeverities)
Definition: rc_item.cpp:141
static RC_TREE_NODE * ToNode(wxDataViewItem aItem)
Definition: rc_item.h:216
static wxString ShowCoord(EDA_UNITS aUnits, const wxPoint &aPos)
Function ShowCoord formats a coordinate or position to text.
Definition: rc_item.cpp:45
KIID niluuid(0)
KIID GetAuxItemID() const
Definition: rc_item.h:140
RC_TREE_MODEL(EDA_DRAW_FRAME *aParentFrame, wxDataViewCtrl *aView)
Definition: rc_item.cpp:120
int GetErrorCode() const
Definition: rc_item.h:156
Functions to provide common constants and other functions to assist in making a consistent UI.
EDA_DRAW_FRAME * m_editFrame
Definition: rc_item.h:289
wxString m_errorMessage
A message describing the details of this specific error.
Definition: rc_item.h:77
bool IsExcluded() const
Definition: marker_base.h:108
void onSizeView(wxSizeEvent &aEvent)
Definition: rc_item.cpp:446
virtual int GetSeverity(int aErrorCode) const
virtual void SetSeverities(int aSeverities)=0
unsigned int GetChildren(wxDataViewItem const &aItem, wxDataViewItemArray &aChildren) const override
Definition: rc_item.cpp:261
NODE_TYPE m_Type
Definition: rc_item.h:200
wxString MessageTextFromValue(EDA_UNITS aUnits, int aValue, bool aUseMils, EDA_DATA_TYPE aType)
Definition: base_units.cpp:124
std::vector< RC_TREE_NODE * > m_tree
Definition: rc_item.h:294
RC_ITEM * m_RcItem
Definition: rc_item.h:201
int m_severities
Definition: rc_item.h:291
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, CPTREE &aTree)
Function Format outputs a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:205
#define _(s)
Definition: 3d_actions.cpp:33
virtual wxString GetSelectMenuText(EDA_UNITS aUnits) const
Function GetSelectMenuText returns the text to display to be used in the selection clarification cont...
EDA_ITEM is a base class for most all the KiCad significant classes, used in schematics and boards.
Definition: base_struct.h:159
KIID m_mainItemUuid
Definition: rc_item.h:81
KIID GetMainItemID() const
Definition: rc_item.h:139
MARKER_BASE * GetParent() const
Definition: rc_item.h:145
bool IsContainer(wxDataViewItem const &aItem) const override
Definition: rc_item.cpp:246
KIID GetAuxItem3ID() const
Definition: rc_item.h:142
virtual const KIID GetUUID() const =0
wxDataViewItem GetParent(wxDataViewItem const &aItem) const override
Definition: rc_item.cpp:255
virtual wxString GetErrorMessage() const
Function GetErrorMessage returns the error message of a RC_ITEM.
Definition: rc_item.cpp:36
virtual void DeleteItem(int aIndex, bool aDeep)=0
Function DeleteItem removes (and optionally deletes) the indexed item from the list.
EDA_UNITS GetUserUnits() const
Return the user units currently in use.
void ValueChanged(RC_TREE_NODE *aNode)
Definition: rc_item.cpp:371
COLOR4D is the color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:99