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