KiCad PCB EDA Suite
dialog_board_reannotate.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2020 Brian Piccioni [email protected]
5  * Copyright (C) 2020-2022 KiCad Developers, see AUTHORS.txt for contributors.
6  * @author Brian Piccioni <[email protected]>
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, you may find one here:
20  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21  * or you may search the http://www.gnu.org website for the version 2 license,
22  * or you may write to the Free Software Foundation, Inc.,
23  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24  */
25 
26 #include <algorithm>
27 #include <base_units.h>
28 #include <bitmaps.h>
29 #include <board_commit.h>
30 #include <confirm.h>
31 #include <ctype.h>
33 #include <string_utils.h> // StrNumCmp
34 #include <kiface_base.h>
35 #include <pcbnew_settings.h>
36 #include <refdes_utils.h>
37 #include <tool/grid_menu.h>
38 #include <wx/valtext.h>
39 
40 
44 
45 //
46 // This converts the index into a sort code. Note that Back sort code will have left and
47 // right swapped.
48 //
50  SORTYFIRST + ASCENDINGFIRST + ASCENDINGSECOND, // "Top to bottom, left to right", // 100
51  SORTYFIRST + ASCENDINGFIRST + DESCENDINGSECOND, // "Top to bottom, right to left", // 101
52  SORTYFIRST + DESCENDINGFIRST + ASCENDINGSECOND, // "Back to Front, left to right", // 110
53  SORTYFIRST + DESCENDINGFIRST + DESCENDINGSECOND, // "Back to Front, right to left", // 111
54  SORTXFIRST + ASCENDINGFIRST + ASCENDINGSECOND, // "Left to right, Front to Back", // 000
55  SORTXFIRST + ASCENDINGFIRST + DESCENDINGSECOND, // "Left to right, Back to Front", // 001
56  SORTXFIRST + DESCENDINGFIRST + ASCENDINGSECOND, // "Right to left, Front to Back", // 010
57  SORTXFIRST + DESCENDINGFIRST + DESCENDINGSECOND // "Right to left, Back to Front", // 011
58 };
59 
60 
61 //
62 // Back Left/Right is opposite because it is a mirror image (coordinates are from the top)
63 //
65  SORTYFIRST + ASCENDINGFIRST + DESCENDINGSECOND, // "Top to bottom, left to right", // 101
66  SORTYFIRST + ASCENDINGFIRST + ASCENDINGSECOND, // "Top to bottom, right to left", // 100
67  SORTYFIRST + DESCENDINGFIRST + DESCENDINGSECOND, // "Bottom to top, left to right", // 111
68  SORTYFIRST + DESCENDINGFIRST + ASCENDINGSECOND, // "Bottom to top, right to left", // 110
69  SORTXFIRST + DESCENDINGFIRST + ASCENDINGSECOND, // "Left to right, top to bottom", // 010
70  SORTXFIRST + DESCENDINGFIRST + DESCENDINGSECOND, // "Left to right, bottom to top", // 011
71  SORTXFIRST + ASCENDINGFIRST + ASCENDINGSECOND, // "Right to left, top to bottom", // 000
72  SORTXFIRST + ASCENDINGFIRST + DESCENDINGSECOND // "Right to left, bottom to top", // 001
73 };
74 
75 #define SetSortCodes( DirArray, Code ) \
76  { \
77  SortYFirst = ( ( DirArray[Code] & SORTYFIRST ) != 0 ); \
78  DescendingFirst = ( ( DirArray[Code] & DESCENDINGFIRST ) != 0 ); \
79  DescendingSecond = ( ( DirArray[Code] & DESCENDINGSECOND ) != 0 ); \
80  }
81 
82 
83 wxString AnnotateString[] = {
84  _( "All" ), // AnnotateAll
85  _( "Only front" ), // AnnotateFront
86  _( "Only back" ), // AnnotateBack
87  _( "Only selected" ) // AnnotateSelected
88 };
89 
90 
91 wxString ActionMessage[] = {
92  "", // UpdateRefDes
93  _( "Empty" ), // EmptyRefDes
94  _( "Invalid" ), // InvalidRefDes
95  _( "Excluded" ) // Exclude
96 };
97 
98 
100  : DIALOG_BOARD_REANNOTATE_BASE( aParentFrame ),
101  m_footprints( aParentFrame->GetBoard()->Footprints() )
102 {
103  m_frame = aParentFrame;
105  InitValues();
106 
107  m_FrontRefDesStart->SetValidator( wxTextValidator( wxFILTER_DIGITS ) );
108  m_BackRefDesStart->SetValidator( wxTextValidator( wxFILTER_DIGITS ) );
109 
110  m_sdbSizerOK->SetLabel( _( "Reannotate PCB" ) );
111  m_sdbSizerCancel->SetLabel( _( "Close" ) );
112  m_sdbSizer->Layout();
113 
114  m_settings = aParentFrame->config();
115  wxArrayString gridslist;
116  GRID_MENU::BuildChoiceList( &gridslist, m_settings, aParentFrame );
117 
118  if( -1 == m_gridIndex ) // If no default loaded
119  m_gridIndex = m_settings->m_Window.grid.last_size_idx; // Get the current grid size
120 
123 
124  m_GridChoice->Set( gridslist );
125  m_GridChoice->SetSelection( m_gridIndex );
126 
127  for( wxRadioButton* button : m_sortButtons )
128  button->SetValue( false );
129 
130  m_sortButtons[m_sortCode]->SetValue( true );
131 
133 
134  if( !m_selection.Empty() )
136 
137  for( wxRadioButton* button : m_scopeRadioButtons )
138  button->SetValue( false );
139 
140  m_scopeRadioButtons[m_annotationScope]->SetValue( true );
141 
150 
151  m_ExcludeList->SetToolTip( m_ExcludeListText->GetToolTipText() );
152  m_GridChoice->SetToolTip( m_SortGridText->GetToolTipText() );
153 
154  m_MessageWindow->SetFileName( Prj().GetProjectPath() + wxT( "report.txt" ) );
155 
157 }
158 
159 
161 {
162  GetParameters(); // Get the current menu settings
164  cfg->m_Reannotate.sort_on_fp_location = m_locationChoice->GetSelection() == 0;
167  cfg->m_Reannotate.exclude_locked = m_ExcludeLocked->GetValue();
168 
173 
176  cfg->m_Reannotate.front_prefix = m_FrontPrefix->GetValue();
177  cfg->m_Reannotate.back_prefix = m_BackPrefix->GetValue();
178  cfg->m_Reannotate.exclude_list = m_ExcludeList->GetValue();
180 }
181 
182 
184 {
186  m_locationChoice->SetSelection( cfg->m_Reannotate.sort_on_fp_location ? 0 : 1 );
189  m_ExcludeLocked->SetValue( cfg->m_Reannotate.exclude_locked );
190 
195 
198  m_FrontPrefix->SetValue( cfg->m_Reannotate.front_prefix );
199  m_BackPrefix->SetValue( cfg->m_Reannotate.back_prefix );
200  m_ExcludeList->SetValue( cfg->m_Reannotate.exclude_list );
202 }
203 
204 
205 void DIALOG_BOARD_REANNOTATE::OnCloseClick( wxCommandEvent& event )
206 {
207  EndDialog( wxID_OK );
208 }
209 
210 
211 void DIALOG_BOARD_REANNOTATE::FilterPrefix( wxTextCtrl* aPrefix )
212 {
213  std::string tmps = VALIDPREFIX;
214 
215  if( aPrefix->GetValue().empty() )
216  return; //Should never happen
217 
218  char lastc = aPrefix->GetValue().Last();
219 
220  if( isalnum( (int) lastc ) )
221  return;
222 
223  if( std::string::npos != tmps.find( lastc ) )
224  return;
225 
226  tmps = aPrefix->GetValue();
227  aPrefix->Clear();
228  tmps.pop_back();
229  aPrefix->AppendText( tmps );
230 }
231 
232 
234  unsigned int aStartRefDes )
235 {
236  unsigned int requiredLastRef = ( aStartRefDes == 0 ? 1 : aStartRefDes ) - 1;
237 
238  for( size_t i = 0; i < m_refDesTypes.size(); i++ ) // See if it is in the types array
239  {
240  if( m_refDesTypes[i].RefDesType == aRefDesPrefix ) // Found it!
241  {
242  m_refDesTypes[i].LastUsedRefDes = std::max( m_refDesTypes[i].LastUsedRefDes,
243  requiredLastRef );
244 
245  return &m_refDesTypes[i];
246  }
247  }
248 
249  // Wasn't in the types array so add it
250  RefDesTypeStr newtype;
251  newtype.RefDesType = aRefDesPrefix;
252  newtype.LastUsedRefDes = requiredLastRef;
253  m_refDesTypes.push_back( newtype );
254 
255  return &m_refDesTypes.back();
256 }
257 
258 
259 void DIALOG_BOARD_REANNOTATE::FilterFrontPrefix( wxCommandEvent& event )
260 {
262 }
263 
264 
265 void DIALOG_BOARD_REANNOTATE::FilterBackPrefix( wxCommandEvent& event )
266 {
268 }
269 
270 
271 void DIALOG_BOARD_REANNOTATE::OnApplyClick( wxCommandEvent& event )
272 {
273  wxString warning;
274 
275  if( m_frame->GetBoard()->IsEmpty() )
276  {
277  ShowReport( _( "No PCB to reannotate!" ), RPT_SEVERITY_ERROR );
278  return;
279  }
280 
281  GetParameters(); // Figure out how this is to be done
282  MakeSampleText( warning );
283 
284  if( !IsOK( m_frame, warning ) )
285  return;
286 
287  if( ReannotateBoard() )
288  {
289  ShowReport( _( "PCB successfully reannotated" ), RPT_SEVERITY_ACTION );
290  ShowReport( _( "PCB annotation changes should be synchronized with schematic using "
291  "the \"Update Schematic from PCB\" tool." ), RPT_SEVERITY_WARNING );
292  }
293 
294  m_MessageWindow->SetLazyUpdate( false );
295  m_MessageWindow->Flush( false );
296  m_frame->GetCanvas()->Refresh(); // Redraw
297  m_frame->OnModify(); // Need to save file on exit.
298 }
299 
300 
301 void DIALOG_BOARD_REANNOTATE::MakeSampleText( wxString& aMessage )
302 {
303  wxString tmp;
304 
305  aMessage.Printf( _( "\n%s footprints will be reannotated." ),
307 
308  if( !m_ExcludeList->GetValue().empty() )
309  {
310  aMessage += wxString::Format( _( "\nAny reference types %s will not be annotated." ),
311  m_ExcludeList->GetValue() );
312  }
313 
314  if( m_ExcludeLocked->GetValue() )
315  aMessage += wxString::Format( _( "\nLocked footprints will not be annotated" ) );
316 
317  if( !m_AnnotateBack->GetValue() )
318  {
319  aMessage += wxString::Format( _( "\nFront footprints will start at %s" ),
320  m_FrontRefDesStart->GetValue() );
321  }
322 
323  if( !m_AnnotateFront->GetValue() )
324  {
325  bool frontPlusOne = ( 0 == wxAtoi( m_BackRefDesStart->GetValue() ) )
326  && !m_AnnotateBack->GetValue();
327 
328  aMessage += wxString::Format( _( "\nBack footprints will start at %s." ),
329  frontPlusOne ? _( "the last front footprint + 1" ) :
330  m_BackRefDesStart->GetValue() );
331  }
332 
333  if( !m_FrontPrefix->GetValue().empty() )
334  {
335  if( m_RemoveFrontPrefix->GetValue() )
336  {
337  aMessage += wxString::Format( _( "\nFront footprints starting with '%s' will have "
338  "the prefix removed." ),
339  m_FrontPrefix->GetValue() );
340  }
341  else
342  {
343  aMessage += wxString::Format( _( "\nFront footprints will have '%s' inserted as a "
344  "prefix." ),
345  m_FrontPrefix->GetValue() );
346  }
347  }
348 
349  if( !m_BackPrefix->GetValue().empty() )
350  {
351  if( m_RemoveBackPrefix->GetValue() )
352  {
353  aMessage += wxString::Format( _( "\nBack footprints starting with '%s' will have the "
354  "prefix removed." ),
355  m_BackPrefix->GetValue() );
356  }
357  else
358  {
359  aMessage += wxString::Format( _( "\nBack footprints will have '%s' inserted as a "
360  "prefix." ),
361  m_BackPrefix->GetValue() );
362  }
363  }
364 
365  bool fpLocation = m_locationChoice->GetSelection() == 0;
366 
367  aMessage += wxString::Format( _( "\nPrior to sorting by %s, the coordinates of which will be "
368  "rounded to a %s, %s grid." ),
369  fpLocation ? _( "footprint location" )
370  : _( "reference designator location" ),
373 
374  ShowReport( aMessage, RPT_SEVERITY_INFO );
375 }
376 
377 
379 {
380  m_sortCode = 0; // Convert radio button to sort direction code
381 
382  for( wxRadioButton* sortbuttons : m_sortButtons )
383  {
384  if( sortbuttons->GetValue() )
385  break;
386 
387  m_sortCode++;
388  }
389 
390  if( m_sortCode >= (int) m_sortButtons.size() )
391  m_sortCode = 0;
392 
393  m_frontPrefixString = m_FrontPrefix->GetValue();
394  m_backPrefixString = m_BackPrefix->GetValue();
395 
396  // Get the chosen sort grid for rounding
397  m_gridIndex = m_GridChoice->GetSelection();
398 
399  if( m_gridIndex >= ( int ) m_settings->m_Window.grid.sizes.size() )
400  {
405  }
406  else
407  {
411  }
412 
414 
415  for( wxRadioButton* button : m_scopeRadioButtons )
416  {
417  if( button->GetValue() )
418  break;
419  else
421  }
422 
424 }
425 
426 
427 int DIALOG_BOARD_REANNOTATE::RoundToGrid( int aCoord, int aGrid )
428 {
429  if( 0 == aGrid )
430  aGrid = MINGRID;
431 
432  int rounder;
433  rounder = aCoord % aGrid;
434  aCoord -= rounder;
435 
436  if( abs( rounder ) > ( aGrid / 2 ) )
437  aCoord += ( aCoord < 0 ? -aGrid : aGrid );
438 
439  return ( aCoord );
440 }
441 
442 
445 static bool ChangeArrayCompare( const RefDesChange& aA, const RefDesChange& aB )
446 {
447  return ( StrNumCmp( aA.OldRefDesString, aB.OldRefDesString ) < 0 );
448 }
449 
450 
453 static bool ModuleCompare( const RefDesInfo& aA, const RefDesInfo& aB )
454 {
455  int X0 = aA.roundedx, X1 = aB.roundedx, Y0 = aA.roundedy, Y1 = aB.roundedy;
456 
457  if( SortYFirst ) //If sorting by Y then X, swap X and Y
458  {
459  std::swap( X0, Y0 );
460  std::swap( X1, Y1 );
461  }
462 
463  // If descending, same compare just swap directions
464  if( DescendingFirst )
465  std::swap( X0, X1 );
466 
467  if( DescendingSecond )
468  std::swap( Y0, Y1 );
469 
470  if( X0 < X1 )
471  return ( true ); // yes, its smaller
472 
473  if( X0 > X1 )
474  return ( false ); // No its not
475 
476  if( Y0 < Y1 )
477  return ( true ); // same but equal
478 
479  return ( false );
480 }
481 
482 
484 {
485  return wxString::Format( wxT( "%s, %s" ),
487  MessageTextFromValue( m_units, aY ) );
488 }
489 
490 
491 void DIALOG_BOARD_REANNOTATE::ShowReport( const wxString& aMessage, SEVERITY aSeverity )
492 {
493  size_t pos = 0, prev = 0;
494 
495  do
496  {
497  pos = aMessage.ToStdString().find( '\n', prev );
498  m_MessageWindow->Report( aMessage.ToStdString().substr( prev, pos - prev ), aSeverity );
499  prev = pos + 1;
500  } while( std::string::npos != pos );
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( RefDesTypeStr 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 RefDesChange& 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  UpdateRefDes != change.Action ? _( " will be ignored" )
536  : wxString() );
537  }
538 
539  ShowReport( message, RPT_SEVERITY_INFO );
540 }
541 
542 
543 void DIALOG_BOARD_REANNOTATE::LogFootprints( const wxString& aMessage,
544  const std::vector<RefDesInfo>& 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 RefDesInfo& 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<RefDesInfo> BadRefDes;
579  wxString message, badrefdes;
580  STRING_FORMATTER stringformatter;
581  RefDesChange* 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 RefDesInfo& 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 
633 bool DIALOG_BOARD_REANNOTATE::BuildFootprintList( std::vector<RefDesInfo>& 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  RefDesInfo 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 = UpdateRefDes; // Usually good
693 
694  if( fpData.RefDesString.IsEmpty() )
695  {
696  fpData.Action = EmptyRefDes;
697  }
698  else
699  {
700  firstnum = fpData.RefDesString.find_first_of( wxT( "0123456789" ) );
701 
702  if( std::string::npos == firstnum )
703  fpData.Action = InvalidRefDes; // 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; // 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;
723  }
724 
725  if( annotateSelected )
726  { // If only annotating selected c
727  fpData.Action = Exclude; // 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 = UpdateRefDes; // 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  {
764  BuildChangeArray( m_frontFootprints, wxAtoi( m_FrontRefDesStart->GetValue() ),
765  m_FrontPrefix->GetValue(), m_RemoveFrontPrefix->GetValue(), aBadRefDes );
766  }
767 
768  if( !m_backFootprints.empty() )
769  {
770  BuildChangeArray( m_backFootprints, wxAtoi( m_BackRefDesStart->GetValue() ),
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 
777  LogChangePlan();
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 != EmptyRefDes )
784  && ( m_changeArray[i].Action != InvalidRefDes ) )
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<RefDesInfo> excludedFootprints;
813 
814  for( RefDesInfo fpData : m_frontFootprints )
815  {
816  if( fpData.Action == Exclude )
817  excludedFootprints.push_back( fpData );
818  }
819 
820  for( RefDesInfo fpData : m_backFootprints )
821  {
822  if( fpData.Action == Exclude )
823  excludedFootprints.push_back( fpData );
824  }
825 
826  for( RefDesInfo fpData : excludedFootprints )
827  {
828  if( fpData.Action == Exclude )
829  {
830  RefDesTypeStr* refDesInfo = GetOrBuildRefDesInfo( fpData.RefDesType );
831  refDesInfo->UnavailableRefs.insert( UTIL::GetRefDesNumber( fpData.RefDesString ) );
832  }
833  }
834 }
835 
836 
837 void DIALOG_BOARD_REANNOTATE::BuildChangeArray( std::vector<RefDesInfo>& aFootprints,
838  unsigned int aStartRefDes, const wxString& aPrefix,
839  bool aRemovePrefix,
840  std::vector<RefDesInfo>& 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( RefDesInfo fpData : aFootprints )
862  {
863  RefDesChange 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 = EmptyRefDes;
873 
874  if( ( change.Action == EmptyRefDes ) || ( change.Action == InvalidRefDes ) )
875  {
876  m_changeArray.push_back( change );
877  aBadRefDes.push_back( fpData );
878  continue;
879  }
880 
881  if( change.Action == UpdateRefDes )
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  RefDesTypeStr* 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 }
bool ReannotateBoard(void)
Actually reannotate the board.
std::vector< wxRadioButton * > m_sortButtons
Class DIALOG_BOARD_REANNOTATE_BASE.
bool DescendingSecond
void OnModify() override
Must be called after a board change to set the modified flag.
wxString MessageTextFromValue(EDA_UNITS aUnits, int aValue, bool aAddUnitLabel, EDA_DATA_TYPE aType)
Convert a value to a string using double notation.
Definition: base_units.cpp:104
void SetLazyUpdate(bool aLazyUpdate)
Forces updating the HTML page, after the report is built in lazy mode If aSort = true,...
COMMIT & Modify(EDA_ITEM *aItem)
Create an undo entry for an item that has been already modified.
Definition: commit.h:103
wxString user_grid_y
Definition: app_settings.h:54
void FilterPrefix(wxTextCtrl *aPrefix)
Check to make sure the prefix (if there is one) is properly constructed.
#define MAXERROR
virtual APP_SETTINGS_BASE * config() const
Returns the settings object used in SaveSettings(), and is overloaded in KICAD_MANAGER_FRAME.
wxString user_grid_x
Definition: app_settings.h:53
Implementation of conversion functions that require both schematic and board internal units.
This file is part of the common library.
wxString AnnotateString[]
KIFACE_BASE & Kiface()
Global KIFACE_BASE "get" accessor.
PCB_DRAW_PANEL_GAL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
void FilterBackPrefix(wxCommandEvent &event) override
void Flush(bool aSort=false)
Set the visible severity filter.
void LogFootprints(const wxString &aMessage, const std::vector< RefDesInfo > &aFootprints)
Create a list of the footprints and their coordinates.
Collection of utility functions for component reference designators (refdes)
static bool ModuleCompare(const RefDesInfo &aA, const RefDesInfo &aB)
Compare function to sort footprints.
bool BuildFootprintList(std::vector< RefDesInfo > &aBadRefDes)
Build the footprint lists, sort it, filter for excludes, then build the change list.
DIALOG_BOARD_REANNOTATE(PCB_EDIT_FRAME *aParentFrame)
#define MINGRID
std::vector< wxString > m_excludeArray
void ShowReport(const wxString &aMessage, SEVERITY aSeverity)
Break report into strings separated by and sent to the reporter.
#define SORTYFIRST
RefDesChange * GetNewRefDes(FOOTPRINT *aFootprint)
std::vector< RefDesChange > m_changeArray
bool DescendingFirst
Definition: kiid.h:44
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
unsigned int LastUsedRefDes
void OnApplyClick(wxCommandEvent &event) override
std::vector< RefDesInfo > m_backFootprints
std::vector< wxString > sizes
Definition: app_settings.h:52
Store information read from a netlist along with the flags used to update the NETLIST in the BOARD.
Definition: pcb_netlist.h:206
std::vector< RefDesInfo > m_frontFootprints
void InitValues(void)
Copy saved app settings to the dialog.
GRID_SETTINGS grid
Definition: app_settings.h:89
PROJECT & Prj() const
Return a reference to the PROJECT associated with this KIWAY.
int RoundToGrid(int aCoord, int aGrid)
Round an int coordinate to a suitable grid.
FOOTPRINTS & Footprints()
Definition: board.h:234
#define ASCENDINGSECOND
std::vector< RefDesTypeStr > m_refDesTypes
const wxString & GetReference() const
Definition: footprint.h:463
KIGFX::GAL * GetGAL() const
Return a pointer to the GAL instance used in the panel.
#define _(s)
#define ASCENDINGFIRST
std::vector< wxRadioButton * > m_scopeRadioButtons
#define SetSortCodes(DirArray, Code)
wxString ActionMessage[]
int BackDirectionsArray[]
int FrontDirectionsArray[]
SEVERITY
void OnCloseClick(wxCommandEvent &event) override
std::set< unsigned int > UnavailableRefs
wxBitmap KiBitmap(BITMAPS aBitmap, int aHeightTag)
Construct a wxBitmap from an image identifier Returns the image from the active theme if the image ha...
Definition: bitmap.cpp:105
virtual KIGFX::PCB_VIEW * GetView() const override
Return a pointer to the #VIEW instance used in the panel.
bool Empty() const
Checks if there is anything selected.
Definition: selection.h:98
#define DESCENDINGSECOND
void Report(const wxString &aText, SEVERITY aSeverity, REPORTER::LOCATION aLocation=REPORTER::LOC_BODY)
Reports the string.
class FOOTPRINT, a footprint
Definition: typeinfo.h:88
BOARD * GetBoard()
const KIID m_Uuid
Definition: eda_item.h:474
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
const VECTOR2D & GetGridSize() const
Return the grid size.
EDA_UNITS m_units
Definition: dialog_shim.h:200
void BuildUnavailableRefsList()
Build list of unavailable references. E.g. unselected footprints or locked footprints.
void finishDialogSettings()
In all dialogs, we must call the same functions to fix minimal dlg size, the default position and per...
static bool ChangeArrayCompare(const RefDesChange &aA, const RefDesChange &aB)
Compare function used to compare ChangeArray element for sort.
WINDOW_SETTINGS m_Window
Definition: app_settings.h:181
void FilterFrontPrefix(wxCommandEvent &event) override
RefDesTypeStr * GetOrBuildRefDesInfo(const wxString &aRefDesPrefix, unsigned int aStartRefDes=0)
Get the structure representing the information currently held for aRefDesPrefix or create one if it d...
Definition: layer_ids.h:71
wxString CoordTowxString(int aX, int aY)
Convert coordinates to wxString.
void BuildChangeArray(std::vector< RefDesInfo > &aFootprints, unsigned int aStartRefDes, const wxString &aPrefix, bool aRemovePrefix, std::vector< RefDesInfo > &aBadRefDes)
Scan through the footprint arrays and create the from -> to array.
static void BuildChoiceList(wxArrayString *aGridsList, APP_SETTINGS_BASE *aCfg, EDA_DRAW_FRAME *aParent)
Definition: grid_menu.cpp:85
virtual void Refresh(bool aEraseBackground=true, const wxRect *aRect=nullptr) override
Update the board display after modifying it by a python script (note: it is automatically called by a...
The main frame for Pcbnew.
PCBNEW_SETTINGS * GetPcbNewSettings() const
The selection tool: currently supports:
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:99
virtual void Push(const wxString &aMessage=wxT("A commit"), bool aCreateUndoEntry=true, bool aSetDirtyBit=true) override
Revert the commit by restoring the modified items state.
DIALOG_REANNOTATE m_Reannotate
APP_SETTINGS_BASE * KifaceSettings() const
Definition: kiface_base.h:92
TOOL_MANAGER * GetToolManager() const
Return the MVC controller.
Definition: tools_holder.h:54
int StrNumCmp(const wxString &aString1, const wxString &aString2, bool aIgnoreCase)
Compare two strings with alphanumerical content.
BOARD * GetBoard() const
void SetFileName(const wxString &aReportFileName)
double DoubleValueFromString(EDA_UNITS aUnits, const wxString &aTextValue, EDA_DATA_TYPE aType)
Function DoubleValueFromString converts aTextValue to a double.
Definition: base_units.cpp:307
void LogChangePlan(void)
Create an audit trail of the changes.
#define DESCENDINGFIRST
Implement an OUTPUTFORMATTER to a memory buffer.
Definition: richio.h:414
bool IsOK(wxWindow *aParent, const wxString &aMessage)
Display a yes/no dialog with aMessage and returns the user response.
Definition: confirm.cpp:323
bool IsEmpty() const
Definition: board.h:282
bool SortYFirst
#define SORTXFIRST
int GetRefDesNumber(const wxString &aRefDes)
Get the numeric suffix from a refdes - e.g.
void MakeSampleText(wxString &aMessage)
Make the text to summarize what is about to happen.
#define VALIDPREFIX