KiCad PCB EDA Suite
Loading...
Searching...
No Matches
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-2023 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>
34#include <string_utils.h> // StrNumCmp
35#include <kiface_base.h>
36#include <pcbnew_settings.h>
37#include <refdes_utils.h>
38#include <tool/grid_menu.h>
40#include <wx/valtext.h>
41
42
46
47//
48// This converts the index into a sort code. Note that Back sort code will have left and
49// right swapped.
50//
52 SORTYFIRST + ASCENDINGFIRST + ASCENDINGSECOND, // "Top to bottom, left to right", // 100
53 SORTYFIRST + ASCENDINGFIRST + DESCENDINGSECOND, // "Top to bottom, right to left", // 101
54 SORTYFIRST + DESCENDINGFIRST + ASCENDINGSECOND, // "Back to Front, left to right", // 110
55 SORTYFIRST + DESCENDINGFIRST + DESCENDINGSECOND, // "Back to Front, right to left", // 111
56 SORTXFIRST + ASCENDINGFIRST + ASCENDINGSECOND, // "Left to right, Front to Back", // 000
57 SORTXFIRST + ASCENDINGFIRST + DESCENDINGSECOND, // "Left to right, Back to Front", // 001
58 SORTXFIRST + DESCENDINGFIRST + ASCENDINGSECOND, // "Right to left, Front to Back", // 010
59 SORTXFIRST + DESCENDINGFIRST + DESCENDINGSECOND // "Right to left, Back to Front", // 011
60};
61
62
63//
64// Back Left/Right is opposite because it is a mirror image (coordinates are from the top)
65//
67 SORTYFIRST + ASCENDINGFIRST + DESCENDINGSECOND, // "Top to bottom, left to right", // 101
68 SORTYFIRST + ASCENDINGFIRST + ASCENDINGSECOND, // "Top to bottom, right to left", // 100
69 SORTYFIRST + DESCENDINGFIRST + DESCENDINGSECOND, // "Bottom to top, left to right", // 111
70 SORTYFIRST + DESCENDINGFIRST + ASCENDINGSECOND, // "Bottom to top, right to left", // 110
71 SORTXFIRST + DESCENDINGFIRST + ASCENDINGSECOND, // "Left to right, top to bottom", // 010
72 SORTXFIRST + DESCENDINGFIRST + DESCENDINGSECOND, // "Left to right, bottom to top", // 011
73 SORTXFIRST + ASCENDINGFIRST + ASCENDINGSECOND, // "Right to left, top to bottom", // 000
74 SORTXFIRST + ASCENDINGFIRST + DESCENDINGSECOND // "Right to left, bottom to top", // 001
75};
76
77#define SetSortCodes( DirArray, Code ) \
78 { \
79 SortYFirst = ( ( DirArray[Code] & SORTYFIRST ) != 0 ); \
80 DescendingFirst = ( ( DirArray[Code] & DESCENDINGFIRST ) != 0 ); \
81 DescendingSecond = ( ( DirArray[Code] & DESCENDINGSECOND ) != 0 ); \
82 }
83
84
85wxString AnnotateString[] = {
86 _( "All" ), // ANNOTATE_ALL
87 _( "Only front" ), // AnnotateFront
88 _( "Only back" ), // AnnotateBack
89 _( "Only selected" ) // ANNOTATE_SELECTED
90};
91
92
93wxString ActionMessage[] = {
94 "", // UPDATE_REFDES
95 _( "Empty" ), // EMPTY_REFDES
96 _( "Invalid" ), // INVALID_REFDES
97 _( "Excluded" ) // EXCLUDE_REFDES
98};
99
100
102 : DIALOG_BOARD_REANNOTATE_BASE( aParentFrame ),
103 m_footprints( aParentFrame->GetBoard()->Footprints() )
104{
105 m_frame = aParentFrame;
107 InitValues();
108
109 // Init bitmaps associated to some wxRadioButton
110 reannotate_down_right_bitmap->SetBitmap( KiBitmapBundle( BITMAPS::reannotate_right_down ) );
111 reannotate_right_down_bitmap->SetBitmap( KiBitmapBundle( BITMAPS::reannotate_left_down ) );
112 reannotate_down_left_bitmap->SetBitmap( KiBitmapBundle( BITMAPS::reannotate_right_up ) );
113 reannotate_left_down_bitmap->SetBitmap( KiBitmapBundle( BITMAPS::reannotate_left_up ) );
114 reannotate_up_right_bitmap->SetBitmap( KiBitmapBundle( BITMAPS::reannotate_down_left ) );
115 reannotate_right_up_bitmap->SetBitmap( KiBitmapBundle( BITMAPS::reannotate_up_left ) );
116 reannotate_up_left_bitmap->SetBitmap( KiBitmapBundle( BITMAPS::reannotate_down_right ) );
117 reannotate_left_up_bitmap->SetBitmap( KiBitmapBundle( BITMAPS::reannotate_up_right ) );
118
119 m_FrontRefDesStart->SetValidator( wxTextValidator( wxFILTER_DIGITS ) );
120 m_BackRefDesStart->SetValidator( wxTextValidator( wxFILTER_DIGITS ) );
121
122 m_sdbSizerOK->SetLabel( _( "Reannotate PCB" ) );
123 m_sdbSizerCancel->SetLabel( _( "Close" ) );
124 m_sdbSizer->Layout();
125
126 m_settings = aParentFrame->config();
127 wxArrayString gridslist;
128 GRID_MENU::BuildChoiceList( &gridslist, m_settings, aParentFrame );
129
130 if( -1 == m_gridIndex ) // If no default loaded
131 m_gridIndex = m_settings->m_Window.grid.last_size_idx; // Get the current grid size
132
135
136 m_GridChoice->Set( gridslist );
137 m_GridChoice->SetSelection( m_gridIndex );
138
139 // Ensure m_sortCode is a valid value (0 .. m_sortButtons.size()-1)
140 m_sortCode = std::max( 0, m_sortCode );
141 m_sortCode = std::min( m_sortCode, (int)m_sortButtons.size()-1 );
142
143 for( wxRadioButton* button : m_sortButtons )
144 button->SetValue( false );
145
147
148 if( !m_selection.Empty() )
150
151 // Ensure m_annotationScope is a valid value (0 .. m_scopeRadioButtons.size()-1)
152 m_annotationScope = std::max( 0, m_annotationScope );
153 m_annotationScope = std::min( m_annotationScope, (int)m_scopeRadioButtons.size()-1 );
154
155 for( wxRadioButton* button : m_scopeRadioButtons )
156 button->SetValue( false );
157
158 m_scopeRadioButtons.at( m_annotationScope )->SetValue( true );
159 m_sortButtons.at( m_sortCode )->SetValue( true );
160
161 m_ExcludeList->SetToolTip( m_ExcludeListText->GetToolTipText() );
162 m_GridChoice->SetToolTip( m_SortGridText->GetToolTipText() );
163
164 m_MessageWindow->SetFileName( Prj().GetProjectPath() + wxT( "report.txt" ) );
165
167}
168
169
171{
172 GetParameters(); // Get the current menu settings
173
174 PCBNEW_SETTINGS* cfg = nullptr;
175
176 try
177 {
178 cfg = m_frame->GetPcbNewSettings();
179 }
180 catch( const std::runtime_error& e )
181 {
182 wxFAIL_MSG( e.what() );
183 }
184
185 if( cfg )
186 {
187 cfg->m_Reannotate.sort_on_fp_location = m_locationChoice->GetSelection() == 0;
191
196
199 cfg->m_Reannotate.front_prefix = m_FrontPrefix->GetValue();
200 cfg->m_Reannotate.back_prefix = m_BackPrefix->GetValue();
201 cfg->m_Reannotate.exclude_list = m_ExcludeList->GetValue();
203 }
204}
205
206
208{
210 m_locationChoice->SetSelection( cfg->m_Reannotate.sort_on_fp_location ? 0 : 1 );
214
219
222 m_FrontPrefix->SetValue( cfg->m_Reannotate.front_prefix );
223 m_BackPrefix->SetValue( cfg->m_Reannotate.back_prefix );
224 m_ExcludeList->SetValue( cfg->m_Reannotate.exclude_list );
226}
227
228
229void DIALOG_BOARD_REANNOTATE::OnCloseClick( wxCommandEvent& event )
230{
231 EndDialog( wxID_OK );
232}
233
234
235void DIALOG_BOARD_REANNOTATE::FilterPrefix( wxTextCtrl* aPrefix )
236{
237 std::string tmps = VALIDPREFIX;
238
239 if( aPrefix->GetValue().empty() )
240 return; //Should never happen
241
242 char lastc = aPrefix->GetValue().Last();
243
244 if( isalnum( (int) lastc ) )
245 return;
246
247 if( tmps.find( lastc ) != std::string::npos )
248 return;
249
250 tmps = aPrefix->GetValue();
251 aPrefix->Clear();
252 tmps.pop_back();
253 aPrefix->AppendText( tmps );
254}
255
256
258 unsigned int aStartRefDes )
259{
260 unsigned int requiredLastRef = ( aStartRefDes == 0 ? 1 : aStartRefDes ) - 1;
261
262 for( size_t i = 0; i < m_refDesTypes.size(); i++ ) // See if it is in the types array
263 {
264 if( m_refDesTypes[i].RefDesType == aRefDesPrefix ) // Found it!
265 {
266 m_refDesTypes[i].LastUsedRefDes = std::max( m_refDesTypes[i].LastUsedRefDes,
267 requiredLastRef );
268
269 return &m_refDesTypes[i];
270 }
271 }
272
273 // Wasn't in the types array so add it
274 REFDES_TYPE_STR newtype;
275 newtype.RefDesType = aRefDesPrefix;
276 newtype.LastUsedRefDes = requiredLastRef;
277 m_refDesTypes.push_back( newtype );
278
279 return &m_refDesTypes.back();
280}
281
282
284{
286}
287
288
289void DIALOG_BOARD_REANNOTATE::FilterBackPrefix( wxCommandEvent& event )
290{
292}
293
294
295void DIALOG_BOARD_REANNOTATE::OnApplyClick( wxCommandEvent& event )
296{
297 wxString warning;
298
299 if( m_frame->GetBoard()->IsEmpty() )
300 {
301 ShowReport( _( "No PCB to reannotate!" ), RPT_SEVERITY_ERROR );
302 return;
303 }
304
305 GetParameters(); // Figure out how this is to be done
306 MakeSampleText( warning );
307
308 if( !IsOK( m_frame, warning ) )
309 return;
310
311 if( ReannotateBoard() )
312 {
313 ShowReport( _( "PCB successfully reannotated" ), RPT_SEVERITY_ACTION );
314 ShowReport( _( "PCB annotation changes should be synchronized with schematic using "
315 "the \"Update Schematic from PCB\" tool." ), RPT_SEVERITY_WARNING );
316 }
317
319 m_MessageWindow->Flush( false );
320 m_frame->GetCanvas()->Refresh(); // Redraw
321 m_frame->OnModify(); // Need to save file on exit.
322}
323
324
326{
327 aMessage.Printf( _( "\n%s footprints will be reannotated." ),
329
330 if( !m_ExcludeList->GetValue().empty() )
331 {
332 aMessage += wxString::Format( _( "\nAny reference types %s will not be annotated." ),
333 m_ExcludeList->GetValue() );
334 }
335
336 if( m_ExcludeLocked->GetValue() )
337 aMessage += wxString::Format( _( "\nLocked footprints will not be annotated" ) );
338
339 if( !m_AnnotateBack->GetValue() )
340 {
341 aMessage += wxString::Format( _( "\nFront footprints will start at %s" ),
342 m_FrontRefDesStart->GetValue() );
343 }
344
345 if( !m_AnnotateFront->GetValue() )
346 {
347 bool frontPlusOne = ( 0 == wxAtoi( m_BackRefDesStart->GetValue() ) )
348 && !m_AnnotateBack->GetValue();
349
350 aMessage += wxString::Format( _( "\nBack footprints will start at %s." ),
351 frontPlusOne ? _( "the last front footprint + 1" ) :
352 m_BackRefDesStart->GetValue() );
353 }
354
355 if( !m_FrontPrefix->GetValue().empty() )
356 {
357 if( m_RemoveFrontPrefix->GetValue() )
358 {
359 aMessage += wxString::Format( _( "\nFront footprints starting with '%s' will have "
360 "the prefix removed." ),
361 m_FrontPrefix->GetValue() );
362 }
363 else
364 {
365 aMessage += wxString::Format( _( "\nFront footprints will have '%s' inserted as a "
366 "prefix." ),
367 m_FrontPrefix->GetValue() );
368 }
369 }
370
371 if( !m_BackPrefix->GetValue().empty() )
372 {
373 if( m_RemoveBackPrefix->GetValue() )
374 {
375 aMessage += wxString::Format( _( "\nBack footprints starting with '%s' will have the "
376 "prefix removed." ),
377 m_BackPrefix->GetValue() );
378 }
379 else
380 {
381 aMessage += wxString::Format( _( "\nBack footprints will have '%s' inserted as a "
382 "prefix." ),
383 m_BackPrefix->GetValue() );
384 }
385 }
386
387 bool fpLocation = m_locationChoice->GetSelection() == 0;
388
389 aMessage += wxString::Format( _( "\nPrior to sorting by %s, the coordinates of which will be "
390 "rounded to a %s, %s grid." ),
391 fpLocation ? _( "footprint location" )
392 : _( "reference designator location" ),
395
396 ShowReport( aMessage, RPT_SEVERITY_INFO );
397}
398
399
401{
402 m_sortCode = 0; // Convert radio button to sort direction code
403
404 for( wxRadioButton* sortbuttons : m_sortButtons )
405 {
406 if( sortbuttons->GetValue() )
407 break;
408
409 m_sortCode++;
410 }
411
412 if( m_sortCode >= (int) m_sortButtons.size() )
413 m_sortCode = 0;
414
416 m_backPrefixString = m_BackPrefix->GetValue();
417
418 // Get the chosen sort grid for rounding
419 m_gridIndex = m_GridChoice->GetSelection();
420
422 pcbIUScale, EDA_UNITS::MILS, m_settings->m_Window.grid.grids[m_gridIndex].x );
424 pcbIUScale, EDA_UNITS::MILS, m_settings->m_Window.grid.grids[m_gridIndex].y );
425
427
428 for( wxRadioButton* button : m_scopeRadioButtons )
429 {
430 if( button->GetValue() )
431 break;
432 else
434 }
435
436 // Ensure m_annotationScope value is valid
437 if( m_annotationScope >= (int)m_scopeRadioButtons.size() )
439
441}
442
443
444int DIALOG_BOARD_REANNOTATE::RoundToGrid( int aCoord, int aGrid )
445{
446 if( 0 == aGrid )
447 aGrid = MINGRID;
448
449 int rounder;
450 rounder = aCoord % aGrid;
451 aCoord -= rounder;
452
453 if( abs( rounder ) > ( aGrid / 2 ) )
454 aCoord += ( aCoord < 0 ? -aGrid : aGrid );
455
456 return ( aCoord );
457}
458
459
462static bool ChangeArrayCompare( const REFDES_CHANGE& aA, const REFDES_CHANGE& aB )
463{
464 return ( StrNumCmp( aA.OldRefDesString, aB.OldRefDesString ) < 0 );
465}
466
467
470static bool ModuleCompare( const REFDES_INFO& aA, const REFDES_INFO& aB )
471{
472 int X0 = aA.roundedx, X1 = aB.roundedx, Y0 = aA.roundedy, Y1 = aB.roundedy;
473
474 if( SortYFirst ) //If sorting by Y then X, swap X and Y
475 {
476 std::swap( X0, Y0 );
477 std::swap( X1, Y1 );
478 }
479
480 // If descending, same compare just swap directions
481 if( DescendingFirst )
482 std::swap( X0, X1 );
483
484 if( DescendingSecond )
485 std::swap( Y0, Y1 );
486
487 if( X0 < X1 )
488 return ( true ); // yes, its smaller
489
490 if( X0 > X1 )
491 return ( false ); // No its not
492
493 if( Y0 < Y1 )
494 return ( true ); // same but equal
495
496 return ( false );
497}
498
499
501{
502 return wxString::Format( wxT( "%s, %s" ),
505}
506
507
508void DIALOG_BOARD_REANNOTATE::ShowReport( const wxString& aMessage, SEVERITY aSeverity )
509{
510 wxStringTokenizer msgs( aMessage, wxT( "\n" ) );
511
512 while( msgs.HasMoreTokens() )
513 {
514 m_MessageWindow->Report( msgs.GetNextToken(), aSeverity );
515 }
516}
517
518
520{
521 int i = 1;
522 wxString message;
523
524 message.Printf( _( "\n\nThere are %i types of reference designations\n"
525 "**********************************************************\n" ),
526 (int) m_refDesTypes.size() );
527
528 for( REFDES_TYPE_STR Type : m_refDesTypes ) // Show all the types of refdes
529 message += Type.RefDesType + ( 0 == ( i++ % 16 ) ? wxT( "\n" ) : wxS( " " ) );
530
531 if( !m_excludeArray.empty() )
532 {
533 wxString excludes;
534
535 for( wxString& exclude : m_excludeArray ) // Show the refdes we are excluding
536 excludes += exclude + wxS( " " );
537
538 message += wxString::Format( _( "\nExcluding: %s from reannotation\n\n" ), excludes );
539 }
540
541 message += _( "\n Change Array\n***********************\n" );
542
543 for( const REFDES_CHANGE& change : m_changeArray )
544 {
545 message += wxString::Format( wxT( "%s -> %s %s %s\n" ),
546 change.OldRefDesString,
547 change.NewRefDes,
548 ActionMessage[change.Action],
549 UPDATE_REFDES != change.Action ? _( " will be ignored" )
550 : wxString( wxT( "" ) ) );
551 }
552
553 ShowReport( message, RPT_SEVERITY_INFO );
554}
555
556
557void DIALOG_BOARD_REANNOTATE::LogFootprints( const wxString& aMessage,
558 const std::vector<REFDES_INFO>& aFootprints )
559{
560 wxString message = aMessage;
561
562 if( aFootprints.empty() )
563 message += _( "\nNo footprints" );
564 else
565 {
566 int i = 1;
567 bool fpLocations = m_locationChoice->GetSelection() == 0;
568
569 message += wxString::Format( _( "\n*********** Sort on %s ***********" ),
570 fpLocations ? _( "Footprint Coordinates" )
571 : _( "Reference Designator Coordinates" ) );
572
573 message += wxString::Format( _( "\nSort Code %d" ), m_sortCode );
574
575 for( const REFDES_INFO& mod : aFootprints )
576 {
577 message += wxString::Format( _( "\n%d %s UUID: [%s], X, Y: %s, Rounded X, Y, %s" ),
578 i++,
579 mod.RefDesString,
580 mod.Uuid.AsString(),
581 CoordTowxString( mod.x, mod.y ),
582 CoordTowxString( mod.roundedx, mod.roundedy ) );
583 }
584 }
585
586 ShowReport( message, RPT_SEVERITY_INFO );
587}
588
589
591{
592 std::vector<REFDES_INFO> BadRefDes;
593 wxString message, badrefdes;
594 STRING_FORMATTER stringformatter;
595 REFDES_CHANGE* newref;
597
598 if( !BuildFootprintList( BadRefDes ) )
599 {
600 ShowReport( _( "Selected options resulted in errors! Change them and try again." ),
602 return false;
603 }
604
605 if( !BadRefDes.empty() )
606 {
607 message.Printf( _( "\nPCB has %d empty or invalid reference designations."
608 "\nRecommend running DRC with 'Test for parity between PCB and "
609 "schematic' checked.\n" ),
610 (int) BadRefDes.size() );
611
612 for( const REFDES_INFO& mod : BadRefDes )
613 {
614 badrefdes += wxString::Format( _( "\nRefDes: %s Footprint: %s:%s at %s on PCB." ),
615 mod.RefDesString,
616 mod.FPID.GetLibNickname().wx_str(),
617 mod.FPID.GetLibItemName().wx_str(),
618 CoordTowxString( mod.x, mod.y ) );
619 }
620
621 ShowReport( message + badrefdes + wxT( "\n" ), RPT_SEVERITY_WARNING );
622 message += _( "Reannotate anyway?" );
623
624 if( !IsOK( m_frame, message ) )
625 return false;
626 }
627
628 BOARD_COMMIT commit( m_frame );
629
630 for( FOOTPRINT* footprint : m_footprints )
631 {
632 newref = GetNewRefDes( footprint );
633
634 if( nullptr == newref )
635 return false;
636
637 commit.Modify( footprint ); // Make a copy for undo
638 footprint->SetReference( newref->NewRefDes ); // Update the PCB reference
639 m_frame->GetCanvas()->GetView()->Update( footprint ); // Touch the footprint
640 }
641
642 commit.Push( wxT( "Geographic reannotation" ) );
643 return true;
644}
645
646
647bool DIALOG_BOARD_REANNOTATE::BuildFootprintList( std::vector<REFDES_INFO>& aBadRefDes )
648{
649 bool annotateSelected = m_AnnotateSelection->GetValue();
650 bool annotateFront = m_AnnotateFront->GetValue(); // Unless only doing back
651 bool annotateBack = m_AnnotateBack->GetValue(); // Unless only doing front
652 bool skipLocked = m_ExcludeLocked->GetValue();
653
654 int errorcount = 0;
655 size_t firstnum = 0;
656
657 m_frontFootprints.clear();
658 m_backFootprints.clear();
659 m_excludeArray.clear();
661
662 std::vector<KIID> selected;
663
664 if( annotateSelected )
665 {
666 for( EDA_ITEM* item : m_selection )
667 {
668 // Get the timestamps of selected footprints
669 if( item->Type() == PCB_FOOTPRINT_T )
670 selected.push_back( item->m_Uuid );
671 }
672 }
673
674 wxString exclude;
675
676 // Break exclude list into words.
677 for( auto thischar : m_ExcludeList->GetValue() )
678 {
679 if( ( ' ' == thischar ) || ( ',' == thischar ) )
680 {
681 m_excludeArray.push_back( exclude );
682 exclude.clear();
683 }
684 else
685 exclude += thischar;
686
687 if( !exclude.empty() )
688 m_excludeArray.push_back( exclude );
689 }
690
691 REFDES_INFO fpData;
692 bool useModuleLocation = m_locationChoice->GetSelection() == 0;
693
694 for( FOOTPRINT* footprint : m_footprints )
695 {
696 fpData.Uuid = footprint->m_Uuid;
697 fpData.RefDesString = footprint->GetReference();
698 fpData.FPID = footprint->GetFPID();
699 fpData.x = useModuleLocation ? footprint->GetPosition().x
700 : footprint->Reference().GetPosition().x;
701 fpData.y = useModuleLocation ? footprint->GetPosition().y
702 : footprint->Reference().GetPosition().y;
703 fpData.roundedx = RoundToGrid( fpData.x, m_sortGridx ); // Round to sort
704 fpData.roundedy = RoundToGrid( fpData.y, m_sortGridy );
705 fpData.Front = footprint->GetLayer() == F_Cu;
706 fpData.Action = UPDATE_REFDES; // Usually good
707
708 if( fpData.RefDesString.IsEmpty() )
709 {
710 fpData.Action = EMPTY_REFDES;
711 }
712 else
713 {
714 firstnum = fpData.RefDesString.find_first_of( wxT( "0123456789" ) );
715
716 if( std::string::npos == firstnum )
717 fpData.Action = INVALID_REFDES; // do not change ref des such as 12 or +1, or L
718 }
719
720 // Get the type (R, C, etc)
721 fpData.RefDesType = fpData.RefDesString.substr( 0, firstnum );
722
723 for( wxString excluded : m_excludeArray )
724 {
725 if( excluded == fpData.RefDesType ) // Am I supposed to exclude this type?
726 {
727 fpData.Action = EXCLUDE_REFDES; // Yes
728 break;
729 }
730 }
731
732 if(( fpData.Front && annotateBack ) || // If a front fp and doing backs only
733 ( !fpData.Front && annotateFront ) || // If a back fp and doing front only
734 ( footprint->IsLocked() && skipLocked ) ) // If excluding locked and it is locked
735 {
736 fpData.Action = EXCLUDE_REFDES;
737 }
738
739 if( annotateSelected )
740 { // If only annotating selected c
741 fpData.Action = EXCLUDE_REFDES; // Assume it isn't selected
742
743 for( KIID sel : selected )
744 {
745 if( fpData.Uuid == sel )
746 { // Found in selected footprints
747 fpData.Action = UPDATE_REFDES; // Update it
748 break;
749 }
750 }
751 }
752
753 if( fpData.Front )
754 m_frontFootprints.push_back( fpData );
755 else
756 m_backFootprints.push_back( fpData );
757 }
758
759 // Determine the sort order for the front.
761
762 // Sort the front footprints.
763 sort( m_frontFootprints.begin(), m_frontFootprints.end(), ModuleCompare );
764
765 // Determine the sort order for the back.
767
768 // Sort the back footprints.
769 sort( m_backFootprints.begin(), m_backFootprints.end(), ModuleCompare );
770
771 m_refDesTypes.clear();
772 m_changeArray.clear();
773
775
776 if( !m_frontFootprints.empty() )
777 {
779 m_FrontPrefix->GetValue(), m_RemoveFrontPrefix->GetValue(), aBadRefDes );
780 }
781
782 if( !m_backFootprints.empty() )
783 {
785 m_BackPrefix->GetValue(), m_RemoveBackPrefix->GetValue(), aBadRefDes );
786 }
787
788 if( !m_changeArray.empty() )
789 sort( m_changeArray.begin(), m_changeArray.end(), ChangeArrayCompare );
790
792
793 size_t changearraysize = m_changeArray.size();
794
795 for( size_t i = 0; i < changearraysize; i++ ) // Scan through for duplicates if update or skip
796 {
797 if( ( m_changeArray[i].Action != EMPTY_REFDES )
798 && ( m_changeArray[i].Action != INVALID_REFDES ) )
799 {
800 for( size_t j = i + 1; j < changearraysize; j++ )
801 {
802 if( m_changeArray[i].NewRefDes == m_changeArray[j].NewRefDes )
803 {
804 ShowReport( wxString::Format( _( "Duplicate instances of %s" ),
805 m_changeArray[j].NewRefDes ),
807
808 if( errorcount++ > MAXERROR )
809 {
810 ShowReport( _( "Aborted: too many errors" ), RPT_SEVERITY_ERROR );
811 break;
812 }
813 }
814 }
815 }
816
817 if( errorcount > MAXERROR )
818 break;
819 }
820
821 return ( 0 == errorcount );
822}
823
825{
826 std::vector<REFDES_INFO> excludedFootprints;
827
828 for( REFDES_INFO fpData : m_frontFootprints )
829 {
830 if( fpData.Action == EXCLUDE_REFDES )
831 excludedFootprints.push_back( fpData );
832 }
833
834 for( REFDES_INFO fpData : m_backFootprints )
835 {
836 if( fpData.Action == EXCLUDE_REFDES )
837 excludedFootprints.push_back( fpData );
838 }
839
840 for( REFDES_INFO fpData : excludedFootprints )
841 {
842 if( fpData.Action == EXCLUDE_REFDES )
843 {
844 REFDES_TYPE_STR* refDesInfo = GetOrBuildRefDesInfo( fpData.RefDesType );
845 refDesInfo->UnavailableRefs.insert( UTIL::GetRefDesNumber( fpData.RefDesString ) );
846 }
847 }
848}
849
850
851void DIALOG_BOARD_REANNOTATE::BuildChangeArray( std::vector<REFDES_INFO>& aFootprints,
852 unsigned int aStartRefDes, const wxString& aPrefix,
853 bool aRemovePrefix,
854 std::vector<REFDES_INFO>& aBadRefDes )
855{
856 size_t prefixsize = aPrefix.size();
857
858 bool haveprefix = ( 0 != prefixsize ); // Do I have a prefix?
859 bool addprefix = haveprefix & !aRemovePrefix; // Yes- and I'm not removing it
860 aRemovePrefix &= haveprefix; // Only remove if I have a prefix
861
862 bool prefixpresent; // Prefix found
863
864 wxString logstring = ( aFootprints.front().Front ) ? _( "\n\nFront Footprints" )
865 : _( "\n\nBack Footprints" );
866 LogFootprints( logstring, aFootprints );
867
868 if( 0 != aStartRefDes ) // Initialize the change array if present
869 {
870 for( size_t i = 0; i < m_refDesTypes.size(); i++ )
871 m_refDesTypes[i].LastUsedRefDes = aStartRefDes;
872 }
873
874
875 for( REFDES_INFO fpData : aFootprints )
876 {
877 REFDES_CHANGE change;
878
879 change.Uuid = fpData.Uuid;
880 change.Action = fpData.Action;
881 change.OldRefDesString = fpData.RefDesString;
882 change.NewRefDes = fpData.RefDesString;
883 change.Front = fpData.Front;
884
885 if( fpData.RefDesString.IsEmpty() )
886 fpData.Action = EMPTY_REFDES;
887
888 if( ( change.Action == EMPTY_REFDES ) || ( change.Action == INVALID_REFDES ) )
889 {
890 m_changeArray.push_back( change );
891 aBadRefDes.push_back( fpData );
892 continue;
893 }
894
895 if( change.Action == UPDATE_REFDES )
896 {
897 prefixpresent = ( fpData.RefDesType.find( aPrefix ) == 0 );
898
899 if( addprefix && !prefixpresent )
900 fpData.RefDesType.insert( 0, aPrefix ); // Add prefix once only
901
902 if( aRemovePrefix && prefixpresent ) // If there is a prefix remove it
903 fpData.RefDesType.erase( 0, prefixsize );
904
905 REFDES_TYPE_STR* refDesInfo = GetOrBuildRefDesInfo( fpData.RefDesType, aStartRefDes );
906 unsigned int newRefDesNumber = refDesInfo->LastUsedRefDes + 1;
907
908 while( refDesInfo->UnavailableRefs.count( newRefDesNumber ) )
909 newRefDesNumber++;
910
911 change.NewRefDes = refDesInfo->RefDesType + std::to_string( newRefDesNumber );
912 refDesInfo->LastUsedRefDes = newRefDesNumber;
913 }
914
915 m_changeArray.push_back( change );
916 }
917}
918
919
921{
922 size_t i;
923
924 for( i = 0; i < m_changeArray.size(); i++ )
925 {
926 if( aFootprint->m_Uuid == m_changeArray[i].Uuid )
927 return ( &m_changeArray[i] );
928 }
929
930 ShowReport( _( "Footprint not found in changelist" ) + wxS( " " ) + aFootprint->GetReference(),
932
933 return nullptr; // Should never happen
934}
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:108
KIFACE_BASE & Kiface()
Global KIFACE_BASE "get" accessor.
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap)
Definition: bitmap.cpp:110
WINDOW_SETTINGS m_Window
Definition: app_settings.h:172
virtual void Push(const wxString &aMessage=wxEmptyString, int aCommitFlags=0) override
Revert the commit by restoring the modified items state.
const FOOTPRINTS & Footprints() const
Definition: board.h:323
bool IsEmpty() const
Definition: board.h:378
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Create an undo entry for an item that has been already modified.
Definition: commit.h:105
Class DIALOG_BOARD_REANNOTATE_BASE.
std::vector< REFDES_CHANGE > m_changeArray
std::vector< wxString > m_excludeArray
bool BuildFootprintList(std::vector< REFDES_INFO > &aBadRefDes)
Build the footprint lists, sort it, filter for excludes, then build the change list.
std::vector< wxRadioButton * > m_scopeRadioButtons
void FilterBackPrefix(wxCommandEvent &event) override
REFDES_TYPE_STR * 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 LogFootprints(const wxString &aMessage, const std::vector< REFDES_INFO > &aFootprints)
Create a list of the footprints and their coordinates.
std::vector< REFDES_INFO > m_frontFootprints
std::vector< REFDES_TYPE_STR > m_refDesTypes
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.
void BuildChangeArray(std::vector< REFDES_INFO > &aFootprints, unsigned int aStartRefDes, const wxString &aPrefix, bool aRemovePrefix, std::vector< REFDES_INFO > &aBadRefDes)
Scan through the footprint arrays and create the from -> to array.
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.
void LogChangePlan(void)
Create an audit trail of the changes.
void OnCloseClick(wxCommandEvent &event) override
REFDES_CHANGE * GetNewRefDes(FOOTPRINT *aFootprint)
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< REFDES_INFO > m_backFootprints
DIALOG_BOARD_REANNOTATE(PCB_EDIT_FRAME *aParentFrame)
void OnApplyClick(wxCommandEvent &event) override
std::vector< wxRadioButton * > m_sortButtons
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
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:88
const KIID m_Uuid
Definition: eda_item.h:485
const wxString & GetReference() const
Definition: footprint.h:589
static void BuildChoiceList(wxArrayString *aGridsList, APP_SETTINGS_BASE *aCfg, EDA_DRAW_FRAME *aParent)
Definition: grid_menu.cpp:83
APP_SETTINGS_BASE * KifaceSettings() const
Definition: kiface_base.h:95
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:75
Definition: kiid.h:49
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:223
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:110
Implement an OUTPUTFORMATTER to a memory buffer.
Definition: richio.h:433
TOOL_MANAGER * GetToolManager() const
Return the MVC controller.
Definition: tools_holder.h:55
wxString MessageTextFromValue(double aValue, bool aAddUnitLabel=true, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE) const
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:250
This file is part of the common library.
int BackDirectionsArray[]
int FrontDirectionsArray[]
bool DescendingSecond
static bool ModuleCompare(const REFDES_INFO &aA, const REFDES_INFO &aB)
Compare function to sort footprints.
bool DescendingFirst
#define SetSortCodes(DirArray, Code)
static bool ChangeArrayCompare(const REFDES_CHANGE &aA, const REFDES_CHANGE &aB)
Compare function used to compare ChangeArray element for sort.
bool SortYFirst
wxString AnnotateString[]
wxString ActionMessage[]
#define MAXERROR
#define DESCENDINGFIRST
#define SORTXFIRST
@ INVALID_REFDES
@ EXCLUDE_REFDES
#define DESCENDINGSECOND
@ ANNOTATE_SELECTED
#define ASCENDINGFIRST
#define SORTYFIRST
#define ASCENDINGSECOND
#define MINGRID
#define VALIDPREFIX
#define _(s)
@ F_Cu
Definition: layer_ids.h:64
KICOMMON_API 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:576
int GetRefDesNumber(const wxString &aRefDes)
Get the numeric suffix from a refdes - e.g.
BOARD * GetBoard()
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.
std::vector< GRID > grids
Definition: grid_settings.h:66
std::set< unsigned int > UnavailableRefs
GRID_SETTINGS grid
Definition: app_settings.h:81
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition: typeinfo.h:86