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/wrapsizer.h>
45#include <wx/wupdlock.h>
58 SetEscapeId( wxID_OK );
73 wxSearchCtrl* search =
new wxSearchCtrl(
m_panelTree, wxID_ANY );
75 search->SetDescriptiveText(
_(
"Filter changes…" ) );
76 search->ShowCancelButton(
true );
78 auto onSearch = [
this, search]( wxCommandEvent& aEvent )
91 search->Bind( wxEVT_TEXT, onSearch );
92 search->Bind( wxEVT_SEARCHCTRL_CANCEL_BTN, onSearch );
94 treeSizer->Insert( 0, search, wxSizerFlags().Expand().Border( wxLEFT | wxRIGHT | wxTOP, 4 ) );
104 wxSplitterWindow* detailSplitter =
105 new wxSplitterWindow(
m_panelDetail, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSP_LIVE_UPDATE );
106 detailSplitter->SetSashGravity( 1.0 );
107 detailSplitter->SetMinimumPaneSize( 40 );
113 if( frame->GetCanvas() )
114 backend = frame->GetCanvas()->GetBackend();
135 layerNames[layer] =
name;
140 wxWrapSizer* checkboxSizer =
new wxWrapSizer( wxHORIZONTAL );
142 const std::array<std::pair<KICAD_DIFF::CATEGORY, wxString>, 4> categories{ {
143 { KICAD_DIFF::CATEGORY::ADDED,
_(
"Added" ) },
144 { KICAD_DIFF::CATEGORY::REMOVED,
_(
"Removed" ) },
145 { KICAD_DIFF::CATEGORY::MODIFIED,
_(
"Modified" ) },
146 { KICAD_DIFF::CATEGORY::CONFLICT,
_(
"Conflict" ) },
149 for(
const auto& [cat, label] : categories )
151 wxCheckBox* cb =
new wxCheckBox(
m_panelDetail, wxID_ANY, label );
152 cb->SetValue(
true );
153 cb->Bind( wxEVT_CHECKBOX,
154 [
this, cat]( wxCommandEvent& aEvent )
156 m_canvas->SetCategoryVisible( cat, aEvent.IsChecked() );
159 checkboxSizer->Add( cb, wxSizerFlags().Border( wxRIGHT, 8 ).Centre() );
162 if( geometryLayers.any() )
164 checkboxSizer->Add(
new wxStaticText(
m_panelDetail, wxID_ANY,
_(
"Layers:" ) ),
165 wxSizerFlags().Border( wxLEFT | wxRIGHT, 4 ).Centre() );
169 auto nameIt = layerNames.find( layer );
170 const wxString label = nameIt != layerNames.end() ? nameIt->second :
LayerName( layer );
171 wxCheckBox* cb =
new wxCheckBox(
m_panelDetail, wxID_ANY, label );
172 cb->SetValue(
true );
173 cb->Bind( wxEVT_CHECKBOX,
174 [
this, layer]( wxCommandEvent& aEvent )
176 m_canvas->SetLayerVisible( layer, aEvent.IsChecked() );
178 checkboxSizer->Add( cb, wxSizerFlags().Border( wxRIGHT, 8 ).Centre() );
182 m_filterSizer->Add( checkboxSizer, wxSizerFlags( 1 ).Centre() );
186 wxCheckBox* propsToggle =
new wxCheckBox(
m_panelDetail, wxID_ANY,
_(
"Properties" ) );
187 propsToggle->SetValue(
true );
188 propsToggle->Bind( wxEVT_CHECKBOX,
189 [
this, detailSplitter]( wxCommandEvent& aEvent )
191 if( aEvent.IsChecked() && !detailSplitter->IsSplit() )
193 else if( !aEvent.IsChecked() && detailSplitter->IsSplit() )
196 m_filterSizer->Add( propsToggle, wxSizerFlags().Border( wxRIGHT, 8 ).Centre() );
205 propsToggle->SetValue( detailSplitter->IsSplit() );
207 size_t insertAt = std::min<size_t>( 1, detailSizer->GetItemCount() );
208 detailSizer->Insert( insertAt++,
m_filterSizer, wxSizerFlags().Expand().Border( wxLEFT | wxRIGHT | wxTOP, 4 ) );
209 detailSizer->Insert( insertAt, detailSplitter, wxSizerFlags( 1 ).Expand().Border( wxALL, 4 ) );
212 m_canvas->SetScene( std::move( scene ) );
215 [
this](
const std::optional<KIID_PATH>& aChangeId )
237 wxPLURAL(
"%zu change detected.",
"%zu changes detected.",
m_diff.Size() ),
m_diff.Size() ) );
257 wxTreeItemId sel = aEvent.GetItem();
265 auto it =
m_changeByTreeId.find(
reinterpret_cast<wxUIntPtr
>( sel.GetID() ) );
282 std::optional<KIID_PATH> selectedId;
284 if( wxTreeItemId sel =
m_treeChanges->GetSelection(); sel.IsOk() )
286 auto it =
m_changeByTreeId.find(
reinterpret_cast<wxUIntPtr
>( sel.GetID() ) );
289 selectedId = it->second->id;
302 std::array<bool, KICAD_DIFF::CATEGORY_COUNT> visibleCats{};
303 visibleCats.fill(
true );
336 wxTreeItemId item = aEvent.GetItem();
341 auto it =
m_changeByTreeId.find(
reinterpret_cast<wxUIntPtr
>( item.GetID() ) );
346 const std::vector<KIID_PATH> ids =
changeRowIds( *it->second );
351 ID_HIDE = wxID_HIGHEST + 1,
359 menu.Append( ID_SHOW,
_(
"Show Change" ) );
361 menu.Append( ID_HIDE,
_(
"Hide Change" ) );
363 menu.Append( ID_SHOW_ALL,
_(
"Show All Hidden" ) );
366 switch(
m_treeChanges->GetPopupMenuSelectionFromUser( menu ) )
387 return { aChange.
id };
389 std::vector<KIID_PATH> ids;
391 std::function<void(
const std::vector<KICAD_DIFF::ITEM_CHANGE>& )> walk =
392 [&](
const std::vector<KICAD_DIFF::ITEM_CHANGE>& aChanges )
398 ids.push_back( c.id );
412 const wxColour grey = wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT );
413 const wxColour
normal = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT );
420 wxTreeItemId node(
reinterpret_cast<void*
>( key ) );
431 wxSizer*
top = GetSizer();
433 if( !
top || aLabels.empty() )
438 wxBoxSizer* row =
new wxBoxSizer( wxHORIZONTAL );
439 row->Add(
new wxStaticText(
this, wxID_ANY,
_(
"Revision:" ) ), wxSizerFlags().Border( wxRIGHT, 4 ).Centre() );
441 wxArrayString choices;
443 for(
const wxString& label : aLabels )
444 choices.Add( label );
446 m_revisionChoice =
new wxChoice(
this, wxID_ANY, wxDefaultPosition, wxDefaultSize, choices );
450 wxButton* olderBtn =
new wxButton(
this, wxID_ANY,
_(
"Older" ), wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT );
451 wxButton* newerBtn =
new wxButton(
this, wxID_ANY,
_(
"Newer" ), wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT );
452 row->Add( olderBtn, wxSizerFlags().Border( wxLEFT, 4 ).Centre() );
453 row->Add( newerBtn, wxSizerFlags().Border( wxLEFT, 2 ).Centre() );
455 auto fire = [
this](
int aIndex )
457 if( aIndex < 0 || aIndex >=
static_cast<int>(
m_revisionChoice->GetCount() ) )
467 [fire]( wxCommandEvent& aEvent )
469 fire( aEvent.GetSelection() );
473 olderBtn->Bind( wxEVT_BUTTON,
474 [
this, fire]( wxCommandEvent& )
478 newerBtn->Bind( wxEVT_BUTTON,
479 [
this, fire]( wxCommandEvent& )
484 top->Insert( 0, row, wxSizerFlags().Expand().Border( wxALL, 4 ) );
497 m_btnUp->SetToolTip(
_(
"Navigate up one sheet in the hierarchy" ) );
501 [
this]( wxCommandEvent& )
527 m_diff = std::move( aDiff );
543 m_canvas->HighlightChange( std::nullopt );
555 m_canvas->SetScene( std::move( scene ) );
572 wxPLURAL(
"%zu change detected.",
"%zu changes detected.",
m_diff.Size() ),
m_diff.Size() ) );
606 m_labelSummary->SetLabel(
_(
"Select a change in the tree to view details" ) );
609 m_canvas->HighlightChange( std::nullopt );
621 if( aChange->
id.size() > 1 )
624 sheetPath.pop_back();
640 std::set<wxString> seen;
646 const wxString label = aPrefix.IsEmpty() ? d.name : aPrefix + wxS(
" / " ) + d.name;
647 const wxString beforeStr = d.before.ToDisplayString( units, iuScale );
648 const wxString afterStr = d.after.ToDisplayString( units, iuScale );
649 const wxString key = label + wxS(
"|" ) + beforeStr + wxS(
"|" ) + afterStr;
651 if( !seen.insert( key ).second )
660 emitDelta( wxEmptyString, d );
673 prefix += wxS(
" " ) + *child.
refdes;
675 if( !aPrefix.IsEmpty() )
676 prefix = aPrefix + wxS(
" / " ) + prefix;
679 emitDelta( prefix, d );
681 walkChildren( prefix, child );
685 walkChildren( wxEmptyString, *aChange );
693 if( change && change->id == aChangeId )
695 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.
void OptOut(wxWindow *aWindow)
Opt out of control state saving.
EDA_UNITS GetUserUnits() const
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.
const EDA_IU_SCALE & IuScaleForDocKind(DOC_KIND aKind)
Internal-unit scale a document kind's coordinates are expressed in.
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.
DOC_KIND DocKindFromDocType(const wxString &aDocType)
Map a DOCUMENT_DIFF::docType tag written by the differs ("kicad_pcb", "kicad_sch",...
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,...
Diff colors with defaults for light backgrounds.
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.
std::map< PCB_LAYER_ID, wxString > layerNames
Board layer names, including user-assigned ones. Empty for documents without a board.
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)