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