KiCad PCB EDA Suite
Loading...
Searching...
No Matches
diff_tree_grouping.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
25
26#include <wx/intl.h>
27
28#include <map>
29
30
31namespace KICAD_DIFF
32{
34{
35 switch( aKind )
36 {
37 case CHANGE_KIND::ADDED: return _( "Added" );
38 case CHANGE_KIND::REMOVED: return _( "Removed" );
39 case CHANGE_KIND::MODIFIED: return _( "Modified" );
40 case CHANGE_KIND::COLLISION: return _( "Collision" );
41 case CHANGE_KIND::DUPLICATE_UUID: return _( "Duplicate ID" );
42 }
43
44 return wxEmptyString;
45}
46
47
48bool ChangeMatchesSearchFilter( const ITEM_CHANGE& aChange, const wxString& aLowercaseFilter )
49{
50 if( aLowercaseFilter.IsEmpty() )
51 return true;
52
53 if( aChange.typeName.Lower().Contains( aLowercaseFilter ) )
54 return true;
55
56 if( aChange.refdes.has_value() && aChange.refdes->Lower().Contains( aLowercaseFilter ) )
57 {
58 return true;
59 }
60
61 return false;
62}
63
64
65std::vector<CHANGE_TREE_GROUP> BuildChangeTreeGroups( const DOCUMENT_DIFF& aDiff, const wxString& aSearchFilter,
66 const std::array<bool, CATEGORY_COUNT>& aVisibleCategories )
67{
68 std::vector<CHANGE_TREE_GROUP> out;
69
70 // Bucket changes by kind. The recursive walk flattens nested children
71 // so a footprint-pad edit shows up as its own bucket entry even when
72 // the parent footprint is also being shown.
73 std::map<CHANGE_KIND, std::vector<CHANGE_TREE_ENTRY>> byKind;
74 std::map<std::pair<CHANGE_KIND, wxString>, const ITEM_CHANGE*> netEntries;
75
76 const bool listChildren = aDiff.docType != wxS( "kicad_sym" );
77
78 auto collect = [&byKind, &netEntries, listChildren]( auto& aSelf, const ITEM_CHANGE& aC ) -> void
79 {
80 if( IsRoutingNetChange( aC ) )
81 {
82 netEntries.try_emplace( std::make_pair( aC.kind, *aC.refdes ), &aC );
83 }
84 else
85 {
86 byKind[aC.kind].push_back( { &aC, ChangeDisplayLabel( aC ) } );
87 }
88
89 if( listChildren )
90 {
91 for( const ITEM_CHANGE& child : aC.children )
92 aSelf( aSelf, child );
93 }
94 };
95
96 for( const ITEM_CHANGE& c : aDiff.changes )
97 collect( collect, c );
98
99 for( const auto& [key, change] : netEntries )
100 {
101 const auto& [kind, netName] = key;
102 byKind[kind].push_back( { change, wxS( "NET [" ) + netName + wxS( "]" ) } );
103 }
104
105 const wxString lowerFilter = aSearchFilter.Lower();
106
107 for( const auto& [kind, items] : byKind )
108 {
109 const CATEGORY cat = CategoryFor( kind );
110
111 if( !aVisibleCategories[static_cast<std::size_t>( cat )] )
112 continue;
113
114 std::vector<CHANGE_TREE_ENTRY> visibleEntries;
115 visibleEntries.reserve( items.size() );
116
117 for( const CHANGE_TREE_ENTRY& item : items )
118 {
119 if( !ChangeMatchesSearchFilter( *item.change, lowerFilter )
120 && !item.itemLabel.Lower().Contains( lowerFilter ) )
121 {
122 continue;
123 }
124
125 visibleEntries.push_back( item );
126 }
127
128 if( visibleEntries.empty() )
129 continue;
130
131 wxString groupLabel;
132
133 if( lowerFilter.IsEmpty() || visibleEntries.size() == items.size() )
134 groupLabel = wxString::Format( wxS( "%s (%zu)" ), ChangeKindLabel( kind ), visibleEntries.size() );
135 else
136 groupLabel = wxString::Format( wxS( "%s (%zu/%zu)" ), ChangeKindLabel( kind ), visibleEntries.size(),
137 items.size() );
138
139 out.push_back( CHANGE_TREE_GROUP{ kind, std::move( groupLabel ), std::move( visibleEntries ) } );
140 }
141
142 return out;
143}
144
145} // namespace KICAD_DIFF
#define _(s)
CHANGE_KIND
Coarse classification of a single item-level change between two documents.
wxString ChangeDisplayLabel(const ITEM_CHANGE &aChange)
User-facing item label used consistently by scene tooltips and change tree entries.
bool ChangeMatchesSearchFilter(const ITEM_CHANGE &aChange, const wxString &aLowercaseFilter)
Predicate: does this item change match the active search filter?
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.
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
CATEGORY CategoryFor(CHANGE_KIND aKind)
Map a CHANGE_KIND to the visual category it belongs to.
One row in a grouped change tree — an ITEM_CHANGE plus its display label (typeName + optional refdes ...
One bucket in a grouped change tree — a CHANGE_KIND, a human- readable group label,...
The full set of changes between two parsed documents of one type.
std::vector< ITEM_CHANGE > changes
One change record on a single item.
std::optional< wxString > refdes
std::vector< ITEM_CHANGE > children