KiCad PCB EDA Suite
Loading...
Searching...
No Matches
sch_downgrade.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 modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or (at your
9 * option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * 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
21
22#include <downgrade_scan.h>
23
24#include <cctype>
25#include <climits>
26#include <functional>
27#include <iterator>
28#include <map>
29#include <memory>
30#include <optional>
31#include <set>
32#include <string>
33#include <vector>
34
35#include <base_units.h>
36#include <common.h>
37#include <bus_alias.h>
38#include <eda_text.h>
40#include <project.h>
41#include <sch_file_versions.h>
44#include <schematic.h>
45#include <sch_screen.h>
46#include <sch_line.h>
47#include <sch_label.h>
48#include <sch_shape.h>
49#include <sch_sheet.h>
50#include <sch_symbol.h>
51#include <sch_group.h>
52#include <sch_pin.h>
53#include <sch_rule_area.h>
54#include <sch_sheet_path.h>
55#include <lib_symbol.h>
56#include <pin_map.h>
57#include <bitmap_base.h>
58#include <sch_bitmap.h>
59#include <string_utils.h>
60#include <eda_shape.h>
63
64
65// Schematic format date each feature landed. A rule fires only when the target predates this.
66static constexpr int SCH_VER_SHAPE_HATCH = 20250222;
67static constexpr int SCH_VER_LOCAL_POWER = 20250227;
68static constexpr int SCH_VER_JUMPER_PINS = 20250324;
69static constexpr int SCH_VER_GROUPS = 20250513;
70static constexpr int SCH_VER_RULE_AREA_FLAGS = 20250610;
71static constexpr int SCH_VER_ROUNDED_RECT = 20250829;
72static constexpr int SCH_VER_LOCKING = 20260326;
73static constexpr int SCH_VER_ELLIPSE = 20260508;
74static constexpr int SCH_VER_STACKED_PINS = 20250901;
75static constexpr int SCH_VER_STACKED_PIN_ESCAPES = 20260622;
76static constexpr int SCH_VER_PIN_MAPS = 20260629;
77static constexpr int SCH_VER_BODY_STYLES = 20250827;
78static constexpr int SCH_VER_POSITION_EXCLUSIONS = 20260101;
79static constexpr int SCH_VER_IMAGE_PPI = 20260623;
80static constexpr int SCH_VER_LINE_ENDINGS = 20260818;
81static constexpr int SYMLIB_VER_LINE_ENDINGS = 20260710;
82static constexpr int SCH_VER_ALTERNATE_SYMBOLS = 20260722;
83static constexpr int SCH_VER_CUSTOM_PROPERTIES = 20260830;
84
85// The formats this feature was built for. If a version moves past its constant, a new feature
86// landed and the rules or denylist may be stale. Review the keywords, then update these.
87static constexpr int SCH_DOWNGRADE_COVERED = 20260830;
88static constexpr int SYMLIB_DOWNGRADE_COVERED = 20260830;
89
90
91static bool isEllipse( const SCH_ITEM* aItem )
92{
93 if( aItem->Type() != SCH_SHAPE_T )
94 return false;
95
96 SHAPE_T shape = static_cast<const SCH_SHAPE*>( aItem )->GetShape();
97 return shape == SHAPE_T::ELLIPSE || shape == SHAPE_T::ELLIPSE_ARC;
98}
99
100
101static bool isHatchedFillItem( const SCH_ITEM& aItem )
102{
103 if( aItem.Type() != SCH_SHAPE_T && aItem.Type() != SCH_TEXTBOX_T )
104 return false;
105
106 return static_cast<const SCH_SHAPE&>( aItem ).IsHatchedFill();
107}
108
109
110// A schematic polyline is only closed by repeating the first point.
111static void closePolyline( std::vector<VECTOR2I>& aPoints )
112{
113 if( !aPoints.empty() && aPoints.front() != aPoints.back() )
114 aPoints.push_back( aPoints.front() );
115}
116
117
118static void ellipseToPolygon( SCH_SHAPE* aShape )
119{
120 int maxError = schIUScale.mmToIU( 0.01 );
121 bool isArc = aShape->GetShape() == SHAPE_T::ELLIPSE_ARC;
122
123 SHAPE_ELLIPSE ellipse = isArc ? SHAPE_ELLIPSE( aShape->GetEllipseCenter(), aShape->GetEllipseMajorRadius(),
124 aShape->GetEllipseMinorRadius(), aShape->GetEllipseRotation(),
125 aShape->GetEllipseStartAngle(), aShape->GetEllipseEndAngle() )
127 aShape->GetEllipseMinorRadius(), aShape->GetEllipseRotation() );
128
129 SHAPE_LINE_CHAIN chain = ellipse.ConvertToPolyline( maxError );
130
131 std::vector<VECTOR2I> points( chain.CPoints().begin(), chain.CPoints().end() );
132
133 // An arc stays open.
134 if( !isArc )
135 closePolyline( points );
136
137 aShape->SetShape( SHAPE_T::POLY );
138 aShape->SetPolyPoints( points );
139}
140
141
142static bool isRoundedRect( const SCH_ITEM* aItem )
143{
144 if( aItem->Type() != SCH_SHAPE_T )
145 return false;
146
147 const SCH_SHAPE* shape = static_cast<const SCH_SHAPE*>( aItem );
148 return shape->GetShape() == SHAPE_T::RECTANGLE && shape->GetCornerRadius() > 0;
149}
150
151
152static void roundedRectToPolygon( SCH_SHAPE* aShape )
153{
154 int maxError = schIUScale.mmToIU( 0.01 );
155 int radius = aShape->GetCornerRadius();
156
157 SHAPE_POLY_SET poly;
158 poly.NewOutline();
159
160 for( const VECTOR2I& corner : aShape->GetRectCorners() )
161 poly.Append( corner );
162
163 poly = poly.Fillet( radius, maxError );
164
165 std::vector<VECTOR2I> points( poly.Outline( 0 ).CPoints().begin(), poly.Outline( 0 ).CPoints().end() );
166
167 closePolyline( points );
168
169 aShape->SetCornerRadius( 0 );
170 aShape->SetShape( SHAPE_T::POLY );
171 aShape->SetPolyPoints( points );
172}
173
174
175// The escaping only exists since 20260622. KiCad 10.0 splits the notation without honouring
176// the backslashes, and 9.0 reads the whole bracket text as one literal number. Either way the
177// pins are silently renamed.
178static bool hasEscapedStackedPinNumber( const wxString& aNumber )
179{
180 if( !aNumber.StartsWith( wxT( "[" ) ) )
181 return false;
182
183 // Invalid notation is read as one literal number by every KiCad, so it never blocks.
184 bool valid = false;
185 ExpandStackedPinNotation( aNumber, &valid );
186
187 if( !valid )
188 return false;
189
190 for( size_t i = 0; i + 1 < aNumber.length(); ++i )
191 {
192 if( aNumber[i] != '\\' )
193 continue;
194
195 // A backslash escapes exactly the characters EscapeStackedPinItem escapes, so this
196 // cannot drift from the notation's structural set.
197 wxString next( aNumber[i + 1] );
198
199 if( EscapeStackedPinItem( next ) != next )
200 return true;
201 }
202
203 return false;
204}
205
206
207// Stacked notation itself only exists since 20250901. A target older than that reads the
208// bracket text as one literal pin number, which silently changes the netlist. Escaped numbers
209// are counted by their own rule.
210static bool usesPlainStackedPinNumber( const wxString& aNumber )
211{
212 if( hasEscapedStackedPinNumber( aNumber ) )
213 return false;
214
215 bool valid = false;
216 std::vector<wxString> expanded = ExpandStackedPinNotation( aNumber, &valid );
217
218 return valid && !( expanded.size() == 1 && expanded.front() == aNumber );
219}
220
221
222// Per-symbol rule scopes. The screen apply functions delegate to these for the library cache,
223// and the standalone .kicad_sym path runs them from the same rule table, so they cannot drift.
224static int countSymbolEllipses( const LIB_SYMBOL* aSymbol )
225{
226 int count = 0;
227
228 for( const SCH_ITEM& item : aSymbol->GetDrawItems() )
229 {
230 if( isEllipse( &item ) )
231 count++;
232 }
233
234 return count;
235}
236
237
238static void lowerSymbolEllipses( LIB_SYMBOL* aSymbol )
239{
240 std::vector<SCH_SHAPE*> ellipses;
241
242 for( SCH_ITEM& item : aSymbol->GetDrawItems() )
243 {
244 if( isEllipse( &item ) )
245 ellipses.push_back( static_cast<SCH_SHAPE*>( &item ) );
246 }
247
248 for( SCH_SHAPE* shape : ellipses )
249 ellipseToPolygon( shape );
250}
251
252
253static int countSymbolHatchedFills( const LIB_SYMBOL* aSymbol )
254{
255 int count = 0;
256
257 for( const SCH_ITEM& item : aSymbol->GetDrawItems() )
258 {
259 if( isHatchedFillItem( item ) )
260 count++;
261 }
262
263 return count;
264}
265
266
267static void lowerSymbolHatchedFills( LIB_SYMBOL* aSymbol )
268{
269 for( SCH_ITEM& item : aSymbol->GetDrawItems() )
270 {
271 if( isHatchedFillItem( item ) )
272 static_cast<SCH_SHAPE&>( item ).SetFillMode( FILL_T::FILLED_SHAPE );
273 }
274}
275
276
277static int countSymbolPinMaps( const LIB_SYMBOL* aSymbol )
278{
279 return ( aSymbol->GetPinMaps() != PIN_MAP_SET() || !aSymbol->GetAssociatedFootprints().empty() ) ? 1 : 0;
280}
281
282
283static void dropSymbolPinMaps( LIB_SYMBOL* aSymbol )
284{
285 aSymbol->SetPinMaps( PIN_MAP_SET() );
286 aSymbol->SetAssociatedFootprints( {} );
287}
288
289
290static int countSymbolRoundedRects( const LIB_SYMBOL* aSymbol )
291{
292 int count = 0;
293
294 for( const SCH_ITEM& item : aSymbol->GetDrawItems() )
295 {
296 if( isRoundedRect( &item ) )
297 count++;
298 }
299
300 return count;
301}
302
303
304static void lowerSymbolRoundedRects( LIB_SYMBOL* aSymbol )
305{
306 std::vector<SCH_SHAPE*> rects;
307
308 for( SCH_ITEM& item : aSymbol->GetDrawItems() )
309 {
310 if( isRoundedRect( &item ) )
311 rects.push_back( static_cast<SCH_SHAPE*>( &item ) );
312 }
313
314 for( SCH_SHAPE* rect : rects )
315 roundedRectToPolygon( rect );
316}
317
318
319static int countSymbolLocalPower( const LIB_SYMBOL* aSymbol )
320{
321 return aSymbol->IsLocalPower() ? 1 : 0;
322}
323
324
325static int countSymbolEscapedStackedPins( const LIB_SYMBOL* aSymbol )
326{
327 int count = 0;
328
329 for( const SCH_PIN* pin : aSymbol->GetGraphicalPins() )
330 {
331 if( hasEscapedStackedPinNumber( pin->GetNumber() ) )
332 count++;
333 }
334
335 return count;
336}
337
338
339static int countSymbolStackedPins( const LIB_SYMBOL* aSymbol )
340{
341 int count = 0;
342
343 for( const SCH_PIN* pin : aSymbol->GetGraphicalPins() )
344 {
345 if( usesPlainStackedPinNumber( pin->GetNumber() ) )
346 count++;
347 }
348
349 return count;
350}
351
352
353static int countSymbolJumperPins( const LIB_SYMBOL* aSymbol )
354{
355 return ( !aSymbol->JumperPinGroups().IsEmpty() || aSymbol->GetDuplicatePinNumbersAreJumpers() ) ? 1 : 0;
356}
357
358
359static void dropSymbolJumperPins( LIB_SYMBOL* aSymbol )
360{
361 aSymbol->JumperPinGroups().Clear();
362 aSymbol->SetDuplicatePinNumbersAreJumpers( false );
363}
364
365
366// DeMorgan pairs survive as the old convert notation. Custom style names and the ability to
367// select styles beyond the first two do not; used higher styles are baked separately below.
368static int countSymbolNamedBodyStyles( const LIB_SYMBOL* aSymbol )
369{
370 return ( aSymbol->IsMultiBodyStyle() && !aSymbol->HasDeMorganBodyStyles() ) ? 1 : 0;
371}
372
373
375{
376 aSymbol->SetBodyStyleNames( {} );
377}
378
379
380static int countSelectedCustomBodyStyles( const SCH_SCREEN* aScreen )
381{
382 int count = 0;
383
384 for( SCH_ITEM* item : aScreen->Items().OfType( SCH_SYMBOL_T ) )
385 {
386 if( static_cast<const SCH_SYMBOL*>( item )->GetBodyStyle() > BODY_STYLE::DEMORGAN )
387 count++;
388 }
389
390 return count;
391}
392
393
394static int countUnresolvedCustomBodyStyles( const SCH_SCREEN* aScreen )
395{
396 int count = 0;
397
398 for( SCH_ITEM* item : aScreen->Items().OfType( SCH_SYMBOL_T ) )
399 {
400 const SCH_SYMBOL* symbol = static_cast<const SCH_SYMBOL*>( item );
401
402 if( symbol->GetBodyStyle() > BODY_STYLE::DEMORGAN
403 && ( !symbol->GetLibSymbolRef() || symbol->IsMissingLibSymbol() ) )
404 {
405 count++;
406 }
407 }
408
409 return count;
410}
411
412
414{
415 std::vector<SCH_SYMBOL*> symbols;
416
417 for( SCH_ITEM* item : aScreen->Items().OfType( SCH_SYMBOL_T ) )
418 {
419 if( static_cast<SCH_SYMBOL*>( item )->GetBodyStyle() > BODY_STYLE::DEMORGAN )
420 symbols.push_back( static_cast<SCH_SYMBOL*>( item ) );
421 }
422
423 std::map<std::pair<wxString, int>, LIB_SYMBOL*> bakedSymbols;
424
425 for( SCH_SYMBOL* symbol : symbols )
426 {
427 wxCHECK_RET( symbol->GetLibSymbolRef() && !symbol->IsMissingLibSymbol(),
428 wxT( "Cannot bake a body style without its library symbol." ) );
429
430 const wxString sourceName = symbol->GetSchSymbolLibraryName();
431 const int bodyStyle = symbol->GetBodyStyle();
432 const auto key = std::make_pair( sourceName, bodyStyle );
433 LIB_SYMBOL*& baked = bakedSymbols[key];
434
435 if( !baked )
436 {
437 // Work on a private flattened cache entry, not the source library. Instances
438 // selecting other styles must keep their own drawings and pin positions.
439 baked = symbol->GetLibSymbolRef()->Flatten().release();
440 std::vector<SCH_ITEM*> remove;
441
442 for( SCH_ITEM& item : baked->GetDrawItems() )
443 {
444 if( item.GetBodyStyle() && item.GetBodyStyle() != bodyStyle )
445 remove.push_back( &item );
446 else if( item.GetBodyStyle() == bodyStyle )
447 item.SetBodyStyle( BODY_STYLE::BASE );
448 }
449
450 for( SCH_ITEM* item : remove )
451 baked->RemoveDrawItem( item );
452
453 baked->SetBodyStyleNames( {} );
454 baked->SetHasDeMorganBodyStyles( false );
455
456 const wxString baseName = wxString::Format( wxT( "%s__KiCad9_body_%d" ), sourceName, bodyStyle );
457 wxString name = baseName;
458
459 for( int suffix = 1; aScreen->GetLibSymbols().count( name ); ++suffix )
460 name = wxString::Format( wxT( "%s_%d" ), baseName, suffix );
461
462 baked->SetName( name );
463 baked->SetLibId( LIB_ID( wxEmptyString, name ) );
464 aScreen->AddLibSymbol( baked );
465 }
466
467 aScreen->Remove( symbol );
468 symbol->SetSchSymbolLibraryName( baked->GetLibId().Format().wx_str() );
469
470 // Change the selector without rebuilding pins against the OLD base style. Rebuild
471 // once against the baked symbol so the selected pins keep their numbers and UUIDs.
472 symbol->SCH_ITEM::SetBodyStyle( BODY_STYLE::BASE );
473 symbol->SetLibSymbol( new LIB_SYMBOL( *baked ) );
474 aScreen->Append( symbol );
475 }
476}
477
478
479static int countSymbolPositionExclusions( const LIB_SYMBOL* aSymbol )
480{
481 return aSymbol->GetExcludedFromPosFiles() ? 1 : 0;
482}
483
484
486{
487 aSymbol->SetExcludedFromPosFiles( false );
488}
489
490
491static int countPositionExclusions( const SCH_SCREEN* aScreen )
492{
493 int count = 0;
494
495 for( SCH_ITEM* item : aScreen->Items().OfType( SCH_SYMBOL_T ) )
496 {
497 if( static_cast<const SCH_SYMBOL*>( item )->GetExcludedFromPosFiles() )
498 count++;
499 }
500
501 for( const auto& [name, symbol] : aScreen->GetLibSymbols() )
502 {
503 if( symbol )
504 count += countSymbolPositionExclusions( symbol );
505 }
506
507 return count;
508}
509
510
511static void dropPositionExclusions( SCH_SCREEN* aScreen )
512{
513 for( SCH_ITEM* item : aScreen->Items().OfType( SCH_SYMBOL_T ) )
514 static_cast<SCH_SYMBOL*>( item )->SetExcludedFromPosFiles( false );
515
516 for( const auto& [name, symbol] : aScreen->GetLibSymbols() )
517 {
518 if( symbol )
520 }
521}
522
523
524// Group locks shipped with group support (20250513) and predate general item locking, so
525// groups are the group rule's business, not this one's. A lock inherited from a locked group
526// survives through the group itself and is not a loss.
527static bool hasOwnLock( const SCH_ITEM* aItem )
528{
529 if( aItem->Type() == SCH_GROUP_T )
530 return false;
531
532 if( EDA_GROUP* group = aItem->GetParentGroup() )
533 {
534 if( group->AsEdaItem()->IsLocked() )
535 return false;
536 }
537
538 return aItem->IsLocked();
539}
540
541
542static int countLockedItems( const SCH_SCREEN* aScreen )
543{
544 int count = 0;
545
546 for( SCH_ITEM* item : aScreen->Items() )
547 {
548 if( hasOwnLock( item ) )
549 count++;
550 }
551
552 return count;
553}
554
555
556static void unlockItems( SCH_SCREEN* aScreen )
557{
558 for( SCH_ITEM* item : aScreen->Items() )
559 {
560 if( hasOwnLock( item ) )
561 item->SetLocked( false );
562 }
563}
564
565
566static int countEllipses( const SCH_SCREEN* aScreen )
567{
568 int count = 0;
569
570 for( SCH_ITEM* item : aScreen->Items().OfType( SCH_SHAPE_T ) )
571 {
572 if( isEllipse( item ) )
573 count++;
574 }
575
576 // Symbol bodies are written from the screen library cache, so ellipses there count too.
577 for( const auto& [name, symbol] : aScreen->GetLibSymbols() )
578 {
579 if( symbol )
580 count += countSymbolEllipses( symbol );
581 }
582
583 return count;
584}
585
586
587static void lowerEllipses( SCH_SCREEN* aScreen )
588{
589 // Collect first, then mutate. Changing items while iterating the R-tree is not safe.
590 std::vector<SCH_SHAPE*> ellipses;
591
592 for( SCH_ITEM* item : aScreen->Items().OfType( SCH_SHAPE_T ) )
593 {
594 if( isEllipse( item ) )
595 ellipses.push_back( static_cast<SCH_SHAPE*>( item ) );
596 }
597
598 for( SCH_SHAPE* shape : ellipses )
599 ellipseToPolygon( shape );
600
601 for( const auto& [name, symbol] : aScreen->GetLibSymbols() )
602 {
603 if( symbol )
604 lowerSymbolEllipses( symbol );
605 }
606}
607
608
609// Every fill-bearing schematic item derives from SCH_SHAPE but has its own type id.
611
612
613static int countHatchedFills( const SCH_SCREEN* aScreen )
614{
615 int count = 0;
616
617 for( KICAD_T type : FILLABLE_TYPES )
618 {
619 for( SCH_ITEM* item : aScreen->Items().OfType( type ) )
620 {
621 if( static_cast<SCH_SHAPE*>( item )->IsHatchedFill() )
622 count++;
623 }
624 }
625
626 for( const auto& [name, symbol] : aScreen->GetLibSymbols() )
627 {
628 if( symbol )
629 count += countSymbolHatchedFills( symbol );
630 }
631
632 return count;
633}
634
635
636static void lowerHatchedFills( SCH_SCREEN* aScreen )
637{
638 for( KICAD_T type : FILLABLE_TYPES )
639 {
640 for( SCH_ITEM* item : aScreen->Items().OfType( type ) )
641 {
642 SCH_SHAPE* shape = static_cast<SCH_SHAPE*>( item );
643
644 if( shape->IsHatchedFill() )
646 }
647 }
648
649 for( const auto& [name, symbol] : aScreen->GetLibSymbols() )
650 {
651 if( symbol )
652 lowerSymbolHatchedFills( symbol );
653 }
654}
655
656
657static bool hasPinMaps( const SCH_SYMBOL* aSymbol )
658{
659 if( !aSymbol->GetPinMapOverride().IsDefault() )
660 return true;
661
662 for( const SCH_SYMBOL_INSTANCE& instance : aSymbol->GetInstances() )
663 {
664 for( const auto& [name, variant] : instance.m_Variants )
665 {
666 if( !variant.m_PinMapOverride.IsDefault() )
667 return true;
668 }
669 }
670
671 const std::unique_ptr<LIB_SYMBOL>& lib = aSymbol->GetLibSymbolRef();
672 return lib && ( lib->GetPinMaps() != PIN_MAP_SET() || !lib->GetAssociatedFootprints().empty() );
673}
674
675
676static int countPinMaps( const SCH_SCREEN* aScreen )
677{
678 int count = 0;
679
680 for( SCH_ITEM* item : aScreen->Items().OfType( SCH_SYMBOL_T ) )
681 {
682 if( hasPinMaps( static_cast<const SCH_SYMBOL*>( item ) ) )
683 count++;
684 }
685
686 // The transform strips the library cache too, so cache-only maps count as well.
687 for( const auto& [name, symbol] : aScreen->GetLibSymbols() )
688 {
689 if( symbol )
690 count += countSymbolPinMaps( symbol );
691 }
692
693 return count;
694}
695
696
697static void dropPinMaps( SCH_SCREEN* aScreen )
698{
699 for( SCH_ITEM* item : aScreen->Items().OfType( SCH_SYMBOL_T ) )
700 {
701 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
702
703 symbol->ClearPinMapOverrides();
704
705 if( symbol->GetLibSymbolRef() )
706 dropSymbolPinMaps( symbol->GetLibSymbolRef().get() );
707 }
708
709 for( const auto& [name, symbol] : aScreen->GetLibSymbols() )
710 {
711 if( symbol )
712 dropSymbolPinMaps( symbol );
713 }
714}
715
716
717static int countNamedBodyStyles( const SCH_SCREEN* aScreen )
718{
719 int count = 0;
720
721 for( const auto& [name, symbol] : aScreen->GetLibSymbols() )
722 {
723 if( symbol )
724 count += countSymbolNamedBodyStyles( symbol );
725 }
726
727 return count;
728}
729
730
731static void dropNamedBodyStyles( SCH_SCREEN* aScreen )
732{
733 for( const auto& [name, symbol] : aScreen->GetLibSymbols() )
734 {
735 if( symbol )
736 dropSymbolBodyStyleNames( symbol );
737 }
738}
739
740
741static int countGroups( const SCH_SCREEN* aScreen )
742{
743 auto groups = aScreen->Items().OfType( SCH_GROUP_T );
744 return static_cast<int>( std::distance( groups.begin(), groups.end() ) );
745}
746
747
748static void ungroupAll( SCH_SCREEN* aScreen )
749{
750 std::vector<SCH_GROUP*> groups;
751
752 for( SCH_ITEM* item : aScreen->Items().OfType( SCH_GROUP_T ) )
753 groups.push_back( static_cast<SCH_GROUP*>( item ) );
754
755 // Detach every member first. Groups can nest, and deleting an inner group while an outer
756 // group still lists it would leave the outer group holding a freed pointer.
757 for( SCH_GROUP* group : groups )
758 group->RemoveAll();
759
760 for( SCH_GROUP* group : groups )
761 {
762 aScreen->Remove( group );
763 delete group;
764 }
765}
766
767
768static int countJumperPinGroups( const SCH_SCREEN* aScreen )
769{
770 int count = 0;
771
772 for( const auto& [name, symbol] : aScreen->GetLibSymbols() )
773 {
774 if( symbol )
775 count += countSymbolJumperPins( symbol );
776 }
777
778 return count;
779}
780
781
782static void dropJumperPinGroups( SCH_SCREEN* aScreen )
783{
784 for( const auto& [name, symbol] : aScreen->GetLibSymbols() )
785 {
786 if( symbol )
787 dropSymbolJumperPins( symbol );
788 }
789}
790
791
792static int countRoundedRects( const SCH_SCREEN* aScreen )
793{
794 int count = 0;
795
796 for( SCH_ITEM* item : aScreen->Items().OfType( SCH_SHAPE_T ) )
797 {
798 if( isRoundedRect( item ) )
799 count++;
800 }
801
802 for( const auto& [name, symbol] : aScreen->GetLibSymbols() )
803 {
804 if( symbol )
805 count += countSymbolRoundedRects( symbol );
806 }
807
808 return count;
809}
810
811
812static void lowerRoundedRects( SCH_SCREEN* aScreen )
813{
814 // Collect first, then mutate. Changing items while iterating the R-tree is not safe.
815 std::vector<SCH_SHAPE*> rects;
816
817 for( SCH_ITEM* item : aScreen->Items().OfType( SCH_SHAPE_T ) )
818 {
819 if( isRoundedRect( item ) )
820 rects.push_back( static_cast<SCH_SHAPE*>( item ) );
821 }
822
823 for( SCH_SHAPE* rect : rects )
824 roundedRectToPolygon( rect );
825
826 for( const auto& [name, symbol] : aScreen->GetLibSymbols() )
827 {
828 if( symbol )
829 lowerSymbolRoundedRects( symbol );
830 }
831}
832
833
834static int countLocalPowerSymbols( const SCH_SCREEN* aScreen )
835{
836 int count = 0;
837
838 for( const auto& [name, symbol] : aScreen->GetLibSymbols() )
839 {
840 if( symbol )
841 count += countSymbolLocalPower( symbol );
842 }
843
844 return count;
845}
846
847
848static bool hasRuleAreaFlags( const SCH_RULE_AREA* aArea )
849{
850 return aArea->GetDNP() || aArea->GetExcludedFromSim() || aArea->GetExcludedFromBOM()
851 || aArea->GetExcludedFromBoard();
852}
853
854
855static int countRuleAreaFlags( const SCH_SCREEN* aScreen )
856{
857 int count = 0;
858
859 for( SCH_ITEM* item : aScreen->Items().OfType( SCH_RULE_AREA_T ) )
860 {
861 if( hasRuleAreaFlags( static_cast<const SCH_RULE_AREA*>( item ) ) )
862 count++;
863 }
864
865 return count;
866}
867
868
869static void dropRuleAreaFlags( SCH_SCREEN* aScreen )
870{
871 for( SCH_ITEM* item : aScreen->Items().OfType( SCH_RULE_AREA_T ) )
872 {
873 SCH_RULE_AREA* area = static_cast<SCH_RULE_AREA*>( item );
874
875 area->SetDNP( false );
876 area->SetExcludedFromSim( false );
877 area->SetExcludedFromBOM( false );
878 area->SetExcludedFromBoard( false );
879 }
880}
881
882
883static int countEscapedStackedPins( const SCH_SCREEN* aScreen )
884{
885 int count = 0;
886
887 for( const auto& [name, symbol] : aScreen->GetLibSymbols() )
888 {
889 if( symbol )
890 count += countSymbolEscapedStackedPins( symbol );
891 }
892
893 return count;
894}
895
896
897static int countStackedPins( const SCH_SCREEN* aScreen )
898{
899 int count = 0;
900
901 for( const auto& [name, symbol] : aScreen->GetLibSymbols() )
902 {
903 if( symbol )
904 count += countSymbolStackedPins( symbol );
905 }
906
907 return count;
908}
909
910
911static void omitSymbolGraphics( LIB_SYMBOL* aSymbol, const std::function<bool( const SCH_ITEM* )>& aMatches )
912{
913 std::vector<SCH_ITEM*> remove;
914
915 for( SCH_ITEM& item : aSymbol->GetDrawItems() )
916 {
917 if( aMatches( &item ) )
918 remove.push_back( &item );
919 }
920
921 // RemoveDrawItem owns and deletes library drawings.
922 for( SCH_ITEM* item : remove )
923 aSymbol->RemoveDrawItem( item );
924}
925
926
927static void omitScreenItems( SCH_SCREEN* aScreen, const std::function<bool( const SCH_ITEM* )>& aMatches )
928{
929 std::vector<SCH_ITEM*> remove;
930
931 for( SCH_ITEM* item : aScreen->Items() )
932 {
933 if( aMatches( item ) )
934 remove.push_back( item );
935 }
936
937 for( SCH_ITEM* item : remove )
938 {
939 // SCH_SCREEN::Remove does not detach group membership.
940 if( EDA_GROUP* group = item->GetParentGroup() )
941 group->RemoveItem( item );
942
943 aScreen->Remove( item );
944 delete item;
945 }
946}
947
948
950{
951 omitScreenItems( aScreen,
952 []( const SCH_ITEM* aItem )
953 {
954 return aItem->Type() == SCH_SYMBOL_T && aItem->GetBodyStyle() > BODY_STYLE::DEMORGAN;
955 } );
956}
957
958
959static void omitSymbolEllipses( LIB_SYMBOL* aSymbol )
960{
961 omitSymbolGraphics( aSymbol, isEllipse );
962}
963static void omitSymbolRoundedRects( LIB_SYMBOL* aSymbol )
964{
966}
967
968
969static void omitEllipses( SCH_SCREEN* aScreen )
970{
971 omitScreenItems( aScreen, isEllipse );
972
973 for( const auto& [name, symbol] : aScreen->GetLibSymbols() )
974 {
975 if( symbol )
976 omitSymbolEllipses( symbol );
977 }
978}
979
980
981static void omitRoundedRects( SCH_SCREEN* aScreen )
982{
983 omitScreenItems( aScreen, isRoundedRect );
984
985 for( const auto& [name, symbol] : aScreen->GetLibSymbols() )
986 {
987 if( symbol )
988 omitSymbolRoundedRects( symbol );
989 }
990}
991
992
993static void omitSymbolHatchedFills( LIB_SYMBOL* aSymbol )
994{
995 for( SCH_ITEM& item : aSymbol->GetDrawItems() )
996 {
997 if( isHatchedFillItem( item ) )
998 static_cast<SCH_SHAPE&>( item ).SetFillMode( FILL_T::NO_FILL );
999 }
1000}
1001
1002
1003static void omitHatchedFills( SCH_SCREEN* aScreen )
1004{
1005 for( KICAD_T type : FILLABLE_TYPES )
1006 {
1007 for( SCH_ITEM* item : aScreen->Items().OfType( type ) )
1008 {
1009 SCH_SHAPE* shape = static_cast<SCH_SHAPE*>( item );
1010
1011 if( shape->IsHatchedFill() )
1012 shape->SetFillMode( FILL_T::NO_FILL );
1013 }
1014 }
1015
1016 for( const auto& [name, symbol] : aScreen->GetLibSymbols() )
1017 {
1018 if( symbol )
1019 omitSymbolHatchedFills( symbol );
1020 }
1021}
1022
1023
1024static void visitSymbolItems( const LIB_SYMBOL* aSymbol, const std::function<void( EDA_ITEM* )>& aVisit )
1025{
1026 aVisit( const_cast<LIB_SYMBOL*>( aSymbol ) );
1027
1028 for( const SCH_ITEM& item : aSymbol->GetDrawItems() )
1029 aVisit( const_cast<SCH_ITEM*>( &item ) );
1030}
1031
1032
1033static void visitScreenItems( const SCH_SCREEN* aScreen, const std::function<void( EDA_ITEM* )>& aVisit )
1034{
1035 std::set<EDA_ITEM*> visited;
1036 auto visit = [&]( EDA_ITEM* item )
1037 {
1038 if( visited.insert( item ).second )
1039 aVisit( item );
1040 };
1041
1042 for( SCH_ITEM* item : aScreen->Items() )
1043 {
1044 visit( item );
1045 item->RunOnChildren(
1046 [&]( SCH_ITEM* child )
1047 {
1048 visit( child );
1049 },
1050 RECURSE );
1051 }
1052
1053 for( const auto& [name, symbol] : aScreen->GetLibSymbols() )
1054 {
1055 if( symbol )
1056 visitSymbolItems( symbol, visit );
1057 }
1058}
1059
1060
1061static bool isPostTargetTextVar( const wxString& aToken )
1062{
1063 const wxString token = aToken.AfterLast( ':' );
1064 return token.StartsWith( wxT( "PROPERTY." ) ) || token == wxT( "SYMBOL_IS_POWER" )
1065 || token == wxT( "SYMBOL_IS_LOCAL_POWER" );
1066}
1067
1068
1069static bool hasPostTargetTextVars( const wxString& aText )
1070{
1071 bool found = false;
1072 std::function<bool( wxString* )> scan = [&]( wxString* token )
1073 {
1074 found |= isPostTargetTextVar( *token );
1075 return false;
1076 };
1077 ExpandTextVars( aText, &scan, INTERNAL );
1078 return found;
1079}
1080
1081
1082static bool hasActiveExpression( const wxString& aText )
1083{
1084 std::function<bool( wxString* )> preserve = []( wxString* )
1085 {
1086 return false;
1087 };
1088 return ExpandTextVars( aText, &preserve, INTERNAL ).Contains( wxT( "@{" ) );
1089}
1090
1091
1092static bool hasEscapedTextControl( const wxString& aText )
1093{
1094 return aText.Contains( wxT( "\\${" ) ) || aText.Contains( wxT( "\\@{" ) );
1095}
1096
1097
1098static bool textNeedsDowngrade( const wxString& aText, const PROJECT* aProject, bool aLegacyExpressions,
1099 std::set<wxString>& aVisiting, const TITLE_BLOCK* aTitles = nullptr )
1100{
1101 bool found = aLegacyExpressions && ( hasActiveExpression( aText ) || hasEscapedTextControl( aText ) );
1102 std::function<bool( wxString* )> scan = [&]( wxString* token )
1103 {
1104 found |= isPostTargetTextVar( *token );
1105
1106 if( aProject )
1107 {
1108 const auto& vars = aProject->GetTextVars();
1109 auto value = vars.find( *token );
1110
1111 if( value != vars.end() && aVisiting.insert( *token ).second )
1112 {
1113 found |= textNeedsDowngrade( value->second, aProject, aLegacyExpressions, aVisiting, aTitles );
1114 aVisiting.erase( *token );
1115 }
1116 }
1117
1118 if( aTitles && aVisiting.insert( *token ).second )
1119 {
1120 wxString value;
1121
1122 if( *token == wxT( "TITLE" ) )
1123 value = aTitles->GetTitle();
1124 else if( *token == wxT( "ISSUE_DATE" ) )
1125 value = aTitles->GetDate();
1126 else if( *token == wxT( "REVISION" ) )
1127 value = aTitles->GetRevision();
1128 else if( *token == wxT( "COMPANY" ) )
1129 value = aTitles->GetCompany();
1130 else if( token->length() == 8 && token->StartsWith( wxT( "COMMENT" ) ) && ( *token )[7] >= '1'
1131 && ( *token )[7] <= '9' )
1132 value = aTitles->GetComment( ( *token )[7].GetValue() - '1' );
1133
1134 found |= textNeedsDowngrade( value, aProject, aLegacyExpressions, aVisiting, aTitles );
1135 aVisiting.erase( *token );
1136 }
1137
1138 return false;
1139 };
1140 ExpandTextVars( aText, &scan, INTERNAL );
1141 return found;
1142}
1143
1144
1145static wxString bakeLegacyExpressions( wxString aText, const SCH_ITEM* aItem, const SCH_SHEET_PATH* aPath,
1146 bool& aFailed )
1147{
1148 for( size_t pos = 0; pos + 1 < aText.length(); ++pos )
1149 {
1150 if( aText[pos] == '\\' && ( aText[pos + 1] == '$' || aText[pos + 1] == '@' ) )
1151 {
1152 ++pos;
1153 continue;
1154 }
1155
1156 if( aText[pos] != '@' || aText[pos + 1] != '{' )
1157 continue;
1158
1159 size_t end = pos + 2;
1160 int depth = 1;
1161
1162 for( ; end < aText.length() && depth; ++end )
1163 {
1164 if( aText[end] == '{' )
1165 ++depth;
1166 else if( aText[end] == '}' )
1167 --depth;
1168 }
1169
1170 if( depth )
1171 {
1172 aFailed = true;
1173 return aText;
1174 }
1175
1176 const wxString expression = aText.Mid( pos, end - pos );
1177 wxString value = aItem->ResolveText( expression, aPath, 0, wxEmptyString );
1179
1180 if( value == expression || hasActiveExpression( value ) || hasPostTargetTextVars( value )
1181 || !ExtractTextVarReferences( value ).empty() || value.Contains( wxT( "<Unresolved:" ) ) )
1182 {
1183 aFailed = true;
1184 return aText;
1185 }
1186
1187 aText = aText.Left( pos ) + value + aText.Mid( end );
1188 pos += value.length();
1189
1190 if( pos )
1191 --pos;
1192 }
1193
1194 return aText;
1195}
1196
1197
1199{
1200 std::map<EDA_TEXT*, wxString> m_replacements;
1201 int m_blocked = 0;
1202};
1203
1204
1205static void planTextDowngrade( EDA_ITEM* aItem, const std::vector<SCH_SHEET_PATH>& aPaths, bool aLibrary,
1206 bool aLegacyExpressions, TEXT_DOWNGRADE_PLAN& aPlan,
1207 const TITLE_BLOCK* aTitles = nullptr )
1208{
1209 EDA_TEXT* text = dynamic_cast<EDA_TEXT*>( aItem );
1210 SCH_ITEM* item = dynamic_cast<SCH_ITEM*>( aItem );
1211
1212 if( !text || !item )
1213 return;
1214
1215 const SCHEMATIC* schematic = item->Schematic();
1216 const PROJECT* project = schematic && schematic->IsValid() ? &schematic->Project() : nullptr;
1217 std::set<wxString> visiting;
1218
1219 if( !textNeedsDowngrade( text->GetText(), project, aLegacyExpressions, visiting, aTitles ) )
1220 return;
1221
1222 if( aLegacyExpressions && hasEscapedTextControl( text->GetText() ) )
1223 {
1224 ++aPlan.m_blocked;
1225 return;
1226 }
1227
1228 if( !aLibrary && ( aPaths.empty() || !project ) )
1229 {
1230 ++aPlan.m_blocked;
1231 return;
1232 }
1233
1234 std::optional<wxString> replacement;
1235 bool failed = false;
1236 auto resolve = [&]( const SCH_SHEET_PATH* path )
1237 {
1238 std::function<bool( wxString* )> resolver = [&]( wxString* token )
1239 {
1240 std::set<wxString> dependencies;
1241
1242 if( !textNeedsDowngrade( wxT( "${" ) + *token + wxT( "}" ), project, aLegacyExpressions, dependencies,
1243 aTitles ) )
1244 return false;
1245
1246 const wxString reference = wxT( "${" ) + *token + wxT( "}" );
1247 wxString value = item->ResolveText( reference, path, 0, wxEmptyString );
1249
1250 if( value == reference || hasPostTargetTextVars( value ) || !ExtractTextVarReferences( value ).empty()
1251 || value.Contains( wxT( "<Unresolved:" ) ) || ( aLegacyExpressions && hasEscapedTextControl( value ) ) )
1252 {
1253 failed = true;
1254 return false;
1255 }
1256
1257 *token = value;
1258 return true;
1259 };
1260 wxString value = ExpandTextVars( text->GetText(), &resolver, INTERNAL );
1261
1262 if( aLegacyExpressions )
1263 value = bakeLegacyExpressions( value, item, path, failed );
1264
1266
1267 if( hasPostTargetTextVars( value ) || ( replacement && *replacement != value ) )
1268 failed = true;
1269
1270 replacement = value;
1271 };
1272
1273 if( aLibrary )
1274 resolve( nullptr );
1275 else
1276 {
1277 for( const SCH_SHEET_PATH& path : aPaths )
1278 resolve( &path );
1279 }
1280
1281 if( failed || !replacement )
1282 ++aPlan.m_blocked;
1283 else
1284 aPlan.m_replacements.emplace( text, *replacement );
1285}
1286
1287
1288static TEXT_DOWNGRADE_PLAN symbolTextPlan( const LIB_SYMBOL* aSymbol, bool aLegacyExpressions )
1289{
1291 visitSymbolItems( aSymbol,
1292 [&]( EDA_ITEM* item )
1293 {
1294 planTextDowngrade( item, {}, true, aLegacyExpressions, plan );
1295 } );
1296 return plan;
1297}
1298
1299
1300static TEXT_DOWNGRADE_PLAN screenTextPlan( const SCH_SCREEN* aScreen, bool aLegacyExpressions )
1301{
1303 std::vector<SCH_SHEET_PATH> paths;
1304
1305 const SCHEMATIC* owner = aScreen->GetParent() && aScreen->GetParent()->Type() == SCHEMATIC_T
1306 ? static_cast<const SCHEMATIC*>( aScreen->GetParent() )
1307 : nullptr;
1308
1309 if( const SCHEMATIC* schematic = owner )
1310 {
1311 for( const SCH_SHEET_PATH& path : schematic->BuildUnorderedSheetList() )
1312 {
1313 if( path.LastScreen() == aScreen )
1314 paths.push_back( path );
1315 }
1316 }
1317
1318 std::set<EDA_ITEM*> visited;
1319 auto planItem = [&]( SCH_ITEM* item )
1320 {
1321 if( visited.insert( item ).second )
1322 planTextDowngrade( item, paths, false, aLegacyExpressions, plan, &aScreen->GetTitleBlock() );
1323 };
1324
1325 for( SCH_ITEM* item : aScreen->Items() )
1326 {
1327 planItem( item );
1328 item->RunOnChildren(
1329 [&]( SCH_ITEM* child )
1330 {
1331 planItem( child );
1332 },
1333 RECURSE );
1334 }
1335
1336 for( const auto& [name, symbol] : aScreen->GetLibSymbols() )
1337 {
1338 if( symbol )
1339 visitSymbolItems( symbol,
1340 [&]( EDA_ITEM* item )
1341 {
1342 planTextDowngrade( item, {}, true, aLegacyExpressions, plan );
1343 } );
1344 }
1345
1346 const SCHEMATIC* schematic = owner;
1347 const PROJECT* project = schematic && schematic->IsValid() ? &schematic->Project() : nullptr;
1348 const TITLE_BLOCK& titles = aScreen->GetTitleBlock();
1349 std::vector<wxString> borderText = { titles.GetTitle(), titles.GetDate(), titles.GetRevision(),
1350 titles.GetCompany() };
1351
1352 for( int i = 0; i < 9; ++i )
1353 borderText.push_back( titles.GetComment( i ) );
1354
1355 for( const wxString& value : borderText )
1356 {
1357 std::set<wxString> visiting;
1358
1359 if( textNeedsDowngrade( value, project, aLegacyExpressions, visiting, &titles ) )
1360 ++plan.m_blocked;
1361 }
1362
1363 return plan;
1364}
1365
1366
1367static void reportTextPlan( const TEXT_DOWNGRADE_PLAN& aPlan, COMPATIBILITY_REPORT& aReport )
1368{
1369 if( !aPlan.m_replacements.empty() )
1370 {
1371 aReport.Add( DOWNGRADE_BUCKET::DROP, _( "Post-target text variables" ),
1372 _( "Dynamic evaluation of unsupported references and expressions is removed. Resolved literal "
1373 "values are kept." ),
1374 aPlan.m_replacements.size() );
1375 }
1376
1377 if( aPlan.m_blocked )
1378 {
1379 aReport.Add( DOWNGRADE_BUCKET::BLOCK, _( "Unresolved post-target text variables" ),
1380 _( "The target cannot preserve these references, literal escapes or page-border expressions and a "
1381 "reliable compatible value is unavailable." ),
1382 aPlan.m_blocked );
1383 }
1384}
1385
1386
1387static void applyTextPlan( const TEXT_DOWNGRADE_PLAN& aPlan )
1388{
1389 for( const auto& [text, value] : aPlan.m_replacements )
1390 text->SetText( value );
1391}
1392
1393
1394static bool hasPaddedBusVector( const wxString& aText )
1395{
1396 if( NET_SETTINGS::ParseBusVector( aText, nullptr, nullptr ) )
1397 {
1398 const wxString range = aText.AfterFirst( '[' ).BeforeFirst( ']' );
1399 const wxString begin = range.BeforeFirst( '.' );
1400 const wxString end = range.AfterLast( '.' );
1401 return ( begin.length() > 1 && begin[0] == '0' ) || ( end.length() > 1 && end[0] == '0' );
1402 }
1403
1404 std::vector<wxString> members;
1405
1406 if( NET_SETTINGS::ParseBusGroup( aText, nullptr, &members ) )
1407 {
1408 for( const wxString& member : members )
1409 {
1410 if( hasPaddedBusVector( member ) )
1411 return true;
1412 }
1413 }
1414
1415 return false;
1416}
1417
1418
1420{
1421 bool m_valid = false;
1422 wxString m_prefix;
1423 long m_begin = 0;
1424 long m_end = 0;
1425};
1426
1427
1428// Acceptance and prefix logic from the released parsers, without their unsafe expansion loop.
1429static FROZEN_BUS_VECTOR frozenBusVector( const wxString& aBus, bool aPreTen )
1430{
1432 const size_t length = aBus.length();
1433 size_t i = 0;
1434 int nesting = 0;
1435 bool quoted = false;
1436 auto digit = []( wxUniChar c )
1437 {
1438 return c >= '0' && c <= '9';
1439 };
1440 auto formatting = []( wxUniChar c )
1441 {
1442 return c == '_' || c == '^' || c == '~';
1443 };
1444 auto escaped = [&]( size_t pos )
1445 {
1446 size_t slashes = 0;
1447 for( ; pos && aBus[pos - 1] == '\\'; --pos )
1448 ++slashes;
1449 return slashes % 2 == 1;
1450 };
1451
1452 for( ; i < length; ++i )
1453 {
1454 if( !aPreTen && aBus[i] == '"' && !escaped( i ) )
1455 {
1456 quoted = !quoted;
1457 continue;
1458 }
1459
1460 if( quoted )
1461 {
1462 if( aBus[i] == '\\' && i + 1 < length )
1463 result.m_prefix += aBus[++i];
1464 else
1465 result.m_prefix += aBus[i];
1466 continue;
1467 }
1468
1469 if( aBus[i] == '{' )
1470 {
1471 if( i == 0 || !formatting( aBus[i - 1] ) )
1472 return result;
1473 ++nesting;
1474 if( !aPreTen )
1475 {
1476 if( !result.m_prefix.IsEmpty() )
1477 result.m_prefix.RemoveLast();
1478 continue;
1479 }
1480 }
1481 else if( aBus[i] == '}' )
1482 {
1483 --nesting;
1484 if( !aPreTen )
1485 continue;
1486 }
1487
1488 if( !aPreTen && aBus[i] == '\\' && i + 1 < length && aBus[i + 1] == ' ' )
1489 {
1490 result.m_prefix += aBus[++i];
1491 continue;
1492 }
1493
1494 if( aBus[i] == ' ' || aBus[i] == ']' )
1495 return result;
1496 if( aBus[i] == '[' )
1497 break;
1498 result.m_prefix += aBus[i];
1499 }
1500
1501 if( ++i >= length )
1502 return result;
1503 wxString number;
1504
1505 for( ; i < length; ++i )
1506 {
1507 if( aBus[i] == '.' && i + 1 < length && aBus[i + 1] == '.' )
1508 {
1509 number.ToLong( &result.m_begin );
1510 i += 2;
1511 break;
1512 }
1513 if( !digit( aBus[i] ) )
1514 return result;
1515 number += aBus[i];
1516 }
1517
1518 if( i >= length )
1519 return result;
1520 number.clear();
1521
1522 for( ; i < length; ++i )
1523 {
1524 if( aBus[i] == ']' )
1525 {
1526 number.ToLong( &result.m_end );
1527 ++i;
1528 break;
1529 }
1530 if( !digit( aBus[i] ) )
1531 return result;
1532 number += aBus[i];
1533 }
1534
1535 for( ; i < length; ++i )
1536 {
1537 if( aBus[i] == '}' )
1538 --nesting;
1539 else if( aPreTen || ( aBus[i] != '+' && aBus[i] != '-' && aBus[i] != 'P' && aBus[i] != 'N' ) )
1540 return result;
1541 }
1542
1543 result.m_valid = nesting == 0 && result.m_begin != result.m_end;
1544 return result;
1545}
1546
1547
1548static bool hasIncompatibleBusVector( const wxString& aText, bool aPreTen )
1549{
1550 wxString currentPrefix;
1551 bool current = NET_SETTINGS::ParseBusVector( aText, &currentPrefix, nullptr );
1552 const FROZEN_BUS_VECTOR legacy = frozenBusVector( aText, aPreTen );
1553
1554 if( current != legacy.m_valid || ( legacy.m_valid && ( legacy.m_begin == LONG_MAX || legacy.m_end == LONG_MAX ) )
1555 || ( current && legacy.m_prefix != currentPrefix ) )
1556 return true;
1557
1558 std::vector<wxString> members;
1559 if( NET_SETTINGS::ParseBusGroup( aText, nullptr, &members ) )
1560 {
1561 for( const wxString& member : members )
1562 {
1563 if( hasIncompatibleBusVector( member, aPreTen ) )
1564 return true;
1565 }
1566 }
1567 return false;
1568}
1569
1570
1571static int countIncompatibleBusLabels( const SCH_SCREEN* aScreen, bool aPreTen )
1572{
1573 int count = 0;
1574 const SCHEMATIC* schematic = aScreen->GetParent() && aScreen->GetParent()->Type() == SCHEMATIC_T
1575 ? static_cast<const SCHEMATIC*>( aScreen->GetParent() )
1576 : nullptr;
1577 visitScreenItems( aScreen,
1578 [&]( EDA_ITEM* item )
1579 {
1580 if( item->Type() != SCH_LABEL_T && item->Type() != SCH_GLOBAL_LABEL_T
1581 && item->Type() != SCH_HIER_LABEL_T && item->Type() != SCH_SHEET_PIN_T )
1582 return;
1583 const EDA_TEXT* text = dynamic_cast<const EDA_TEXT*>( item );
1584 const SCH_LABEL_BASE* label = dynamic_cast<const SCH_LABEL_BASE*>( item );
1585 if( !text || !label )
1586 return;
1587 bool incompatible = hasIncompatibleBusVector( text->GetText(), aPreTen );
1588 if( schematic && schematic->IsValid() )
1589 {
1590 for( const SCH_SHEET_PATH& path : schematic->BuildUnorderedSheetList() )
1591 {
1592 if( path.LastScreen() != aScreen )
1593 continue;
1594 wxString resolved = label->GetShownText( &path, FOR_NETNAME );
1595 incompatible |= hasIncompatibleBusVector( resolved, aPreTen )
1596 || hasPaddedBusVector( resolved );
1597 }
1598 }
1599 count += incompatible;
1600 } );
1601 return count;
1602}
1603
1604
1605static int countPaddedBusLabels( const SCH_SCREEN* aScreen )
1606{
1607 int count = 0;
1608 visitScreenItems( aScreen,
1609 [&]( EDA_ITEM* item )
1610 {
1611 if( item->Type() == SCH_LABEL_T || item->Type() == SCH_GLOBAL_LABEL_T
1612 || item->Type() == SCH_HIER_LABEL_T || item->Type() == SCH_DIRECTIVE_LABEL_T
1613 || item->Type() == SCH_SHEET_PIN_T )
1614 {
1615 if( EDA_TEXT* text = dynamic_cast<EDA_TEXT*>( item ) )
1616 count += hasPaddedBusVector( text->GetText() );
1617 }
1618 } );
1619 return count;
1620}
1621
1622
1623static void reportPaddedBuses( int aCount, COMPATIBILITY_REPORT& aReport )
1624{
1625 if( aCount )
1626 {
1627 aReport.Add( DOWNGRADE_BUCKET::BLOCK, _( "Zero-padded bus vectors" ),
1628 _( "The target removes the leading zeros from bus members and would change their net names." ),
1629 aCount );
1630 }
1631}
1632
1633
1634static void reportIncompatibleBuses( int aCount, COMPATIBILITY_REPORT& aReport )
1635{
1636 if( aCount )
1637 aReport.Add( DOWNGRADE_BUCKET::BLOCK, _( "Incompatible bus-vector parsing" ),
1638 _( "The target would reinterpret a literal as a bus, rename formatted bus members or overflow "
1639 "while expanding the range." ),
1640 aCount );
1641}
1642
1643
1644static int countSymbolCustomProperties( const LIB_SYMBOL* aSymbol )
1645{
1646 int count = 0;
1647 visitSymbolItems( aSymbol,
1648 [&]( EDA_ITEM* item )
1649 {
1650 count += item->GetCustomProperties().size();
1651 } );
1652 return count;
1653}
1654
1655
1656static int countCustomProperties( const SCH_SCREEN* aScreen )
1657{
1658 int count = 0;
1659 visitScreenItems( aScreen,
1660 [&]( EDA_ITEM* item )
1661 {
1662 count += item->GetCustomProperties().size();
1663 } );
1664 return count;
1665}
1666
1667
1669{
1670 visitSymbolItems( aSymbol,
1671 []( EDA_ITEM* item )
1672 {
1673 item->ClearCustomProperties();
1674 } );
1675}
1676
1677
1678static void dropCustomProperties( SCH_SCREEN* aScreen )
1679{
1680 visitScreenItems( aScreen,
1681 []( EDA_ITEM* item )
1682 {
1683 item->ClearCustomProperties();
1684 } );
1685
1686 for( SCH_ITEM* item : aScreen->Items().OfType( SCH_SYMBOL_T ) )
1687 {
1688 if( LIB_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item )->GetLibSymbolRef().get() )
1690 }
1691}
1692
1693
1694static int countAlternateSymbols( const SCH_SCREEN* aScreen )
1695{
1696 int count = 0;
1697
1698 for( SCH_ITEM* item : aScreen->Items().OfType( SCH_SYMBOL_T ) )
1699 {
1700 for( const SCH_SYMBOL_INSTANCE& instance : static_cast<SCH_SYMBOL*>( item )->GetInstances() )
1701 {
1702 for( const auto& [name, variant] : instance.m_Variants )
1703 count += variant.m_SymbolOverride.has_value();
1704 }
1705 }
1706
1707 return count;
1708}
1709
1710
1711static void dropAlternateSymbols( SCH_SCREEN* aScreen )
1712{
1713 for( SCH_ITEM* item : aScreen->Items().OfType( SCH_SYMBOL_T ) )
1714 {
1715 auto& instances =
1716 const_cast<std::vector<SCH_SYMBOL_INSTANCE>&>( static_cast<SCH_SYMBOL*>( item )->GetInstances() );
1717
1718 for( SCH_SYMBOL_INSTANCE& instance : instances )
1719 {
1720 for( auto& [name, variant] : instance.m_Variants )
1721 variant.m_SymbolOverride.reset();
1722 }
1723 }
1724}
1725
1726
1727static bool hasLineEndings( const SCH_ITEM* aItem )
1728{
1729 if( const SCH_SHAPE* shape = dynamic_cast<const SCH_SHAPE*>( aItem ) )
1730 return shape->GetStartEndingStyle() != LINE_ENDING_STYLE::NONE
1731 || shape->GetEndEndingStyle() != LINE_ENDING_STYLE::NONE;
1732
1733 if( const SCH_LINE* line = dynamic_cast<const SCH_LINE*>( aItem ) )
1734 return line->GetStartEndingStyle() != LINE_ENDING_STYLE::NONE
1735 || line->GetEndEndingStyle() != LINE_ENDING_STYLE::NONE;
1736
1737 return false;
1738}
1739
1740
1741static SCH_SHAPE lineEndingShape( const SCH_ITEM* aItem )
1742{
1743 if( const SCH_SHAPE* shape = dynamic_cast<const SCH_SHAPE*>( aItem ) )
1744 return *shape;
1745
1746 const SCH_LINE* line = static_cast<const SCH_LINE*>( aItem );
1747 SCH_SHAPE shape( SHAPE_T::SEGMENT );
1748 shape.SetStart( line->GetStartPoint() );
1749 shape.SetEnd( line->GetEndPoint() );
1750 STROKE_PARAMS stroke = line->GetStroke();
1751 stroke.SetWidth( line->GetPenWidth() );
1752 shape.SetStroke( stroke );
1753 shape.SetStartEnding( line->GetStartEnding() );
1754 shape.SetEndEnding( line->GetEndEnding() );
1755 shape.SetLayer( line->GetLayer() );
1756 return shape;
1757}
1758
1759
1760static bool canLowerLineEndings( const SCH_ITEM* aItem )
1761{
1762 SCH_SHAPE shape = lineEndingShape( aItem );
1763
1764 if( shape.IsClosed() )
1765 return true;
1766
1767 if( aItem->Type() == SCH_LINE_T && aItem->GetLayer() != LAYER_NOTES )
1768 return false;
1769
1770 if( shape.GetFillMode() != FILL_T::NO_FILL )
1771 return false;
1772
1773 auto solid = []( LINE_STYLE style )
1774 {
1775 return style == LINE_STYLE::SOLID || style == LINE_STYLE::DEFAULT;
1776 };
1777
1778 if( !solid( shape.GetLineStyle() ) )
1779 return false;
1780
1781 for( const LINE_ENDING* ending : { &shape.GetStartEnding(), &shape.GetEndEnding() } )
1782 {
1783 if( ending->GetStyle() == LINE_ENDING_STYLE::NONE )
1784 continue;
1785
1786 if( !solid( ending->GetStroke().GetLineStyle() ) )
1787 return false;
1788
1789 const auto& color = ending->GetStroke().GetColor();
1790
1791 if( color != COLOR4D::UNSPECIFIED && color != shape.GetStroke().GetColor() )
1792 return false;
1793 }
1794
1795 return shape.GetEffectiveWidth() > 0;
1796}
1797
1798
1799static void clearLineEndings( SCH_ITEM* aItem )
1800{
1801 if( SCH_SHAPE* shape = dynamic_cast<SCH_SHAPE*>( aItem ) )
1802 {
1803 shape->SetStartEnding( LINE_ENDING() );
1804 shape->SetEndEnding( LINE_ENDING() );
1805 }
1806 else if( SCH_LINE* line = dynamic_cast<SCH_LINE*>( aItem ) )
1807 {
1808 line->SetStartEnding( LINE_ENDING() );
1809 line->SetEndEnding( LINE_ENDING() );
1810 }
1811}
1812
1813
1814static int countSymbolLineEndings( const LIB_SYMBOL* aSymbol )
1815{
1816 int count = 0;
1817
1818 for( const SCH_ITEM& item : aSymbol->GetDrawItems() )
1819 count += hasLineEndings( &item );
1820
1821 return count;
1822}
1823
1824
1825static int countLineEndings( const SCH_SCREEN* aScreen )
1826{
1827 int count = 0;
1828 visitScreenItems( aScreen,
1829 [&]( EDA_ITEM* item )
1830 {
1831 if( SCH_ITEM* schItem = dynamic_cast<SCH_ITEM*>( item ) )
1832 count += hasLineEndings( schItem );
1833 } );
1834 return count;
1835}
1836
1837
1838static std::vector<SCH_SHAPE*> lineEndingPolygons( SCH_ITEM* aItem )
1839{
1840 SCH_SHAPE source = lineEndingShape( aItem );
1841 STROKE_PARAMS stroke = source.GetStroke();
1842 stroke.SetWidth( source.GetEffectiveWidth() );
1843 source.SetStroke( stroke );
1844 SHAPE_POLY_SET polygons;
1845 source.TransformWithLineEndingsToPolygon( polygons, 0, schIUScale.mmToIU( 0.01 ), ERROR_INSIDE );
1846 polygons.Simplify();
1847 polygons.Fracture();
1848 std::vector<SCH_SHAPE*> result;
1849
1850 for( int i = 0; i < polygons.OutlineCount(); ++i )
1851 {
1852 SCH_SHAPE* shape = new SCH_SHAPE( source );
1853 shape->SetShape( SHAPE_T::POLY );
1854 shape->SetPolyShape( SHAPE_POLY_SET( polygons.Outline( i ) ) );
1855 shape->SetStroke( STROKE_PARAMS( -1, LINE_STYLE::SOLID ) );
1858 shape->SetFillColor( stroke.GetColor() );
1859 shape->ClearCustomProperties();
1860 clearLineEndings( shape );
1861 const_cast<KIID&>( shape->m_Uuid ) = i == 0 ? aItem->m_Uuid : KIID();
1862 result.push_back( shape );
1863 }
1864
1865 return result;
1866}
1867
1868
1869static void lowerSymbolLineEndings( LIB_SYMBOL* aSymbol )
1870{
1871 std::vector<SCH_ITEM*> items;
1872
1873 for( SCH_ITEM& item : aSymbol->GetDrawItems() )
1874 {
1875 if( hasLineEndings( &item ) )
1876 items.push_back( &item );
1877 }
1878
1879 for( SCH_ITEM* item : items )
1880 {
1881 if( lineEndingShape( item ).IsClosed() )
1882 clearLineEndings( item );
1883 else if( canLowerLineEndings( item ) )
1884 {
1885 std::vector<SCH_SHAPE*> shapes = lineEndingPolygons( item );
1886
1887 if( shapes.empty() )
1888 {
1889 clearLineEndings( item );
1890 continue;
1891 }
1892
1893 for( SCH_SHAPE* shape : shapes )
1894 {
1895 shape->SetParent( aSymbol );
1896 aSymbol->AddDrawItem( shape );
1897 }
1898
1899 aSymbol->RemoveDrawItem( item );
1900 }
1901 }
1902}
1903
1904
1905static void lowerLineEndings( SCH_SCREEN* aScreen )
1906{
1907 std::vector<SCH_ITEM*> items;
1908
1909 for( SCH_ITEM* item : aScreen->Items() )
1910 {
1911 if( hasLineEndings( item ) )
1912 items.push_back( item );
1913 }
1914
1915 for( SCH_ITEM* item : items )
1916 {
1917 if( lineEndingShape( item ).IsClosed() )
1918 clearLineEndings( item );
1919 else if( canLowerLineEndings( item ) )
1920 {
1921 SCH_GROUP* group = static_cast<SCH_GROUP*>( item->GetParentGroup() );
1922 std::vector<SCH_SHAPE*> shapes = lineEndingPolygons( item );
1923
1924 if( shapes.empty() )
1925 {
1926 clearLineEndings( item );
1927 continue;
1928 }
1929
1930 if( group )
1931 group->RemoveItem( item );
1932
1933 aScreen->Remove( item );
1934
1935 for( SCH_SHAPE* shape : shapes )
1936 {
1937 shape->SetParentGroup( nullptr );
1938 aScreen->Append( shape );
1939
1940 if( group )
1941 group->AddItem( shape );
1942 }
1943
1944 delete item;
1945 }
1946 }
1947
1948 for( const auto& [name, symbol] : aScreen->GetLibSymbols() )
1949 {
1950 if( symbol )
1951 lowerSymbolLineEndings( symbol );
1952 }
1953}
1954
1955
1956static void dropSymbolLineEndings( LIB_SYMBOL* aSymbol )
1957{
1958 for( SCH_ITEM& item : aSymbol->GetDrawItems() )
1959 clearLineEndings( &item );
1960}
1961
1962
1963static void dropLineEndings( SCH_SCREEN* aScreen )
1964{
1965 visitScreenItems( aScreen,
1966 []( EDA_ITEM* item )
1967 {
1968 if( SCH_ITEM* schItem = dynamic_cast<SCH_ITEM*>( item ) )
1969 clearLineEndings( schItem );
1970 } );
1971}
1972
1973
1974// Same pattern as board_downgrade.cpp. One list drives both the report and the transform.
1976{
1979 wxString m_feature;
1980 wxString m_detail;
1981 std::function<int( const SCH_SCREEN* )> m_count;
1982 std::function<void( SCH_SCREEN* )> m_apply;
1983 std::function<int( const LIB_SYMBOL* )> m_countSymbol = {};
1984 std::function<void( LIB_SYMBOL* )> m_applySymbol = {};
1985 std::function<void( SCH_SCREEN* )> m_omit = {};
1986 std::function<void( LIB_SYMBOL* )> m_omitSymbol = {};
1987 wxString m_omitDetail = {};
1988};
1989
1990
1991static std::vector<SCREEN_RULE> screenRules()
1992{
1993 return {
1994 { SCH_VER_CUSTOM_PROPERTIES, DOWNGRADE_BUCKET::DROP, _( "Custom user properties" ),
1995 _( "Custom user-property metadata is removed." ), countCustomProperties, dropCustomProperties,
1997 { SCH_VER_ALTERNATE_SYMBOLS, DOWNGRADE_BUCKET::DROP, _( "Alternate variant symbols" ),
1998 _( "Alternate-symbol choices in retained variant definitions are removed. The base symbol is unchanged." ),
2000 // Bake before the cache-wide lowering rules so the new entries receive those too.
2003 _( "Selected custom body styles" ),
2004 _( "The selected drawing and pins are baked into a separate base-style symbol." ),
2007 {},
2008 {},
2010 {},
2011 _( "Symbols selecting a custom body style are omitted, including their pins. Connectivity may change." ) },
2012 { SCH_VER_BODY_STYLES, DOWNGRADE_BUCKET::BLOCK, _( "Unresolved custom body styles" ),
2013 _( "The selected drawing cannot be preserved without its library symbol." ), countUnresolvedCustomBodyStyles,
2014 nullptr },
2017 _( "Locked items" ),
2018 _( "The lock is not kept." ),
2021 {},
2022 {},
2024 {},
2025 _( "Lock metadata is removed; the supported items are kept." ) },
2026 { SCH_VER_LINE_ENDINGS, DOWNGRADE_BUCKET::LOWER, _( "Line endings" ),
2027 _( "Solid line bodies and endings are approximated by filled polygons." ), countLineEndings, lowerLineEndings,
2029 _( "Line-ending embellishments are removed. The original supported body is kept." ) },
2030 { SCH_VER_ELLIPSE, DOWNGRADE_BUCKET::LOWER, _( "Ellipse graphics" ), _( "Approximated by a polygon." ),
2032 _( "Ellipse graphics are omitted instead of approximated." ) },
2033 { SCH_VER_SHAPE_HATCH, DOWNGRADE_BUCKET::LOWER, _( "Hatched shape fills" ), _( "Converted to a solid fill." ),
2035 omitSymbolHatchedFills, _( "The hatch fill is omitted; the supported outline is kept." ) },
2036 { SCH_VER_PIN_MAPS, DOWNGRADE_BUCKET::DROP, _( "Pin-to-pad maps" ),
2037 _( "No equivalent in the target. The map is removed." ), countPinMaps, dropPinMaps, countSymbolPinMaps,
2041 _( "Groups" ),
2042 _( "Grouping is removed; the items are kept." ),
2044 ungroupAll,
2045 {},
2046 {},
2047 ungroupAll,
2048 {},
2049 _( "Group metadata is removed; the supported items are kept." ) },
2050 { SCH_VER_JUMPER_PINS, DOWNGRADE_BUCKET::DROP, _( "Jumper pin groups" ),
2051 _( "Jumper pin groupings are removed." ), countJumperPinGroups, dropJumperPinGroups, countSymbolJumperPins,
2053 { SCH_VER_BODY_STYLES, DOWNGRADE_BUCKET::DROP, _( "Named body styles" ),
2054 _( "Custom names are removed. The target can select only the base and De Morgan styles." ),
2056 { SCH_VER_POSITION_EXCLUSIONS, DOWNGRADE_BUCKET::DROP, _( "Position file exclusions" ),
2057 _( "The target cannot exclude schematic symbols from position files." ), countPositionExclusions,
2059 { SCH_VER_ROUNDED_RECT, DOWNGRADE_BUCKET::LOWER, _( "Rounded rectangles" ), _( "Approximated by a polygon." ),
2061 omitSymbolRoundedRects, _( "Rounded rectangles are omitted instead of approximated." ) },
2062 { SCH_VER_LOCAL_POWER, DOWNGRADE_BUCKET::BLOCK, _( "Local power symbols" ),
2063 _( "The target would treat it as global power and merge nets across sheets." ), countLocalPowerSymbols,
2064 nullptr, countSymbolLocalPower },
2065 { SCH_VER_RULE_AREA_FLAGS, DOWNGRADE_BUCKET::DROP, _( "Rule area flags" ),
2066 _( "DNP and exclusion flags on rule areas are removed." ), countRuleAreaFlags, dropRuleAreaFlags },
2067 { SCH_VER_STACKED_PIN_ESCAPES, DOWNGRADE_BUCKET::BLOCK, _( "Escaped stacked pin numbers" ),
2068 _( "The target misreads the escaping and would rename the pins." ), countEscapedStackedPins, nullptr,
2070 { SCH_VER_STACKED_PINS, DOWNGRADE_BUCKET::BLOCK, _( "Stacked pin numbers" ),
2071 _( "The target reads the brackets as one literal pin number and the netlist changes." ), countStackedPins,
2072 nullptr, countSymbolStackedPins },
2073 };
2074}
2075
2076
2078 bool aDropInsteadOfApproximate )
2079{
2080 // The rules were written against a specific format. Warn developers when the format has
2081 // moved on, since a frozen writer silently omits features that have no rule yet.
2083 wxT( "Schematic format changed. Review the downgrade rules and denylist, then "
2084 "update SCH_DOWNGRADE_COVERED." ) );
2085
2086 COMPATIBILITY_REPORT report;
2087
2089 {
2091 reportPaddedBuses( countPaddedBusLabels( aScreen ), report );
2094 }
2095
2096 if( aTarget.m_schVersion < SCH_VER_LINE_ENDINGS && !aDropInsteadOfApproximate )
2097 {
2098 int blocked = 0;
2099 visitScreenItems( aScreen,
2100 [&]( EDA_ITEM* item )
2101 {
2102 if( SCH_ITEM* schItem = dynamic_cast<SCH_ITEM*>( item ) )
2103 blocked += hasLineEndings( schItem ) && !canLowerLineEndings( schItem );
2104 } );
2105
2106 if( blocked )
2107 report.Add( DOWNGRADE_BUCKET::BLOCK, _( "Styled line endings" ),
2108 _( "Dashed, filled, differently colored or non-graphical endings cannot be approximated "
2109 "faithfully. Explicitly dropping approximations keeps the supported body." ),
2110 blocked );
2111 }
2112
2113 for( const SCREEN_RULE& rule : screenRules() )
2114 {
2115 if( aTarget.m_schVersion >= rule.m_introducedIn )
2116 continue;
2117
2118 if( int count = rule.m_count( aScreen ); count > 0 )
2119 {
2120 bool omit = aDropInsteadOfApproximate && rule.m_bucket == DOWNGRADE_BUCKET::LOWER;
2121 report.Add( omit ? DOWNGRADE_BUCKET::DROP : rule.m_bucket, rule.m_feature,
2122 omit ? rule.m_omitDetail : rule.m_detail, count );
2123 }
2124 }
2125
2126 return report;
2127}
2128
2129
2130// The stored scale compensates for the truncated pixels/cm PPI that targets before 20260623
2131// compute, so it must be rewound. The catalog keeps images with a silent correction, so this
2132// is not a table rule and adds no report row.
2134{
2135 for( SCH_ITEM* item : aScreen->Items().OfType( SCH_BITMAP_T ) )
2136 {
2137 REFERENCE_IMAGE& ref = static_cast<SCH_BITMAP*>( item )->GetReferenceImage();
2138 int legacyPPI = ref.GetImage().GetLegacyPPI();
2139
2140 if( legacyPPI > 0 && ref.GetImage().GetPPI() != legacyPPI )
2141 ref.SetImageScale( ref.GetImageScale() * legacyPPI / ref.GetImage().GetPPI() );
2142 }
2143}
2144
2145
2147 const DOWNGRADE_TARGET& aTarget )
2148{
2149 COMPATIBILITY_REPORT report;
2150
2151 // A flat hierarchy (20251012) has several top-level sheets. The target opens a single root
2152 // sheet only, so refuse rather than write a project it cannot represent.
2153 if( aTarget.m_schVersion < SCH_VER_FLAT_HIERARCHY && aSchematic.GetTopLevelSheets().size() > 1 )
2154 {
2155 report.Add( DOWNGRADE_BUCKET::BLOCK, _( "Multiple top-level sheets" ),
2156 _( "The target can only open a single root sheet." ), 1 );
2157 }
2158
2159 // A variant that selects a different library symbol cannot be represented by a target that
2160 // keeps variants but not the choice. The variant would survive and quietly resolve to the base
2161 // symbol, changing that variant's pins. A target that removes variants loses the choice along
2162 // with them, which the screen rule reports as a drop.
2164 {
2165 SCH_SCREENS screens( aSchematic.Root() );
2166 int count = 0;
2167
2168 for( SCH_SCREEN* screen = screens.GetFirst(); screen; screen = screens.GetNext() )
2169 count += countAlternateSymbols( screen );
2170
2171 if( count > 0 )
2172 {
2173 report.Add( DOWNGRADE_BUCKET::BLOCK, _( "Unrepresentable alternate variant symbols" ),
2174 _( "The target keeps variants but cannot store a variant's alternate symbol, so the "
2175 "variant would resolve to the base symbol." ),
2176 count );
2177 }
2178 }
2179
2181 {
2182 int count = 0;
2183 int incompatible = 0;
2184
2185 for( const auto& alias : aSchematic.GetAllBusAliases() )
2186 {
2187 for( const wxString& member : alias->Members() )
2188 {
2189 count += hasPaddedBusVector( member );
2190 incompatible +=
2192 }
2193 }
2194
2195 reportPaddedBuses( count, report );
2196 reportIncompatibleBuses( incompatible, report );
2197 }
2198
2199 return report;
2200}
2201
2202
2203void DowngradeScreenInPlace( SCH_SCREEN* aScreen, const DOWNGRADE_TARGET& aTarget, bool aDropInsteadOfApproximate )
2204{
2207
2208 if( aTarget.m_schVersion < SCH_VER_IMAGE_PPI )
2210
2211 for( const SCREEN_RULE& rule : screenRules() )
2212 {
2213 if( aTarget.m_schVersion >= rule.m_introducedIn )
2214 continue;
2215
2216 bool omit = aDropInsteadOfApproximate && rule.m_bucket == DOWNGRADE_BUCKET::LOWER;
2217 const auto& apply = omit ? rule.m_omit : rule.m_apply;
2218 wxASSERT_MSG( !omit || apply, wxT( "A LOWER rule needs an omission transform." ) );
2219
2220 if( apply )
2221 apply( aScreen );
2222 }
2223}
2224
2225
2226// Symbol-scoped subset for standalone .kicad_sym files the screen pass never sees.
2228 bool aDropInsteadOfApproximate )
2229{
2231 wxT( "Symbol library format changed. Review the symbol downgrade path, then "
2232 "update SYMLIB_DOWNGRADE_COVERED." ) );
2233
2234 COMPATIBILITY_REPORT report;
2235
2238
2239 if( aTarget.m_symLibVersion < SYMLIB_VER_LINE_ENDINGS && !aDropInsteadOfApproximate )
2240 {
2241 int blocked = 0;
2242
2243 for( const SCH_ITEM& item : aSymbol->GetDrawItems() )
2244 blocked += hasLineEndings( &item ) && !canLowerLineEndings( &item );
2245
2246 if( blocked )
2247 report.Add( DOWNGRADE_BUCKET::BLOCK, _( "Styled line endings" ),
2248 _( "Dashed, filled or differently colored endings cannot be approximated faithfully. "
2249 "Explicitly dropping approximations keeps the supported body." ),
2250 blocked );
2251 }
2252
2253 for( const SCREEN_RULE& rule : screenRules() )
2254 {
2255 if( aTarget.m_schVersion >= rule.m_introducedIn || !rule.m_countSymbol )
2256 continue;
2257
2258 if( int count = rule.m_countSymbol( aSymbol ); count > 0 )
2259 {
2260 bool omit = aDropInsteadOfApproximate && rule.m_bucket == DOWNGRADE_BUCKET::LOWER;
2261 report.Add( omit ? DOWNGRADE_BUCKET::DROP : rule.m_bucket, rule.m_feature,
2262 omit ? rule.m_omitDetail : rule.m_detail, count );
2263 }
2264 }
2265
2266 return report;
2267}
2268
2269
2270void DowngradeLibSymbolInPlace( LIB_SYMBOL* aSymbol, const DOWNGRADE_TARGET& aTarget, bool aDropInsteadOfApproximate )
2271{
2274
2275 for( const SCREEN_RULE& rule : screenRules() )
2276 {
2277 if( aTarget.m_schVersion >= rule.m_introducedIn )
2278 continue;
2279
2280 bool omit = aDropInsteadOfApproximate && rule.m_bucket == DOWNGRADE_BUCKET::LOWER;
2281 const auto& apply = omit ? rule.m_omitSymbol : rule.m_applySymbol;
2282 wxASSERT_MSG( !omit || !rule.m_applySymbol || apply,
2283 wxT( "A symbol LOWER rule needs an omission transform." ) );
2284
2285 if( apply )
2286 apply( aSymbol );
2287 }
2288}
2289
2290
2291// Node heads dated by the format that introduced them. The versioned writers cannot emit these,
2292// so this gate is a safety net against a wrong or miswired writer. To add a token, add a row
2293// with its format version.
2294static constexpr DATED_TOKEN SCH_TOKENS[] = {
2295 { "symbol_override", SCH_VER_ALTERNATE_SYMBOLS },
2296 { "start_shape", SYMLIB_VER_LINE_ENDINGS },
2297 { "end_shape", SYMLIB_VER_LINE_ENDINGS },
2298 { "custom_property", SCH_VER_CUSTOM_PROPERTIES },
2299 { "jumper_pin_groups", SCH_VER_JUMPER_PINS },
2300 { "group", SCH_VER_GROUPS },
2301 { "body_style", SCH_VER_BODY_STYLES },
2302 { "body_styles", SCH_VER_BODY_STYLES },
2303 { "variant", SCH_VER_VARIANTS },
2304 { "field", SCH_VER_VARIANTS },
2305 // Dated by the group lock, the oldest legal use. The unlock rule strips all others.
2306 { "locked", SCH_VER_GROUPS },
2307 { "ellipse", SCH_VER_ELLIPSE },
2308 { "ellipse_arc", SCH_VER_ELLIPSE },
2309 { "rotation_angle", SCH_VER_ELLIPSE },
2310 { "net_chain", SCH_VER_NET_CHAINS },
2311 { "net_class", SCH_VER_NET_CHAINS },
2312 { "nets", SCH_VER_NET_CHAINS },
2313 { "passthrough", SCH_VER_NET_CHAINS },
2314 { "pin_map", SCH_VER_PIN_MAPS },
2315 { "pin_maps", SCH_VER_PIN_MAPS },
2316 { "pin_map_override", SCH_VER_PIN_MAPS },
2317 { "named_map", SCH_VER_PIN_MAPS },
2318 { "associated_footprints", SCH_VER_PIN_MAPS },
2319 { "entry", SCH_VER_PIN_MAPS },
2320 { "map", SCH_VER_PIN_MAPS },
2321 { "edit", SCH_VER_PIN_MAPS },
2322};
2323
2324// Bareword values the target cannot parse. The node scan cannot see these.
2325static constexpr DATED_TOKEN SCH_VALUES[] = {
2326 { "hatch", SCH_VER_SHAPE_HATCH },
2327 { "reverse_hatch", SCH_VER_SHAPE_HATCH },
2328 { "cross_hatch", SCH_VER_SHAPE_HATCH },
2329};
2330
2331
2332// Each target has its own writer, extracted from that release. A target without a writer writes
2333// nothing, and the fail-closed check downstream refuses the export.
2334void SaveSchematicForTarget( SCH_SHEET* aSheet, SCHEMATIC* aSchematic, const wxString& aPath,
2335 const DOWNGRADE_TARGET& aTarget )
2336{
2338 SCH_WRITER_V10().SaveSchematicFile( aPath, aSheet, aSchematic );
2339 else if( aTarget.m_schVersion == SCH_WRITER_V9::FORMAT_VERSION )
2340 SCH_WRITER_V9().SaveSchematicFile( aPath, aSheet, aSchematic );
2341 else
2342 wxFAIL_MSG( wxT( "No writer for this downgrade target." ) );
2343}
2344
2345
2346void SaveSymbolLibraryForTarget( const std::vector<LIB_SYMBOL*>& aSymbols, const wxString& aPath,
2347 const DOWNGRADE_TARGET& aTarget )
2348{
2350 SCH_WRITER_V10().SaveSymbolLibrary( aPath, aSymbols );
2352 SCH_WRITER_V9().SaveSymbolLibrary( aPath, aSymbols );
2353 else
2354 wxFAIL_MSG( wxT( "No writer for this downgrade target." ) );
2355}
2356
2357
2362
2363
2368
2369
2370wxString FindUnsupportedSchToken( const wxString& aSerialized, const DOWNGRADE_TARGET& aTarget )
2371{
2373 return wxEmptyString;
2374
2375 return FindUnsupportedToken( aSerialized, SCH_TOKENS, std::size( SCH_TOKENS ), SCH_VALUES, std::size( SCH_VALUES ),
2376 aTarget.m_schVersion );
2377}
2378
2379
2380// The effective values of one symbol under one sheet instance and the chosen variant.
2382{
2383 bool m_dnp = false;
2384 bool m_excludedFromSim = false;
2385 bool m_excludedFromBOM = false;
2388 std::map<wxString, wxString> m_fieldOverrides;
2390 std::optional<LIB_ID> m_symbolOverride;
2392
2393 bool operator==( const SYMBOL_VARIANT_SNAPSHOT& ) const = default;
2394};
2395
2396
2398 const wxString& aVariantName )
2399{
2401
2402 snap.m_dnp = aSymbol->GetDNP( &aPath, aVariantName );
2403 snap.m_excludedFromSim = aSymbol->GetExcludedFromSim( &aPath, aVariantName );
2404 snap.m_excludedFromBOM = aSymbol->GetExcludedFromBOM( &aPath, aVariantName );
2405 snap.m_excludedFromBoard = aSymbol->GetExcludedFromBoard( &aPath, aVariantName );
2406 snap.m_excludedFromPosFiles = aSymbol->GetExcludedFromPosFiles( &aPath, aVariantName );
2407 snap.m_pinMap = aSymbol->GetPinMapOverride( &aPath, aVariantName );
2408
2409 for( const SCH_SYMBOL_INSTANCE& instance : aSymbol->GetInstances() )
2410 {
2411 if( instance.m_Path != aPath.Path() )
2412 continue;
2413
2414 auto it = instance.m_Variants.find( aVariantName );
2415
2416 if( it != instance.m_Variants.end() )
2417 snap.m_symbolOverride = it->second.m_SymbolOverride;
2418
2419 break;
2420 }
2421
2422 if( snap.m_symbolOverride && *snap.m_symbolOverride != aSymbol->GetLibId() && aSymbol->Schematic()
2423 && aSymbol->Schematic()->IsValid() )
2424 snap.m_alternate = aSymbol->GetVariantLibSymbol( aVariantName, aPath );
2425
2426 // The native resolver includes alternate-library values and explicit empty overrides.
2427 for( const SCH_FIELD& field : aSymbol->GetFields() )
2428 {
2429 if( field.GetId() != FIELD_T::REFERENCE )
2430 snap.m_fieldOverrides[field.GetName()] = aSymbol->GetFieldText( field.GetName(), &aPath, aVariantName );
2431 }
2432
2433 return snap;
2434}
2435
2436
2438{
2439 bool m_dnp = false;
2440 bool m_excludedFromSim = false;
2441 bool m_excludedFromBOM = false;
2443
2444 std::map<wxString, wxString> m_fieldOverrides;
2445
2446 bool operator==( const SHEET_VARIANT_SNAPSHOT& ) const = default;
2447};
2448
2449
2451 const wxString& aVariantName )
2452{
2454
2455 snap.m_dnp = aSheet->GetDNP( &aPath, aVariantName );
2456 snap.m_excludedFromSim = aSheet->GetExcludedFromSim( &aPath, aVariantName );
2457 snap.m_excludedFromBOM = aSheet->GetExcludedFromBOM( &aPath, aVariantName );
2458 snap.m_excludedFromBoard = aSheet->GetExcludedFromBoard( &aPath, aVariantName );
2459
2460 for( const SCH_SHEET_INSTANCE& instance : aSheet->GetInstances() )
2461 {
2462 if( instance.m_Path != aPath.Path() )
2463 continue;
2464
2465 auto it = instance.m_Variants.find( aVariantName );
2466
2467 if( it != instance.m_Variants.end() )
2468 snap.m_fieldOverrides = it->second.m_Fields;
2469
2470 break;
2471 }
2472
2473 return snap;
2474}
2475
2476
2477bool FlattenSchematicVariant( SCHEMATIC& aSchematic, const wxString& aVariantName, wxString* aConflict )
2478{
2479 std::map<SCH_SYMBOL*, SYMBOL_VARIANT_SNAPSHOT> symbols;
2480 std::map<SCH_SHEET*, SHEET_VARIANT_SNAPSHOT> sheets;
2481 std::map<SCH_SYMBOL*, SCH_SCREEN*> screens;
2482
2483 for( const SCH_SHEET_PATH& path : aSchematic.BuildUnorderedSheetList() )
2484 {
2485 SCH_SCREEN* screen = path.LastScreen();
2486
2487 if( !screen )
2488 continue;
2489
2490 for( SCH_ITEM* item : screen->Items().OfType( SCH_SYMBOL_T ) )
2491 {
2492 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
2493 SYMBOL_VARIANT_SNAPSHOT snap = snapshotSymbol( symbol, path, aVariantName );
2494
2495 if( snap.m_symbolOverride && *snap.m_symbolOverride != symbol->GetLibId()
2496 && ( !snap.m_alternate || !symbol->GetLibSymbolRef() || symbol->IsMissingLibSymbol() ) )
2497 {
2498 if( aConflict )
2499 {
2500 *aConflict = wxString::Format(
2501 _( "Alternate symbol '%s' cannot be resolved and validated against the base symbol." ),
2502 snap.m_symbolOverride->Format().wx_str() );
2503 }
2504
2505 return false;
2506 }
2507
2508 auto [it, inserted] = symbols.emplace( symbol, snap );
2509 screens.emplace( symbol, screen );
2510
2511 // A symbol shared between sheet instances must resolve the same everywhere, or
2512 // the single set of base attributes cannot hold the result.
2513 if( !inserted && !( it->second == snap ) )
2514 {
2515 if( aConflict )
2516 *aConflict = wxString::Format(
2517 _( "Shared symbol '%s' has different effective values between sheet instances." ),
2518 symbol->GetField( FIELD_T::REFERENCE )->GetText() );
2519
2520 return false;
2521 }
2522 }
2523
2524 if( SCH_SHEET* sheet = path.Last() )
2525 {
2526 SHEET_VARIANT_SNAPSHOT snap = snapshotSheet( sheet, path, aVariantName );
2527
2528 auto [it, inserted] = sheets.emplace( sheet, snap );
2529
2530 if( !inserted && !( it->second == snap ) )
2531 {
2532 if( aConflict )
2533 *aConflict = wxString::Format(
2534 _( "Shared sheet '%s' has different effective values between sheet instances." ),
2535 sheet->GetName() );
2536
2537 return false;
2538 }
2539 }
2540 }
2541
2542 for( auto& [symbol, snap] : symbols )
2543 {
2544 if( snap.m_alternate )
2545 {
2546 SCH_SCREEN* screen = screens.at( symbol );
2547 std::unique_ptr<LIB_SYMBOL> baked = snap.m_alternate->Flatten();
2548 const wxString baseName = baked->GetName() + wxT( "__KiCad_variant" );
2549 wxString name = baseName;
2550
2551 for( int suffix = 1; screen->GetLibSymbols().count( name ); ++suffix )
2552 name = wxString::Format( wxT( "%s_%d" ), baseName, suffix );
2553
2554 baked->SetName( name );
2555 baked->SetLibId( LIB_ID( wxEmptyString, name ) );
2556 screen->Remove( symbol );
2557 symbol->SetLibId( *snap.m_symbolOverride );
2558 symbol->SetSchSymbolLibraryName( name );
2559 symbol->SetLibSymbol( new LIB_SYMBOL( *baked ) );
2560 screen->AddLibSymbol( baked.release() );
2561 screen->Append( symbol );
2562 }
2563
2564 symbol->SetDNP( snap.m_dnp );
2565 symbol->SetExcludedFromSim( snap.m_excludedFromSim );
2566 symbol->SetExcludedFromBOM( snap.m_excludedFromBOM );
2567 symbol->SetExcludedFromBoard( snap.m_excludedFromBoard );
2568 symbol->SetExcludedFromPosFiles( snap.m_excludedFromPosFiles );
2569 symbol->SetPinMapOverride( snap.m_pinMap );
2570
2571 auto& instances = const_cast<std::vector<SCH_SYMBOL_INSTANCE>&>( symbol->GetInstances() );
2572
2573 for( SCH_SYMBOL_INSTANCE& instance : instances )
2574 {
2575 auto variant = instance.m_Variants.find( aVariantName );
2576
2577 if( variant != instance.m_Variants.end() )
2578 {
2579 variant->second.m_SymbolOverride.reset();
2580 variant->second.m_PinMapOverride = {};
2581 }
2582 }
2583
2584 for( const auto& [name, value] : snap.m_fieldOverrides )
2585 {
2586 for( SCH_FIELD& field : symbol->GetFields() )
2587 {
2588 if( field.GetId() != FIELD_T::REFERENCE && field.GetName() == name )
2589 field.SetText( value );
2590 }
2591 }
2592 }
2593
2594 for( auto& [sheet, snap] : sheets )
2595 {
2596 sheet->SetDNP( snap.m_dnp );
2597 sheet->SetExcludedFromSim( snap.m_excludedFromSim );
2598 sheet->SetExcludedFromBOM( snap.m_excludedFromBOM );
2599 sheet->SetExcludedFromBoard( snap.m_excludedFromBoard );
2600
2601 for( const auto& [name, value] : snap.m_fieldOverrides )
2602 {
2603 for( SCH_FIELD& field : sheet->GetFields() )
2604 {
2605 bool mandatory = field.GetId() == FIELD_T::SHEET_NAME || field.GetId() == FIELD_T::SHEET_FILENAME;
2606
2607 if( !mandatory && field.GetName() == name )
2608 field.SetText( value );
2609 }
2610 }
2611 }
2612
2613 return true;
2614}
const char * name
@ ERROR_INSIDE
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:130
int GetPPI() const
int GetLegacyPPI() const
Recompute the PPI the way it was computed before the pixels/cm truncation fix.
static const COLOR4D UNSPECIFIED
For legacy support; used as a value to indicate color hasn't been set yet.
Definition color4d.h:400
What an export to a given target will keep, approximate, drop, or block.
void Add(DOWNGRADE_BUCKET aBucket, const wxString &aFeature, const wxString &aDetail=wxEmptyString, int aCount=1)
A set of EDA_ITEMs (i.e., without duplicates).
Definition eda_group.h:43
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:98
void ClearCustomProperties()
Definition eda_item.h:270
const KIID m_Uuid
Definition eda_item.h:599
virtual EDA_GROUP * GetParentGroup() const
Definition eda_item.h:116
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
EDA_ITEM * GetParent() const
Definition eda_item.h:112
const std::map< wxString, wxString > & GetCustomProperties() const
Custom user-defined string key/value properties attached to this item.
Definition eda_item.h:242
virtual void SetEnd(const VECTOR2I &aEnd)
Definition eda_shape.h:329
int GetEllipseMinorRadius() const
Definition eda_shape.h:395
const VECTOR2I & GetEllipseCenter() const
Definition eda_shape.h:377
void SetEndEnding(const LINE_ENDING &aEnding)
Definition eda_shape.h:181
EDA_ANGLE GetEllipseEndAngle() const
Definition eda_shape.h:423
FILL_T GetFillMode() const
Definition eda_shape.h:148
virtual void SetCornerRadius(int aRadius)
int GetEllipseMajorRadius() const
Definition eda_shape.h:386
EDA_ANGLE GetEllipseRotation() const
Definition eda_shape.h:404
SHAPE_T GetShape() const
Definition eda_shape.h:175
void SetStartEnding(const LINE_ENDING &aEnding)
Definition eda_shape.h:178
bool IsHatchedFill() const
Definition eda_shape.h:130
void SetFillColor(const COLOR4D &aColor)
Definition eda_shape.h:160
bool IsClosed() const
LINE_STYLE GetLineStyle() const
virtual void SetShape(SHAPE_T aShape)
Definition eda_shape.h:174
std::vector< VECTOR2I > GetRectCorners() const
EDA_ANGLE GetEllipseStartAngle() const
Definition eda_shape.h:414
const LINE_ENDING & GetStartEnding() const
Definition eda_shape.h:177
const LINE_ENDING & GetEndEnding() const
Definition eda_shape.h:180
void SetPolyPoints(const std::vector< VECTOR2I > &aPoints)
int GetCornerRadius() const
void SetFillMode(FILL_T aFill)
virtual void SetPolyShape(const SHAPE_POLY_SET &aShape)
Definition eda_shape.h:514
virtual void SetStart(const VECTOR2I &aStart)
Definition eda_shape.h:279
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition eda_text.h:94
EE_TYPE OfType(KICAD_T aType) const
Definition sch_rtree.h:248
bool IsEmpty() const
Definition kiid.h:46
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:45
UTF8 Format() const
Definition lib_id.cpp:132
Define a library symbol object.
Definition lib_symbol.h:114
const LIB_ID & GetLibId() const override
Definition lib_symbol.h:190
void SetDuplicatePinNumbersAreJumpers(bool aEnabled)
Definition lib_symbol.h:885
bool IsMultiBodyStyle() const override
Definition lib_symbol.h:902
JUMPER_GROUP_SET & JumperPinGroups()
Each jumper pin group is a set of pin numbers that should be treated as internally connected.
Definition lib_symbol.h:891
void SetAssociatedFootprints(std::vector< ASSOCIATED_FOOTPRINT > aList)
Definition lib_symbol.h:272
void SetBodyStyleNames(const std::vector< wxString > &aBodyStyleNames)
Definition lib_symbol.h:913
LIB_ITEMS_CONTAINER & GetDrawItems()
Return a reference to the draw item list.
Definition lib_symbol.h:843
bool IsLocalPower() const override
void RemoveDrawItem(SCH_ITEM *aItem)
Remove draw aItem from list.
void SetHasDeMorganBodyStyles(bool aFlag)
Definition lib_symbol.h:912
void SetPinMaps(const PIN_MAP_SET &aPinMaps)
Definition lib_symbol.h:268
bool HasDeMorganBodyStyles() const override
const PIN_MAP_SET & GetPinMaps() const
Pin-to-pad mapping (issue #2282).
Definition lib_symbol.h:266
std::unique_ptr< LIB_SYMBOL > Flatten() const
Return a flattened symbol inheritance to the caller.
void SetLibId(const LIB_ID &aLibId)
bool GetDuplicatePinNumbersAreJumpers() const
Definition lib_symbol.h:884
std::vector< SCH_PIN * > GetGraphicalPins(int aUnit=0, int aBodyStyle=0) const override
Graphical pins: Return schematic pin objects as drawn (unexpanded), filtered by unit/body.
void AddDrawItem(SCH_ITEM *aItem, bool aSort=true)
Add a new draw aItem to the draw object list and sort according to aSort.
virtual void SetName(const wxString &aName)
const std::vector< ASSOCIATED_FOOTPRINT > & GetAssociatedFootprints() const
Definition lib_symbol.h:270
Decorative shape (arrowhead, circle, square) at the start or end of a graphic line,...
Definition line_ending.h:62
static bool ParseBusGroup(const wxString &aGroup, wxString *name, std::vector< wxString > *aMemberList, size_t *aPrefixEnd=nullptr)
Parse a bus group label into the name and a list of components.
static bool ParseBusVector(const wxString &aBus, wxString *aName, std::vector< wxString > *aMemberList)
Parse a bus vector (e.g.
A symbol-owned ordered set of named pin maps.
Definition pin_map.h:124
Container for project specific data.
Definition project.h:63
virtual std::map< wxString, wxString > & GetTextVars() const
Definition project.cpp:126
A REFERENCE_IMAGE is a wrapper around a BITMAP_IMAGE that is displayed in an editor as a reference fo...
const BITMAP_BASE & GetImage() const
Get the underlying image.
double GetImageScale() const
void SetImageScale(double aScale)
Set the image "zoom" value.
Holds all the data relating to one schematic.
Definition schematic.h:149
const std::vector< std::shared_ptr< BUS_ALIAS > > & GetAllBusAliases() const
Definition schematic.h:349
SCH_SHEET_LIST BuildUnorderedSheetList() const
PROJECT & Project() const
Return a reference to the project this schematic is part of.
Definition schematic.h:171
bool IsValid() const
A simple test if the schematic is loaded, not a complete one.
Definition schematic.h:289
SCH_SHEET & Root() const
Definition schematic.h:200
std::vector< SCH_SHEET * > GetTopLevelSheets() const
Get the list of top-level sheets.
Object to handle a bitmap image that can be inserted in a schematic.
Definition sch_bitmap.h:36
virtual const wxString & GetText() const override
Return the string associated with the text object.
Definition sch_field.h:139
A set of SCH_ITEMs (i.e., without duplicates).
Definition sch_group.h:48
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:170
SCHEMATIC * Schematic() const
Search the item hierarchy to find a SCHEMATIC.
Definition sch_item.cpp:302
int GetBodyStyle() const
Definition sch_item.h:253
bool IsLocked() const override
Definition sch_item.cpp:179
void SetLayer(SCH_LAYER_ID aLayer)
Definition sch_item.h:354
SCH_LAYER_ID GetLayer() const
Return the layer this item is on.
Definition sch_item.h:353
wxString ResolveText(const wxString &aText, const SCH_SHEET_PATH *aPath, int aDepth=0) const
Definition sch_item.cpp:464
wxString GetShownText(const SCH_SHEET_PATH *aPath, RESOLUTION_CONTEXT aContext, int aDepth=0) const override
Segment description base class to describe items which have 2 end points (track, wire,...
Definition sch_line.h:39
int GetPenWidth() const override
Definition sch_line.cpp:445
virtual STROKE_PARAMS GetStroke() const override
Definition sch_line.h:198
const LINE_ENDING & GetEndEnding() const
Definition sch_line.h:204
VECTOR2I GetEndPoint() const
Definition sch_line.h:145
VECTOR2I GetStartPoint() const
Definition sch_line.h:136
const LINE_ENDING & GetStartEnding() const
Definition sch_line.h:201
bool GetExcludedFromSim(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const override
void SetDNP(bool aDNP, const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) override
void SetExcludedFromSim(bool aExcludeFromSim, const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) override
Set or clear the exclude from simulation flag.
void SetExcludedFromBOM(bool aExcludeFromBOM, const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) override
Set or clear the exclude from schematic bill of materials flag.
bool GetExcludedFromBOM(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const override
bool GetDNP(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const override
Set or clear the 'Do Not Populate' flag.
bool GetExcludedFromBoard(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const override
void SetExcludedFromBoard(bool aExclude, const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) override
Set or clear exclude from board netlist flag.
Container class that holds multiple SCH_SCREEN objects in a hierarchy.
Definition sch_screen.h:818
SCH_SCREEN * GetNext()
SCH_SCREEN * GetFirst()
void Append(SCH_ITEM *aItem, bool aUpdateLibSymbol=true)
void AddLibSymbol(LIB_SYMBOL *aLibSymbol)
Add aLibSymbol to the library symbol map.
const std::map< wxString, LIB_SYMBOL * > & GetLibSymbols() const
Fetch a list of unique LIB_SYMBOL object pointers required to properly render each SCH_SYMBOL in this...
Definition sch_screen.h:557
EE_RTREE & Items()
Get the full RTree, usually for iterating.
Definition sch_screen.h:122
bool Remove(SCH_ITEM *aItem, bool aUpdateLibSymbol=true)
Remove aItem from the schematic associated with this screen.
TITLE_BLOCK & GetTitleBlock()
Definition sch_screen.h:168
void SetStroke(const STROKE_PARAMS &aStroke) override
Definition sch_shape.cpp:98
STROKE_PARAMS GetStroke() const override
Definition sch_shape.h:57
int GetEffectiveWidth() const override
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
KIID_PATH Path() const
Get the sheet path as an KIID_PATH.
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition sch_sheet.h:48
bool GetExcludedFromBOM(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const override
bool GetExcludedFromSim(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const override
bool GetDNP(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const override
Set or clear the 'Do Not Populate' flags.
bool GetExcludedFromBoard(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const override
Definition sch_sheet.h:477
const std::vector< SCH_SHEET_INSTANCE > & GetInstances() const
Definition sch_sheet.h:521
Schematic symbol object.
Definition sch_symbol.h:73
void ClearPinMapOverrides()
Clear the base pin-to-pad map override and every variant's override.
bool GetExcludedFromSim(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const override
const std::vector< SCH_SYMBOL_INSTANCE > & GetInstances() const
Definition sch_symbol.h:133
wxString GetFieldText(const wxString &aFieldName, const SCH_SHEET_PATH *aPath=nullptr, const wxString &aVariantName=wxEmptyString) const
PIN_MAP_INSTANCE_OVERRIDE GetPinMapOverride(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const
bool GetExcludedFromBOM(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const override
void GetFields(std::vector< SCH_FIELD * > &aVector, bool aVisibleOnly) const override
Populate a std::vector with SCH_FIELDs, sorted in ordinal order.
bool IsMissingLibSymbol() const
Check to see if the library symbol is set to the dummy library symbol.
const LIB_ID & GetLibId() const override
Definition sch_symbol.h:163
LIB_SYMBOL * GetVariantLibSymbol(const wxString &aVariantName, const SCH_SHEET_PATH &aPath) const
Resolve the alternate library symbol for a given variant.
bool GetExcludedFromPosFiles(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const override
bool GetExcludedFromBoard(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const override
std::unique_ptr< LIB_SYMBOL > & GetLibSymbolRef()
Definition sch_symbol.h:182
virtual bool GetDNP(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const override
Set or clear the 'Do Not Populate' flag.
SCH_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this symbol.
Writes a schematic or symbol library in the KiCad 10.0 file format.
void SaveSchematicFile(const wxString &aFileName, SCH_SHEET *aSheet, SCHEMATIC *aSchematic)
static constexpr int FORMAT_VERSION
void SaveSymbolLibrary(const wxString &aFileName, const std::vector< LIB_SYMBOL * > &aSymbols)
static constexpr int SYMBOL_LIB_VERSION
Writes a schematic or symbol library in the KiCad 9.0 file format.
void SaveSymbolLibrary(const wxString &aFileName, const std::vector< LIB_SYMBOL * > &aSymbols)
static constexpr int FORMAT_VERSION
void SaveSchematicFile(const wxString &aFileName, SCH_SHEET *aSheet, SCHEMATIC *aSchematic)
static constexpr int SYMBOL_LIB_VERSION
SHAPE_LINE_CHAIN ConvertToPolyline(int aMaxError) const
Build a polyline approximation of the ellipse or arc.
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
const std::vector< VECTOR2I > & CPoints() const
Represent a set of closed polygons.
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Appends a vertex at the end of the given outline/hole (default: the last outline)
void Simplify()
Simplify the polyset (merges overlapping polys, eliminates degeneracy/self-intersections)
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
int NewOutline()
Creates a new empty polygon in the set and returns its index.
int OutlineCount() const
Return the number of outlines in the set.
SHAPE_POLY_SET Fillet(int aRadius, int aErrorMax)
Return a filleted version of the polygon set.
void Fracture(bool aSimplify=true)
Convert a set of polygons with holes to a single outline with "slits"/"fractures" connecting the oute...
Simple container to manage line stroke parameters.
void SetWidth(int aWidth)
KIGFX::COLOR4D GetColor() const
bool GetExcludedFromPosFiles(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const override
Definition symbol.h:231
void SetExcludedFromPosFiles(bool aExclude, const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) override
Set or clear exclude from position files flag.
Definition symbol.h:225
Hold the information shown in the lower right corner of a plot, printout, or editing view.
Definition title_block.h:38
const wxString & GetCompany() const
Definition title_block.h:93
const wxString & GetRevision() const
Definition title_block.h:83
const wxString & GetDate() const
Definition title_block.h:73
const wxString & GetComment(int aIdx) const
const wxString & GetTitle() const
Definition title_block.h:60
wxString wx_str() const
Definition utf8.cpp:41
void FinalizeTextVarExpansion(wxString &aText, RESOLUTION_CONTEXT aContext)
Definition common.cpp:88
wxString ExpandTextVars(const wxString &aSource, const PROJECT *aProject, RESOLUTION_CONTEXT aContext)
Definition common.cpp:60
std::vector< TEXT_VAR_REF_KEY > ExtractTextVarReferences(const wxString &aSource)
Lex-scan aSource and return every ${...} reference that appears, without resolving.
Definition common.cpp:468
@ FOR_NETNAME
Definition common.h:92
@ INTERNAL
Definition common.h:94
DOWNGRADE_BUCKET
What happens to a feature when we export to an older KiCad.
@ BLOCK
Cannot be represented at all, export to this target is not possible.
@ LOWER
Approximated down to a simpler equivalent.
@ DROP
No equivalent, left out.
static bool empty(const wxTextEntryBase *aCtrl)
wxString FindUnsupportedToken(const wxString &aSerialized, const DATED_TOKEN *aNodes, size_t aNodeCount, const DATED_TOKEN *aValues, size_t aValueCount, int aTargetVersion)
Scan serialized s-expression text against dated node and value denylists.
#define _(s)
@ FILLED_WITH_COLOR
Definition eda_fill.h:33
@ NO_FILL
Definition eda_fill.h:30
@ FILLED_SHAPE
Fill with object color.
Definition eda_fill.h:31
@ RECURSE
Definition eda_item.h:51
SHAPE_T
Definition eda_shape.h:54
@ ELLIPSE
Definition eda_shape.h:62
@ SEGMENT
Definition eda_shape.h:56
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:57
@ ELLIPSE_ARC
Definition eda_shape.h:63
std::string source
@ LAYER_NOTES
Definition layer_ids.h:489
static std::vector< PENDING_PROPERTY > plan(const EDA_ITEM &aSource, const EDA_ITEM &aTarget, const std::set< wxString > &aEnabledKeys)
Target properties to write, paired with values read off the source.
CITER next(CITER it)
Definition ptree.cpp:120
static constexpr int SCH_VER_PIN_MAPS
static int countJumperPinGroups(const SCH_SCREEN *aScreen)
static void unlockItems(SCH_SCREEN *aScreen)
static bool hasLineEndings(const SCH_ITEM *aItem)
static constexpr int SCH_VER_ELLIPSE
static int countCustomProperties(const SCH_SCREEN *aScreen)
COMPATIBILITY_REPORT ClassifySchematicStructureForDowngrade(const SCHEMATIC &aSchematic, const DOWNGRADE_TARGET &aTarget)
Schematic-wide structure the per-screen rules cannot see. Does not modify it.
static constexpr int SCH_VER_GROUPS
static int countEscapedStackedPins(const SCH_SCREEN *aScreen)
void DowngradeScreenInPlace(SCH_SCREEN *aScreen, const DOWNGRADE_TARGET &aTarget, bool aDropInsteadOfApproximate)
Approximate or remove, in place, what the target cannot represent.
static std::vector< SCREEN_RULE > screenRules()
static constexpr int SYMLIB_VER_LINE_ENDINGS
static void omitScreenItems(SCH_SCREEN *aScreen, const std::function< bool(const SCH_ITEM *)> &aMatches)
static int countSymbolEscapedStackedPins(const LIB_SYMBOL *aSymbol)
static int countSymbolRoundedRects(const LIB_SYMBOL *aSymbol)
static void omitSymbolRoundedRects(LIB_SYMBOL *aSymbol)
static SHEET_VARIANT_SNAPSHOT snapshotSheet(const SCH_SHEET *aSheet, const SCH_SHEET_PATH &aPath, const wxString &aVariantName)
static void dropAlternateSymbols(SCH_SCREEN *aScreen)
static void dropSymbolPinMaps(LIB_SYMBOL *aSymbol)
static int countPinMaps(const SCH_SCREEN *aScreen)
static int countSymbolLineEndings(const LIB_SYMBOL *aSymbol)
static int countUnresolvedCustomBodyStyles(const SCH_SCREEN *aScreen)
static bool usesPlainStackedPinNumber(const wxString &aNumber)
static bool hasActiveExpression(const wxString &aText)
static int countSymbolLocalPower(const LIB_SYMBOL *aSymbol)
static void planTextDowngrade(EDA_ITEM *aItem, const std::vector< SCH_SHEET_PATH > &aPaths, bool aLibrary, bool aLegacyExpressions, TEXT_DOWNGRADE_PLAN &aPlan, const TITLE_BLOCK *aTitles=nullptr)
static void dropRuleAreaFlags(SCH_SCREEN *aScreen)
static int countRuleAreaFlags(const SCH_SCREEN *aScreen)
static int countPositionExclusions(const SCH_SCREEN *aScreen)
static void omitHatchedFills(SCH_SCREEN *aScreen)
static void dropPinMaps(SCH_SCREEN *aScreen)
static bool hasIncompatibleBusVector(const wxString &aText, bool aPreTen)
static void ungroupAll(SCH_SCREEN *aScreen)
bool FlattenSchematicVariant(SCHEMATIC &aSchematic, const wxString &aVariantName, wxString *aConflict)
Bake a variant into the base attributes of every symbol and sheet.
static void omitSelectedCustomBodyStyles(SCH_SCREEN *aScreen)
static void lowerEllipses(SCH_SCREEN *aScreen)
static void dropSymbolCustomProperties(LIB_SYMBOL *aSymbol)
static void omitEllipses(SCH_SCREEN *aScreen)
static constexpr int SCH_DOWNGRADE_COVERED
static SCH_SHAPE lineEndingShape(const SCH_ITEM *aItem)
static void roundedRectToPolygon(SCH_SHAPE *aShape)
static constexpr int SCH_VER_LINE_ENDINGS
static constexpr int SCH_VER_RULE_AREA_FLAGS
static bool hasEscapedTextControl(const wxString &aText)
static int countStackedPins(const SCH_SCREEN *aScreen)
static bool isEllipse(const SCH_ITEM *aItem)
static bool hasPaddedBusVector(const wxString &aText)
static wxString bakeLegacyExpressions(wxString aText, const SCH_ITEM *aItem, const SCH_SHEET_PATH *aPath, bool &aFailed)
static void lowerRoundedRects(SCH_SCREEN *aScreen)
static int countLocalPowerSymbols(const SCH_SCREEN *aScreen)
void SaveSymbolLibraryForTarget(const std::vector< LIB_SYMBOL * > &aSymbols, const wxString &aPath, const DOWNGRADE_TARGET &aTarget)
Save a .kicad_sym library stamped for the target.
int SymbolLibDowngradeCoveredVersion()
The symbol library format the denylist was generated against.
static void dropLineEndings(SCH_SCREEN *aScreen)
static constexpr DATED_TOKEN SCH_TOKENS[]
static void dropSymbolPositionExclusions(LIB_SYMBOL *aSymbol)
static void omitSymbolHatchedFills(LIB_SYMBOL *aSymbol)
static void omitSymbolGraphics(LIB_SYMBOL *aSymbol, const std::function< bool(const SCH_ITEM *)> &aMatches)
static constexpr int SCH_VER_LOCAL_POWER
static constexpr int SYMLIB_DOWNGRADE_COVERED
static bool isRoundedRect(const SCH_ITEM *aItem)
static void closePolyline(std::vector< VECTOR2I > &aPoints)
static FROZEN_BUS_VECTOR frozenBusVector(const wxString &aBus, bool aPreTen)
static int countSymbolEllipses(const LIB_SYMBOL *aSymbol)
static bool hasPostTargetTextVars(const wxString &aText)
static void reportTextPlan(const TEXT_DOWNGRADE_PLAN &aPlan, COMPATIBILITY_REPORT &aReport)
COMPATIBILITY_REPORT ClassifyLibSymbolForDowngrade(const LIB_SYMBOL *aSymbol, const DOWNGRADE_TARGET &aTarget, bool aDropInsteadOfApproximate)
Report what a library symbol would lose if exported to an older KiCad. Does not modify it.
static constexpr KICAD_T FILLABLE_TYPES[]
static int countNamedBodyStyles(const SCH_SCREEN *aScreen)
static bool hasOwnLock(const SCH_ITEM *aItem)
void DowngradeLibSymbolInPlace(LIB_SYMBOL *aSymbol, const DOWNGRADE_TARGET &aTarget, bool aDropInsteadOfApproximate)
Symbol-scoped downgrade for standalone .kicad_sym files the screen pass never sees.
static int countLineEndings(const SCH_SCREEN *aScreen)
static void compensateReferenceImageScales(SCH_SCREEN *aScreen)
static int countRoundedRects(const SCH_SCREEN *aScreen)
static constexpr int SCH_VER_ALTERNATE_SYMBOLS
static void lowerLineEndings(SCH_SCREEN *aScreen)
static int countSymbolHatchedFills(const LIB_SYMBOL *aSymbol)
static bool canLowerLineEndings(const SCH_ITEM *aItem)
static constexpr int SCH_VER_JUMPER_PINS
static void bakeSelectedCustomBodyStyles(SCH_SCREEN *aScreen)
static void dropNamedBodyStyles(SCH_SCREEN *aScreen)
static TEXT_DOWNGRADE_PLAN screenTextPlan(const SCH_SCREEN *aScreen, bool aLegacyExpressions)
static void omitRoundedRects(SCH_SCREEN *aScreen)
static void lowerSymbolHatchedFills(LIB_SYMBOL *aSymbol)
static int countLockedItems(const SCH_SCREEN *aScreen)
static void visitSymbolItems(const LIB_SYMBOL *aSymbol, const std::function< void(EDA_ITEM *)> &aVisit)
static constexpr int SCH_VER_CUSTOM_PROPERTIES
static void visitScreenItems(const SCH_SCREEN *aScreen, const std::function< void(EDA_ITEM *)> &aVisit)
static void dropJumperPinGroups(SCH_SCREEN *aScreen)
static int countGroups(const SCH_SCREEN *aScreen)
static constexpr int SCH_VER_LOCKING
static int countSymbolPinMaps(const LIB_SYMBOL *aSymbol)
static constexpr int SCH_VER_IMAGE_PPI
static void lowerHatchedFills(SCH_SCREEN *aScreen)
static void lowerSymbolLineEndings(LIB_SYMBOL *aSymbol)
static int countPaddedBusLabels(const SCH_SCREEN *aScreen)
static constexpr int SCH_VER_ROUNDED_RECT
void SaveSchematicForTarget(SCH_SHEET *aSheet, SCHEMATIC *aSchematic, const wxString &aPath, const DOWNGRADE_TARGET &aTarget)
Save a schematic sheet file stamped for the target, using the writer for that version.
static void lowerSymbolEllipses(LIB_SYMBOL *aSymbol)
static void reportIncompatibleBuses(int aCount, COMPATIBILITY_REPORT &aReport)
static constexpr int SCH_VER_POSITION_EXCLUSIONS
static constexpr int SCH_VER_BODY_STYLES
static void dropPositionExclusions(SCH_SCREEN *aScreen)
static bool textNeedsDowngrade(const wxString &aText, const PROJECT *aProject, bool aLegacyExpressions, std::set< wxString > &aVisiting, const TITLE_BLOCK *aTitles=nullptr)
static void clearLineEndings(SCH_ITEM *aItem)
static void lowerSymbolRoundedRects(LIB_SYMBOL *aSymbol)
static constexpr int SCH_VER_SHAPE_HATCH
static void omitSymbolEllipses(LIB_SYMBOL *aSymbol)
static int countSymbolStackedPins(const LIB_SYMBOL *aSymbol)
static bool isHatchedFillItem(const SCH_ITEM &aItem)
static int countSymbolCustomProperties(const LIB_SYMBOL *aSymbol)
static SYMBOL_VARIANT_SNAPSHOT snapshotSymbol(const SCH_SYMBOL *aSymbol, const SCH_SHEET_PATH &aPath, const wxString &aVariantName)
static void dropSymbolBodyStyleNames(LIB_SYMBOL *aSymbol)
static void dropSymbolJumperPins(LIB_SYMBOL *aSymbol)
static std::vector< SCH_SHAPE * > lineEndingPolygons(SCH_ITEM *aItem)
static bool hasRuleAreaFlags(const SCH_RULE_AREA *aArea)
static int countSelectedCustomBodyStyles(const SCH_SCREEN *aScreen)
wxString FindUnsupportedSchToken(const wxString &aSerialized, const DOWNGRADE_TARGET &aTarget)
Scan serialized schematic text for tokens the target cannot parse.
static int countHatchedFills(const SCH_SCREEN *aScreen)
static bool hasPinMaps(const SCH_SYMBOL *aSymbol)
static bool hasEscapedStackedPinNumber(const wxString &aNumber)
static int countSymbolPositionExclusions(const LIB_SYMBOL *aSymbol)
static void applyTextPlan(const TEXT_DOWNGRADE_PLAN &aPlan)
static int countIncompatibleBusLabels(const SCH_SCREEN *aScreen, bool aPreTen)
static bool isPostTargetTextVar(const wxString &aToken)
static void reportPaddedBuses(int aCount, COMPATIBILITY_REPORT &aReport)
static void dropSymbolLineEndings(LIB_SYMBOL *aSymbol)
int SchDowngradeCoveredVersion()
The format the denylist was generated against. A format bump forces a review.
static constexpr DATED_TOKEN SCH_VALUES[]
COMPATIBILITY_REPORT ClassifyScreenForDowngrade(const SCH_SCREEN *aScreen, const DOWNGRADE_TARGET &aTarget, bool aDropInsteadOfApproximate)
Report what a schematic screen would lose if exported to an older KiCad.
static int countSymbolNamedBodyStyles(const LIB_SYMBOL *aSymbol)
static void dropCustomProperties(SCH_SCREEN *aScreen)
static void ellipseToPolygon(SCH_SHAPE *aShape)
static int countSymbolJumperPins(const LIB_SYMBOL *aSymbol)
static int countAlternateSymbols(const SCH_SCREEN *aScreen)
static constexpr int SCH_VER_STACKED_PINS
static constexpr int SCH_VER_STACKED_PIN_ESCAPES
static int countEllipses(const SCH_SCREEN *aScreen)
static TEXT_DOWNGRADE_PLAN symbolTextPlan(const LIB_SYMBOL *aSymbol, bool aLegacyExpressions)
constexpr int SCH_VER_VARIANTS
Format dates for rules the jobs handler applies at schematic scope.
constexpr int SCH_VER_FLAT_HIERARCHY
constexpr int SCH_VER_NET_CHAINS
#define SEXPR_SYMBOL_LIB_FILE_VERSION
This file contains the file format version information for the s-expression schematic and symbol libr...
#define SEXPR_SCHEMATIC_FILE_VERSION
Schematic file version.
Class to handle a set of SCH_ITEMs.
@ BASE
Definition sch_item.h:64
@ DEMORGAN
Definition sch_item.h:65
Definition of the SCH_SHEET_PATH and SCH_SHEET_LIST classes for Eeschema.
std::vector< wxString > ExpandStackedPinNotation(const wxString &aPinName, bool *aValid)
Expand stacked pin notation like [1,2,3], [1-4], [A1-A4], or [AA1-AA3,AB4,CD12-CD14] into individual ...
wxString EscapeStackedPinItem(const wxString &aPinNumber)
Escape the characters that carry structural meaning inside stacked pin notation ('[',...
LINE_STYLE
Dashed line types.
A token dated by the format that introduced it, for the fail-closed downgrade gates.
An older KiCad release we can export a project back to.
int m_schVersion
SEXPR_SCHEMATIC_FILE_VERSION for the target.
int m_symLibVersion
SEXPR_SYMBOL_LIB_FILE_VERSION for the target.
Per-instance override of the active pin map and a sparse delta on top.
Definition pin_map.h:200
bool IsDefault() const
Definition pin_map.h:210
A simple container for sheet instance information.
A simple container for schematic symbol instance information.
std::function< void(SCH_SCREEN *)> m_apply
std::function< int(const SCH_SCREEN *)> m_count
DOWNGRADE_BUCKET m_bucket
std::function< void(SCH_SCREEN *)> m_omit
wxString m_omitDetail
std::function< void(LIB_SYMBOL *)> m_applySymbol
std::function< void(LIB_SYMBOL *)> m_omitSymbol
std::function< int(const LIB_SYMBOL *)> m_countSymbol
Symbol-scoped part, for .kicad_sym files.
bool operator==(const SHEET_VARIANT_SNAPSHOT &) const =default
std::map< wxString, wxString > m_fieldOverrides
std::optional< LIB_ID > m_symbolOverride
PIN_MAP_INSTANCE_OVERRIDE m_pinMap
std::map< wxString, wxString > m_fieldOverrides
bool operator==(const SYMBOL_VARIANT_SNAPSHOT &) const =default
std::map< EDA_TEXT *, wxString > m_replacements
@ REFERENCE
Field Reference of part, i.e. "IC21".
std::string path
KIBIS_PIN * pin
const SHAPE_LINE_CHAIN chain
int radius
VECTOR2I end
wxString result
Test unit parsing edge cases and error handling.
KICAD_T
The set of class identification values stored in EDA_ITEM::m_structType.
Definition typeinfo.h:70
@ SCH_GROUP_T
Definition typeinfo.h:169
@ SCH_LINE_T
Definition typeinfo.h:159
@ SCH_SYMBOL_T
Definition typeinfo.h:168
@ SCH_DIRECTIVE_LABEL_T
Definition typeinfo.h:167
@ SCH_LABEL_T
Definition typeinfo.h:163
@ SCH_SHAPE_T
Definition typeinfo.h:145
@ SCH_RULE_AREA_T
Definition typeinfo.h:166
@ SCH_HIER_LABEL_T
Definition typeinfo.h:165
@ SCHEMATIC_T
Definition typeinfo.h:200
@ SCH_SHEET_PIN_T
Definition typeinfo.h:170
@ SCH_BITMAP_T
Definition typeinfo.h:160
@ SCH_TEXTBOX_T
Definition typeinfo.h:148
@ SCH_GLOBAL_LABEL_T
Definition typeinfo.h:164
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708