KiCad PCB EDA Suite
Loading...
Searching...
No Matches
dialog_kicad_diff.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 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 3
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/gpl-3.0.html
19 * or you may search the http://www.gnu.org website for the version 3 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#include "dialog_kicad_diff.h"
25
28
29#include <layer_ids.h>
30#include <lseq.h>
31#include <set>
32#include <wx/button.h>
33#include <wx/checkbox.h>
34#include <wx/choice.h>
35#include <wx/menu.h>
36#include <wx/settings.h>
37#include <wx/srchctrl.h>
38#include <wx/sizer.h>
39#include <wx/splitter.h>
40#include <wx/stattext.h>
41#include <wx/wupdlock.h>
42
43
44DIALOG_KICAD_DIFF::DIALOG_KICAD_DIFF( wxWindow* aParent, const wxString& aReferencePath,
45 const wxString& aComparisonPath, const KICAD_DIFF::DOCUMENT_DIFF& aDiff,
46 KICAD_DIFF::DOCUMENT_GEOMETRY aReferenceGeometry,
47 KICAD_DIFF::DOCUMENT_GEOMETRY aComparisonGeometry, SHEET_SWITCHER aSheetSwitcher,
48 KIID_PATH aInitialSheet ) :
49 DIALOG_KICAD_DIFF_BASE( aParent ),
50 m_diff( aDiff ),
51 m_sheetSwitcher( std::move( aSheetSwitcher ) ),
52 m_currentCanvasSheet( std::move( aInitialSheet ) )
53{
54 SetEscapeId( wxID_OK );
55
56 m_treeChanges->Bind( wxEVT_TREE_ITEM_MENU, &DIALOG_KICAD_DIFF::OnTreeItemMenu, this );
57
58 m_pathReference->SetLabel( aReferencePath );
59 m_pathComparison->SetLabel( aComparisonPath );
60
61 // Two property columns: name | before -> after
62 m_listProperties->AppendColumn( _( "Property" ), wxLIST_FORMAT_LEFT, 180 );
63 m_listProperties->AppendColumn( _( "Before" ), wxLIST_FORMAT_LEFT, 160 );
64 m_listProperties->AppendColumn( _( "After" ), wxLIST_FORMAT_LEFT, 160 );
65
66 // Splice a search box above the change tree. Empty filter = show all.
67 if( wxSizer* treeSizer = m_panelTree->GetSizer() )
68 {
69 wxSearchCtrl* search = new wxSearchCtrl( m_panelTree, wxID_ANY );
70 search->SetDescriptiveText( _( "Filter changes…" ) );
71 search->ShowCancelButton( true );
72
73 auto onSearch = [this, search]( wxCommandEvent& aEvent )
74 {
75 // BuildChangeTreeGroups lowercases internally; keep the
76 // raw input here so a future surface displaying the
77 // active filter (e.g. tooltip) preserves capitalization.
78 m_searchFilter = search->GetValue();
79 buildTree();
80 aEvent.Skip();
81 };
82
83 // wxSearchCtrl's cancel button fires its own event on some ports
84 // (notably macOS); binding both keeps the filter cleared whichever
85 // path the user takes to empty the box.
86 search->Bind( wxEVT_TEXT, onSearch );
87 search->Bind( wxEVT_SEARCHCTRL_CANCEL_BTN, onSearch );
88
89 treeSizer->Insert( 0, search, wxSizerFlags().Expand().Border( wxLEFT | wxRIGHT | wxTOP, 4 ) );
90 m_panelTree->Layout();
91 }
92
93 // Splice the thumbnail into the detail sizer between the summary label
94 // and the property list. Only construct when we can attach.
95 if( wxSizer* detailSizer = m_panelDetail->GetSizer() )
96 {
97 // Board on top, property list below, with a draggable sash. Gravity 1
98 // gives extra height to the board when the dialog grows.
99 wxSplitterWindow* detailSplitter =
100 new wxSplitterWindow( m_panelDetail, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSP_LIVE_UPDATE );
101 detailSplitter->SetSashGravity( 1.0 );
102 detailSplitter->SetMinimumPaneSize( 40 );
103
104 m_canvas = new WIDGET_DIFF_CANVAS( detailSplitter );
105
106 if( m_sheetSwitcher )
108
111
112 scene.referenceGeometry = std::move( aReferenceGeometry );
113 scene.comparisonGeometry = std::move( aComparisonGeometry );
115
116 const LSET geometryLayers = KICAD_DIFF::GeometryLayerSet( scene.referenceGeometry )
118
119 wxBoxSizer* filterSizer = new wxBoxSizer( wxHORIZONTAL );
120
121 const std::array<std::pair<KICAD_DIFF::CATEGORY, wxString>, 4> categories{ {
122 { KICAD_DIFF::CATEGORY::ADDED, _( "Added" ) },
123 { KICAD_DIFF::CATEGORY::REMOVED, _( "Removed" ) },
124 { KICAD_DIFF::CATEGORY::MODIFIED, _( "Modified" ) },
125 { KICAD_DIFF::CATEGORY::CONFLICT, _( "Conflict" ) },
126 } };
127
128 for( const auto& [cat, label] : categories )
129 {
130 wxCheckBox* cb = new wxCheckBox( m_panelDetail, wxID_ANY, label );
131 cb->SetValue( true );
132 cb->Bind( wxEVT_CHECKBOX,
133 [this, cat]( wxCommandEvent& aEvent )
134 {
135 m_canvas->SetCategoryVisible( cat, aEvent.IsChecked() );
136 buildTree();
137 } );
138 filterSizer->Add( cb, wxSizerFlags().Border( wxRIGHT, 8 ).Centre() );
139 }
140
141 if( geometryLayers.any() )
142 {
143 filterSizer->Add( new wxStaticText( m_panelDetail, wxID_ANY, _( "Layers:" ) ),
144 wxSizerFlags().Border( wxLEFT | wxRIGHT, 4 ).Centre() );
145
146 for( PCB_LAYER_ID layer : geometryLayers.UIOrder() )
147 {
148 wxCheckBox* cb = new wxCheckBox( m_panelDetail, wxID_ANY, LayerName( layer ) );
149 cb->SetValue( true );
150 cb->Bind( wxEVT_CHECKBOX,
151 [this, layer]( wxCommandEvent& aEvent )
152 {
153 m_canvas->SetLayerVisible( layer, aEvent.IsChecked() );
154 } );
155 filterSizer->Add( cb, wxSizerFlags().Border( wxRIGHT, 8 ).Centre() );
156 }
157 }
158
159 // Toggle the property list so the board can use the full detail panel.
160 filterSizer->AddStretchSpacer();
161
162 wxCheckBox* propsToggle = new wxCheckBox( m_panelDetail, wxID_ANY, _( "Properties" ) );
163 propsToggle->SetValue( true );
164 propsToggle->Bind( wxEVT_CHECKBOX,
165 [this, detailSplitter]( wxCommandEvent& aEvent )
166 {
167 if( aEvent.IsChecked() && !detailSplitter->IsSplit() )
168 detailSplitter->SplitHorizontally( m_canvas, m_listProperties );
169 else if( !aEvent.IsChecked() && detailSplitter->IsSplit() )
170 detailSplitter->Unsplit( m_listProperties );
171 } );
172 filterSizer->Add( propsToggle, wxSizerFlags().Border( wxRIGHT, 8 ).Centre() );
173
174 // Move the property list out of the detail sizer and under the splitter
175 // so the sash can resize it. A small min height lets it shrink freely.
176 detailSizer->Detach( m_listProperties );
177 m_listProperties->Reparent( detailSplitter );
178 m_listProperties->SetMinSize( wxSize( -1, 40 ) );
179 detailSplitter->SplitHorizontally( m_canvas, m_listProperties, -200 );
180
181 propsToggle->SetValue( detailSplitter->IsSplit() );
182
183 size_t insertAt = std::min<size_t>( 1, detailSizer->GetItemCount() );
184 detailSizer->Insert( insertAt++, filterSizer, wxSizerFlags().Expand().Border( wxLEFT | wxRIGHT | wxTOP, 4 ) );
185 detailSizer->Insert( insertAt, detailSplitter, wxSizerFlags( 1 ).Expand().Border( wxALL, 4 ) );
186 m_panelDetail->Layout();
187
188 m_canvas->SetScene( std::move( scene ) );
189
190 m_canvas->SetPickHandler(
191 [this]( const std::optional<KIID_PATH>& aChangeId )
192 {
193 if( aChangeId )
194 {
195 m_suppressCenter = true;
196 selectChangeById( *aChangeId );
197 m_suppressCenter = false;
198 }
199 else if( m_treeChanges )
200 {
201 m_treeChanges->UnselectAll();
202 showChange( nullptr );
203 }
204 } );
205 }
206
207 buildTree();
208
209 if( m_diff.Empty() )
210 m_labelSummary->SetLabel( _( "No differences detected." ) );
211 else
212 m_labelSummary->SetLabel( wxString::Format( _( "%zu change(s) detected." ), m_diff.Size() ) );
213
214 Layout();
215}
216
217
218void DIALOG_KICAD_DIFF::OnClose( wxCloseEvent& aEvent )
219{
220 EndModal( wxID_OK );
221}
222
223
224void DIALOG_KICAD_DIFF::OnOK( wxCommandEvent& aEvent )
225{
226 EndModal( wxID_OK );
227}
228
229
231{
232 wxTreeItemId sel = aEvent.GetItem();
233
234 if( !sel.IsOk() )
235 {
236 showChange( nullptr );
237 return;
238 }
239
240 auto it = m_changeByTreeId.find( reinterpret_cast<wxUIntPtr>( sel.GetID() ) );
241
242 if( it == m_changeByTreeId.end() )
243 {
244 showChange( nullptr ); // group node — no per-item details
245 return;
246 }
247
248 showChange( it->second );
249}
250
251
253{
254 // Snapshot the selected change so it can be restored after rebuild —
255 // wxTreeItemIds don't survive DeleteAllItems, so we re-find the node
256 // by its underlying KIID_PATH below.
257 std::optional<KIID_PATH> selectedId;
258
259 if( wxTreeItemId sel = m_treeChanges->GetSelection(); sel.IsOk() )
260 {
261 auto it = m_changeByTreeId.find( reinterpret_cast<wxUIntPtr>( sel.GetID() ) );
262
263 if( it != m_changeByTreeId.end() && it->second )
264 selectedId = it->second->id;
265 }
266
267 wxWindowUpdateLocker updateLock( m_treeChanges );
268
269 m_treeChanges->DeleteAllItems();
270 m_changeByTreeId.clear();
271
272 wxTreeItemId root = m_treeChanges->AddRoot( wxS( "root" ) );
273
274 // Per-category visibility comes from the canvas's checkbox row when
275 // present (the dialog hosts both a thumbnail canvas and the tree).
276 // With no canvas (degenerate test path) treat every category as visible.
277 std::array<bool, KICAD_DIFF::CATEGORY_COUNT> visibleCats{};
278 visibleCats.fill( true );
279
280 if( m_canvas )
281 {
282 for( std::size_t i = 0; i < KICAD_DIFF::CATEGORY_COUNT; ++i )
283 {
284 visibleCats[i] = m_canvas->IsCategoryVisible( static_cast<KICAD_DIFF::CATEGORY>( i ) );
285 }
286 }
287
290 {
291 wxTreeItemId groupNode = m_treeChanges->AppendItem( root, group.groupLabel );
292
293 for( const KICAD_DIFF::CHANGE_TREE_ENTRY& entry : group.entries )
294 {
295 wxTreeItemId itemNode = m_treeChanges->AppendItem( groupNode, entry.itemLabel );
296 m_changeByTreeId[reinterpret_cast<wxUIntPtr>( itemNode.GetID() )] = entry.change;
297 }
298
299 m_treeChanges->Expand( groupNode );
300 }
301
302 if( selectedId && !selectChangeById( *selectedId ) )
303 showChange( nullptr );
304
306}
307
308
309void DIALOG_KICAD_DIFF::OnTreeItemMenu( wxTreeEvent& aEvent )
310{
311 wxTreeItemId item = aEvent.GetItem();
312
313 if( !item.IsOk() )
314 return;
315
316 auto it = m_changeByTreeId.find( reinterpret_cast<wxUIntPtr>( item.GetID() ) );
317
318 if( it == m_changeByTreeId.end() || !it->second )
319 return; // group node, nothing to hide
320
321 const std::vector<KIID_PATH> ids = changeRowIds( *it->second );
322 const bool hidden = m_hiddenChanges.count( it->second->id ) > 0;
323
324 enum
325 {
326 ID_HIDE = wxID_HIGHEST + 1,
327 ID_SHOW,
328 ID_SHOW_ALL
329 };
330
331 wxMenu menu;
332
333 if( hidden )
334 menu.Append( ID_SHOW, _( "Show Change" ) );
335 else
336 menu.Append( ID_HIDE, _( "Hide Change" ) );
337
338 menu.Append( ID_SHOW_ALL, _( "Show All Hidden" ) );
339 menu.Enable( ID_SHOW_ALL, !m_hiddenChanges.empty() );
340
341 switch( m_treeChanges->GetPopupMenuSelectionFromUser( menu ) )
342 {
343 case ID_HIDE: m_hiddenChanges.insert( ids.begin(), ids.end() ); break;
344 case ID_SHOW:
345 for( const KIID_PATH& id : ids )
346 m_hiddenChanges.erase( id );
347 break;
348 case ID_SHOW_ALL: m_hiddenChanges.clear(); break;
349 default: return;
350 }
351
352 if( m_canvas )
353 m_canvas->SetHiddenChanges( m_hiddenChanges );
354
356}
357
358
359std::vector<KIID_PATH> DIALOG_KICAD_DIFF::changeRowIds( const KICAD_DIFF::ITEM_CHANGE& aChange ) const
360{
361 if( !KICAD_DIFF::IsRoutingNetChange( aChange ) )
362 return { aChange.id };
363
364 std::vector<KIID_PATH> ids;
365
366 std::function<void( const std::vector<KICAD_DIFF::ITEM_CHANGE>& )> walk =
367 [&]( const std::vector<KICAD_DIFF::ITEM_CHANGE>& aChanges )
368 {
369 for( const KICAD_DIFF::ITEM_CHANGE& c : aChanges )
370 {
371 if( KICAD_DIFF::IsRoutingNetChange( c ) && c.kind == aChange.kind && c.refdes == aChange.refdes )
372 {
373 ids.push_back( c.id );
374 }
375
376 walk( c.children );
377 }
378 };
379
380 walk( m_diff.changes );
381 return ids;
382}
383
384
386{
387 const wxColour grey = wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT );
388 const wxColour normal = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT );
389
390 for( const auto& [key, change] : m_changeByTreeId )
391 {
392 if( !change )
393 continue;
394
395 wxTreeItemId node( reinterpret_cast<void*>( key ) );
396 const bool hidden = m_hiddenChanges.count( change->id ) > 0;
397
398 m_treeChanges->SetItemTextColour( node, hidden ? grey : normal );
399 }
400}
401
402
403void DIALOG_KICAD_DIFF::SetRevisionChooser( const std::vector<wxString>& aLabels, int aSelected,
404 REVISION_HANDLER aOnChange )
405{
406 wxSizer* top = GetSizer();
407
408 if( !top || aLabels.empty() )
409 return;
410
411 m_revisionHandler = std::move( aOnChange );
412
413 wxBoxSizer* row = new wxBoxSizer( wxHORIZONTAL );
414 row->Add( new wxStaticText( this, wxID_ANY, _( "Revision:" ) ), wxSizerFlags().Border( wxRIGHT, 4 ).Centre() );
415
416 wxArrayString choices;
417
418 for( const wxString& label : aLabels )
419 choices.Add( label );
420
421 m_revisionChoice = new wxChoice( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, choices );
422 m_revisionChoice->SetSelection( aSelected );
423 row->Add( m_revisionChoice, wxSizerFlags( 1 ).Centre() );
424
425 wxButton* olderBtn = new wxButton( this, wxID_ANY, _( "Older" ), wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT );
426 wxButton* newerBtn = new wxButton( this, wxID_ANY, _( "Newer" ), wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT );
427 row->Add( olderBtn, wxSizerFlags().Border( wxLEFT, 4 ).Centre() );
428 row->Add( newerBtn, wxSizerFlags().Border( wxLEFT, 2 ).Centre() );
429
430 auto fire = [this]( int aIndex )
431 {
432 if( aIndex < 0 || aIndex >= static_cast<int>( m_revisionChoice->GetCount() ) )
433 return;
434
435 m_revisionChoice->SetSelection( aIndex );
436
438 m_revisionHandler( aIndex );
439 };
440
441 m_revisionChoice->Bind( wxEVT_CHOICE,
442 [fire]( wxCommandEvent& aEvent )
443 {
444 fire( aEvent.GetSelection() );
445 } );
446
447 // Snapshots are newest-first, so older steps up the index and newer down.
448 olderBtn->Bind( wxEVT_BUTTON,
449 [this, fire]( wxCommandEvent& )
450 {
451 fire( m_revisionChoice->GetSelection() + 1 );
452 } );
453 newerBtn->Bind( wxEVT_BUTTON,
454 [this, fire]( wxCommandEvent& )
455 {
456 fire( m_revisionChoice->GetSelection() - 1 );
457 } );
458
459 top->Insert( 0, row, wxSizerFlags().Expand().Border( wxALL, 4 ) );
460 Layout();
461}
462
463
464void DIALOG_KICAD_DIFF::Reload( const wxString& aReferencePath, const wxString& aComparisonPath,
466 KICAD_DIFF::DOCUMENT_GEOMETRY aComparisonGeometry, SHEET_SWITCHER aSheetSwitcher,
467 KIID_PATH aInitialSheet )
468{
469 m_pathReference->SetLabel( aReferencePath );
470 m_pathComparison->SetLabel( aComparisonPath );
471
472 m_diff = std::move( aDiff );
473
474 // Outlives the canvas reconfigure below: it owns cloned items the view still references.
475 SHEET_SWITCHER previousSwitcher = std::move( m_sheetSwitcher );
476 m_sheetSwitcher = std::move( aSheetSwitcher );
477 m_currentCanvasSheet = std::move( aInitialSheet );
478
479 if( m_canvas )
480 {
481 // Hold rendering so reconfiguring the context and setting the new scene
482 // produce a single repaint instead of flashing an intermediate frame.
483 // EndUpdate must run even on a throw, or rendering stays frozen.
484 m_canvas->BeginUpdate();
485
486 try
487 {
488 m_canvas->HighlightChange( std::nullopt );
489
490 if( m_sheetSwitcher )
492
495
496 scene.referenceGeometry = std::move( aReferenceGeometry );
497 scene.comparisonGeometry = std::move( aComparisonGeometry );
499
500 m_canvas->SetScene( std::move( scene ) );
501 }
502 catch( ... )
503 {
504 m_canvas->EndUpdate();
505 throw;
506 }
507
508 m_canvas->EndUpdate();
509 }
510
511 buildTree();
512
513 if( m_diff.Empty() )
514 m_labelSummary->SetLabel( _( "No differences detected." ) );
515 else
516 m_labelSummary->SetLabel( wxString::Format( _( "%zu change(s) detected." ), m_diff.Size() ) );
517
518 showChange( nullptr );
519 Layout();
520}
521
522
524{
525 if( !m_canvas || !m_sheetSwitcher )
526 return;
527
528 if( aSheetPath == m_currentCanvasSheet )
529 return;
530
531 m_currentCanvasSheet = aSheetPath;
532 m_sheetSwitcher( *m_canvas, aSheetPath );
533
534 if( m_treeChanges )
535 m_treeChanges->UnselectAll();
536
537 showChange( nullptr );
538}
539
540
542{
543 m_listProperties->DeleteAllItems();
544
546 m_changeSelectedFn( aChange ? aChange->id : KIID_PATH() );
547
548 if( !aChange )
549 {
550 m_labelSummary->SetLabel( _( "Select a change in the tree to view details" ) );
551
552 if( m_canvas )
553 m_canvas->HighlightChange( std::nullopt );
554
555 return;
556 }
557
558 m_labelSummary->SetLabel(
559 wxString::Format( wxS( "%s — %s" ), KICAD_DIFF::ChangeKindLabel( aChange->kind ), aChange->typeName ) );
560
561 if( m_canvas )
562 {
563 // Only auto-switch when the change carries a sheet prefix. Scoped
564 // diffs emit single-element ids with no sheet path.
565 if( aChange->id.size() > 1 )
566 {
567 KIID_PATH sheetPath = aChange->id;
568 sheetPath.pop_back();
569
570 if( sheetPath != m_currentCanvasSheet && m_sheetSwitcher )
571 {
572 m_currentCanvasSheet = sheetPath;
573 m_sheetSwitcher( *m_canvas, sheetPath );
574 }
575 }
576
577 m_canvas->HighlightChange( aChange->id );
578
579 if( !m_suppressCenter )
580 m_canvas->CenterOnHighlight();
581 }
582
583 long row = 0;
584 std::set<wxString> seen;
585
586 auto emitDelta = [&]( const wxString& aPrefix, const KICAD_DIFF::PROPERTY_DELTA& d )
587 {
588 const wxString label = aPrefix.IsEmpty() ? d.name : aPrefix + wxS( " / " ) + d.name;
589 const wxString beforeStr = d.before.ToDisplayString();
590 const wxString afterStr = d.after.ToDisplayString();
591 const wxString key = label + wxS( "|" ) + beforeStr + wxS( "|" ) + afterStr;
592
593 if( !seen.insert( key ).second )
594 return;
595
596 long idx = m_listProperties->InsertItem( row++, label );
597 m_listProperties->SetItem( idx, 1, beforeStr );
598 m_listProperties->SetItem( idx, 2, afterStr );
599 };
600
601 for( const KICAD_DIFF::PROPERTY_DELTA& d : aChange->properties )
602 emitDelta( wxEmptyString, d );
603
604 // When the clicked item carries its real diffs in children (e.g. a
605 // FOOTPRINT whose pad or field changed), surface those here so the panel
606 // is not empty for the parent.
607 std::function<void( const wxString&, const KICAD_DIFF::ITEM_CHANGE& )> walkChildren =
608 [&]( const wxString& aPrefix, const KICAD_DIFF::ITEM_CHANGE& aC )
609 {
610 for( const KICAD_DIFF::ITEM_CHANGE& child : aC.children )
611 {
612 wxString prefix = child.typeName;
613
614 if( child.refdes && !child.refdes->IsEmpty() )
615 prefix += wxS( " " ) + *child.refdes;
616
617 if( !aPrefix.IsEmpty() )
618 prefix = aPrefix + wxS( " / " ) + prefix;
619
620 for( const KICAD_DIFF::PROPERTY_DELTA& d : child.properties )
621 emitDelta( prefix, d );
622
623 walkChildren( prefix, child );
624 }
625 };
626
627 walkChildren( wxEmptyString, *aChange );
628}
629
630
632{
633 for( const auto& [treeIdNum, change] : m_changeByTreeId )
634 {
635 if( change && change->id == aChangeId )
636 {
637 wxTreeItemId treeId( reinterpret_cast<void*>( treeIdNum ) );
638
639 m_treeChanges->EnsureVisible( treeId );
640 m_treeChanges->SelectItem( treeId );
641 return true;
642 }
643 }
644
645 return false;
646}
DIALOG_KICAD_DIFF_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxString &title=_("Compare Files"), const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(900, 650), long style=wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER)
std::vector< KIID_PATH > changeRowIds(const KICAD_DIFF::ITEM_CHANGE &aChange) const
Change ids a tree row stands for.
SHEET_SWITCHER m_sheetSwitcher
void OnTreeSelectionChanged(wxTreeEvent &aEvent) override
std::set< KIID_PATH > m_hiddenChanges
Changes the user has muted via the tree's right-click menu.
void showChange(const KICAD_DIFF::ITEM_CHANGE *aChange)
Populate the property list for the change associated with the selected tree node, or clear the list i...
std::function< void(int aIndex)> REVISION_HANDLER
WIDGET_DIFF_CANVAS * m_canvas
GAL-backed canvas showing the DIFF_SCENE shape rectangles.
std::map< wxUIntPtr, const KICAD_DIFF::ITEM_CHANGE * > m_changeByTreeId
Maps tree item IDs to the underlying change record so selection can find the data without walking the...
void OnOK(wxCommandEvent &aEvent) override
void buildTree()
Populate the tree from m_diff. Groups by CHANGE_KIND.
std::function< void(WIDGET_DIFF_CANVAS &, const KIID_PATH &)> SHEET_SWITCHER
wxString m_searchFilter
Lowercased free-text filter applied to typeName / refdes.
void applyHiddenToTree()
Grey out tree rows whose change is in m_hiddenChanges, restore the rest.
DIALOG_KICAD_DIFF(wxWindow *aParent, const wxString &aReferencePath, const wxString &aComparisonPath, const KICAD_DIFF::DOCUMENT_DIFF &aDiff, KICAD_DIFF::DOCUMENT_GEOMETRY aReferenceGeometry={}, KICAD_DIFF::DOCUMENT_GEOMETRY aComparisonGeometry={}, SHEET_SWITCHER aSheetSwitcher={}, KIID_PATH aInitialSheet={})
bool selectChangeById(const KIID_PATH &aChangeId)
Select the tree row whose change has the given KIID_PATH.
KICAD_DIFF::DOCUMENT_DIFF m_diff
void OnClose(wxCloseEvent &aEvent) override
REVISION_HANDLER m_revisionHandler
void OnTreeItemMenu(wxTreeEvent &aEvent)
void SwitchCanvasToSheet(const KIID_PATH &aSheetPath)
CHANGE_SELECTED_FN m_changeSelectedFn
bool m_suppressCenter
True while a canvas click is propagating into the tree, so the tree- selection handler skips re-cente...
void SetRevisionChooser(const std::vector< wxString > &aLabels, int aSelected, REVISION_HANDLER aOnChange)
Add a revision dropdown (with prev/next) at the top.
void Reload(const wxString &aReferencePath, const wxString &aComparisonPath, KICAD_DIFF::DOCUMENT_DIFF aDiff, KICAD_DIFF::DOCUMENT_GEOMETRY aReferenceGeometry, KICAD_DIFF::DOCUMENT_GEOMETRY aComparisonGeometry, SHEET_SWITCHER aSheetSwitcher, KIID_PATH aInitialSheet)
Swap in a fresh diff with new schematics.
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
LSEQ UIOrder() const
Return the copper, technical and user layers in the order shown in layer widget.
Definition lset.cpp:739
GAL-backed canvas for visualizing a KICAD_DIFF::DIFF_SCENE.
#define _(s)
wxString LayerName(int aLayer)
Returns the default display name for a given layer.
Definition layer_id.cpp:31
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
constexpr std::size_t CATEGORY_COUNT
Definition diff_scene.h:75
DIFF_SCENE BuildScene(const DOCUMENT_DIFF &aDiff, const DIFF_COLOR_THEME &aTheme)
Build a DIFF_SCENE from a DOCUMENT_DIFF, populating the shape lists and computing the union bbox.
LSET GeometryLayerSet(const DOCUMENT_GEOMETRY &aGeometry)
Return the union of every non-empty layer set carried by the geometry.
std::vector< CHANGE_TREE_GROUP > BuildChangeTreeGroups(const DOCUMENT_DIFF &aDiff, const wxString &aSearchFilter, const std::array< bool, CATEGORY_COUNT > &aVisibleCategories)
Group the changes in a DOCUMENT_DIFF by kind, apply category and search filters, and return the resul...
wxString ChangeKindLabel(CHANGE_KIND aKind)
Human-readable label for a CHANGE_KIND (e.g.
void ExpandBBoxToGeometry(DIFF_SCENE &aScene)
Grow the scene's documentBBox to also include the extent of any background geometry.
bool IsRoutingNetChange(const ITEM_CHANGE &aChange)
Presentation predicate for PCB routing changes that should be displayed as one net-level entry/shape.
CATEGORY
Visual category each ITEM_CHANGE belongs to in the scene.
Definition diff_scene.h:52
STL namespace.
One row in a grouped change tree — an ITEM_CHANGE plus its display label (typeName + optional refdes ...
const ITEM_CHANGE * change
wxString itemLabel
One bucket in a grouped change tree — a CHANGE_KIND, a human- readable group label,...
DOCUMENT_GEOMETRY referenceGeometry
Background geometry from the two source documents.
Definition diff_scene.h:239
DOCUMENT_GEOMETRY comparisonGeometry
Definition diff_scene.h:240
The full set of changes between two parsed documents of one type.
Aggregate of background geometry extracted from one source document.
Definition diff_scene.h:163
One change record on a single item.
std::vector< PROPERTY_DELTA > properties
std::optional< wxString > refdes
std::vector< ITEM_CHANGE > children
Single (name, before, after) triple for one mutated property on an item.
KIBIS top(path, &reporter)