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