You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

770 lines
23 KiB

4 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright The 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. #include <wx/wupdlock.h>
  24. #include <wx/dataview.h>
  25. #include <wx/settings.h>
  26. #include <widgets/ui_common.h>
  27. #include <marker_base.h>
  28. #include <eda_draw_frame.h>
  29. #include <rc_item.h>
  30. #include <rc_json_schema.h>
  31. #include <eda_item.h>
  32. #include <base_units.h>
  33. #define WX_DATAVIEW_WINDOW_PADDING 6
  34. wxString RC_ITEM::GetErrorMessage() const
  35. {
  36. if( m_errorMessage.IsEmpty() )
  37. return GetErrorText();
  38. else
  39. return m_errorMessage;
  40. }
  41. static wxString showCoord( UNITS_PROVIDER* aUnitsProvider, const VECTOR2I& aPos )
  42. {
  43. return wxString::Format( wxT( "@(%s, %s)" ),
  44. aUnitsProvider->MessageTextFromValue( aPos.x ),
  45. aUnitsProvider->MessageTextFromValue( aPos.y ) );
  46. }
  47. void RC_ITEM::AddItem( EDA_ITEM* aItem )
  48. {
  49. m_ids.push_back( aItem->m_Uuid );
  50. }
  51. void RC_ITEM::SetItems( const EDA_ITEM* aItem, const EDA_ITEM* bItem,
  52. const EDA_ITEM* cItem, const EDA_ITEM* dItem )
  53. {
  54. m_ids.clear();
  55. if( aItem )
  56. m_ids.push_back( aItem->m_Uuid );
  57. if( bItem )
  58. m_ids.push_back( bItem->m_Uuid );
  59. if( cItem )
  60. m_ids.push_back( cItem->m_Uuid );
  61. if( dItem )
  62. m_ids.push_back( dItem->m_Uuid );
  63. }
  64. wxString RC_ITEM::getSeverityString( SEVERITY aSeverity )
  65. {
  66. wxString severity;
  67. switch( aSeverity )
  68. {
  69. case RPT_SEVERITY_ERROR: severity = wxS( "error" ); break;
  70. case RPT_SEVERITY_WARNING: severity = wxS( "warning" ); break;
  71. case RPT_SEVERITY_ACTION: severity = wxS( "action" ); break;
  72. case RPT_SEVERITY_INFO: severity = wxS( "info" ); break;
  73. case RPT_SEVERITY_EXCLUSION: severity = wxS( "exclusion" ); break;
  74. case RPT_SEVERITY_DEBUG: severity = wxS( "debug" ); break;
  75. default:;
  76. };
  77. return severity;
  78. }
  79. wxString RC_ITEM::ShowReport( UNITS_PROVIDER* aUnitsProvider, SEVERITY aSeverity,
  80. const std::map<KIID, EDA_ITEM*>& aItemMap ) const
  81. {
  82. wxString severity = getSeverityString( aSeverity );
  83. if( m_parent && m_parent->IsExcluded() )
  84. severity += wxT( " (excluded)" );
  85. EDA_ITEM* mainItem = nullptr;
  86. EDA_ITEM* auxItem = nullptr;
  87. auto ii = aItemMap.find( GetMainItemID() );
  88. if( ii != aItemMap.end() )
  89. mainItem = ii->second;
  90. ii = aItemMap.find( GetAuxItemID() );
  91. if( ii != aItemMap.end() )
  92. auxItem = ii->second;
  93. // Note: some customers machine-process these. So:
  94. // 1) don't translate
  95. // 2) try not to re-order or change syntax
  96. // 3) report settings key (which should be more stable) in addition to message
  97. wxString msg;
  98. if( mainItem && auxItem )
  99. {
  100. msg.Printf( wxT( "[%s]: %s\n %s; %s\n %s: %s\n %s: %s\n" ),
  101. GetSettingsKey(),
  102. GetErrorMessage(),
  103. GetViolatingRuleDesc(),
  104. severity,
  105. showCoord( aUnitsProvider, mainItem->GetPosition()),
  106. mainItem->GetItemDescription( aUnitsProvider, true ),
  107. showCoord( aUnitsProvider, auxItem->GetPosition()),
  108. auxItem->GetItemDescription( aUnitsProvider, true ) );
  109. }
  110. else if( mainItem )
  111. {
  112. msg.Printf( wxT( "[%s]: %s\n %s; %s\n %s: %s\n" ),
  113. GetSettingsKey(),
  114. GetErrorMessage(),
  115. GetViolatingRuleDesc(),
  116. severity,
  117. showCoord( aUnitsProvider, mainItem->GetPosition()),
  118. mainItem->GetItemDescription( aUnitsProvider, true ) );
  119. }
  120. else
  121. {
  122. msg.Printf( wxT( "[%s]: %s\n %s; %s\n" ),
  123. GetSettingsKey(),
  124. GetErrorMessage(),
  125. GetViolatingRuleDesc(),
  126. severity );
  127. }
  128. if( m_parent && m_parent->IsExcluded() && !m_parent->GetComment().IsEmpty() )
  129. msg += wxString::Format( wxS( " %s\n" ), m_parent->GetComment() );
  130. return msg;
  131. }
  132. void RC_ITEM::GetJsonViolation( RC_JSON::VIOLATION& aViolation, UNITS_PROVIDER* aUnitsProvider,
  133. SEVERITY aSeverity,
  134. const std::map<KIID, EDA_ITEM*>& aItemMap ) const
  135. {
  136. wxString severity = getSeverityString( aSeverity );
  137. aViolation.severity = severity;
  138. aViolation.description = GetErrorMessage();
  139. aViolation.type = GetSettingsKey();
  140. aViolation.excluded = ( m_parent && m_parent->IsExcluded() );
  141. EDA_ITEM* mainItem = nullptr;
  142. EDA_ITEM* auxItem = nullptr;
  143. auto ii = aItemMap.find( GetMainItemID() );
  144. if( ii != aItemMap.end() )
  145. mainItem = ii->second;
  146. ii = aItemMap.find( GetAuxItemID() );
  147. if( ii != aItemMap.end() )
  148. auxItem = ii->second;
  149. if( mainItem )
  150. {
  151. RC_JSON::AFFECTED_ITEM item;
  152. item.description = mainItem->GetItemDescription( aUnitsProvider, true );
  153. item.uuid = mainItem->m_Uuid.AsString();
  154. item.pos.x = EDA_UNIT_UTILS::UI::ToUserUnit( aUnitsProvider->GetIuScale(),
  155. aUnitsProvider->GetUserUnits(),
  156. mainItem->GetPosition().x );
  157. item.pos.y = EDA_UNIT_UTILS::UI::ToUserUnit( aUnitsProvider->GetIuScale(),
  158. aUnitsProvider->GetUserUnits(),
  159. mainItem->GetPosition().y );
  160. aViolation.items.emplace_back( item );
  161. }
  162. if( auxItem )
  163. {
  164. RC_JSON::AFFECTED_ITEM item;
  165. item.description = auxItem->GetItemDescription( aUnitsProvider, true );
  166. item.uuid = auxItem->m_Uuid.AsString();
  167. item.pos.x = EDA_UNIT_UTILS::UI::ToUserUnit( aUnitsProvider->GetIuScale(),
  168. aUnitsProvider->GetUserUnits(),
  169. auxItem->GetPosition().x );
  170. item.pos.y = EDA_UNIT_UTILS::UI::ToUserUnit( aUnitsProvider->GetIuScale(),
  171. aUnitsProvider->GetUserUnits(),
  172. auxItem->GetPosition().y );
  173. aViolation.items.emplace_back( item );
  174. }
  175. }
  176. KIID RC_TREE_MODEL::ToUUID( wxDataViewItem aItem )
  177. {
  178. const RC_TREE_NODE* node = RC_TREE_MODEL::ToNode( aItem );
  179. if( node && node->m_RcItem )
  180. {
  181. const std::shared_ptr<RC_ITEM> rc_item = node->m_RcItem;
  182. switch( node->m_Type )
  183. {
  184. case RC_TREE_NODE::MARKER:
  185. case RC_TREE_NODE::COMMENT:
  186. // rc_item->GetParent() can be null, if the parent is not existing
  187. // when a RC item has no corresponding ERC/DRC marker
  188. if( rc_item->GetParent() )
  189. return rc_item->GetParent()->GetUUID();
  190. break;
  191. case RC_TREE_NODE::MAIN_ITEM: return rc_item->GetMainItemID();
  192. case RC_TREE_NODE::AUX_ITEM: return rc_item->GetAuxItemID();
  193. case RC_TREE_NODE::AUX_ITEM2: return rc_item->GetAuxItem2ID();
  194. case RC_TREE_NODE::AUX_ITEM3: return rc_item->GetAuxItem3ID();
  195. }
  196. }
  197. return niluuid;
  198. }
  199. RC_TREE_MODEL::RC_TREE_MODEL( EDA_DRAW_FRAME* aParentFrame, wxDataViewCtrl* aView ) :
  200. m_editFrame( aParentFrame ),
  201. m_view( aView ),
  202. m_severities( 0 ),
  203. m_rcItemsProvider( nullptr )
  204. {
  205. }
  206. RC_TREE_MODEL::~RC_TREE_MODEL()
  207. {
  208. for( RC_TREE_NODE* topLevelNode : m_tree )
  209. delete topLevelNode;
  210. }
  211. void RC_TREE_MODEL::rebuildModel( std::shared_ptr<RC_ITEMS_PROVIDER> aProvider, int aSeverities )
  212. {
  213. wxWindowUpdateLocker updateLock( m_view );
  214. std::shared_ptr<RC_ITEM> selectedRcItem = nullptr;
  215. if( m_view )
  216. {
  217. RC_TREE_NODE* selectedNode = ToNode( m_view->GetSelection() );
  218. selectedRcItem = selectedNode ? selectedNode->m_RcItem : nullptr;
  219. // Even with the updateLock, wxWidgets sometimes ties its knickers in a knot trying
  220. // to run a wxdataview_selection_changed_callback() on a row that has been deleted.
  221. m_view->UnselectAll();
  222. }
  223. BeforeReset();
  224. m_rcItemsProvider = std::move( aProvider );
  225. if( aSeverities != m_severities )
  226. m_severities = aSeverities;
  227. if( m_rcItemsProvider )
  228. m_rcItemsProvider->SetSeverities( m_severities );
  229. for( RC_TREE_NODE* topLevelNode : m_tree )
  230. delete topLevelNode;
  231. m_tree.clear();
  232. // wxDataView::ExpandAll() pukes with large lists
  233. int count = 0;
  234. if( m_rcItemsProvider )
  235. count = std::min( 1000, m_rcItemsProvider->GetCount() );
  236. for( int i = 0; i < count; ++i )
  237. {
  238. std::shared_ptr<RC_ITEM> rcItem = m_rcItemsProvider->GetItem( i );
  239. m_tree.push_back( new RC_TREE_NODE( nullptr, rcItem, RC_TREE_NODE::MARKER ) );
  240. RC_TREE_NODE* n = m_tree.back();
  241. if( rcItem->GetMainItemID() != niluuid )
  242. n->m_Children.push_back( new RC_TREE_NODE( n, rcItem, RC_TREE_NODE::MAIN_ITEM ) );
  243. if( rcItem->GetAuxItemID() != niluuid )
  244. n->m_Children.push_back( new RC_TREE_NODE( n, rcItem, RC_TREE_NODE::AUX_ITEM ) );
  245. if( rcItem->GetAuxItem2ID() != niluuid )
  246. n->m_Children.push_back( new RC_TREE_NODE( n, rcItem, RC_TREE_NODE::AUX_ITEM2 ) );
  247. if( rcItem->GetAuxItem3ID() != niluuid )
  248. n->m_Children.push_back( new RC_TREE_NODE( n, rcItem, RC_TREE_NODE::AUX_ITEM3 ) );
  249. if( MARKER_BASE* marker = rcItem->GetParent() )
  250. {
  251. if( marker->IsExcluded() && !marker->GetComment().IsEmpty() )
  252. n->m_Children.push_back( new RC_TREE_NODE( n, rcItem, RC_TREE_NODE::COMMENT ) );
  253. }
  254. }
  255. // Must be called after a significant change of items to force the
  256. // wxDataViewModel to reread all of them, repopulating itself entirely.
  257. AfterReset();
  258. #ifdef __WXGTK__
  259. // The fastest method to update wxDataViewCtrl is to rebuild from
  260. // scratch by calling Cleared(). Linux requires to reassociate model to
  261. // display data, but Windows will create multiple associations.
  262. // On MacOS, this crashes KiCad. See https://gitlab.com/kicad/code/kicad/-/issues/3666
  263. // and https://gitlab.com/kicad/code/kicad/-/issues/3653
  264. m_view->AssociateModel( this );
  265. #endif
  266. m_view->ClearColumns();
  267. m_view->AppendTextColumn( wxEmptyString, 0, wxDATAVIEW_CELL_INERT, wxCOL_WIDTH_AUTOSIZE );
  268. ExpandAll();
  269. // Most annoyingly wxWidgets won't tell us the scroll position (and no, all the usual
  270. // routines don't work), so we can only restore the scroll position based on a selection.
  271. if( selectedRcItem )
  272. {
  273. for( RC_TREE_NODE* candidate : m_tree )
  274. {
  275. if( candidate->m_RcItem == selectedRcItem )
  276. {
  277. m_view->Select( ToItem( candidate ) );
  278. m_view->EnsureVisible( ToItem( candidate ) );
  279. break;
  280. }
  281. }
  282. }
  283. }
  284. void RC_TREE_MODEL::Update( std::shared_ptr<RC_ITEMS_PROVIDER> aProvider, int aSeverities )
  285. {
  286. rebuildModel( aProvider, aSeverities );
  287. }
  288. void RC_TREE_MODEL::ExpandAll()
  289. {
  290. for( RC_TREE_NODE* topLevelNode : m_tree )
  291. m_view->Expand( ToItem( topLevelNode ) );
  292. }
  293. bool RC_TREE_MODEL::IsContainer( wxDataViewItem const& aItem ) const
  294. {
  295. if( ToNode( aItem ) == nullptr ) // must be tree root...
  296. return true;
  297. else
  298. return ToNode( aItem )->m_Type == RC_TREE_NODE::MARKER;
  299. }
  300. wxDataViewItem RC_TREE_MODEL::GetParent( wxDataViewItem const& aItem ) const
  301. {
  302. return ToItem( ToNode( aItem)->m_Parent );
  303. }
  304. unsigned int RC_TREE_MODEL::GetChildren( wxDataViewItem const& aItem,
  305. wxDataViewItemArray& aChildren ) const
  306. {
  307. const RC_TREE_NODE* node = ToNode( aItem );
  308. const std::vector<RC_TREE_NODE*>& children = node ? node->m_Children : m_tree;
  309. for( const RC_TREE_NODE* child: children )
  310. aChildren.push_back( ToItem( child ) );
  311. return children.size();
  312. }
  313. void RC_TREE_MODEL::GetValue( wxVariant& aVariant,
  314. wxDataViewItem const& aItem,
  315. unsigned int aCol ) const
  316. {
  317. if( !aItem.IsOk() || m_view->IsFrozen() )
  318. return;
  319. const RC_TREE_NODE* node = ToNode( aItem );
  320. const std::shared_ptr<RC_ITEM> rcItem = node->m_RcItem;
  321. MARKER_BASE* marker = rcItem->GetParent();
  322. EDA_ITEM* item = nullptr;
  323. wxString msg;
  324. switch( node->m_Type )
  325. {
  326. case RC_TREE_NODE::MARKER:
  327. if( marker )
  328. {
  329. SEVERITY severity = marker->GetSeverity();
  330. if( severity == RPT_SEVERITY_EXCLUSION )
  331. {
  332. if( m_editFrame->GetSeverity( rcItem->GetErrorCode() ) == RPT_SEVERITY_WARNING )
  333. msg = _( "Excluded warning: " );
  334. else
  335. msg = _( "Excluded error: " );
  336. }
  337. else if( severity == RPT_SEVERITY_WARNING )
  338. {
  339. msg = _( "Warning: " );
  340. }
  341. else
  342. {
  343. msg = _( "Error: " );
  344. }
  345. }
  346. msg += rcItem->GetErrorMessage();
  347. break;
  348. case RC_TREE_NODE::MAIN_ITEM:
  349. if( marker && marker->GetMarkerType() == MARKER_BASE::MARKER_DRAWING_SHEET )
  350. msg = _( "Drawing Sheet" );
  351. else
  352. item = m_editFrame->GetItem( rcItem->GetMainItemID() );
  353. break;
  354. case RC_TREE_NODE::AUX_ITEM:
  355. item = m_editFrame->GetItem( rcItem->GetAuxItemID() );
  356. break;
  357. case RC_TREE_NODE::AUX_ITEM2:
  358. item = m_editFrame->GetItem( rcItem->GetAuxItem2ID() );
  359. break;
  360. case RC_TREE_NODE::AUX_ITEM3:
  361. item = m_editFrame->GetItem( rcItem->GetAuxItem3ID() );
  362. break;
  363. case RC_TREE_NODE::COMMENT:
  364. if( marker )
  365. msg = marker->GetComment();
  366. break;
  367. }
  368. if( item )
  369. msg += item->GetItemDescription( m_editFrame, true );
  370. msg.Replace( wxS( "\n" ), wxS( " " ) );
  371. aVariant = msg;
  372. }
  373. bool RC_TREE_MODEL::GetAttr( wxDataViewItem const& aItem,
  374. unsigned int aCol,
  375. wxDataViewItemAttr& aAttr ) const
  376. {
  377. if( !aItem.IsOk() || m_view->IsFrozen() )
  378. return false;
  379. const RC_TREE_NODE* node = ToNode( aItem );
  380. bool ret = false;
  381. bool heading = node->m_Type == RC_TREE_NODE::MARKER;
  382. if( heading )
  383. {
  384. aAttr.SetBold( true );
  385. ret = true;
  386. }
  387. if( node->m_RcItem->GetParent()
  388. && node->m_RcItem->GetParent()->GetSeverity() == RPT_SEVERITY_EXCLUSION )
  389. {
  390. wxColour textColour = wxSystemSettings::GetColour( wxSYS_COLOUR_LISTBOXTEXT );
  391. double brightness = KIGFX::COLOR4D( textColour ).GetBrightness();
  392. if( brightness > 0.5 )
  393. {
  394. int lightness = static_cast<int>( brightness * ( heading ? 50 : 60 ) );
  395. aAttr.SetColour( textColour.ChangeLightness( lightness ) );
  396. }
  397. else
  398. {
  399. aAttr.SetColour( textColour.ChangeLightness( heading ? 170 : 165 ) );
  400. }
  401. aAttr.SetItalic( true ); // Strikethrough would be better, if wxWidgets supported it
  402. ret = true;
  403. }
  404. return ret;
  405. }
  406. void RC_TREE_MODEL::ValueChanged( RC_TREE_NODE* aNode )
  407. {
  408. if( aNode->m_Type != RC_TREE_NODE::MARKER )
  409. {
  410. ValueChanged( aNode->m_Parent );
  411. return;
  412. }
  413. wxDataViewItem markerItem = ToItem( aNode );
  414. wxDataViewModel::ValueChanged( markerItem, 0 );
  415. for( const RC_TREE_NODE* child : aNode->m_Children )
  416. wxDataViewModel::ValueChanged( ToItem( child ), 0 );
  417. // Comment items can come and go depening on exclusion state and comment content.
  418. //
  419. const std::shared_ptr<RC_ITEM> rcItem = aNode->m_RcItem;
  420. MARKER_BASE* marker = rcItem ? rcItem->GetParent() : nullptr;
  421. if( marker )
  422. {
  423. bool needsCommentNode = marker->IsExcluded() && !marker->GetComment().IsEmpty();
  424. RC_TREE_NODE* commentNode = aNode->m_Children.back();
  425. if( commentNode && commentNode->m_Type != RC_TREE_NODE::COMMENT )
  426. commentNode = nullptr;
  427. if( needsCommentNode && !commentNode )
  428. {
  429. commentNode = new RC_TREE_NODE( aNode, rcItem, RC_TREE_NODE::COMMENT );
  430. wxDataViewItemArray newItems;
  431. newItems.push_back( ToItem( commentNode ) );
  432. aNode->m_Children.push_back( commentNode );
  433. ItemsAdded( markerItem, newItems );
  434. }
  435. else if( commentNode && !needsCommentNode )
  436. {
  437. wxDataViewItemArray deletedItems;
  438. deletedItems.push_back( ToItem( commentNode ) );
  439. aNode->m_Children.erase( aNode->m_Children.end() - 1 );
  440. ItemsDeleted( markerItem, deletedItems );
  441. }
  442. }
  443. }
  444. void RC_TREE_MODEL::DeleteCurrentItem( bool aDeep )
  445. {
  446. DeleteItems( true, true, aDeep );
  447. }
  448. void RC_TREE_MODEL::DeleteItems( bool aCurrentOnly, bool aIncludeExclusions, bool aDeep )
  449. {
  450. RC_TREE_NODE* current_node = m_view ? ToNode( m_view->GetCurrentItem() ) : nullptr;
  451. const std::shared_ptr<RC_ITEM> current_item = current_node ? current_node->m_RcItem : nullptr;
  452. /// Keep a vector of elements to free after wxWidgets is definitely done accessing them
  453. std::vector<RC_TREE_NODE*> to_delete;
  454. std::vector<RC_TREE_NODE*> expanded;
  455. if( aCurrentOnly && !current_item )
  456. {
  457. wxBell();
  458. return;
  459. }
  460. // wxWidgets 3.1.x on MacOS (at least) loses the expanded state of the tree when deleting
  461. // items.
  462. if( m_view && aCurrentOnly )
  463. {
  464. for( RC_TREE_NODE* node : m_tree )
  465. {
  466. if( m_view->IsExpanded( ToItem( node ) ) )
  467. expanded.push_back( node );
  468. }
  469. }
  470. int lastGood = -1;
  471. bool itemDeleted = false;
  472. if( m_view )
  473. {
  474. m_view->UnselectAll();
  475. wxSafeYield();
  476. m_view->Freeze();
  477. }
  478. if( !m_rcItemsProvider )
  479. return;
  480. for( int i = m_rcItemsProvider->GetCount() - 1; i >= 0; --i )
  481. {
  482. std::shared_ptr<RC_ITEM> rcItem = m_rcItemsProvider->GetItem( i );
  483. MARKER_BASE* marker = rcItem->GetParent();
  484. bool excluded = false;
  485. if( marker && marker->GetSeverity() == RPT_SEVERITY_EXCLUSION )
  486. excluded = true;
  487. if( aCurrentOnly && itemDeleted && lastGood >= 0 )
  488. break;
  489. if( aCurrentOnly && rcItem != current_item )
  490. {
  491. lastGood = i;
  492. continue;
  493. }
  494. if( excluded && !aIncludeExclusions )
  495. continue;
  496. if( i < (int) m_tree.size() ) // Careful; tree is truncated for large datasets
  497. {
  498. wxDataViewItem markerItem = ToItem( m_tree[i] );
  499. wxDataViewItemArray childItems;
  500. wxDataViewItem parentItem = ToItem( m_tree[i]->m_Parent );
  501. for( RC_TREE_NODE* child : m_tree[i]->m_Children )
  502. {
  503. childItems.push_back( ToItem( child ) );
  504. to_delete.push_back( child );
  505. }
  506. m_tree[i]->m_Children.clear();
  507. ItemsDeleted( markerItem, childItems );
  508. to_delete.push_back( m_tree[i] );
  509. m_tree.erase( m_tree.begin() + i );
  510. ItemDeleted( parentItem, markerItem );
  511. }
  512. // Only deep delete the current item here; others will be done by the caller, which
  513. // can more efficiently delete all markers on the board.
  514. m_rcItemsProvider->DeleteItem( i, aDeep && aCurrentOnly );
  515. if( lastGood > i )
  516. lastGood--;
  517. itemDeleted = true;
  518. }
  519. if( m_view && aCurrentOnly && lastGood >= 0 )
  520. {
  521. for( RC_TREE_NODE* node : expanded )
  522. {
  523. wxDataViewItem item = ToItem( node );
  524. if( item.IsOk() )
  525. m_view->Expand( item );
  526. }
  527. wxDataViewItem selItem = ToItem( m_tree[ lastGood ] );
  528. m_view->Select( selItem );
  529. // wxEVT_COMMAND_DATAVIEW_SELECTION_CHANGED doesn't get propogated from the Select()
  530. // call on (at least) MSW.
  531. wxDataViewEvent selectEvent( wxEVT_COMMAND_DATAVIEW_SELECTION_CHANGED, m_view, selItem );
  532. m_view->GetEventHandler()->ProcessEvent( selectEvent );
  533. }
  534. for( RC_TREE_NODE* item : to_delete )
  535. delete( item );
  536. if( m_view )
  537. m_view->Thaw();
  538. }
  539. void RC_TREE_MODEL::PrevMarker()
  540. {
  541. RC_TREE_NODE* currentNode = ToNode( m_view->GetCurrentItem() );
  542. RC_TREE_NODE* prevMarker = nullptr;
  543. while( currentNode && currentNode->m_Type != RC_TREE_NODE::MARKER )
  544. currentNode = currentNode->m_Parent;
  545. for( RC_TREE_NODE* candidate : m_tree )
  546. {
  547. if( candidate == currentNode )
  548. break;
  549. else
  550. prevMarker = candidate;
  551. }
  552. if( prevMarker )
  553. m_view->Select( ToItem( prevMarker ) );
  554. }
  555. void RC_TREE_MODEL::NextMarker()
  556. {
  557. RC_TREE_NODE* currentNode = ToNode( m_view->GetCurrentItem() );
  558. while( currentNode && currentNode->m_Type != RC_TREE_NODE::MARKER )
  559. currentNode = currentNode->m_Parent;
  560. RC_TREE_NODE* nextMarker = nullptr;
  561. bool trigger = currentNode == nullptr;
  562. for( RC_TREE_NODE* candidate : m_tree )
  563. {
  564. if( candidate == currentNode )
  565. {
  566. trigger = true;
  567. }
  568. else if( trigger )
  569. {
  570. nextMarker = candidate;
  571. break;
  572. }
  573. }
  574. if( nextMarker )
  575. m_view->Select( ToItem( nextMarker ) );
  576. }
  577. void RC_TREE_MODEL::SelectMarker( const MARKER_BASE* aMarker )
  578. {
  579. wxCHECK( !m_view->IsFrozen(), /* void */ );
  580. for( RC_TREE_NODE* candidate : m_tree )
  581. {
  582. if( candidate->m_RcItem->GetParent() == aMarker )
  583. {
  584. m_view->Select( ToItem( candidate ) );
  585. return;
  586. }
  587. }
  588. }
  589. void RC_TREE_MODEL::CenterMarker( const MARKER_BASE* aMarker )
  590. {
  591. wxCHECK( !m_view->IsFrozen(), /* void */ );
  592. for( RC_TREE_NODE* candidate : m_tree )
  593. {
  594. if( candidate->m_RcItem->GetParent() == aMarker )
  595. {
  596. m_view->EnsureVisible( ToItem( candidate ) );
  597. return;
  598. }
  599. }
  600. }