33#include <wx/checkbox.h>
36#include <wx/settings.h>
37#include <wx/srchctrl.h>
39#include <wx/splitter.h>
40#include <wx/stattext.h>
41#include <wx/wupdlock.h>
54 SetEscapeId( wxID_OK );
69 wxSearchCtrl* search =
new wxSearchCtrl(
m_panelTree, wxID_ANY );
70 search->SetDescriptiveText(
_(
"Filter changes…" ) );
71 search->ShowCancelButton(
true );
73 auto onSearch = [
this, search]( wxCommandEvent& aEvent )
86 search->Bind( wxEVT_TEXT, onSearch );
87 search->Bind( wxEVT_SEARCHCTRL_CANCEL_BTN, onSearch );
89 treeSizer->Insert( 0, search, wxSizerFlags().Expand().Border( wxLEFT | wxRIGHT | wxTOP, 4 ) );
99 wxSplitterWindow* detailSplitter =
100 new wxSplitterWindow(
m_panelDetail, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSP_LIVE_UPDATE );
101 detailSplitter->SetSashGravity( 1.0 );
102 detailSplitter->SetMinimumPaneSize( 40 );
119 wxBoxSizer* filterSizer =
new wxBoxSizer( wxHORIZONTAL );
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" ) },
128 for(
const auto& [cat, label] : categories )
130 wxCheckBox* cb =
new wxCheckBox(
m_panelDetail, wxID_ANY, label );
131 cb->SetValue(
true );
132 cb->Bind( wxEVT_CHECKBOX,
133 [
this, cat]( wxCommandEvent& aEvent )
135 m_canvas->SetCategoryVisible( cat, aEvent.IsChecked() );
138 filterSizer->Add( cb, wxSizerFlags().Border( wxRIGHT, 8 ).Centre() );
141 if( geometryLayers.any() )
143 filterSizer->Add(
new wxStaticText(
m_panelDetail, wxID_ANY,
_(
"Layers:" ) ),
144 wxSizerFlags().Border( wxLEFT | wxRIGHT, 4 ).Centre() );
149 cb->SetValue(
true );
150 cb->Bind( wxEVT_CHECKBOX,
151 [
this, layer]( wxCommandEvent& aEvent )
153 m_canvas->SetLayerVisible( layer, aEvent.IsChecked() );
155 filterSizer->Add( cb, wxSizerFlags().Border( wxRIGHT, 8 ).Centre() );
160 filterSizer->AddStretchSpacer();
162 wxCheckBox* propsToggle =
new wxCheckBox(
m_panelDetail, wxID_ANY,
_(
"Properties" ) );
163 propsToggle->SetValue(
true );
164 propsToggle->Bind( wxEVT_CHECKBOX,
165 [
this, detailSplitter]( wxCommandEvent& aEvent )
167 if( aEvent.IsChecked() && !detailSplitter->IsSplit() )
169 else if( !aEvent.IsChecked() && detailSplitter->IsSplit() )
172 filterSizer->Add( propsToggle, wxSizerFlags().Border( wxRIGHT, 8 ).Centre() );
181 propsToggle->SetValue( detailSplitter->IsSplit() );
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 ) );
188 m_canvas->SetScene( std::move( scene ) );
191 [
this](
const std::optional<KIID_PATH>& aChangeId )
232 wxTreeItemId sel = aEvent.GetItem();
240 auto it =
m_changeByTreeId.find(
reinterpret_cast<wxUIntPtr
>( sel.GetID() ) );
257 std::optional<KIID_PATH> selectedId;
259 if( wxTreeItemId sel =
m_treeChanges->GetSelection(); sel.IsOk() )
261 auto it =
m_changeByTreeId.find(
reinterpret_cast<wxUIntPtr
>( sel.GetID() ) );
264 selectedId = it->second->id;
277 std::array<bool, KICAD_DIFF::CATEGORY_COUNT> visibleCats{};
278 visibleCats.fill(
true );
311 wxTreeItemId item = aEvent.GetItem();
316 auto it =
m_changeByTreeId.find(
reinterpret_cast<wxUIntPtr
>( item.GetID() ) );
321 const std::vector<KIID_PATH> ids =
changeRowIds( *it->second );
326 ID_HIDE = wxID_HIGHEST + 1,
334 menu.Append( ID_SHOW,
_(
"Show Change" ) );
336 menu.Append( ID_HIDE,
_(
"Hide Change" ) );
338 menu.Append( ID_SHOW_ALL,
_(
"Show All Hidden" ) );
341 switch(
m_treeChanges->GetPopupMenuSelectionFromUser( menu ) )
362 return { aChange.
id };
364 std::vector<KIID_PATH> ids;
366 std::function<void(
const std::vector<KICAD_DIFF::ITEM_CHANGE>& )> walk =
367 [&](
const std::vector<KICAD_DIFF::ITEM_CHANGE>& aChanges )
373 ids.push_back( c.id );
387 const wxColour grey = wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT );
388 const wxColour
normal = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT );
395 wxTreeItemId node(
reinterpret_cast<void*
>( key ) );
406 wxSizer*
top = GetSizer();
408 if( !
top || aLabels.empty() )
413 wxBoxSizer* row =
new wxBoxSizer( wxHORIZONTAL );
414 row->Add(
new wxStaticText(
this, wxID_ANY,
_(
"Revision:" ) ), wxSizerFlags().Border( wxRIGHT, 4 ).Centre() );
416 wxArrayString choices;
418 for(
const wxString& label : aLabels )
419 choices.Add( label );
421 m_revisionChoice =
new wxChoice(
this, wxID_ANY, wxDefaultPosition, wxDefaultSize, choices );
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() );
430 auto fire = [
this](
int aIndex )
432 if( aIndex < 0 || aIndex >=
static_cast<int>(
m_revisionChoice->GetCount() ) )
442 [fire]( wxCommandEvent& aEvent )
444 fire( aEvent.GetSelection() );
448 olderBtn->Bind( wxEVT_BUTTON,
449 [
this, fire]( wxCommandEvent& )
453 newerBtn->Bind( wxEVT_BUTTON,
454 [
this, fire]( wxCommandEvent& )
459 top->Insert( 0, row, wxSizerFlags().Expand().Border( wxALL, 4 ) );
472 m_diff = std::move( aDiff );
488 m_canvas->HighlightChange( std::nullopt );
500 m_canvas->SetScene( std::move( scene ) );
550 m_labelSummary->SetLabel(
_(
"Select a change in the tree to view details" ) );
553 m_canvas->HighlightChange( std::nullopt );
565 if( aChange->
id.size() > 1 )
568 sheetPath.pop_back();
584 std::set<wxString> seen;
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;
593 if( !seen.insert( key ).second )
602 emitDelta( wxEmptyString, d );
615 prefix += wxS(
" " ) + *child.
refdes;
617 if( !aPrefix.IsEmpty() )
618 prefix = aPrefix + wxS(
" / " ) + prefix;
621 emitDelta( prefix, d );
623 walkChildren( prefix, child );
627 walkChildren( wxEmptyString, *aChange );
635 if( change && change->id == aChangeId )
637 wxTreeItemId treeId(
reinterpret_cast<void*
>( treeIdNum ) );
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)
wxStaticText * m_pathReference
wxStaticText * m_pathComparison
wxListCtrl * m_listProperties
wxTreeCtrl * m_treeChanges
wxStaticText * m_labelSummary
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...
KIID_PATH m_currentCanvasSheet
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...
wxChoice * m_revisionChoice
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.
LSEQ UIOrder() const
Return the copper, technical and user layers in the order shown in layer widget.
wxString LayerName(int aLayer)
Returns the default display name for a given layer.
PCB_LAYER_ID
A quick note on layer IDs:
constexpr std::size_t CATEGORY_COUNT
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.
One row in a grouped change tree — an ITEM_CHANGE plus its display label (typeName + optional refdes ...
const ITEM_CHANGE * change
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.
DOCUMENT_GEOMETRY comparisonGeometry
The full set of changes between two parsed documents of one type.
Aggregate of background geometry extracted from one source document.
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)