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( KiBitmap( BITMAPS::reannotate_right_down ) );
111 reannotate_right_down_bitmap->SetBitmap( KiBitmap( BITMAPS::reannotate_left_down ) );
112 reannotate_down_left_bitmap->SetBitmap( KiBitmap( BITMAPS::reannotate_right_up ) );
113 reannotate_left_down_bitmap->SetBitmap( KiBitmap( BITMAPS::reannotate_left_up ) );
114 reannotate_up_right_bitmap->SetBitmap( KiBitmap( BITMAPS::reannotate_down_left ) );
115 reannotate_right_up_bitmap->SetBitmap( KiBitmap( BITMAPS::reannotate_up_left ) );
116 reannotate_up_left_bitmap->SetBitmap( KiBitmap( BITMAPS::reannotate_down_right ) );
117 reannotate_left_up_bitmap->SetBitmap( KiBitmap( 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
174 cfg->m_Reannotate.sort_on_fp_location = m_locationChoice->GetSelection() == 0;
178
183
186 cfg->m_Reannotate.front_prefix = m_FrontPrefix->GetValue();
187 cfg->m_Reannotate.back_prefix = m_BackPrefix->GetValue();
188 cfg->m_Reannotate.exclude_list = m_ExcludeList->GetValue();
190}
191
192
194{
196 m_locationChoice->SetSelection( cfg->m_Reannotate.sort_on_fp_location ? 0 : 1 );
200
205
208 m_FrontPrefix->SetValue( cfg->m_Reannotate.front_prefix );
209 m_BackPrefix->SetValue( cfg->m_Reannotate.back_prefix );
210 m_ExcludeList->SetValue( cfg->m_Reannotate.exclude_list );
212}
213
214
215void DIALOG_BOARD_REANNOTATE::OnCloseClick( wxCommandEvent& event )
216{
217 EndDialog( wxID_OK );
218}
219
220
221void DIALOG_BOARD_REANNOTATE::FilterPrefix( wxTextCtrl* aPrefix )
222{
223 std::string tmps = VALIDPREFIX;
224
225 if( aPrefix->GetValue().empty() )
226 return; //Should never happen
227
228 char lastc = aPrefix->GetValue().Last();
229
230 if( isalnum( (int) lastc ) )
231 return;
232
233 if( std::string::npos != tmps.find( lastc ) )
234 return;
235
236 tmps = aPrefix->GetValue();
237 aPrefix->Clear();
238 tmps.pop_back();
239 aPrefix->AppendText( tmps );
240}
241
242
244 unsigned int aStartRefDes )
245{
246 unsigned int requiredLastRef = ( aStartRefDes == 0 ? 1 : aStartRefDes ) - 1;
247
248 for( size_t i = 0; i < m_refDesTypes.size(); i++ ) // See if it is in the types array
249 {
250 if( m_refDesTypes[i].RefDesType == aRefDesPrefix ) // Found it!
251 {
252 m_refDesTypes[i].LastUsedRefDes = std::max( m_refDesTypes[i].LastUsedRefDes,
253 requiredLastRef );
254
255 return &m_refDesTypes[i];
256 }
257 }
258
259 // Wasn't in the types array so add it
260 REFDES_TYPE_STR newtype;
261 newtype.RefDesType = aRefDesPrefix;
262 newtype.LastUsedRefDes = requiredLastRef;
263 m_refDesTypes.push_back( newtype );
264
265 return &m_refDesTypes.back();
266}
267
268
270{
272}
273
274
275void DIALOG_BOARD_REANNOTATE::FilterBackPrefix( wxCommandEvent& event )
276{
278}
279
280
281void DIALOG_BOARD_REANNOTATE::OnApplyClick( wxCommandEvent& event )
282{
283 wxString warning;
284
285 if( m_frame->GetBoard()->IsEmpty() )
286 {
287 ShowReport( _( "No PCB to reannotate!" ), RPT_SEVERITY_ERROR );
288 return;
289 }
290
291 GetParameters(); // Figure out how this is to be done
292 MakeSampleText( warning );
293
294 if( !IsOK( m_frame, warning ) )
295 return;
296
297 if( ReannotateBoard() )
298 {
299 ShowReport( _( "PCB successfully reannotated" ), RPT_SEVERITY_ACTION );
300 ShowReport( _( "PCB annotation changes should be synchronized with schematic using "
301 "the \"Update Schematic from PCB\" tool." ), RPT_SEVERITY_WARNING );
302 }
303
305 m_MessageWindow->Flush( false );
306 m_frame->GetCanvas()->Refresh(); // Redraw
307 m_frame->OnModify(); // Need to save file on exit.
308}
309
310
312{
313 aMessage.Printf( _( "\n%s footprints will be reannotated." ),
315
316 if( !m_ExcludeList->GetValue().empty() )
317 {
318 aMessage += wxString::Format( _( "\nAny reference types %s will not be annotated." ),
319 m_ExcludeList->GetValue() );
320 }
321
322 if( m_ExcludeLocked->GetValue() )
323 aMessage += wxString::Format( _( "\nLocked footprints will not be annotated" ) );
324
325 if( !m_AnnotateBack->GetValue() )
326 {
327 aMessage += wxString::Format( _( "\nFront footprints will start at %s" ),
328 m_FrontRefDesStart->GetValue() );
329 }
330
331 if( !m_AnnotateFront->GetValue() )
332 {
333 bool frontPlusOne = ( 0 == wxAtoi( m_BackRefDesStart->GetValue() ) )
334 && !m_AnnotateBack->GetValue();
335
336 aMessage += wxString::Format( _( "\nBack footprints will start at %s." ),
337 frontPlusOne ? _( "the last front footprint + 1" ) :
338 m_BackRefDesStart->GetValue() );
339 }
340
341 if( !m_FrontPrefix->GetValue().empty() )
342 {
343 if( m_RemoveFrontPrefix->GetValue() )
344 {
345 aMessage += wxString::Format( _( "\nFront footprints starting with '%s' will have "
346 "the prefix removed." ),
347 m_FrontPrefix->GetValue() );
348 }
349 else
350 {
351 aMessage += wxString::Format( _( "\nFront footprints will have '%s' inserted as a "
352 "prefix." ),
353 m_FrontPrefix->GetValue() );
354 }
355 }
356
357 if( !m_BackPrefix->GetValue().empty() )
358 {
359 if( m_RemoveBackPrefix->GetValue() )
360 {
361 aMessage += wxString::Format( _( "\nBack footprints starting with '%s' will have the "
362 "prefix removed." ),
363 m_BackPrefix->GetValue() );
364 }
365 else
366 {
367 aMessage += wxString::Format( _( "\nBack footprints will have '%s' inserted as a "
368 "prefix." ),
369 m_BackPrefix->GetValue() );
370 }
371 }
372
373 bool fpLocation = m_locationChoice->GetSelection() == 0;
374
375 aMessage += wxString::Format( _( "\nPrior to sorting by %s, the coordinates of which will be "
376 "rounded to a %s, %s grid." ),
377 fpLocation ? _( "footprint location" )
378 : _( "reference designator location" ),
381
382 ShowReport( aMessage, RPT_SEVERITY_INFO );
383}
384
385
387{
388 m_sortCode = 0; // Convert radio button to sort direction code
389
390 for( wxRadioButton* sortbuttons : m_sortButtons )
391 {
392 if( sortbuttons->GetValue() )
393 break;
394
395 m_sortCode++;
396 }
397
398 if( m_sortCode >= (int) m_sortButtons.size() )
399 m_sortCode = 0;
400
402 m_backPrefixString = m_BackPrefix->GetValue();
403
404 // Get the chosen sort grid for rounding
405 m_gridIndex = m_GridChoice->GetSelection();
406
408 pcbIUScale, EDA_UNITS::MILS, m_settings->m_Window.grid.grids[m_gridIndex].x );
410 pcbIUScale, EDA_UNITS::MILS, m_settings->m_Window.grid.grids[m_gridIndex].y );
411
413
414 for( wxRadioButton* button : m_scopeRadioButtons )
415 {
416 if( button->GetValue() )
417 break;
418 else
420 }
421
422 // Ensure m_annotationScope value is valid
423 if( m_annotationScope >= (int)m_scopeRadioButtons.size() )
425
427}
428
429
430int DIALOG_BOARD_REANNOTATE::RoundToGrid( int aCoord, int aGrid )
431{
432 if( 0 == aGrid )
433 aGrid = MINGRID;
434
435 int rounder;
436 rounder = aCoord % aGrid;
437 aCoord -= rounder;
438
439 if( abs( rounder ) > ( aGrid / 2 ) )
440 aCoord += ( aCoord < 0 ? -aGrid : aGrid );
441
442 return ( aCoord );
443}
444
445
448static bool ChangeArrayCompare( const REFDES_CHANGE& aA, const REFDES_CHANGE& aB )
449{
450 return ( StrNumCmp( aA.OldRefDesString, aB.OldRefDesString ) < 0 );
451}
452
453
456static bool ModuleCompare( const REFDES_INFO& aA, const REFDES_INFO& aB )
457{
458 int X0 = aA.roundedx, X1 = aB.roundedx, Y0 = aA.roundedy, Y1 = aB.roundedy;
459
460 if( SortYFirst ) //If sorting by Y then X, swap X and Y
461 {
462 std::swap( X0, Y0 );
463 std::swap( X1, Y1 );
464 }
465
466 // If descending, same compare just swap directions
467 if( DescendingFirst )
468 std::swap( X0, X1 );
469
470 if( DescendingSecond )
471 std::swap( Y0, Y1 );
472
473 if( X0 < X1 )
474 return ( true ); // yes, its smaller
475
476 if( X0 > X1 )
477 return ( false ); // No its not
478
479 if( Y0 < Y1 )
480 return ( true ); // same but equal
481
482 return ( false );
483}
484
485
487{
488 return wxString::Format( wxT( "%s, %s" ),
491}
492
493
494void DIALOG_BOARD_REANNOTATE::ShowReport( const wxString& aMessage, SEVERITY aSeverity )
495{
496 wxStringTokenizer msgs( aMessage, wxT( "\n" ) );
497
498 while( msgs.HasMoreTokens() )
499 {
500 m_MessageWindow->Report( msgs.GetNextToken(), aSeverity );
501 }
502}
503
504
506{
507 int i = 1;
508 wxString message;
509
510 message.Printf( _( "\n\nThere are %i types of reference designations\n"
511 "**********************************************************\n" ),
512 (int) m_refDesTypes.size() );
513
514 for( REFDES_TYPE_STR Type : m_refDesTypes ) // Show all the types of refdes
515 message += Type.RefDesType + ( 0 == ( i++ % 16 ) ? wxT( "\n" ) : wxS( " " ) );
516
517 if( !m_excludeArray.empty() )
518 {
519 wxString excludes;
520
521 for( wxString& exclude : m_excludeArray ) // Show the refdes we are excluding
522 excludes += exclude + wxS( " " );
523
524 message += wxString::Format( _( "\nExcluding: %s from reannotation\n\n" ), excludes );
525 }
526
527 message += _( "\n Change Array\n***********************\n" );
528
529 for( const REFDES_CHANGE& change : m_changeArray )
530 {
531 message += wxString::Format( wxT( "%s -> %s %s %s\n" ),
532 change.OldRefDesString,
533 change.NewRefDes,
534 ActionMessage[change.Action],
535 UPDATE_REFDES != change.Action ? _( " will be ignored" )
536 : wxString( wxT( "" ) ) );
537 }
538
539 ShowReport( message, RPT_SEVERITY_INFO );
540}
541
542
543void DIALOG_BOARD_REANNOTATE::LogFootprints( const wxString& aMessage,
544 const std::vector<REFDES_INFO>& aFootprints )
545{
546 wxString message = aMessage;
547
548 if( aFootprints.empty() )
549 message += _( "\nNo footprints" );
550 else
551 {
552 int i = 1;
553 bool fpLocations = m_locationChoice->GetSelection() == 0;
554
555 message += wxString::Format( _( "\n*********** Sort on %s ***********" ),
556 fpLocations ? _( "Footprint Coordinates" )
557 : _( "Reference Designator Coordinates" ) );
558
559 message += wxString::Format( _( "\nSort Code %d" ), m_sortCode );
560
561 for( const REFDES_INFO& mod : aFootprints )
562 {
563 message += wxString::Format( _( "\n%d %s UUID: [%s], X, Y: %s, Rounded X, Y, %s" ),
564 i++,
565 mod.RefDesString,
566 mod.Uuid.AsString(),
567 CoordTowxString( mod.x, mod.y ),
568 CoordTowxString( mod.roundedx, mod.roundedy ) );
569 }
570 }
571
572 ShowReport( message, RPT_SEVERITY_INFO );
573}
574
575
577{
578 std::vector<REFDES_INFO> BadRefDes;
579 wxString message, badrefdes;
580 STRING_FORMATTER stringformatter;
581 REFDES_CHANGE* newref;
583
584 if( !BuildFootprintList( BadRefDes ) )
585 {
586 ShowReport( _( "Selected options resulted in errors! Change them and try again." ),
588 return false;
589 }
590
591 if( !BadRefDes.empty() )
592 {
593 message.Printf( _( "\nPCB has %d empty or invalid reference designations."
594 "\nRecommend running DRC with 'Test for parity between PCB and "
595 "schematic' checked.\n" ),
596 (int) BadRefDes.size() );
597
598 for( const REFDES_INFO& mod : BadRefDes )
599 {
600 badrefdes += wxString::Format( _( "\nRefDes: %s Footprint: %s:%s at %s on PCB." ),
601 mod.RefDesString,
602 mod.FPID.GetLibNickname().wx_str(),
603 mod.FPID.GetLibItemName().wx_str(),
604 CoordTowxString( mod.x, mod.y ) );
605 }
606
607 ShowReport( message + badrefdes + wxT( "\n" ), RPT_SEVERITY_WARNING );
608 message += _( "Reannotate anyway?" );
609
610 if( !IsOK( m_frame, message ) )
611 return false;
612 }
613
614 BOARD_COMMIT commit( m_frame );
615
616 for( FOOTPRINT* footprint : m_footprints )
617 {
618 newref = GetNewRefDes( footprint );
619
620 if( nullptr == newref )
621 return false;
622
623 commit.Modify( footprint ); // Make a copy for undo
624 footprint->SetReference( newref->NewRefDes ); // Update the PCB reference
625 m_frame->GetCanvas()->GetView()->Update( footprint ); // Touch the footprint
626 }
627
628 commit.Push( wxT( "Geographic reannotation" ) );
629 return true;
630}
631
632
633bool DIALOG_BOARD_REANNOTATE::BuildFootprintList( std::vector<REFDES_INFO>& aBadRefDes )
634{
635 bool annotateSelected = m_AnnotateSelection->GetValue();
636 bool annotateFront = m_AnnotateFront->GetValue(); // Unless only doing back
637 bool annotateBack = m_AnnotateBack->GetValue(); // Unless only doing front
638 bool skipLocked = m_ExcludeLocked->GetValue();
639
640 int errorcount = 0;
641 size_t firstnum = 0;
642
643 m_frontFootprints.clear();
644 m_backFootprints.clear();
645 m_excludeArray.clear();
647
648 std::vector<KIID> selected;
649
650 if( annotateSelected )
651 {
652 for( EDA_ITEM* item : m_selection )
653 {
654 // Get the timestamps of selected footprints
655 if( item->Type() == PCB_FOOTPRINT_T )
656 selected.push_back( item->m_Uuid );
657 }
658 }
659
660 wxString exclude;
661
662 // Break exclude list into words.
663 for( auto thischar : m_ExcludeList->GetValue() )
664 {
665 if( ( ' ' == thischar ) || ( ',' == thischar ) )
666 {
667 m_excludeArray.push_back( exclude );
668 exclude.clear();
669 }
670 else
671 exclude += thischar;
672
673 if( !exclude.empty() )
674 m_excludeArray.push_back( exclude );
675 }
676
677 REFDES_INFO fpData;
678 bool useModuleLocation = m_locationChoice->GetSelection() == 0;
679
680 for( FOOTPRINT* footprint : m_footprints )
681 {
682 fpData.Uuid = footprint->m_Uuid;
683 fpData.RefDesString = footprint->GetReference();
684 fpData.FPID = footprint->GetFPID();
685 fpData.x = useModuleLocation ? footprint->GetPosition().x
686 : footprint->Reference().GetPosition().x;
687 fpData.y = useModuleLocation ? footprint->GetPosition().y
688 : footprint->Reference().GetPosition().y;
689 fpData.roundedx = RoundToGrid( fpData.x, m_sortGridx ); // Round to sort
690 fpData.roundedy = RoundToGrid( fpData.y, m_sortGridy );
691 fpData.Front = footprint->GetLayer() == F_Cu;
692 fpData.Action = UPDATE_REFDES; // Usually good
693
694 if( fpData.RefDesString.IsEmpty() )
695 {
696 fpData.Action = EMPTY_REFDES;
697 }
698 else
699 {
700 firstnum = fpData.RefDesString.find_first_of( wxT( "0123456789" ) );
701
702 if( std::string::npos == firstnum )
703 fpData.Action = INVALID_REFDES; // do not change ref des such as 12 or +1, or L
704 }
705
706 // Get the type (R, C, etc)
707 fpData.RefDesType = fpData.RefDesString.substr( 0, firstnum );
708
709 for( wxString excluded : m_excludeArray )
710 {
711 if( excluded == fpData.RefDesType ) // Am I supposed to exclude this type?
712 {
713 fpData.Action = EXCLUDE_REFDES; // Yes
714 break;
715 }
716 }
717
718 if(( fpData.Front && annotateBack ) || // If a front fp and doing backs only
719 ( !fpData.Front && annotateFront ) || // If a back fp and doing front only
720 ( footprint->IsLocked() && skipLocked ) ) // If excluding locked and it is locked
721 {
722 fpData.Action = EXCLUDE_REFDES;
723 }
724
725 if( annotateSelected )
726 { // If only annotating selected c
727 fpData.Action = EXCLUDE_REFDES; // Assume it isn't selected
728
729 for( KIID sel : selected )
730 {
731 if( fpData.Uuid == sel )
732 { // Found in selected footprints
733 fpData.Action = UPDATE_REFDES; // Update it
734 break;
735 }
736 }
737 }
738
739 if( fpData.Front )
740 m_frontFootprints.push_back( fpData );
741 else
742 m_backFootprints.push_back( fpData );
743 }
744
745 // Determine the sort order for the front.
747
748 // Sort the front footprints.
749 sort( m_frontFootprints.begin(), m_frontFootprints.end(), ModuleCompare );
750
751 // Determine the sort order for the back.
753
754 // Sort the back footprints.
755 sort( m_backFootprints.begin(), m_backFootprints.end(), ModuleCompare );
756
757 m_refDesTypes.clear();
758 m_changeArray.clear();
759
761
762 if( !m_frontFootprints.empty() )
763 {
765 m_FrontPrefix->GetValue(), m_RemoveFrontPrefix->GetValue(), aBadRefDes );
766 }
767
768 if( !m_backFootprints.empty() )
769 {
771 m_BackPrefix->GetValue(), m_RemoveBackPrefix->GetValue(), aBadRefDes );
772 }
773
774 if( !m_changeArray.empty() )
775 sort( m_changeArray.begin(), m_changeArray.end(), ChangeArrayCompare );
776
778
779 size_t changearraysize = m_changeArray.size();
780
781 for( size_t i = 0; i < changearraysize; i++ ) // Scan through for duplicates if update or skip
782 {
783 if( ( m_changeArray[i].Action != EMPTY_REFDES )
784 && ( m_changeArray[i].Action != INVALID_REFDES ) )
785 {
786 for( size_t j = i + 1; j < changearraysize; j++ )
787 {
788 if( m_changeArray[i].NewRefDes == m_changeArray[j].NewRefDes )
789 {
790 ShowReport( wxString::Format( _( "Duplicate instances of %s" ),
791 m_changeArray[j].NewRefDes ),
793
794 if( errorcount++ > MAXERROR )
795 {
796 ShowReport( _( "Aborted: too many errors" ), RPT_SEVERITY_ERROR );
797 break;
798 }
799 }
800 }
801 }
802
803 if( errorcount > MAXERROR )
804 break;
805 }
806
807 return ( 0 == errorcount );
808}
809
811{
812 std::vector<REFDES_INFO> excludedFootprints;
813
814 for( REFDES_INFO fpData : m_frontFootprints )
815 {
816 if( fpData.Action == EXCLUDE_REFDES )
817 excludedFootprints.push_back( fpData );
818 }
819
820 for( REFDES_INFO fpData : m_backFootprints )
821 {
822 if( fpData.Action == EXCLUDE_REFDES )
823 excludedFootprints.push_back( fpData );
824 }
825
826 for( REFDES_INFO fpData : excludedFootprints )
827 {
828 if( fpData.Action == EXCLUDE_REFDES )
829 {
830 REFDES_TYPE_STR* refDesInfo = GetOrBuildRefDesInfo( fpData.RefDesType );
831 refDesInfo->UnavailableRefs.insert( UTIL::GetRefDesNumber( fpData.RefDesString ) );
832 }
833 }
834}
835
836
837void DIALOG_BOARD_REANNOTATE::BuildChangeArray( std::vector<REFDES_INFO>& aFootprints,
838 unsigned int aStartRefDes, const wxString& aPrefix,
839 bool aRemovePrefix,
840 std::vector<REFDES_INFO>& aBadRefDes )
841{
842 size_t prefixsize = aPrefix.size();
843
844 bool haveprefix = ( 0 != prefixsize ); // Do I have a prefix?
845 bool addprefix = haveprefix & !aRemovePrefix; // Yes- and I'm not removing it
846 aRemovePrefix &= haveprefix; // Only remove if I have a prefix
847
848 bool prefixpresent; // Prefix found
849
850 wxString logstring = ( aFootprints.front().Front ) ? _( "\n\nFront Footprints" )
851 : _( "\n\nBack Footprints" );
852 LogFootprints( logstring, aFootprints );
853
854 if( 0 != aStartRefDes ) // Initialize the change array if present
855 {
856 for( size_t i = 0; i < m_refDesTypes.size(); i++ )
857 m_refDesTypes[i].LastUsedRefDes = aStartRefDes;
858 }
859
860
861 for( REFDES_INFO fpData : aFootprints )
862 {
863 REFDES_CHANGE change;
864
865 change.Uuid = fpData.Uuid;
866 change.Action = fpData.Action;
867 change.OldRefDesString = fpData.RefDesString;
868 change.NewRefDes = fpData.RefDesString;
869 change.Front = fpData.Front;
870
871 if( fpData.RefDesString.IsEmpty() )
872 fpData.Action = EMPTY_REFDES;
873
874 if( ( change.Action == EMPTY_REFDES ) || ( change.Action == INVALID_REFDES ) )
875 {
876 m_changeArray.push_back( change );
877 aBadRefDes.push_back( fpData );
878 continue;
879 }
880
881 if( change.Action == UPDATE_REFDES )
882 {
883 prefixpresent = ( 0 == fpData.RefDesType.find( aPrefix ) );
884
885 if( addprefix && !prefixpresent )
886 fpData.RefDesType.insert( 0, aPrefix ); // Add prefix once only
887
888 if( aRemovePrefix && prefixpresent ) // If there is a prefix remove it
889 fpData.RefDesType.erase( 0, prefixsize );
890
891 REFDES_TYPE_STR* refDesInfo = GetOrBuildRefDesInfo( fpData.RefDesType, aStartRefDes );
892 unsigned int newRefDesNumber = refDesInfo->LastUsedRefDes + 1;
893
894 while( refDesInfo->UnavailableRefs.count( newRefDesNumber ) )
895 newRefDesNumber++;
896
897 change.NewRefDes = refDesInfo->RefDesType + std::to_string( newRefDesNumber );
898 refDesInfo->LastUsedRefDes = newRefDesNumber;
899 }
900
901 m_changeArray.push_back( change );
902 }
903}
904
905
907{
908 size_t i;
909
910 for( i = 0; i < m_changeArray.size(); i++ )
911 {
912 if( aFootprint->m_Uuid == m_changeArray[i].Uuid )
913 return ( &m_changeArray[i] );
914 }
915
916 ShowReport( _( "Footprint not found in changelist" ) + wxS( " " ) + aFootprint->GetReference(),
918
919 return nullptr; // Should never happen
920}
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:106
WINDOW_SETTINGS m_Window
Definition: app_settings.h:169
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:313
bool IsEmpty() const
Definition: board.h:364
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Create an undo entry for an item that has been already modified.
Definition: commit.h:103
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:85
const KIID m_Uuid
Definition: eda_item.h:482
const wxString & GetReference() const
Definition: footprint.h:553
static void BuildChoiceList(wxArrayString *aGridsList, APP_SETTINGS_BASE *aCfg, EDA_DRAW_FRAME *aParent)
Definition: grid_menu.cpp:88
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:93
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:109
Implement an OUTPUTFORMATTER to a memory buffer.
Definition: richio.h:427
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)
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:360
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:65
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:445
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