KiCad PCB EDA Suite
Loading...
Searching...
No Matches
sch_tool_utils.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 2
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, see <https://www.gnu.org/licenses/>.
18 */
19
20#include "sch_tool_utils.h"
21
22#include <sch_text.h>
23#include <sch_field.h>
24#include <sch_pin.h>
25#include <sch_reference_list.h>
26#include <sch_symbol.h>
27#include <sch_table.h>
28#include <sch_tablecell.h>
29#include <sch_textbox.h>
30#include <schematic.h>
31#include <sch_sheet_path.h>
32#include <sch_sheet.h>
33#include <kiid.h>
34
35#include <wx/arrstr.h>
36
37wxString GetSchItemAsText( const SCH_ITEM& aItem )
38{
39 switch( aItem.Type() )
40 {
41 case SCH_TEXT_T:
42 case SCH_LABEL_T:
46 case SCH_SHEET_PIN_T:
47 {
48 const SCH_TEXT& text = static_cast<const SCH_TEXT&>( aItem );
49 return text.GetShownText( true );
50 }
51
52 case SCH_FIELD_T:
53 {
54 // Goes via EDA_TEXT
55 const SCH_FIELD& field = static_cast<const SCH_FIELD&>( aItem );
56 return field.GetShownText( true );
57 }
58
59 case SCH_TEXTBOX_T:
60 case SCH_TABLECELL_T:
61 {
62 // Also EDA_TEXT
63 const SCH_TEXTBOX& textbox = static_cast<const SCH_TEXTBOX&>( aItem );
64
65 // Call the correct GetShownText overload with nullptr for settings/path and aDepth=0
66 // This ensures proper variable expansion and escape marker conversion
67 return textbox.GetShownText( nullptr, nullptr, true, 0 );
68 }
69
70 case SCH_PIN_T:
71 {
72 // This is a choice - probably the name makes more sense than the number
73 // (or should it be name/number?)
74 const SCH_PIN& pin = static_cast<const SCH_PIN&>( aItem );
75 return pin.GetShownName();
76 }
77
78 case SCH_TABLE_T:
79 {
80 // A simple tabbed list of the cells seems like a place to start here
81 const SCH_TABLE& table = static_cast<const SCH_TABLE&>( aItem );
82 wxString s;
83
84 for( int row = 0; row < table.GetRowCount(); ++row )
85 {
86 for( int col = 0; col < table.GetColCount(); ++col )
87 {
88 const SCH_TABLECELL* cell = table.GetCell( row, col );
89 s << cell->GetShownText( true );
90
91 if( col < table.GetColCount() - 1 )
92 {
93 s << '\t';
94 }
95 }
96
97 if( row < table.GetRowCount() - 1 )
98 {
99 s << '\n';
100 }
101 }
102 return s;
103 }
104
105 default:
106 break;
107 }
108
109 return wxEmptyString;
110};
111
112
113wxString GetSelectedItemsAsText( const SELECTION& aSel )
114{
115 wxArrayString itemTexts;
116
117 for( EDA_ITEM* item : aSel )
118 {
119 if( item->IsSCH_ITEM() )
120 {
121 const SCH_ITEM& schItem = static_cast<const SCH_ITEM&>( *item );
122 wxString itemText = GetSchItemAsText( schItem );
123
124 itemText.Trim( false ).Trim( true );
125
126 if( !itemText.IsEmpty() )
127 {
128 itemTexts.Add( std::move( itemText ) );
129 }
130 }
131 }
132
133 return wxJoin( itemTexts, '\n', '\0' );
134}
135
136
137bool IsUnannotatedUnitOccupied( const SCH_REFERENCE_LIST& aRefs, const wxString& aRef,
138 const LIB_ID& aLibId, int aUnit )
139{
140 for( size_t i = 0; i < aRefs.GetCount(); ++i )
141 {
142 const SCH_REFERENCE& ref = aRefs[i];
143
144 if( ref.GetUnit() != aUnit )
145 continue;
146
147 if( ref.GetRef() != aRef )
148 continue;
149
150 SCH_SYMBOL* refSym = ref.GetSymbol();
151
152 if( refSym && refSym->GetLibId() == aLibId )
153 return true;
154 }
155
156 return false;
157}
158
159
160std::set<int> GetUnplacedUnitsForSymbol( const SCH_SYMBOL& aSym )
161{
162 SCHEMATIC const* schematic = aSym.Schematic();
163
164 if( !schematic )
165 return {};
166
167 const wxString symRefDes = aSym.GetRef( &schematic->CurrentSheet(), false );
168
169 // Pre-annotation references all share the same "U?" form regardless of which library symbol
170 // they came from, so an unannotated AD8620 and an unannotated OPA1664 would otherwise be
171 // collapsed into one logical part. Match library identity as a tie-breaker when the
172 // reference is still unannotated.
173 const bool refIsUnannotated = !symRefDes.IsEmpty() && symRefDes.Last() == '?';
174 const LIB_ID& symLibId = aSym.GetLibId();
175
176 // Get a list of all references in the schematic
177 SCH_SHEET_LIST hierarchy = schematic->Hierarchy();
178 SCH_REFERENCE_LIST existingRefs;
179 hierarchy.GetSymbols( existingRefs, SYMBOL_FILTER_ALL );
180
181 std::set<int> missingUnits;
182
183 for( int unit = 1; unit <= aSym.GetUnitCount(); ++unit )
184 missingUnits.insert( unit );
185
186 for( const SCH_REFERENCE& ref : existingRefs )
187 {
188 if( symRefDes != ref.GetRef() )
189 continue;
190
191 if( refIsUnannotated && ref.GetSymbol() && ref.GetSymbol()->GetLibId() != symLibId )
192 continue;
193
194 missingUnits.erase( ref.GetUnit() );
195 }
196
197 return missingUnits;
198}
199
200
201std::optional<SCH_REFERENCE> FindSymbolByRefAndUnit( const SCHEMATIC& aSchematic,
202 const wxString& aRef, int aUnit )
203{
205 aSchematic.Hierarchy().GetSymbols( refs, SYMBOL_FILTER_ALL );
206
207 for( const SCH_REFERENCE& ref : refs )
208 {
209 if( ref.GetRef() == aRef && ref.GetUnit() == aUnit )
210 {
211 return ref;
212 }
213 }
214
215 return std::nullopt;
216}
217
218
219std::vector<SCH_SYMBOL*> GetSameSymbolMultiUnitSelection( const SELECTION& aSel )
220{
221 std::vector<SCH_SYMBOL*> result;
222
223 if( aSel.GetSize() < 2 || !aSel.OnlyContains( { SCH_SYMBOL_T } ) )
224 return result;
225
226 wxString rootRef;
227 LIB_ID rootLibId;
228 bool haveRootLibId = false;
229 size_t rootPinCount = 0;
230 bool haveRootPinCount = false;
231
232 // Preserve selection order for cyclical swaps A->B->C
233 std::vector<EDA_ITEM*> itemsInOrder = aSel.GetItemsSortedBySelectionOrder();
234
235 for( EDA_ITEM* it : itemsInOrder )
236 {
237 SCH_SYMBOL* sym = dynamic_cast<SCH_SYMBOL*>( it );
238
239 if( !sym || !sym->GetLibSymbolRef() || sym->GetLibSymbolRef()->GetUnitCount() < 2 )
240 return {};
241
242 const SCH_SHEET_PATH& sheet = sym->Schematic()->CurrentSheet();
243
244 // Get unit-less reference
245 wxString ref = sym->GetRef( &sheet, false );
246
247 if( rootRef.IsEmpty() )
248 rootRef = ref;
249
250 if( ref != rootRef )
251 return {};
252
253 // Make sure the user isn't selecting units that are misreferenced such that
254 // they have U1A and U1B that are actually from different library symbols.
255 const LIB_ID& libId = sym->GetLibId();
256
257 if( !haveRootLibId )
258 {
259 rootLibId = libId;
260 haveRootLibId = true;
261 }
262
263 if( libId != rootLibId )
264 return {};
265
266 // Ensure same pin count across selected units
267 size_t pinCount = sym->GetPins( &sheet ).size();
268
269 if( !haveRootPinCount )
270 {
271 rootPinCount = pinCount;
272 haveRootPinCount = true;
273 }
274
275 if( pinCount != rootPinCount )
276 return {};
277
278 result.push_back( sym );
279 }
280
281 if( result.size() < 2 )
282 return {};
283
284 return result;
285}
286
287
288bool SwapPinGeometry( SCH_PIN* aFirst, SCH_PIN* aSecond )
289{
290 wxCHECK_MSG( aFirst && aSecond, false, "Invalid pins supplied to SwapPinGeometry" );
291
292 // If the schematic pin is still backed by a library definition, swap that library pin; once the
293 // caller sees the true return value it can decide to clone the updated library data into the
294 // schematic cache. Otherwise the schematic already owns a local copy, so swap in place.
295 SCH_PIN* firstPin = aFirst->GetLibPin() ? aFirst->GetLibPin() : aFirst;
296 SCH_PIN* secondPin = aSecond->GetLibPin() ? aSecond->GetLibPin() : aSecond;
297
298 VECTOR2I firstLocal = firstPin->GetLocalPosition();
299 VECTOR2I secondLocal = secondPin->GetLocalPosition();
300 firstPin->SetPosition( secondLocal );
301 secondPin->SetPosition( firstLocal );
302
303 PIN_ORIENTATION firstOrientation = firstPin->GetOrientation();
304 PIN_ORIENTATION secondOrientation = secondPin->GetOrientation();
305 firstPin->SetOrientation( secondOrientation );
306 secondPin->SetOrientation( firstOrientation );
307
308 int firstLength = firstPin->GetLength();
309 int secondLength = secondPin->GetLength();
310 firstPin->SetLength( secondLength );
311 secondPin->SetLength( firstLength );
312
313 const wxString& firstOp = firstPin->GetOperatingPoint();
314 const wxString& secondOp = secondPin->GetOperatingPoint();
315 firstPin->SetOperatingPoint( secondOp );
316 secondPin->SetOperatingPoint( firstOp );
317
318 // Return true if we touched the library-backed copy so callers can refresh the schematic symbol
319 // cache once the swap completes.
320 return ( firstPin != aFirst ) || ( secondPin != aSecond );
321}
322
323
324bool SymbolHasSheetInstances( const SCH_SYMBOL& aSymbol, const wxString& aCurrentProject,
325 std::set<wxString>* aSheetPaths, std::set<wxString>* aProjectNames )
326{
327 std::set<KIID_PATH> uniquePaths;
328 std::set<wxString> sheetPaths;
329 std::set<wxString> otherProjects;
330
331 for( const SCH_SYMBOL_INSTANCE& instance : aSymbol.GetInstances() )
332 {
333 uniquePaths.insert( instance.m_Path );
334
335 if( !instance.m_Path.empty() )
336 sheetPaths.insert( instance.m_Path.AsString() );
337
338 if( !instance.m_ProjectName.IsEmpty() )
339 {
340 if( aCurrentProject.IsEmpty() || !instance.m_ProjectName.IsSameAs( aCurrentProject ) )
341 otherProjects.insert( instance.m_ProjectName );
342 }
343 }
344
345 bool sharedWithinProject = uniquePaths.size() > 1;
346 bool sharedWithOtherProjects = !otherProjects.empty();
347
348 if( aSheetPaths )
349 {
350 if( sharedWithinProject )
351 *aSheetPaths = sheetPaths;
352 else
353 aSheetPaths->clear();
354 }
355
356 if( aProjectNames )
357 {
358 if( sharedWithOtherProjects )
359 *aProjectNames = otherProjects;
360 else
361 aProjectNames->clear();
362 }
363
364 return sharedWithinProject || sharedWithOtherProjects;
365}
366
367
368std::set<wxString> GetSheetNamesFromPaths( const std::set<wxString>& aSheetPaths, const SCHEMATIC& aSchematic )
369{
370 std::set<wxString> friendlyNames;
371
372 if( aSheetPaths.empty() )
373 return friendlyNames;
374
375 SCH_SHEET_LIST hierarchy = aSchematic.Hierarchy();
376
377 for( const wxString& pathStr : aSheetPaths )
378 {
379 wxString display = pathStr;
380
381 try
382 {
383 KIID_PATH kiidPath( pathStr );
384
385 for( const SCH_SHEET_PATH& sheetPath : hierarchy )
386 {
387 if( sheetPath.Path() == kiidPath )
388 {
389 wxString sheetNames;
390
391 for( size_t ii = 0; ii < sheetPath.size(); ++ii )
392 {
393 SCH_SHEET* sheet = sheetPath.at( ii );
394
395 if( !sheet )
396 continue;
397
398 const SCH_FIELD* nameField = sheet->GetField( FIELD_T::SHEET_NAME );
399
400 if( !nameField )
401 continue;
402
403 wxString name = nameField->GetShownText( false );
404
405 if( name.IsEmpty() )
406 continue;
407
408 if( !sheetNames.IsEmpty() )
409 sheetNames << wxS( "/" );
410
411 sheetNames << name;
412 }
413
414 if( sheetNames.IsEmpty() )
415 display = sheetPath.PathHumanReadable( false, true );
416 else
417 display = sheetNames;
418
419 break;
420 }
421 }
422 }
423 catch( ... )
424 {
425 // If the path cannot be parsed, fall back to the raw string.
426 }
427
428 friendlyNames.insert( display );
429 }
430
431 return friendlyNames;
432}
const char * name
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:96
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:108
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:45
int GetUnitCount() const override
Holds all the data relating to one schematic.
Definition schematic.h:90
SCH_SHEET_LIST Hierarchy() const
Return the full schematic flattened hierarchical sheet list.
SCH_SHEET_PATH & CurrentSheet() const
Definition schematic.h:189
wxString GetShownText(const SCH_SHEET_PATH *aPath, bool aAllowExtraText, int aDepth=0, const wxString &aVariantName=wxEmptyString) const
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:162
SCHEMATIC * Schematic() const
Search the item hierarchy to find a SCHEMATIC.
Definition sch_item.cpp:268
VECTOR2I GetLocalPosition() const
Definition sch_pin.h:253
int GetLength() const
Definition sch_pin.cpp:388
const wxString & GetOperatingPoint() const
Definition sch_pin.h:342
void SetOrientation(PIN_ORIENTATION aOrientation)
Definition sch_pin.h:96
SCH_PIN * GetLibPin() const
Definition sch_pin.h:92
void SetPosition(const VECTOR2I &aPos) override
Definition sch_pin.h:254
void SetLength(int aLength)
Definition sch_pin.h:102
PIN_ORIENTATION GetOrientation() const
Definition sch_pin.cpp:353
void SetOperatingPoint(const wxString &aText)
Definition sch_pin.h:343
Container to create a flattened list of symbols because in a complex hierarchy, a symbol can be used ...
A helper to define a symbol's reference designator in a schematic.
SCH_SYMBOL * GetSymbol() const
wxString GetRef() const
int GetUnit() const
A container for handling SCH_SHEET_PATH objects in a flattened hierarchy.
void GetSymbols(SCH_REFERENCE_LIST &aReferences, SYMBOL_FILTER aSymbolFilter, bool aForceIncludeOrphanSymbols=false) const
Add a SCH_REFERENCE object to aReferences for each symbol in the list of sheets.
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition sch_sheet.h:44
SCH_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this sheet.
Schematic symbol object.
Definition sch_symbol.h:69
const std::vector< SCH_SYMBOL_INSTANCE > & GetInstances() const
Definition sch_symbol.h:128
std::vector< const SCH_PIN * > GetPins(const SCH_SHEET_PATH *aSheet) const
Retrieve a list of the SCH_PINs for the given sheet path.
const LIB_ID & GetLibId() const override
Definition sch_symbol.h:158
int GetUnitCount() const override
Return the number of units per package of the symbol.
std::unique_ptr< LIB_SYMBOL > & GetLibSymbolRef()
Definition sch_symbol.h:177
const wxString GetRef(const SCH_SHEET_PATH *aSheet, bool aIncludeUnit=false) const override
wxString GetShownText(const RENDER_SETTINGS *aSettings, const SCH_SHEET_PATH *aPath, bool aAllowExtraText, int aDepth=0) const override
virtual wxString GetShownText(const RENDER_SETTINGS *aSettings, const SCH_SHEET_PATH *aPath, bool aAllowExtraText, int aDepth=0) const
virtual unsigned int GetSize() const override
Return the number of stored items.
Definition selection.h:101
std::vector< EDA_ITEM * > GetItemsSortedBySelectionOrder() const
bool OnlyContains(std::vector< KICAD_T > aList) const
Checks if all items in the selection have a type in aList.
PIN_ORIENTATION
The symbol library pin object orientations.
Definition pin_type.h:101
Definition of the SCH_SHEET_PATH and SCH_SHEET_LIST classes for Eeschema.
@ SYMBOL_FILTER_ALL
bool IsUnannotatedUnitOccupied(const SCH_REFERENCE_LIST &aRefs, const wxString &aRef, const LIB_ID &aLibId, int aUnit)
Decide whether aUnit of an unannotated multi-unit symbol is already placed.
std::set< wxString > GetSheetNamesFromPaths(const std::set< wxString > &aSheetPaths, const SCHEMATIC &aSchematic)
Get human-readable sheet names from a set of sheet paths, e.g.
bool SwapPinGeometry(SCH_PIN *aFirst, SCH_PIN *aSecond)
Swap the positions/lengths/etc.
std::set< int > GetUnplacedUnitsForSymbol(const SCH_SYMBOL &aSym)
Get a list of unplaced (i.e.
wxString GetSelectedItemsAsText(const SELECTION &aSel)
bool SymbolHasSheetInstances(const SCH_SYMBOL &aSymbol, const wxString &aCurrentProject, std::set< wxString > *aSheetPaths, std::set< wxString > *aProjectNames)
Returns true when the given symbol has instances, e.g.
std::optional< SCH_REFERENCE > FindSymbolByRefAndUnit(const SCHEMATIC &aSchematic, const wxString &aRef, int aUnit)
Find a symbol by reference and unit.
std::vector< SCH_SYMBOL * > GetSameSymbolMultiUnitSelection(const SELECTION &aSel)
Validates and gathers a selection containing multiple symbol units that all belong to the same refere...
wxString GetSchItemAsText(const SCH_ITEM &aItem)
A simple container for schematic symbol instance information.
KIBIS_PIN * pin
std::vector< std::vector< std::string > > table
wxString result
Test unit parsing edge cases and error handling.
@ SCH_TABLE_T
Definition typeinfo.h:162
@ SCH_TABLECELL_T
Definition typeinfo.h:163
@ SCH_FIELD_T
Definition typeinfo.h:147
@ SCH_DIRECTIVE_LABEL_T
Definition typeinfo.h:168
@ SCH_LABEL_T
Definition typeinfo.h:164
@ SCH_HIER_LABEL_T
Definition typeinfo.h:166
@ SCH_SHEET_PIN_T
Definition typeinfo.h:171
@ SCH_TEXT_T
Definition typeinfo.h:148
@ SCH_TEXTBOX_T
Definition typeinfo.h:149
@ SCH_GLOBAL_LABEL_T
Definition typeinfo.h:165
@ SCH_PIN_T
Definition typeinfo.h:150
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683