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 <wx/settings.h>
28 #include <widgets/ui_common.h>
29 #include <marker_base.h>
30 #include <eda_draw_frame.h>
31 #include <rc_item.h>
32 #include <base_units.h>
33 
34 #define WX_DATAVIEW_WINDOW_PADDING 6
35 
36 
37 wxString RC_ITEM::GetErrorMessage() const
38 {
39  if( m_errorMessage.IsEmpty() )
40  return GetErrorText();
41  else
42  return m_errorMessage;
43 }
44 
45 
46 wxString RC_ITEM::ShowCoord( EDA_UNITS aUnits, const wxPoint& aPos )
47 {
48  return wxString::Format( "@(%s, %s)",
49  MessageTextFromValue( aUnits, aPos.x ),
50  MessageTextFromValue( aUnits, aPos.y ) );
51 }
52 
53 
54 wxString RC_ITEM::ShowReport( EDA_UNITS aUnits, SEVERITY aSeverity,
55  const std::map<KIID, EDA_ITEM*>& aItemMap ) const
56 {
57  wxString severity;
58 
59  switch( aSeverity )
60  {
61  case RPT_SEVERITY_ERROR: severity = wxT( "Severity: error" ); break;
62  case RPT_SEVERITY_WARNING: severity = wxT( "Severity: warning" ); break;
63  case RPT_SEVERITY_ACTION: severity = wxT( "Severity: action" ); break;
64  case RPT_SEVERITY_INFO: severity = wxT( "Severity: info" ); break;
65  default: ;
66  };
67 
68  if( m_parent && m_parent->IsExcluded() )
69  severity += wxT( " (excluded)" );
70 
71  EDA_ITEM* mainItem = nullptr;
72  EDA_ITEM* auxItem = nullptr;
73 
74  auto ii = aItemMap.find( GetMainItemID() );
75 
76  if( ii != aItemMap.end() )
77  mainItem = ii->second;
78 
79  ii = aItemMap.find( GetAuxItemID() );
80 
81  if( ii != aItemMap.end() )
82  auxItem = ii->second;
83 
84  // Note: some customers machine-process these. So:
85  // 1) don't translate
86  // 2) try not to re-order or change syntax
87  // 3) report numeric error code (which should be more stable) in addition to message
88 
89  if( mainItem && auxItem )
90  {
91  return wxString::Format( wxT( "[%s]: %s %s\n %s: %s\n %s: %s\n" ),
94  severity,
95  ShowCoord( aUnits, mainItem->GetPosition() ),
96  mainItem->GetSelectMenuText( aUnits ),
97  ShowCoord( aUnits, auxItem->GetPosition() ),
98  auxItem->GetSelectMenuText( aUnits ) );
99  }
100  else if( mainItem )
101  {
102  return wxString::Format( wxT( "[%s]: %s %s\n %s: %s\n" ),
103  GetSettingsKey(),
104  GetErrorMessage(),
105  severity,
106  ShowCoord( aUnits, mainItem->GetPosition() ),
107  mainItem->GetSelectMenuText( aUnits ) );
108  }
109  else
110  {
111  return wxString::Format( wxT( "[%s]: %s %s\n" ),
112  GetSettingsKey(),
113  GetErrorMessage(),
114  severity );
115  }
116 }
117 
118 
119 KIID RC_TREE_MODEL::ToUUID( wxDataViewItem aItem )
120 {
121  const RC_TREE_NODE* node = RC_TREE_MODEL::ToNode( aItem );
122 
123  if( node && node->m_RcItem )
124  {
125  const std::shared_ptr<RC_ITEM> rc_item = node->m_RcItem;
126 
127  switch( node->m_Type )
128  {
130  // rc_item->GetParent() can be null, if the parent is not existing
131  // when a RC item has no corresponding ERC/DRC marker
132  if( rc_item->GetParent() )
133  return rc_item->GetParent()->GetUUID();
134 
135  break;
136 
137  case RC_TREE_NODE::MAIN_ITEM: return rc_item->GetMainItemID();
138  case RC_TREE_NODE::AUX_ITEM: return rc_item->GetAuxItemID();
139  case RC_TREE_NODE::AUX_ITEM2: return rc_item->GetAuxItem2ID();
140  case RC_TREE_NODE::AUX_ITEM3: return rc_item->GetAuxItem3ID();
141  }
142  }
143 
144  return niluuid;
145 }
146 
147 
148 RC_TREE_MODEL::RC_TREE_MODEL( EDA_DRAW_FRAME* aParentFrame, wxDataViewCtrl* aView ) :
149  m_editFrame( aParentFrame ),
150  m_view( aView ),
151  m_severities( 0 ),
152  m_rcItemsProvider( nullptr )
153 {
154  m_view->GetMainWindow()->Connect( wxEVT_SIZE,
155  wxSizeEventHandler( RC_TREE_MODEL::onSizeView ),
156  NULL, this );
157 }
158 
159 
161 {
162  delete m_rcItemsProvider;
163 
164  for( RC_TREE_NODE* topLevelNode : m_tree )
165  delete topLevelNode;
166 }
167 
168 
169 void RC_TREE_MODEL::rebuildModel( RC_ITEMS_PROVIDER* aProvider, int aSeverities )
170 {
171  wxWindowUpdateLocker updateLock( m_view );
172 
173  std::shared_ptr<RC_ITEM> selectedRcItem = nullptr;
174 
175  if( m_view )
176  {
177  RC_TREE_NODE* selectedNode = ToNode( m_view->GetSelection() );
178  selectedRcItem = selectedNode ? selectedNode->m_RcItem : nullptr;
179 
180  // Even with the updateLock, wxWidgets sometimes ties its knickers in a knot trying
181  // to run a wxdataview_selection_changed_callback() on a row that has been deleted.
182  m_view->UnselectAll();
183  }
184 
185  if( aProvider != m_rcItemsProvider )
186  {
187  delete m_rcItemsProvider;
188  m_rcItemsProvider = aProvider;
189  }
190 
191  if( aSeverities != m_severities )
192  m_severities = aSeverities;
193 
194  if( m_rcItemsProvider )
196 
197  for( RC_TREE_NODE* topLevelNode : m_tree )
198  delete topLevelNode;
199 
200  m_tree.clear();
201 
202  for( int i = 0; m_rcItemsProvider && i < m_rcItemsProvider->GetCount(); ++i )
203  {
204  std::shared_ptr<RC_ITEM> rcItem = m_rcItemsProvider->GetItem( i );
205 
206  m_tree.push_back( new RC_TREE_NODE( nullptr, rcItem, RC_TREE_NODE::MARKER ) );
207  RC_TREE_NODE* n = m_tree.back();
208 
209  if( rcItem->GetMainItemID() != niluuid )
210  n->m_Children.push_back( new RC_TREE_NODE( n, rcItem, RC_TREE_NODE::MAIN_ITEM ) );
211 
212  if( rcItem->GetAuxItemID() != niluuid )
213  n->m_Children.push_back( new RC_TREE_NODE( n, rcItem, RC_TREE_NODE::AUX_ITEM ) );
214 
215  if( rcItem->GetAuxItem2ID() != niluuid )
216  n->m_Children.push_back( new RC_TREE_NODE( n, rcItem, RC_TREE_NODE::AUX_ITEM2 ) );
217 
218  if( rcItem->GetAuxItem3ID() != niluuid )
219  n->m_Children.push_back( new RC_TREE_NODE( n, rcItem, RC_TREE_NODE::AUX_ITEM3 ) );
220  }
221 
222  // Must be called after a significant change of items to force the
223  // wxDataViewModel to reread all of them, repopulating itself entirely.
224  Cleared();
225 
226 #ifdef __WXGTK__
227  // The fastest method to update wxDataViewCtrl is to rebuild from
228  // scratch by calling Cleared(). Linux requires to reassociate model to
229  // display data, but Windows will create multiple associations.
230  // On MacOS, this crashes kicad. See https://gitlab.com/kicad/code/kicad/issues/3666
231  // and https://gitlab.com/kicad/code/kicad/issues/3653
232  m_view->AssociateModel( this );
233 #endif
234 
235  m_view->ClearColumns();
236  int width = m_view->GetMainWindow()->GetRect().GetWidth() - WX_DATAVIEW_WINDOW_PADDING;
237  m_view->AppendTextColumn( wxEmptyString, 0, wxDATAVIEW_CELL_INERT, width );
238 
239  ExpandAll();
240 
241  // Most annoyingly wxWidgets won't tell us the scroll position (and no, all the usual
242  // routines don't work), so we can only restore the scroll position based on a selection.
243  if( selectedRcItem )
244  {
245  for( RC_TREE_NODE* candidate : m_tree )
246  {
247  if( candidate->m_RcItem == selectedRcItem )
248  {
249  m_view->Select( ToItem( candidate ) );
250  m_view->EnsureVisible( ToItem( candidate ) );
251  break;
252  }
253  }
254  }
255 }
256 
257 
259 {
260  rebuildModel( aProvider, m_severities );
261 }
262 
263 
264 void RC_TREE_MODEL::SetSeverities( int aSeverities )
265 {
266  rebuildModel( m_rcItemsProvider, aSeverities );
267 }
268 
269 
271 {
272  for( RC_TREE_NODE* topLevelNode : m_tree )
273  m_view->Expand( ToItem( topLevelNode ) );
274 }
275 
276 
277 bool RC_TREE_MODEL::IsContainer( wxDataViewItem const& aItem ) const
278 {
279  if( ToNode( aItem ) == nullptr ) // must be tree root...
280  return true;
281  else
282  return ToNode( aItem )->m_Type == RC_TREE_NODE::MARKER;
283 }
284 
285 
286 wxDataViewItem RC_TREE_MODEL::GetParent( wxDataViewItem const& aItem ) const
287 {
288  return ToItem( ToNode( aItem)->m_Parent );
289 }
290 
291 
292 unsigned int RC_TREE_MODEL::GetChildren( wxDataViewItem const& aItem,
293  wxDataViewItemArray& aChildren ) const
294 {
295  const RC_TREE_NODE* node = ToNode( aItem );
296  const std::vector<RC_TREE_NODE*>& children = node ? node->m_Children : m_tree;
297 
298  for( const RC_TREE_NODE* child: children )
299  aChildren.push_back( ToItem( child ) );
300 
301  return children.size();
302 }
303 
304 
308 void RC_TREE_MODEL::GetValue( wxVariant& aVariant,
309  wxDataViewItem const& aItem,
310  unsigned int aCol ) const
311 {
312  const RC_TREE_NODE* node = ToNode( aItem );
313  const std::shared_ptr<RC_ITEM> rcItem = node->m_RcItem;
314 
315  switch( node->m_Type )
316  {
318  {
319  wxString prefix;
320 
321  if( rcItem->GetParent() && rcItem->GetParent()->IsExcluded() )
322  prefix = _( "Excluded " );
323 
324  switch( m_editFrame->GetSeverity( rcItem->GetErrorCode() ) )
325  {
326  case RPT_SEVERITY_ERROR: prefix += _( "Error: " ); break;
327  case RPT_SEVERITY_WARNING: prefix += _( "Warning: " ); break;
328  }
329 
330  aVariant = prefix + rcItem->GetErrorMessage();
331  }
332  break;
333 
335  {
336  EDA_ITEM* item = m_editFrame->GetItem( rcItem->GetMainItemID() );
337  aVariant = item->GetSelectMenuText( m_editFrame->GetUserUnits() );
338  }
339  break;
340 
342  {
343  EDA_ITEM* item = m_editFrame->GetItem( rcItem->GetAuxItemID() );
344  aVariant = item->GetSelectMenuText( m_editFrame->GetUserUnits() );
345  }
346  break;
347 
349  {
350  EDA_ITEM* item = m_editFrame->GetItem( rcItem->GetAuxItem2ID() );
351  aVariant = item->GetSelectMenuText( m_editFrame->GetUserUnits() );
352  }
353  break;
354 
356  {
357  EDA_ITEM* item = m_editFrame->GetItem( rcItem->GetAuxItem3ID() );
358  aVariant = item->GetSelectMenuText( m_editFrame->GetUserUnits() );
359  }
360  break;
361  }
362 }
363 
364 
369 bool RC_TREE_MODEL::GetAttr( wxDataViewItem const& aItem,
370  unsigned int aCol,
371  wxDataViewItemAttr& aAttr ) const
372 {
373  const RC_TREE_NODE* node = ToNode( aItem );
374  wxASSERT( node );
375 
376  bool ret = false;
377  bool heading = node->m_Type == RC_TREE_NODE::MARKER;
378 
379  if( heading )
380  {
381  aAttr.SetBold( true );
382  ret = true;
383  }
384 
385  if( node->m_RcItem->GetParent() && node->m_RcItem->GetParent()->IsExcluded() )
386  {
387  wxColour textColour = wxSystemSettings::GetColour( wxSYS_COLOUR_LISTBOXTEXT );
388 
389  if( KIGFX::COLOR4D( textColour ).GetBrightness() > 0.5 )
390  aAttr.SetColour( textColour.ChangeLightness( heading ? 30 : 35 ) );
391  else
392  aAttr.SetColour( textColour.ChangeLightness( heading ? 170 : 165 ) );
393 
394  aAttr.SetItalic( true ); // Strikethrough would be better, if wxWidgets supported it
395  ret = true;
396  }
397 
398  return ret;
399 }
400 
401 
403 {
404  if( aNode->m_Type == RC_TREE_NODE::MAIN_ITEM || aNode->m_Type == RC_TREE_NODE::AUX_ITEM )
405  {
406  ValueChanged( aNode->m_Parent );
407  }
408 
409  if( aNode->m_Type == RC_TREE_NODE::MARKER )
410  {
411  wxDataViewModel::ValueChanged( ToItem( aNode ), 0 );
412 
413  for( RC_TREE_NODE* child : aNode->m_Children )
414  wxDataViewModel::ValueChanged( ToItem( child ), 0 );
415  }
416 }
417 
418 
420 {
421  DeleteItems( true, true, aDeep );
422 }
423 
424 
425 void RC_TREE_MODEL::DeleteItems( bool aCurrentOnly, bool aIncludeExclusions, bool aDeep )
426 {
427  RC_TREE_NODE* current_node = ToNode( m_view->GetCurrentItem() );
428  const std::shared_ptr<RC_ITEM> current_item = current_node ? current_node->m_RcItem : nullptr;
429 
430  if( aCurrentOnly && !current_item )
431  {
432  wxBell();
433  return;
434  }
435 
436  int lastGood = -1;
437  bool found = false;
438 
439  if( m_view )
440  {
441  m_view->UnselectAll();
442  wxYield();
443  m_view->Freeze();
444  }
445 
446  for( int i = m_rcItemsProvider->GetCount() - 1; i >= 0; --i )
447  {
448  std::shared_ptr<RC_ITEM> rcItem = m_rcItemsProvider->GetItem( i );
449  MARKER_BASE* marker = rcItem->GetParent();
450  bool excluded = marker ? marker->IsExcluded() : false;
451 
452  if( aCurrentOnly && rcItem != current_item )
453  {
454  if( found && lastGood >= 0 )
455  break;
456 
457  lastGood = i;
458  continue;
459  }
460 
461  if( excluded && !aIncludeExclusions )
462  continue;
463 
464  found = true;
465 
466  wxDataViewItem markerItem = ToItem( m_tree[i] );
467  wxDataViewItemArray childItems;
468  wxDataViewItem parentItem = ToItem( m_tree[i]->m_Parent );
469 
470  for( RC_TREE_NODE* child : m_tree[i]->m_Children )
471  {
472  childItems.push_back( ToItem( child ) );
473  delete child;
474  }
475 
476  m_tree[i]->m_Children.clear();
477  ItemsDeleted( markerItem, childItems );
478 
479  delete m_tree[i];
480  m_tree.erase( m_tree.begin() + i );
481  ItemDeleted( parentItem, markerItem );
482 
483  // Only deep delete the current item here; others will be done through the
484  // DeleteAllItems() call below, which is more efficient.
485  m_rcItemsProvider->DeleteItem( i, aDeep && aCurrentOnly );
486  }
487 
488  if( m_view && aCurrentOnly && lastGood >= 0 )
489  m_view->Select( ToItem( m_tree[ lastGood ] ) );
490 
491  if( !aCurrentOnly )
492  m_rcItemsProvider->DeleteAllItems( aIncludeExclusions, aDeep );
493 
494  if( m_view )
495  m_view->Thaw();
496 }
497 
498 
499 void RC_TREE_MODEL::onSizeView( wxSizeEvent& aEvent )
500 {
501  int width = m_view->GetMainWindow()->GetRect().GetWidth() - WX_DATAVIEW_WINDOW_PADDING;
502 
503  if( m_view->GetColumnCount() > 0 )
504  m_view->GetColumn( 0 )->SetWidth( width );
505 
506  // Pass size event to other widgets
507  aEvent.Skip();
508 }
EDA_UNITS
Definition: common.h:200
void DeleteItems(bool aCurrentOnly, bool aIncludeExclusions, bool aDeep)
Deletes the current item or all items.
Definition: rc_item.cpp:425
virtual EDA_ITEM * GetItem(const KIID &aId)
Fetch an item by KIID.
wxString MessageTextFromValue(EDA_UNITS aUnits, int aValue, bool aAddUnitLabel, EDA_DATA_TYPE aType)
Definition: base_units.cpp:122
virtual void DeleteAllItems(bool aIncludeExclusions, bool aDeep)=0
static wxDataViewItem ToItem(RC_TREE_NODE const *aNode)
Definition: rc_item.h:216
void SetProvider(RC_ITEMS_PROVIDER *aProvider)
Definition: rc_item.cpp:258
Implementation of conversion functions that require both schematic and board internal units.
SEVERITY
Definition: ui_common.h:46
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:369
RC_TREE_NODE * m_Parent
Definition: rc_item.h:208
virtual int GetCount(int aSeverity=-1)=0
static KIID ToUUID(wxDataViewItem aItem)
Definition: rc_item.cpp:119
wxDataViewCtrl * m_view
Definition: rc_item.h:295
#define WX_DATAVIEW_WINDOW_PADDING
Definition: rc_item.cpp:34
virtual wxPoint GetPosition() const
Definition: eda_item.h:325
The base class for create windows for drawing purpose.
virtual std::shared_ptr< RC_ITEM > GetItem(int aIndex)=0
Retrieve a RC_ITEM by index.
void DeleteCurrentItem(bool aDeep)
Definition: rc_item.cpp:419
void SetSeverities(int aSeverities)
Definition: rc_item.cpp:264
Provide an abstract interface of a RC_ITEM* list manager.
Definition: rc_item.h:44
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:308
wxString GetErrorText() const
Definition: rc_item.h:171
std::vector< RC_TREE_NODE * > m_Children
Definition: rc_item.h:209
RC_ITEMS_PROVIDER * m_rcItemsProvider
Definition: rc_item.h:297
Definition: common.h:70
std::shared_ptr< RC_ITEM > m_RcItem
Definition: rc_item.h:206
#define NULL
void ExpandAll()
Definition: rc_item.cpp:270
void rebuildModel(RC_ITEMS_PROVIDER *aProvider, int aSeverities)
Definition: rc_item.cpp:169
static RC_TREE_NODE * ToNode(wxDataViewItem aItem)
Definition: rc_item.h:221
static wxString ShowCoord(EDA_UNITS aUnits, const wxPoint &aPos)
Format a coordinate or position to text.
Definition: rc_item.cpp:46
KIID niluuid(0)
KIID GetAuxItemID() const
Definition: rc_item.h:147
RC_TREE_MODEL(EDA_DRAW_FRAME *aParentFrame, wxDataViewCtrl *aView)
Definition: rc_item.cpp:148
Functions to provide common constants and other functions to assist in making a consistent UI.
EDA_DRAW_FRAME * m_editFrame
Definition: rc_item.h:294
wxString m_errorMessage
A message describing the details of this specific error.
Definition: rc_item.h:80
bool IsExcluded() const
Definition: marker_base.h:109
void onSizeView(wxSizeEvent &aEvent)
Definition: rc_item.cpp:499
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:292
NODE_TYPE m_Type
Definition: rc_item.h:205
std::vector< RC_TREE_NODE * > m_tree
Definition: rc_item.h:299
virtual wxString ShowReport(EDA_UNITS aUnits, SEVERITY aSeverity, const std::map< KIID, EDA_ITEM * > &aItemMap) const
Translate this object into a text string suitable for saving to disk in a report.
Definition: rc_item.cpp:54
MARKER_BASE * m_parent
The marker this item belongs to, if any.
Definition: rc_item.h:83
int m_severities
Definition: rc_item.h:296
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:201
#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...
Definition: eda_item.cpp:123
EDA_ITEM is a base class for most all the KiCad significant classes, used in schematics and boards.
Definition: eda_item.h:147
wxString GetSettingsKey() const
Definition: rc_item.h:176
KIID GetMainItemID() const
Definition: rc_item.h:146
bool IsContainer(wxDataViewItem const &aItem) const override
Definition: rc_item.cpp:277
wxDataViewItem GetParent(wxDataViewItem const &aItem) const override
Definition: rc_item.cpp:286
virtual wxString GetErrorMessage() const
Return the error message of a RC_ITEM.
Definition: rc_item.cpp:37
virtual void DeleteItem(int aIndex, bool aDeep)=0
Remove (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:402
COLOR4D is the color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:100