KiCad PCB EDA Suite
dialog_board_reannotate.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 (C) 2020 Brian Piccioni [email protected]
5 * Copyright (C) 2020-2022 KiCad Developers, see AUTHORS.txt for contributors.
6 * @author Brian Piccioni <[email protected]>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, you may find one here:
20 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21 * or you may search the http://www.gnu.org website for the version 2 license,
22 * or you may write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 */
25
26#include <algorithm>
27#include <base_units.h>
28#include <bitmaps.h>
29#include <board_commit.h>
30#include <confirm.h>
31#include <ctype.h>
33#include <string_utils.h> // StrNumCmp
34#include <kiface_base.h>
35#include <pcbnew_settings.h>
36#include <refdes_utils.h>
37#include <tool/grid_menu.h>
39#include <wx/valtext.h>
40
41
45
46//
47// This converts the index into a sort code. Note that Back sort code will have left and
48// right swapped.
49//
51 SORTYFIRST + ASCENDINGFIRST + ASCENDINGSECOND, // "Top to bottom, left to right", // 100
52 SORTYFIRST + ASCENDINGFIRST + DESCENDINGSECOND, // "Top to bottom, right to left", // 101
53 SORTYFIRST + DESCENDINGFIRST + ASCENDINGSECOND, // "Back to Front, left to right", // 110
54 SORTYFIRST + DESCENDINGFIRST + DESCENDINGSECOND, // "Back to Front, right to left", // 111
55 SORTXFIRST + ASCENDINGFIRST + ASCENDINGSECOND, // "Left to right, Front to Back", // 000
56 SORTXFIRST + ASCENDINGFIRST + DESCENDINGSECOND, // "Left to right, Back to Front", // 001
57 SORTXFIRST + DESCENDINGFIRST + ASCENDINGSECOND, // "Right to left, Front to Back", // 010
58 SORTXFIRST + DESCENDINGFIRST + DESCENDINGSECOND // "Right to left, Back to Front", // 011
59};
60
61
62//
63// Back Left/Right is opposite because it is a mirror image (coordinates are from the top)
64//
66 SORTYFIRST + ASCENDINGFIRST + DESCENDINGSECOND, // "Top to bottom, left to right", // 101
67 SORTYFIRST + ASCENDINGFIRST + ASCENDINGSECOND, // "Top to bottom, right to left", // 100
68 SORTYFIRST + DESCENDINGFIRST + DESCENDINGSECOND, // "Bottom to top, left to right", // 111
69 SORTYFIRST + DESCENDINGFIRST + ASCENDINGSECOND, // "Bottom to top, right to left", // 110
70 SORTXFIRST + DESCENDINGFIRST + ASCENDINGSECOND, // "Left to right, top to bottom", // 010
71 SORTXFIRST + DESCENDINGFIRST + DESCENDINGSECOND, // "Left to right, bottom to top", // 011
72 SORTXFIRST + ASCENDINGFIRST + ASCENDINGSECOND, // "Right to left, top to bottom", // 000
73 SORTXFIRST + ASCENDINGFIRST + DESCENDINGSECOND // "Right to left, bottom to top", // 001
74};
75
76#define SetSortCodes( DirArray, Code ) \
77 { \
78 SortYFirst = ( ( DirArray[Code] & SORTYFIRST ) != 0 ); \
79 DescendingFirst = ( ( DirArray[Code] & DESCENDINGFIRST ) != 0 ); \
80 DescendingSecond = ( ( DirArray[Code] & DESCENDINGSECOND ) != 0 ); \
81 }
82
83
84wxString AnnotateString[] = {
85 _( "All" ), // AnnotateAll
86 _( "Only front" ), // AnnotateFront
87 _( "Only back" ), // AnnotateBack
88 _( "Only selected" ) // AnnotateSelected
89};
90
91
92wxString ActionMessage[] = {
93 "", // UpdateRefDes
94 _( "Empty" ), // EmptyRefDes
95 _( "Invalid" ), // InvalidRefDes
96 _( "Excluded" ) // Exclude
97};
98
99
101 : DIALOG_BOARD_REANNOTATE_BASE( aParentFrame ),
102 m_footprints( aParentFrame->GetBoard()->Footprints() )
103{
104 m_frame = aParentFrame;
106 InitValues();
107
108 m_FrontRefDesStart->SetValidator( wxTextValidator( wxFILTER_DIGITS ) );
109 m_BackRefDesStart->SetValidator( wxTextValidator( wxFILTER_DIGITS ) );
110
111 m_sdbSizerOK->SetLabel( _( "Reannotate PCB" ) );
112 m_sdbSizerCancel->SetLabel( _( "Close" ) );
113 m_sdbSizer->Layout();
114
115 m_settings = aParentFrame->config();
116 wxArrayString gridslist;
117 GRID_MENU::BuildChoiceList( &gridslist, m_settings, aParentFrame );
118
119 if( -1 == m_gridIndex ) // If no default loaded
120 m_gridIndex = m_settings->m_Window.grid.last_size_idx; // Get the current grid size
121
124
125 m_GridChoice->Set( gridslist );
126 m_GridChoice->SetSelection( m_gridIndex );
127
128 for( wxRadioButton* button : m_sortButtons )
129 button->SetValue( false );
130
131 m_sortButtons[m_sortCode]->SetValue( true );
132
134
135 if( !m_selection.Empty() )
137
138 for( wxRadioButton* button : m_scopeRadioButtons )
139 button->SetValue( false );
140
141 m_scopeRadioButtons[m_annotationScope]->SetValue( true );
142
151
152 m_ExcludeList->SetToolTip( m_ExcludeListText->GetToolTipText() );
153 m_GridChoice->SetToolTip( m_SortGridText->GetToolTipText() );
154
155 m_MessageWindow->SetFileName( Prj().GetProjectPath() + wxT( "report.txt" ) );
156
158}
159
160
162{
163 GetParameters(); // Get the current menu settings
165 cfg->m_Reannotate.sort_on_fp_location = m_locationChoice->GetSelection() == 0;
169
174
177 cfg->m_Reannotate.front_prefix = m_FrontPrefix->GetValue();
178 cfg->m_Reannotate.back_prefix = m_BackPrefix->GetValue();
179 cfg->m_Reannotate.exclude_list = m_ExcludeList->GetValue();
181}
182
183
185{
187 m_locationChoice->SetSelection( cfg->m_Reannotate.sort_on_fp_location ? 0 : 1 );
191
196
199 m_FrontPrefix->SetValue( cfg->m_Reannotate.front_prefix );
200 m_BackPrefix->SetValue( cfg->m_Reannotate.back_prefix );
201 m_ExcludeList->SetValue( cfg->m_Reannotate.exclude_list );
203}
204
205
206void DIALOG_BOARD_REANNOTATE::OnCloseClick( wxCommandEvent& event )
207{
208 EndDialog( wxID_OK );
209}
210
211
212void DIALOG_BOARD_REANNOTATE::FilterPrefix( wxTextCtrl* aPrefix )
213{
214 std::string tmps = VALIDPREFIX;
215
216 if( aPrefix->GetValue().empty() )
217 return; //Should never happen
218
219 char lastc = aPrefix->GetValue().Last();
220
221 if( isalnum( (int) lastc ) )
222 return;
223
224 if( std::string::npos != tmps.find( lastc ) )
225 return;
226
227 tmps = aPrefix->GetValue();
228 aPrefix->Clear();
229 tmps.pop_back();
230 aPrefix->AppendText( tmps );
231}
232
233
235 unsigned int aStartRefDes )
236{
237 unsigned int requiredLastRef = ( aStartRefDes == 0 ? 1 : aStartRefDes ) - 1;
238
239 for( size_t i = 0; i < m_refDesTypes.size(); i++ ) // See if it is in the types array
240 {
241 if( m_refDesTypes[i].RefDesType == aRefDesPrefix ) // Found it!
242 {
243 m_refDesTypes[i].LastUsedRefDes = std::max( m_refDesTypes[i].LastUsedRefDes,
244 requiredLastRef );
245
246 return &m_refDesTypes[i];
247 }
248 }
249
250 // Wasn't in the types array so add it
251 RefDesTypeStr newtype;
252 newtype.RefDesType = aRefDesPrefix;
253 newtype.LastUsedRefDes = requiredLastRef;
254 m_refDesTypes.push_back( newtype );
255
256 return &m_refDesTypes.back();
257}
258
259
261{
263}
264
265
266void DIALOG_BOARD_REANNOTATE::FilterBackPrefix( wxCommandEvent& event )
267{
269}
270
271
272void DIALOG_BOARD_REANNOTATE::OnApplyClick( wxCommandEvent& event )
273{
274 wxString warning;
275
276 if( m_frame->GetBoard()->IsEmpty() )
277 {
278 ShowReport( _( "No PCB to reannotate!" ), RPT_SEVERITY_ERROR );
279 return;
280 }
281
282 GetParameters(); // Figure out how this is to be done
283 MakeSampleText( warning );
284
285 if( !IsOK( m_frame, warning ) )
286 return;
287
288 if( ReannotateBoard() )
289 {
290 ShowReport( _( "PCB successfully reannotated" ), RPT_SEVERITY_ACTION );
291 ShowReport( _( "PCB annotation changes should be synchronized with schematic using "
292 "the \"Update Schematic from PCB\" tool." ), RPT_SEVERITY_WARNING );
293 }
294
296 m_MessageWindow->Flush( false );
297 m_frame->GetCanvas()->Refresh(); // Redraw
298 m_frame->OnModify(); // Need to save file on exit.
299}
300
301
303{
304 aMessage.Printf( _( "\n%s footprints will be reannotated." ),
306
307 if( !m_ExcludeList->GetValue().empty() )
308 {
309 aMessage += wxString::Format( _( "\nAny reference types %s will not be annotated." ),
310 m_ExcludeList->GetValue() );
311 }
312
313 if( m_ExcludeLocked->GetValue() )
314 aMessage += wxString::Format( _( "\nLocked footprints will not be annotated" ) );
315
316 if( !m_AnnotateBack->GetValue() )
317 {
318 aMessage += wxString::Format( _( "\nFront footprints will start at %s" ),
319 m_FrontRefDesStart->GetValue() );
320 }
321
322 if( !m_AnnotateFront->GetValue() )
323 {
324 bool frontPlusOne = ( 0 == wxAtoi( m_BackRefDesStart->GetValue() ) )
325 && !m_AnnotateBack->GetValue();
326
327 aMessage += wxString::Format( _( "\nBack footprints will start at %s." ),
328 frontPlusOne ? _( "the last front footprint + 1" ) :
329 m_BackRefDesStart->GetValue() );
330 }
331
332 if( !m_FrontPrefix->GetValue().empty() )
333 {
334 if( m_RemoveFrontPrefix->GetValue() )
335 {
336 aMessage += wxString::Format( _( "\nFront footprints starting with '%s' will have "
337 "the prefix removed." ),
338 m_FrontPrefix->GetValue() );
339 }
340 else
341 {
342 aMessage += wxString::Format( _( "\nFront footprints will have '%s' inserted as a "
343 "prefix." ),
344 m_FrontPrefix->GetValue() );
345 }
346 }
347
348 if( !m_BackPrefix->GetValue().empty() )
349 {
350 if( m_RemoveBackPrefix->GetValue() )
351 {
352 aMessage += wxString::Format( _( "\nBack footprints starting with '%s' will have the "
353 "prefix removed." ),
354 m_BackPrefix->GetValue() );
355 }
356 else
357 {
358 aMessage += wxString::Format( _( "\nBack footprints will have '%s' inserted as a "
359 "prefix." ),
360 m_BackPrefix->GetValue() );
361 }
362 }
363
364 bool fpLocation = m_locationChoice->GetSelection() == 0;
365
366 aMessage += wxString::Format( _( "\nPrior to sorting by %s, the coordinates of which will be "
367 "rounded to a %s, %s grid." ),
368 fpLocation ? _( "footprint location" )
369 : _( "reference designator location" ),
372
373 ShowReport( aMessage, RPT_SEVERITY_INFO );
374}
375
376
378{
379 m_sortCode = 0; // Convert radio button to sort direction code
380
381 for( wxRadioButton* sortbuttons : m_sortButtons )
382 {
383 if( sortbuttons->GetValue() )
384 break;
385
386 m_sortCode++;
387 }
388
389 if( m_sortCode >= (int) m_sortButtons.size() )
390 m_sortCode = 0;
391
393 m_backPrefixString = m_BackPrefix->GetValue();
394
395 // Get the chosen sort grid for rounding
396 m_gridIndex = m_GridChoice->GetSelection();
397
398 if( m_gridIndex >= ( int ) m_settings->m_Window.grid.sizes.size() )
399 {
404 }
405 else
406 {
410 }
411
413
414 for( wxRadioButton* button : m_scopeRadioButtons )
415 {
416 if( button->GetValue() )
417 break;
418 else
420 }
421
423}
424
425
426int DIALOG_BOARD_REANNOTATE::RoundToGrid( int aCoord, int aGrid )
427{
428 if( 0 == aGrid )
429 aGrid = MINGRID;
430
431 int rounder;
432 rounder = aCoord % aGrid;
433 aCoord -= rounder;
434
435 if( abs( rounder ) > ( aGrid / 2 ) )
436 aCoord += ( aCoord < 0 ? -aGrid : aGrid );
437
438 return ( aCoord );
439}
440
441
444static bool ChangeArrayCompare( const RefDesChange& aA, const RefDesChange& aB )
445{
446 return ( StrNumCmp( aA.OldRefDesString, aB.OldRefDesString ) < 0 );
447}
448
449
452static bool ModuleCompare( const RefDesInfo& aA, const RefDesInfo& aB )
453{
454 int X0 = aA.roundedx, X1 = aB.roundedx, Y0 = aA.roundedy, Y1 = aB.roundedy;
455
456 if( SortYFirst ) //If sorting by Y then X, swap X and Y
457 {
458 std::swap( X0, Y0 );
459 std::swap( X1, Y1 );
460 }
461
462 // If descending, same compare just swap directions
463 if( DescendingFirst )
464 std::swap( X0, X1 );
465
466 if( DescendingSecond )
467 std::swap( Y0, Y1 );
468
469 if( X0 < X1 )
470 return ( true ); // yes, its smaller
471
472 if( X0 > X1 )
473 return ( false ); // No its not
474
475 if( Y0 < Y1 )
476 return ( true ); // same but equal
477
478 return ( false );
479}
480
481
483{
484 return wxString::Format( wxT( "%s, %s" ),
487}
488
489
490void DIALOG_BOARD_REANNOTATE::ShowReport( const wxString& aMessage, SEVERITY aSeverity )
491{
492 size_t pos = 0, prev = 0;
493
494 do
495 {
496 pos = aMessage.ToStdString().find( '\n', prev );
497 m_MessageWindow->Report( aMessage.ToStdString().substr( prev, pos - prev ), aSeverity );
498 prev = pos + 1;
499 } while( std::string::npos != pos );
500
501}
502
503
505{
506 int i = 1;
507 wxString message;
508
509 message.Printf( _( "\n\nThere are %i types of reference designations\n"
510 "**********************************************************\n" ),
511 (int) m_refDesTypes.size() );
512
513 for( RefDesTypeStr Type : m_refDesTypes ) // Show all the types of refdes
514 message += Type.RefDesType + ( 0 == ( i++ % 16 ) ? wxT( "\n" ) : wxS( " " ) );
515
516 if( !m_excludeArray.empty() )
517 {
518 wxString excludes;
519
520 for( wxString& exclude : m_excludeArray ) // Show the refdes we are excluding
521 excludes += exclude + wxS( " " );
522
523 message += wxString::Format( _( "\nExcluding: %s from reannotation\n\n" ), excludes );
524 }
525
526 message += _( "\n Change Array\n***********************\n" );
527
528 for( const RefDesChange& change : m_changeArray )
529 {
530 message += wxString::Format( wxT( "%s -> %s %s %s\n" ),
531 change.OldRefDesString,
532 change.NewRefDes,
533 ActionMessage[change.Action],
534 UpdateRefDes != change.Action ? _( " will be ignored" )
535 : wxT( "" ) );
536 }
537
538 ShowReport( message, RPT_SEVERITY_INFO );
539}
540
541
542void DIALOG_BOARD_REANNOTATE::LogFootprints( const wxString& aMessage,
543 const std::vector<RefDesInfo>& aFootprints )
544{
545 wxString message = aMessage;
546
547 if( aFootprints.empty() )
548 message += _( "\nNo footprints" );
549 else
550 {
551 int i = 1;
552 bool fpLocations = m_locationChoice->GetSelection() == 0;
553
554 message += wxString::Format( _( "\n*********** Sort on %s ***********" ),
555 fpLocations ? _( "Footprint Coordinates" )
556 : _( "Reference Designator Coordinates" ) );
557
558 message += wxString::Format( _( "\nSort Code %d" ), m_sortCode );
559
560 for( const RefDesInfo& mod : aFootprints )
561 {
562 message += wxString::Format( _( "\n%d %s UUID: [%s], X, Y: %s, Rounded X, Y, %s" ),
563 i++,
564 mod.RefDesString,
565 mod.Uuid.AsString(),
566 CoordTowxString( mod.x, mod.y ),
567 CoordTowxString( mod.roundedx, mod.roundedy ) );
568 }
569 }
570
571 ShowReport( message, RPT_SEVERITY_INFO );
572}
573
574
576{
577 std::vector<RefDesInfo> BadRefDes;
578 wxString message, badrefdes;
579 STRING_FORMATTER stringformatter;
580 RefDesChange* newref;
582
583 if( !BuildFootprintList( BadRefDes ) )
584 {
585 ShowReport( _( "Selected options resulted in errors! Change them and try again." ),
587 return false;
588 }
589
590 if( !BadRefDes.empty() )
591 {
592 message.Printf( _( "\nPCB has %d empty or invalid reference designations."
593 "\nRecommend running DRC with 'Test for parity between PCB and "
594 "schematic' checked.\n" ),
595 (int) BadRefDes.size() );
596
597 for( const RefDesInfo& mod : BadRefDes )
598 {
599 badrefdes += wxString::Format( _( "\nRefDes: %s Footprint: %s:%s at %s on PCB." ),
600 mod.RefDesString,
601 mod.FPID.GetLibNickname().wx_str(),
602 mod.FPID.GetLibItemName().wx_str(),
603 CoordTowxString( mod.x, mod.y ) );
604 }
605
606 ShowReport( message + badrefdes + wxT( "\n" ), RPT_SEVERITY_WARNING );
607 message += _( "Reannotate anyway?" );
608
609 if( !IsOK( m_frame, message ) )
610 return false;
611 }
612
613 BOARD_COMMIT commit( m_frame );
614
615 for( FOOTPRINT* footprint : m_footprints )
616 {
617 newref = GetNewRefDes( footprint );
618
619 if( nullptr == newref )
620 return false;
621
622 commit.Modify( footprint ); // Make a copy for undo
623 footprint->SetReference( newref->NewRefDes ); // Update the PCB reference
624 m_frame->GetCanvas()->GetView()->Update( footprint ); // Touch the footprint
625 }
626
627 commit.Push( wxT( "Geographic reannotation" ) );
628 return true;
629}
630
631
632bool DIALOG_BOARD_REANNOTATE::BuildFootprintList( std::vector<RefDesInfo>& aBadRefDes )
633{
634 bool annotateSelected = m_AnnotateSelection->GetValue();
635 bool annotateFront = m_AnnotateFront->GetValue(); // Unless only doing back
636 bool annotateBack = m_AnnotateBack->GetValue(); // Unless only doing front
637 bool skipLocked = m_ExcludeLocked->GetValue();
638
639 int errorcount = 0;
640 size_t firstnum = 0;
641
642 m_frontFootprints.clear();
643 m_backFootprints.clear();
644 m_excludeArray.clear();
646
647 std::vector<KIID> selected;
648
649 if( annotateSelected )
650 {
651 for( EDA_ITEM* item : m_selection )
652 {
653 // Get the timestamps of selected footprints
654 if( item->Type() == PCB_FOOTPRINT_T )
655 selected.push_back( item->m_Uuid );
656 }
657 }
658
659 wxString exclude;
660
661 // Break exclude list into words.
662 for( auto thischar : m_ExcludeList->GetValue() )
663 {
664 if( ( ' ' == thischar ) || ( ',' == thischar ) )
665 {
666 m_excludeArray.push_back( exclude );
667 exclude.clear();
668 }
669 else
670 exclude += thischar;
671
672 if( !exclude.empty() )
673 m_excludeArray.push_back( exclude );
674 }
675
676 RefDesInfo fpData;
677 bool useModuleLocation = m_locationChoice->GetSelection() == 0;
678
679 for( FOOTPRINT* footprint : m_footprints )
680 {
681 fpData.Uuid = footprint->m_Uuid;
682 fpData.RefDesString = footprint->GetReference();
683 fpData.FPID = footprint->GetFPID();
684 fpData.x = useModuleLocation ? footprint->GetPosition().x
685 : footprint->Reference().GetPosition().x;
686 fpData.y = useModuleLocation ? footprint->GetPosition().y
687 : footprint->Reference().GetPosition().y;
688 fpData.roundedx = RoundToGrid( fpData.x, m_sortGridx ); // Round to sort
689 fpData.roundedy = RoundToGrid( fpData.y, m_sortGridy );
690 fpData.Front = footprint->GetLayer() == F_Cu;
691 fpData.Action = UpdateRefDes; // Usually good
692
693 if( fpData.RefDesString.IsEmpty() )
694 {
695 fpData.Action = EmptyRefDes;
696 }
697 else
698 {
699 firstnum = fpData.RefDesString.find_first_of( wxT( "0123456789" ) );
700
701 if( std::string::npos == firstnum )
702 fpData.Action = InvalidRefDes; // do not change ref des such as 12 or +1, or L
703 }
704
705 // Get the type (R, C, etc)
706 fpData.RefDesType = fpData.RefDesString.substr( 0, firstnum );
707
708 for( wxString excluded : m_excludeArray )
709 {
710 if( excluded == fpData.RefDesType ) // Am I supposed to exclude this type?
711 {
712 fpData.Action = Exclude; // Yes
713 break;
714 }
715 }
716
717 if(( fpData.Front && annotateBack ) || // If a front fp and doing backs only
718 ( !fpData.Front && annotateFront ) || // If a back fp and doing front only
719 ( footprint->IsLocked() && skipLocked ) ) // If excluding locked and it is locked
720 {
721 fpData.Action = Exclude;
722 }
723
724 if( annotateSelected )
725 { // If only annotating selected c
726 fpData.Action = Exclude; // Assume it isn't selected
727
728 for( KIID sel : selected )
729 {
730 if( fpData.Uuid == sel )
731 { // Found in selected footprints
732 fpData.Action = UpdateRefDes; // Update it
733 break;
734 }
735 }
736 }
737
738 if( fpData.Front )
739 m_frontFootprints.push_back( fpData );
740 else
741 m_backFootprints.push_back( fpData );
742 }
743
744 // Determine the sort order for the front.
746
747 // Sort the front footprints.
748 sort( m_frontFootprints.begin(), m_frontFootprints.end(), ModuleCompare );
749
750 // Determine the sort order for the back.
752
753 // Sort the back footprints.
754 sort( m_backFootprints.begin(), m_backFootprints.end(), ModuleCompare );
755
756 m_refDesTypes.clear();
757 m_changeArray.clear();
758
760
761 if( !m_frontFootprints.empty() )
762 {
764 m_FrontPrefix->GetValue(), m_RemoveFrontPrefix->GetValue(), aBadRefDes );
765 }
766
767 if( !m_backFootprints.empty() )
768 {
770 m_BackPrefix->GetValue(), m_RemoveBackPrefix->GetValue(), aBadRefDes );
771 }
772
773 if( !m_changeArray.empty() )
774 sort( m_changeArray.begin(), m_changeArray.end(), ChangeArrayCompare );
775
777
778 size_t changearraysize = m_changeArray.size();
779
780 for( size_t i = 0; i < changearraysize; i++ ) // Scan through for duplicates if update or skip
781 {
782 if( ( m_changeArray[i].Action != EmptyRefDes )
783 && ( m_changeArray[i].Action != InvalidRefDes ) )
784 {
785 for( size_t j = i + 1; j < changearraysize; j++ )
786 {
787 if( m_changeArray[i].NewRefDes == m_changeArray[j].NewRefDes )
788 {
789 ShowReport( wxString::Format( _( "Duplicate instances of %s" ),
790 m_changeArray[j].NewRefDes ),
792
793 if( errorcount++ > MAXERROR )
794 {
795 ShowReport( _( "Aborted: too many errors" ), RPT_SEVERITY_ERROR );
796 break;
797 }
798 }
799 }
800 }
801
802 if( errorcount > MAXERROR )
803 break;
804 }
805
806 return ( 0 == errorcount );
807}
808
810{
811 std::vector<RefDesInfo> excludedFootprints;
812
813 for( RefDesInfo fpData : m_frontFootprints )
814 {
815 if( fpData.Action == Exclude )
816 excludedFootprints.push_back( fpData );
817 }
818
819 for( RefDesInfo fpData : m_backFootprints )
820 {
821 if( fpData.Action == Exclude )
822 excludedFootprints.push_back( fpData );
823 }
824
825 for( RefDesInfo fpData : excludedFootprints )
826 {
827 if( fpData.Action == Exclude )
828 {
829 RefDesTypeStr* refDesInfo = GetOrBuildRefDesInfo( fpData.RefDesType );
830 refDesInfo->UnavailableRefs.insert( UTIL::GetRefDesNumber( fpData.RefDesString ) );
831 }
832 }
833}
834
835
836void DIALOG_BOARD_REANNOTATE::BuildChangeArray( std::vector<RefDesInfo>& aFootprints,
837 unsigned int aStartRefDes, const wxString& aPrefix,
838 bool aRemovePrefix,
839 std::vector<RefDesInfo>& aBadRefDes )
840{
841 size_t prefixsize = aPrefix.size();
842
843 bool haveprefix = ( 0 != prefixsize ); // Do I have a prefix?
844 bool addprefix = haveprefix & !aRemovePrefix; // Yes- and I'm not removing it
845 aRemovePrefix &= haveprefix; // Only remove if I have a prefix
846
847 bool prefixpresent; // Prefix found
848
849 wxString logstring = ( aFootprints.front().Front ) ? _( "\n\nFront Footprints" )
850 : _( "\n\nBack Footprints" );
851 LogFootprints( logstring, aFootprints );
852
853 if( 0 != aStartRefDes ) // Initialize the change array if present
854 {
855 for( size_t i = 0; i < m_refDesTypes.size(); i++ )
856 m_refDesTypes[i].LastUsedRefDes = aStartRefDes;
857 }
858
859
860 for( RefDesInfo fpData : aFootprints )
861 {
862 RefDesChange change;
863
864 change.Uuid = fpData.Uuid;
865 change.Action = fpData.Action;
866 change.OldRefDesString = fpData.RefDesString;
867 change.NewRefDes = fpData.RefDesString;
868 change.Front = fpData.Front;
869
870 if( fpData.RefDesString.IsEmpty() )
871 fpData.Action = EmptyRefDes;
872
873 if( ( change.Action == EmptyRefDes ) || ( change.Action == InvalidRefDes ) )
874 {
875 m_changeArray.push_back( change );
876 aBadRefDes.push_back( fpData );
877 continue;
878 }
879
880 if( change.Action == UpdateRefDes )
881 {
882 prefixpresent = ( 0 == fpData.RefDesType.find( aPrefix ) );
883
884 if( addprefix && !prefixpresent )
885 fpData.RefDesType.insert( 0, aPrefix ); // Add prefix once only
886
887 if( aRemovePrefix && prefixpresent ) // If there is a prefix remove it
888 fpData.RefDesType.erase( 0, prefixsize );
889
890 RefDesTypeStr* refDesInfo = GetOrBuildRefDesInfo( fpData.RefDesType, aStartRefDes );
891 unsigned int newRefDesNumber = refDesInfo->LastUsedRefDes + 1;
892
893 while( refDesInfo->UnavailableRefs.count( newRefDesNumber ) )
894 newRefDesNumber++;
895
896 change.NewRefDes = refDesInfo->RefDesType + std::to_string( newRefDesNumber );
897 refDesInfo->LastUsedRefDes = newRefDesNumber;
898 }
899
900 m_changeArray.push_back( change );
901 }
902}
903
904
906{
907 size_t i;
908
909 for( i = 0; i < m_changeArray.size(); i++ )
910 {
911 if( aFootprint->m_Uuid == m_changeArray[i].Uuid )
912 return ( &m_changeArray[i] );
913 }
914
915 ShowReport( _( "Footprint not found in changelist" ) + wxS( " " ) + aFootprint->GetReference(),
917
918 return nullptr; // Should never happen
919}
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:109
KIFACE_BASE & Kiface()
Global KIFACE_BASE "get" accessor.
wxBitmap KiBitmap(BITMAPS aBitmap, int aHeightTag)
Construct a wxBitmap from an image identifier Returns the image from the active theme if the image ha...
Definition: bitmap.cpp:105
@ reannotate_down_left
@ reannotate_up_right
@ reannotate_right_down
@ reannotate_up_left
@ reannotate_left_down
@ reannotate_down_right
@ reannotate_left_up
@ reannotate_right_up
WINDOW_SETTINGS m_Window
Definition: app_settings.h:184
virtual void Push(const wxString &aMessage=wxT("A commit"), int aCommitFlags=0) override
Revert the commit by restoring the modified items state.
FOOTPRINTS & Footprints()
Definition: board.h:307
bool IsEmpty() const
Definition: board.h:355
COMMIT & Modify(EDA_ITEM *aItem)
Create an undo entry for an item that has been already modified.
Definition: commit.h:103
Class DIALOG_BOARD_REANNOTATE_BASE.
void BuildChangeArray(std::vector< RefDesInfo > &aFootprints, unsigned int aStartRefDes, const wxString &aPrefix, bool aRemovePrefix, std::vector< RefDesInfo > &aBadRefDes)
Scan through the footprint arrays and create the from -> to array.
std::vector< wxString > m_excludeArray
std::vector< wxRadioButton * > m_scopeRadioButtons
void FilterBackPrefix(wxCommandEvent &event) override
std::vector< RefDesChange > m_changeArray
std::vector< RefDesInfo > m_frontFootprints
wxString CoordTowxString(int aX, int aY)
Convert coordinates to wxString.
void FilterPrefix(wxTextCtrl *aPrefix)
Check to make sure the prefix (if there is one) is properly constructed.
int RoundToGrid(int aCoord, int aGrid)
Round an int coordinate to a suitable grid.
void MakeSampleText(wxString &aMessage)
Make the text to summarize what is about to happen.
void BuildUnavailableRefsList()
Build list of unavailable references. E.g. unselected footprints or locked footprints.
RefDesTypeStr * GetOrBuildRefDesInfo(const wxString &aRefDesPrefix, unsigned int aStartRefDes=0)
Get the structure representing the information currently held for aRefDesPrefix or create one if it d...
void LogChangePlan(void)
Create an audit trail of the changes.
bool BuildFootprintList(std::vector< RefDesInfo > &aBadRefDes)
Build the footprint lists, sort it, filter for excludes, then build the change list.
RefDesChange * GetNewRefDes(FOOTPRINT *aFootprint)
void OnCloseClick(wxCommandEvent &event) override
bool ReannotateBoard(void)
Actually reannotate the board.
void InitValues(void)
Copy saved app settings to the dialog.
void ShowReport(const wxString &aMessage, SEVERITY aSeverity)
Break report into strings separated by and sent to the reporter.
std::vector< RefDesInfo > m_backFootprints
DIALOG_BOARD_REANNOTATE(PCB_EDIT_FRAME *aParentFrame)
void OnApplyClick(wxCommandEvent &event) override
std::vector< wxRadioButton * > m_sortButtons
void LogFootprints(const wxString &aMessage, const std::vector< RefDesInfo > &aFootprints)
Create a list of the footprints and their coordinates.
std::vector< RefDesTypeStr > m_refDesTypes
void FilterFrontPrefix(wxCommandEvent &event) override
void finishDialogSettings()
In all dialogs, we must call the same functions to fix minimal dlg size, the default position and per...
virtual APP_SETTINGS_BASE * config() const
Returns the settings object used in SaveSettings(), and is overloaded in KICAD_MANAGER_FRAME.
virtual void Refresh(bool aEraseBackground=true, const wxRect *aRect=nullptr) override
Update the board display after modifying it by a python script (note: it is automatically called by a...
KIGFX::GAL * GetGAL() const
Return a pointer to the GAL instance used in the panel.
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:85
const KIID m_Uuid
Definition: eda_item.h:492
const wxString & GetReference() const
Definition: footprint.h:519
static void BuildChoiceList(wxArrayString *aGridsList, APP_SETTINGS_BASE *aCfg, EDA_DRAW_FRAME *aParent)
Definition: grid_menu.cpp:85
APP_SETTINGS_BASE * KifaceSettings() const
Definition: kiface_base.h:93
const VECTOR2D & GetGridSize() const
Return the grid size.
virtual void Update(const VIEW_ITEM *aItem, int aUpdateFlags) const override
For dynamic VIEWs, inform the associated VIEW that the graphical representation of this item has chan...
Definition: pcb_view.cpp:92
Definition: kiid.h:47
PROJECT & Prj() const
Return a reference to the PROJECT associated with this KIWAY.
Store information read from a netlist along with the flags used to update the NETLIST in the BOARD.
Definition: pcb_netlist.h:213
DIALOG_REANNOTATE m_Reannotate
PCBNEW_SETTINGS * GetPcbNewSettings() const
PCB_DRAW_PANEL_GAL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
BOARD * GetBoard() const
virtual KIGFX::PCB_VIEW * GetView() const override
Return a pointer to the #VIEW instance used in the panel.
The main frame for Pcbnew.
void OnModify() override
Must be called after a board change to set the modified flag.
The selection tool: currently supports:
bool Empty() const
Checks if there is anything selected.
Definition: selection.h:107
Implement an OUTPUTFORMATTER to a memory buffer.
Definition: richio.h:415
TOOL_MANAGER * GetToolManager() const
Return the MVC controller.
Definition: tools_holder.h:54
wxString MessageTextFromValue(double aValue, bool aAddUnitLabel=true, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
A lower-precision version of StringFromValue().
void SetLazyUpdate(bool aLazyUpdate)
Forces updating the HTML page, after the report is built in lazy mode If aSort = true,...
void SetFileName(const wxString &aReportFileName)
void Report(const wxString &aText, SEVERITY aSeverity, REPORTER::LOCATION aLocation=REPORTER::LOC_BODY)
Reports the string.
void Flush(bool aSort=false)
Set the visible severity filter.
bool IsOK(wxWindow *aParent, const wxString &aMessage)
Display a yes/no dialog with aMessage and returns the user response.
Definition: confirm.cpp:342
This file is part of the common library.
int BackDirectionsArray[]
int FrontDirectionsArray[]
static bool ModuleCompare(const RefDesInfo &aA, const RefDesInfo &aB)
Compare function to sort footprints.
bool DescendingSecond
bool DescendingFirst
#define SetSortCodes(DirArray, Code)
bool SortYFirst
static bool ChangeArrayCompare(const RefDesChange &aA, const RefDesChange &aB)
Compare function used to compare ChangeArray element for sort.
wxString AnnotateString[]
wxString ActionMessage[]
#define MAXERROR
#define DESCENDINGFIRST
#define SORTXFIRST
@ AnnotateSelected
#define DESCENDINGSECOND
#define ASCENDINGFIRST
#define SORTYFIRST
#define ASCENDINGSECOND
#define MINGRID
#define VALIDPREFIX
#define _(s)
@ F_Cu
Definition: layer_ids.h:64
double DoubleValueFromString(const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnits, const wxString &aTextValue, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
Function DoubleValueFromString converts aTextValue to a double.
Definition: eda_units.cpp:451
int GetRefDesNumber(const wxString &aRefDes)
Get the numeric suffix from a refdes - e.g.
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition: eda_angle.h:401
BOARD * GetBoard()
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
Collection of utility functions for component reference designators (refdes)
SEVERITY
@ RPT_SEVERITY_WARNING
@ RPT_SEVERITY_ERROR
@ RPT_SEVERITY_INFO
@ RPT_SEVERITY_ACTION
int StrNumCmp(const wxString &aString1, const wxString &aString2, bool aIgnoreCase)
Compare two strings with alphanumerical content.
wxString user_grid_x
Definition: app_settings.h:54
wxString user_grid_y
Definition: app_settings.h:55
std::vector< wxString > sizes
Definition: app_settings.h:53
unsigned int LastUsedRefDes
std::set< unsigned int > UnavailableRefs
GRID_SETTINGS grid
Definition: app_settings.h:90
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition: typeinfo.h:86