34#include <wx/bmpbuttn.h>
36#include <wx/checkbox.h>
39#include <wx/settings.h>
40#include <wx/srchctrl.h>
42#include <wx/splitter.h>
43#include <wx/stattext.h>
44#include <wx/wupdlock.h>
57 SetEscapeId( wxID_OK );
72 wxSearchCtrl* search =
new wxSearchCtrl(
m_panelTree, wxID_ANY );
73 search->SetDescriptiveText(
_(
"Filter changes…" ) );
74 search->ShowCancelButton(
true );
76 auto onSearch = [
this, search]( wxCommandEvent& aEvent )
89 search->Bind( wxEVT_TEXT, onSearch );
90 search->Bind( wxEVT_SEARCHCTRL_CANCEL_BTN, onSearch );
92 treeSizer->Insert( 0, search, wxSizerFlags().Expand().Border( wxLEFT | wxRIGHT | wxTOP, 4 ) );
102 wxSplitterWindow* detailSplitter =
103 new wxSplitterWindow(
m_panelDetail, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSP_LIVE_UPDATE );
104 detailSplitter->SetSashGravity( 1.0 );
105 detailSplitter->SetMinimumPaneSize( 40 );
111 if( frame->GetCanvas() )
112 backend = frame->GetCanvas()->GetBackend();
132 const std::array<std::pair<KICAD_DIFF::CATEGORY, wxString>, 4> categories{ {
133 { KICAD_DIFF::CATEGORY::ADDED,
_(
"Added" ) },
134 { KICAD_DIFF::CATEGORY::REMOVED,
_(
"Removed" ) },
135 { KICAD_DIFF::CATEGORY::MODIFIED,
_(
"Modified" ) },
136 { KICAD_DIFF::CATEGORY::CONFLICT,
_(
"Conflict" ) },
139 for(
const auto& [cat, label] : categories )
141 wxCheckBox* cb =
new wxCheckBox(
m_panelDetail, wxID_ANY, label );
142 cb->SetValue(
true );
143 cb->Bind( wxEVT_CHECKBOX,
144 [
this, cat]( wxCommandEvent& aEvent )
146 m_canvas->SetCategoryVisible( cat, aEvent.IsChecked() );
149 m_filterSizer->Add( cb, wxSizerFlags().Border( wxRIGHT, 8 ).Centre() );
152 if( geometryLayers.any() )
155 wxSizerFlags().Border( wxLEFT | wxRIGHT, 4 ).Centre() );
160 cb->SetValue(
true );
161 cb->Bind( wxEVT_CHECKBOX,
162 [
this, layer]( wxCommandEvent& aEvent )
164 m_canvas->SetLayerVisible( layer, aEvent.IsChecked() );
166 m_filterSizer->Add( cb, wxSizerFlags().Border( wxRIGHT, 8 ).Centre() );
173 wxCheckBox* propsToggle =
new wxCheckBox(
m_panelDetail, wxID_ANY,
_(
"Properties" ) );
174 propsToggle->SetValue(
true );
175 propsToggle->Bind( wxEVT_CHECKBOX,
176 [
this, detailSplitter]( wxCommandEvent& aEvent )
178 if( aEvent.IsChecked() && !detailSplitter->IsSplit() )
180 else if( !aEvent.IsChecked() && detailSplitter->IsSplit() )
183 m_filterSizer->Add( propsToggle, wxSizerFlags().Border( wxRIGHT, 8 ).Centre() );
192 propsToggle->SetValue( detailSplitter->IsSplit() );
194 size_t insertAt = std::min<size_t>( 1, detailSizer->GetItemCount() );
195 detailSizer->Insert( insertAt++,
m_filterSizer, wxSizerFlags().Expand().Border( wxLEFT | wxRIGHT | wxTOP, 4 ) );
196 detailSizer->Insert( insertAt, detailSplitter, wxSizerFlags( 1 ).Expand().Border( wxALL, 4 ) );
199 m_canvas->SetScene( std::move( scene ) );
202 [
this](
const std::optional<KIID_PATH>& aChangeId )
243 wxTreeItemId sel = aEvent.GetItem();
251 auto it =
m_changeByTreeId.find(
reinterpret_cast<wxUIntPtr
>( sel.GetID() ) );
268 std::optional<KIID_PATH> selectedId;
270 if( wxTreeItemId sel =
m_treeChanges->GetSelection(); sel.IsOk() )
272 auto it =
m_changeByTreeId.find(
reinterpret_cast<wxUIntPtr
>( sel.GetID() ) );
275 selectedId = it->second->id;
288 std::array<bool, KICAD_DIFF::CATEGORY_COUNT> visibleCats{};
289 visibleCats.fill(
true );
322 wxTreeItemId item = aEvent.GetItem();
327 auto it =
m_changeByTreeId.find(
reinterpret_cast<wxUIntPtr
>( item.GetID() ) );
332 const std::vector<KIID_PATH> ids =
changeRowIds( *it->second );
337 ID_HIDE = wxID_HIGHEST + 1,
345 menu.Append( ID_SHOW,
_(
"Show Change" ) );
347 menu.Append( ID_HIDE,
_(
"Hide Change" ) );
349 menu.Append( ID_SHOW_ALL,
_(
"Show All Hidden" ) );
352 switch(
m_treeChanges->GetPopupMenuSelectionFromUser( menu ) )
373 return { aChange.
id };
375 std::vector<KIID_PATH> ids;
377 std::function<void(
const std::vector<KICAD_DIFF::ITEM_CHANGE>& )> walk =
378 [&](
const std::vector<KICAD_DIFF::ITEM_CHANGE>& aChanges )
384 ids.push_back( c.id );
398 const wxColour grey = wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT );
399 const wxColour
normal = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT );
406 wxTreeItemId node(
reinterpret_cast<void*
>( key ) );
417 wxSizer*
top = GetSizer();
419 if( !
top || aLabels.empty() )
424 wxBoxSizer* row =
new wxBoxSizer( wxHORIZONTAL );
425 row->Add(
new wxStaticText(
this, wxID_ANY,
_(
"Revision:" ) ), wxSizerFlags().Border( wxRIGHT, 4 ).Centre() );
427 wxArrayString choices;
429 for(
const wxString& label : aLabels )
430 choices.Add( label );
432 m_revisionChoice =
new wxChoice(
this, wxID_ANY, wxDefaultPosition, wxDefaultSize, choices );
436 wxButton* olderBtn =
new wxButton(
this, wxID_ANY,
_(
"Older" ), wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT );
437 wxButton* newerBtn =
new wxButton(
this, wxID_ANY,
_(
"Newer" ), wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT );
438 row->Add( olderBtn, wxSizerFlags().Border( wxLEFT, 4 ).Centre() );
439 row->Add( newerBtn, wxSizerFlags().Border( wxLEFT, 2 ).Centre() );
441 auto fire = [
this](
int aIndex )
443 if( aIndex < 0 || aIndex >=
static_cast<int>(
m_revisionChoice->GetCount() ) )
453 [fire]( wxCommandEvent& aEvent )
455 fire( aEvent.GetSelection() );
459 olderBtn->Bind( wxEVT_BUTTON,
460 [
this, fire]( wxCommandEvent& )
464 newerBtn->Bind( wxEVT_BUTTON,
465 [
this, fire]( wxCommandEvent& )
470 top->Insert( 0, row, wxSizerFlags().Expand().Border( wxALL, 4 ) );
483 m_btnUp->SetToolTip(
_(
"Navigate up one sheet in the hierarchy" ) );
487 [
this]( wxCommandEvent& )
513 m_diff = std::move( aDiff );
529 m_canvas->HighlightChange( std::nullopt );
541 m_canvas->SetScene( std::move( scene ) );
591 m_labelSummary->SetLabel(
_(
"Select a change in the tree to view details" ) );
594 m_canvas->HighlightChange( std::nullopt );
606 if( aChange->
id.size() > 1 )
609 sheetPath.pop_back();
625 std::set<wxString> seen;
629 const wxString label = aPrefix.IsEmpty() ? d.name : aPrefix + wxS(
" / " ) + d.name;
630 const wxString beforeStr = d.before.ToDisplayString();
631 const wxString afterStr = d.after.ToDisplayString();
632 const wxString key = label + wxS(
"|" ) + beforeStr + wxS(
"|" ) + afterStr;
634 if( !seen.insert( key ).second )
643 emitDelta( wxEmptyString, d );
656 prefix += wxS(
" " ) + *child.
refdes;
658 if( !aPrefix.IsEmpty() )
659 prefix = aPrefix + wxS(
" / " ) + prefix;
662 emitDelta( prefix, d );
664 walkChildren( prefix, child );
668 walkChildren( wxEmptyString, *aChange );
676 if( change && change->id == aChangeId )
678 wxTreeItemId treeId(
reinterpret_cast<void*
>( treeIdNum ) );
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
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::function< void()> UP_HANDLER
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.
void SetUpHandler(UP_HANDLER aHandler)
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
wxBoxSizer * m_filterSizer
void OnClose(wxCloseEvent &aEvent) override
REVISION_HANDLER m_revisionHandler
void OnTreeItemMenu(wxTreeEvent &aEvent)
void SwitchCanvasToSheet(const KIID_PATH &aSheetPath)
CHANGE_SELECTED_FN m_changeSelectedFn
void EnableUp(bool aEnable)
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.
The base class for create windows for drawing purpose.
static constexpr GAL_TYPE GAL_FALLBACK
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)