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 brian@documenteddesigns.com
5  * Copyright (C) 2020-2021 KiCad Developers, see AUTHORS.txt for contributors.
6  * @author Brian Piccioni <brian@documenteddesigns.com>
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 <base_units.h>
27 #include <bitmaps.h>
28 #include <board_commit.h>
29 #include <confirm.h>
30 #include <ctype.h>
32 #include <fstream>
33 #include <kicad_string.h> // StrNumCmp
34 #include <kiface_i.h>
35 #include <mail_type.h>
36 #include <pcbnew_settings.h>
37 #include <sstream>
38 #include <tool/tool_manager.h>
39 #include <tool/grid_menu.h>
40 #include <wx/valtext.h>
41 
42 
46 
47 //
48 // This converts the index into a sort code. Note that Back sort code will have left and
49 // right swapped.
50 //
52  SORTYFIRST + ASCENDINGFIRST + ASCENDINGSECOND, // "Top to bottom, left to right", // 100
53  SORTYFIRST + ASCENDINGFIRST + DESCENDINGSECOND, // "Top to bottom, right to left", // 101
54  SORTYFIRST + DESCENDINGFIRST + ASCENDINGSECOND, // "Back to Front, left to right", // 110
55  SORTYFIRST + DESCENDINGFIRST + DESCENDINGSECOND, // "Back to Front, right to left", // 111
56  SORTXFIRST + ASCENDINGFIRST + ASCENDINGSECOND, // "Left to right, Front to Back", // 000
57  SORTXFIRST + ASCENDINGFIRST + DESCENDINGSECOND, // "Left to right, Back to Front", // 001
58  SORTXFIRST + DESCENDINGFIRST + ASCENDINGSECOND, // "Right to left, Front to Back", // 010
59  SORTXFIRST + DESCENDINGFIRST + DESCENDINGSECOND // "Right to left, Back to Front", // 011
60 };
61 
62 
63 //
64 // Back Left/Right is opposite because it is a mirror image (coordinates are from the top)
65 //
67  SORTYFIRST + ASCENDINGFIRST + DESCENDINGSECOND, // "Top to bottom, left to right", // 101
68  SORTYFIRST + ASCENDINGFIRST + ASCENDINGSECOND, // "Top to bottom, right to left", // 100
69  SORTYFIRST + DESCENDINGFIRST + DESCENDINGSECOND, // "Bottom to top, left to right", // 111
70  SORTYFIRST + DESCENDINGFIRST + ASCENDINGSECOND, // "Bottom to top, right to left", // 110
71  SORTXFIRST + DESCENDINGFIRST + ASCENDINGSECOND, // "Left to right, top to bottom", // 010
72  SORTXFIRST + DESCENDINGFIRST + DESCENDINGSECOND, // "Left to right, bottom to top", // 011
73  SORTXFIRST + ASCENDINGFIRST + ASCENDINGSECOND, // "Right to left, top to bottom", // 000
74  SORTXFIRST + ASCENDINGFIRST + DESCENDINGSECOND // "Right to left, bottom to top", // 001
75 };
76 
77 #define SetSortCodes( DirArray, Code ) \
78  { \
79  SortYFirst = ( ( DirArray[Code] & SORTYFIRST ) != 0 ); \
80  DescendingFirst = ( ( DirArray[Code] & DESCENDINGFIRST ) != 0 ); \
81  DescendingSecond = ( ( DirArray[Code] & DESCENDINGSECOND ) != 0 ); \
82  }
83 
84 
85 wxString AnnotateString[] = {
86  _( "All" ), // AnnotateAll
87  _( "Only front" ), // AnnotateFront
88  _( "Only back" ), // AnnotateBack
89  _( "Only selected" ) // AnnotateSelected
90 };
91 
92 
93 wxString ActionMessage[] = {
94  "", // UpdateRefDes
95  _( "Empty" ), // EmptyRefDes
96  _( "Invalid" ), // InvalidRefDes
97  _( "Excluded" ) // Exclude
98 };
99 
100 
102  : DIALOG_BOARD_REANNOTATE_BASE( aParentFrame ),
103  m_footprints( aParentFrame->GetBoard()->Footprints() )
104 {
105  m_frame = aParentFrame;
107  InitValues();
108 
109  m_FrontRefDesStart->SetValidator( wxTextValidator( wxFILTER_DIGITS ) );
110  m_BackRefDesStart->SetValidator( wxTextValidator( wxFILTER_DIGITS ) );
111 
112  m_sdbSizerOK->SetLabel( _( "Reannotate PCB" ) );
113  m_sdbSizerCancel->SetLabel( _( "Close" ) );
114  m_sdbSizer->Layout();
115 
116  m_settings = aParentFrame->config();
117  wxArrayString gridslist;
118  GRID_MENU::BuildChoiceList( &gridslist, m_settings, aParentFrame );
119 
120  if( -1 == m_gridIndex ) // If no default loaded
121  m_gridIndex = m_settings->m_Window.grid.last_size_idx; // Get the current grid size
122 
125 
126  m_GridChoice->Set( gridslist );
127  m_GridChoice->SetSelection( m_gridIndex );
128 
129  for( wxRadioButton* button : m_sortButtons )
130  button->SetValue( false );
131 
132  m_sortButtons[m_sortCode]->SetValue( true );
133 
135 
136  if( !m_selection.Empty() )
138 
139  for( wxRadioButton* button : AnnotateWhat )
140  button->SetValue( false );
141 
142  m_annotationChoice = ( m_sortCode >= (int) AnnotateWhat.size() ) ?
145 
146  AnnotateWhat[m_annotationChoice]->SetValue( true );
147 
156 
157  m_ExcludeList->SetToolTip( m_ExcludeListText->GetToolTipText() );
158  m_GridChoice->SetToolTip( m_SortGridText->GetToolTipText() );
159 
160  m_MessageWindow->SetFileName( Prj().GetProjectPath() + wxT( "report.txt" ) );
161 
163 }
164 
165 
167 {
168  GetParameters(); // Get the current menu settings
170  cfg->m_Reannotate.sort_on_fp_location = m_locationChoice->GetSelection() == 0;
173  cfg->m_Reannotate.exclude_locked = m_ExcludeLocked->GetValue();
174 
179 
182  cfg->m_Reannotate.front_prefix = m_FrontPrefix->GetValue();
183  cfg->m_Reannotate.back_prefix = m_BackPrefix->GetValue();
184  cfg->m_Reannotate.exclude_list = m_ExcludeList->GetValue();
186 }
187 
188 
190 {
192  m_locationChoice->SetSelection( cfg->m_Reannotate.sort_on_fp_location ? 0 : 1 );
195  m_ExcludeLocked->SetValue( cfg->m_Reannotate.exclude_locked );
196 
201 
204  m_FrontPrefix->SetValue( cfg->m_Reannotate.front_prefix );
205  m_BackPrefix->SetValue( cfg->m_Reannotate.back_prefix );
206  m_ExcludeList->SetValue( cfg->m_Reannotate.exclude_list );
208 }
209 
210 
211 void DIALOG_BOARD_REANNOTATE::OnCloseClick( wxCommandEvent& event )
212 {
213  EndDialog( wxID_OK );
214 }
215 
216 
217 void DIALOG_BOARD_REANNOTATE::FilterPrefix( wxTextCtrl* aPrefix )
218 {
219  std::string tmps = VALIDPREFIX;
220 
221  if( aPrefix->GetValue().empty() )
222  return; //Should never happen
223 
224  char lastc = aPrefix->GetValue().Last();
225 
226  if( isalnum( (int) lastc ) )
227  return;
228 
229  if( std::string::npos != tmps.find( lastc ) )
230  return;
231 
232  tmps = aPrefix->GetValue();
233  aPrefix->Clear();
234  tmps.pop_back();
235  aPrefix->AppendText( tmps );
236 }
237 
238 
239 void DIALOG_BOARD_REANNOTATE::FilterFrontPrefix( wxCommandEvent& event )
240 {
242 }
243 
244 
245 void DIALOG_BOARD_REANNOTATE::FilterBackPrefix( wxCommandEvent& event )
246 {
248 }
249 
250 
251 void DIALOG_BOARD_REANNOTATE::OnApplyClick( wxCommandEvent& event )
252 {
253  wxString warning;
254 
255  if( m_frame->GetBoard()->IsEmpty() )
256  {
257  ShowReport( _( "No PCB to reannotate!" ), RPT_SEVERITY_ERROR );
258  return;
259  }
260 
261  GetParameters(); // Figure out how this is to be done
262  MakeSampleText( warning );
263 
264  if( !IsOK( m_frame, warning ) )
265  return;
266 
267  if( ReannotateBoard() )
268  {
269  ShowReport( _( "PCB successfully reannotated" ), RPT_SEVERITY_ACTION );
270  ShowReport( _( "PCB annotation changes should be synchronized with schematic using "
271  "the \"Update Schematic from PCB\" tool." ), RPT_SEVERITY_WARNING );
272  }
273 
274  m_MessageWindow->SetLazyUpdate( false );
275  m_MessageWindow->Flush( false );
276  m_frame->GetCanvas()->Refresh(); // Redraw
277  m_frame->OnModify(); // Need to save file on exit.
278 }
279 
280 
281 void DIALOG_BOARD_REANNOTATE::MakeSampleText( wxString& aMessage )
282 {
283  wxString tmp;
284 
285  aMessage.Printf( _( "\n%s footprints will be reannotated." ),
287 
288  if( !m_ExcludeList->GetValue().empty() )
289  {
290  aMessage += wxString::Format( _( "\nAny reference types %s will not be annotated." ),
291  m_ExcludeList->GetValue() );
292  }
293 
294  if( m_ExcludeLocked->GetValue() )
295  aMessage += wxString::Format( _( "\nLocked footprints will not be annotated" ) );
296 
297  if( !m_AnnotateBack->GetValue() )
298  {
299  aMessage += wxString::Format( _( "\nFront footprints will start at %s" ),
300  m_FrontRefDesStart->GetValue() );
301  }
302 
303  if( !m_AnnotateFront->GetValue() )
304  {
305  bool frontPlusOne = ( 0 == wxAtoi( m_BackRefDesStart->GetValue() ) )
306  && !m_AnnotateBack->GetValue();
307 
308  aMessage += wxString::Format( _( "\nBack footprints will start at %s." ),
309  frontPlusOne ? _( "the last front footprint + 1" ) :
310  m_BackRefDesStart->GetValue() );
311  }
312 
313  if( !m_FrontPrefix->GetValue().empty() )
314  {
315  if( m_RemoveFrontPrefix->GetValue() )
316  {
317  aMessage += wxString::Format( _( "\nFront footprints starting with '%s' will have "
318  "the prefix removed." ),
319  m_FrontPrefix->GetValue() );
320  }
321  else
322  {
323  aMessage += wxString::Format( _( "\nFront footprints will have '%s' inserted as a "
324  "prefix." ),
325  m_FrontPrefix->GetValue() );
326  }
327  }
328 
329  if( !m_BackPrefix->GetValue().empty() )
330  {
331  if( m_RemoveBackPrefix->GetValue() )
332  {
333  aMessage += wxString::Format( _( "\nBack footprints starting with '%s' will have the "
334  "prefix removed." ),
335  m_BackPrefix->GetValue() );
336  }
337  else
338  {
339  aMessage += wxString::Format( _( "\nBack footprints will have '%s' inserted as a "
340  "prefix." ),
341  m_BackPrefix->GetValue() );
342  }
343  }
344 
345  bool fpLocation = m_locationChoice->GetSelection() == 0;
346 
347  aMessage += wxString::Format( _( "\nPrior to sorting by %s, the coordinates of which will be "
348  "rounded to a %s, %s grid." ),
349  fpLocation ? _( "footprint location" )
350  : _( "reference designator location" ),
353 
354  ShowReport( aMessage, RPT_SEVERITY_INFO );
355 }
356 
357 
359 {
360  m_sortCode = 0; // Convert radio button to sort direction code
361 
362  for( wxRadioButton* sortbuttons : m_sortButtons )
363  {
364  if( sortbuttons->GetValue() )
365  break;
366 
367  m_sortCode++;
368  }
369 
370  if( m_sortCode >= (int) m_sortButtons.size() )
371  m_sortCode = 0;
372 
373  m_frontPrefixString = m_FrontPrefix->GetValue();
374  m_backPrefixString = m_BackPrefix->GetValue();
375 
376  // Get the chosen sort grid for rounding
377  m_gridIndex = m_GridChoice->GetSelection();
378 
379  if( m_gridIndex >= ( int ) m_settings->m_Window.grid.sizes.size() )
380  {
385  }
386  else
387  {
391  }
392 
393  int i = 0;
394 
395  for( wxRadioButton* button : AnnotateWhat )
396  {
397  if( button->GetValue() )
398  break;
399  else
400  i++;
401  }
402 
403  m_annotationChoice = ( i >= (int) AnnotateWhat.size() ) ? AnnotationChoice::AnnotateAll : i;
404 
406 }
407 
408 
409 int DIALOG_BOARD_REANNOTATE::RoundToGrid( int aCoord, int aGrid )
410 {
411  if( 0 == aGrid )
412  aGrid = MINGRID;
413 
414  int rounder;
415  rounder = aCoord % aGrid;
416  aCoord -= rounder;
417 
418  if( abs( rounder ) > ( aGrid / 2 ) )
419  aCoord += ( aCoord < 0 ? -aGrid : aGrid );
420 
421  return ( aCoord );
422 }
423 
424 
427 static bool ChangeArrayCompare( const RefDesChange& aA, const RefDesChange& aB )
428 {
429  return ( StrNumCmp( aA.OldRefDesString, aB.OldRefDesString ) < 0 );
430 }
431 
432 
435 static bool ModuleCompare( const RefDesInfo& aA, const RefDesInfo& aB )
436 {
437  int X0 = aA.roundedx, X1 = aB.roundedx, Y0 = aA.roundedy, Y1 = aB.roundedy;
438 
439  if( SortYFirst ) //If sorting by Y then X, swap X and Y
440  {
441  std::swap( X0, Y0 );
442  std::swap( X1, Y1 );
443  }
444 
445  // If descending, same compare just swap directions
446  if( DescendingFirst )
447  std::swap( X0, X1 );
448 
449  if( DescendingSecond )
450  std::swap( Y0, Y1 );
451 
452  if( X0 < X1 )
453  return ( true ); // yes, its smaller
454 
455  if( X0 > X1 )
456  return ( false ); // No its not
457 
458  if( Y0 < Y1 )
459  return ( true ); // same but equal
460 
461  return ( false );
462 }
463 
464 
466 {
467  return wxString::Format( "%s, %s",
469  MessageTextFromValue( m_units, aY ) );
470 }
471 
472 
473 void DIALOG_BOARD_REANNOTATE::ShowReport( wxString aMessage, SEVERITY aSeverity )
474 {
475  size_t pos = 0, prev = 0;
476 
477  do
478  {
479  pos = aMessage.ToStdString().find( '\n', prev );
480  m_MessageWindow->Report( aMessage.ToStdString().substr( prev, pos - prev ), aSeverity );
481  prev = pos + 1;
482  } while( std::string::npos != pos );
483 
484 }
485 
486 
488 {
489  int i = 1;
490  wxString message;
491 
492  message.Printf( _( "\n\nThere are %i types of reference designations\n"
493  "**********************************************************\n" ),
494  (int) m_refDesTypes.size() );
495 
496  for( RefDesTypeStr Type : m_refDesTypes ) // Show all the types of refdes
497  message += Type.RefDesType + ( 0 == ( i++ % 16 ) ? "\n" : " " );
498 
499  if( !m_excludeArray.empty() )
500  {
501  wxString excludes;
502 
503  for( wxString& exclude : m_excludeArray ) // Show the refdes we are excluding
504  excludes += exclude + " ";
505 
506  message += wxString::Format( _( "\nExcluding: %s from reannotation\n\n" ), excludes );
507  }
508 
509  message += _( "\n Change Array\n***********************\n" );
510 
511  for( const RefDesChange& change : m_changeArray )
512  {
513  message += wxString::Format(
514  "%s -> %s %s %s\n", change.OldRefDesString, change.NewRefDes,
515  ActionMessage[change.Action],
516  UpdateRefDes != change.Action ? _( " will be ignored" ) : wxT( "" ) );
517  }
518 
519  ShowReport( message, RPT_SEVERITY_INFO );
520 }
521 
522 
523 void DIALOG_BOARD_REANNOTATE::LogFootprints( const wxString& aMessage,
524  const std::vector<RefDesInfo>& aFootprints )
525 {
526  wxString message = aMessage;
527 
528  if( aFootprints.empty() )
529  message += _( "\nNo footprints" );
530  else
531  {
532  int i = 1;
533  bool fpLocations = m_locationChoice->GetSelection() == 0;
534 
535  message += wxString::Format( _( "\n*********** Sort on %s ***********" ),
536  fpLocations ? _( "Footprint Coordinates" )
537  : _( "Reference Designator Coordinates" ) );
538 
539  message += wxString::Format( _( "\nSort Code %d" ), m_sortCode );
540 
541  for( const RefDesInfo& mod : aFootprints )
542  {
543  message += wxString::Format( _( "\n%d %s UUID: [%s], X, Y: %s, Rounded X, Y, %s" ),
544  i++,
545  mod.RefDesString,
546  mod.Uuid.AsString(),
547  CoordTowxString( mod.x, mod.y ),
548  CoordTowxString( mod.roundedx, mod.roundedy ) );
549  }
550  }
551 
552  ShowReport( message, RPT_SEVERITY_INFO );
553 }
554 
555 
557 {
558  std::vector<RefDesInfo> BadRefDes;
559  wxString message, badrefdes;
560  STRING_FORMATTER stringformatter;
561  RefDesChange* newref;
563 
564  if( !BuildFootprintList( BadRefDes ) )
565  {
566  ShowReport( "Selected options resulted in errors! Change them and try again.",
568  return false;
569  }
570 
571  if( !BadRefDes.empty() )
572  {
573  message.Printf(
574  _( "\nPCB has %d empty or invalid reference designations."
575  "\nRecommend running DRC with 'Test footprints against schematic' checked.\n" ),
576  (int) BadRefDes.size() );
577 
578  for( const RefDesInfo& mod : BadRefDes )
579  {
580  badrefdes += wxString::Format( _( "\nRefDes: %s Footprint: %s:%s at %s on PCB." ),
581  mod.RefDesString,
582  mod.FPID.GetLibNickname().wx_str(),
583  mod.FPID.GetLibItemName().wx_str(),
584  CoordTowxString( mod.x, mod.y ) );
585  }
586 
587  ShowReport( message + badrefdes + "\n", RPT_SEVERITY_WARNING );
588  message += _( "Reannotate anyway?" );
589 
590  if( !IsOK( m_frame, message ) )
591  return false;
592  }
593 
594  BOARD_COMMIT commit( m_frame );
595 
596  for( FOOTPRINT* footprint : m_footprints )
597  {
598  newref = GetNewRefDes( footprint );
599 
600  if( nullptr == newref )
601  return false;
602 
603  commit.Modify( footprint ); // Make a copy for undo
604  footprint->SetReference( newref->NewRefDes ); // Update the PCB reference
605  m_frame->GetCanvas()->GetView()->Update( footprint ); // Touch the footprint
606  }
607 
608  commit.Push( "Geographic reannotation" );
609  return true;
610 }
611 
612 
613 bool DIALOG_BOARD_REANNOTATE::BuildFootprintList( std::vector<RefDesInfo>& aBadRefDes )
614 {
615  bool annotateSelected;
616  bool annotateFront = m_AnnotateFront->GetValue(); // Unless only doing back
617  bool annotateBack = m_AnnotateBack->GetValue(); // Unless only doing front
618  bool skipLocked = m_ExcludeLocked->GetValue();
619 
620  int errorcount = 0;
621  unsigned int backstartrefdes;
622  size_t firstnum = 0;
623 
624  m_frontFootprints.clear();
625  m_backFootprints.clear();
626  m_excludeArray.clear();
628 
629  std::vector<KIID> selected;
630 
631  if( m_AnnotateSelection->GetValue() )
632  {
633  for( EDA_ITEM* item : m_selection )
634  {
635  // Get the timestamps of selected footprints
636  if( item->Type() == PCB_FOOTPRINT_T )
637  selected.push_back( item->m_Uuid );
638  }
639  }
640 
641  annotateSelected = !selected.empty();
642 
643  wxString exclude;
644 
645  // Break exclude list into words.
646  for( auto thischar : m_ExcludeList->GetValue() )
647  {
648  if( ( ' ' == thischar ) || ( ',' == thischar ) )
649  {
650  m_excludeArray.push_back( exclude );
651  exclude.clear();
652  }
653  else
654  exclude += thischar;
655 
656  if( !exclude.empty() )
657  m_excludeArray.push_back( exclude );
658  }
659 
660  RefDesInfo fpData;
661  bool useModuleLocation = m_locationChoice->GetSelection() == 0;
662 
663  for( FOOTPRINT* footprint : m_footprints )
664  {
665  fpData.Uuid = footprint->m_Uuid;
666  fpData.RefDesString = footprint->GetReference();
667  fpData.FPID = footprint->GetFPID();
668  fpData.x = useModuleLocation ? footprint->GetPosition().x
669  : footprint->Reference().GetPosition().x;
670  fpData.y = useModuleLocation ? footprint->GetPosition().y
671  : footprint->Reference().GetPosition().y;
672  fpData.roundedx = RoundToGrid( fpData.x, m_sortGridx ); // Round to sort
673  fpData.roundedy = RoundToGrid( fpData.y, m_sortGridy );
674  fpData.Front = footprint->GetLayer() == F_Cu;
675  fpData.Action = UpdateRefDes; // Usually good
676 
677  if( fpData.RefDesString.IsEmpty() )
678  {
679  fpData.Action = EmptyRefDes;
680  }
681  else
682  {
683  firstnum = fpData.RefDesString.find_first_of( "0123456789" );
684 
685  if( std::string::npos == firstnum )
686  fpData.Action = InvalidRefDes; // do not change ref des such as 12 or +1, or L
687  }
688 
689  // Get the type (R, C, etc)
690  fpData.RefDesType = fpData.RefDesString.substr( 0, firstnum );
691 
692  for( wxString excluded : m_excludeArray )
693  {
694  if( excluded == fpData.RefDesType ) // Am I supposed to exclude this type?
695  {
696  fpData.Action = Exclude; // Yes
697  break;
698  }
699  }
700 
701  if(( fpData.Front && annotateBack ) || // If a front fp and doing backs only
702  ( !fpData.Front && annotateFront ) || // If a back fp and doing front only
703  ( footprint->IsLocked() && skipLocked ) ) // If excluding locked and it is locked
704  {
705  fpData.Action = Exclude;
706  }
707 
708  if( annotateSelected )
709  { // If only annotating selected c
710  fpData.Action = Exclude; // Assume it isn't selected
711 
712  for( KIID sel : selected )
713  {
714  if( fpData.Uuid == sel )
715  { // Found in selected footprints
716  fpData.Action = UpdateRefDes; // Update it
717  break;
718  }
719  }
720  }
721 
722  if( fpData.Front )
723  m_frontFootprints.push_back( fpData );
724  else
725  m_backFootprints.push_back( fpData );
726  }
727 
728  // Determine the sort order for the front.
730 
731  // Sort the front footprints.
732  sort( m_frontFootprints.begin(), m_frontFootprints.end(), ModuleCompare );
733 
734  // Determine the sort order for the back.
736 
737  // Sort the back footprints.
738  sort( m_backFootprints.begin(), m_backFootprints.end(), ModuleCompare );
739 
740  m_refDesTypes.clear();
741  m_changeArray.clear();
742  backstartrefdes = wxAtoi( m_BackRefDesStart->GetValue() );
743 
744  if( !m_frontFootprints.empty() )
745  {
746  BuildChangeArray( m_frontFootprints, wxAtoi( m_FrontRefDesStart->GetValue() ),
747  m_FrontPrefix->GetValue(), m_RemoveFrontPrefix->GetValue(), aBadRefDes );
748  }
749 
750  if( !m_backFootprints.empty() )
751  {
752  BuildChangeArray( m_backFootprints, backstartrefdes, m_BackPrefix->GetValue(),
753  m_RemoveBackPrefix->GetValue(), aBadRefDes );
754  }
755 
756  if( !m_changeArray.empty() )
757  sort( m_changeArray.begin(), m_changeArray.end(), ChangeArrayCompare );
758 
759  LogChangePlan();
760 
761  size_t changearraysize = m_changeArray.size();
762 
763  for( size_t i = 0; i < changearraysize; i++ ) // Scan through for duplicates if update or skip
764  {
765  if( ( m_changeArray[i].Action != EmptyRefDes )
766  && ( m_changeArray[i].Action != InvalidRefDes ) )
767  {
768  for( size_t j = i + 1; j < changearraysize; j++ )
769  {
770  if( m_changeArray[i].NewRefDes == m_changeArray[j].NewRefDes )
771  {
772  ShowReport( "Duplicate instances of " + m_changeArray[j].NewRefDes,
774 
775  if( errorcount++ > MAXERROR )
776  {
777  ShowReport( _( "Aborted: too many errors" ), RPT_SEVERITY_ERROR );
778  break;
779  }
780  }
781  }
782  }
783 
784  if( errorcount > MAXERROR )
785  break;
786  }
787 
788  return ( 0 == errorcount );
789 }
790 
791 
792 void DIALOG_BOARD_REANNOTATE::BuildChangeArray( std::vector<RefDesInfo>& aFootprints,
793  unsigned int aStartRefDes, wxString aPrefix,
794  bool aRemovePrefix,
795  std::vector<RefDesInfo>& aBadRefDes )
796 {
797  size_t i;
798  RefDesChange change;
799  RefDesTypeStr newtype;
800 
801  wxString refdestype;
802  size_t prefixsize = aPrefix.size();
803 
804  bool haveprefix = ( 0 != prefixsize ); // Do I have a prefix?
805  bool addprefix = haveprefix & !aRemovePrefix; // Yes- and I'm not removing it
806  aRemovePrefix &= haveprefix; // Only remove if I have a prefix
807 
808  bool prefixpresent; // Prefix found
809 
810  wxString logstring = ( aFootprints.front().Front ) ? _( "\n\nFront Footprints" )
811  : _( "\n\nBack Footprints" );
812  LogFootprints( logstring, aFootprints );
813 
814  if( 0 != aStartRefDes ) // Initialize the change array if present
815  {
816  for( i = 0; i < m_refDesTypes.size(); i++ )
817  m_refDesTypes[i].RefDesCount = aStartRefDes;
818  }
819 
820  for( RefDesInfo fpData : aFootprints )
821  {
822  change.Uuid = fpData.Uuid;
823  change.Action = fpData.Action;
824  change.OldRefDesString = fpData.RefDesString;
825  change.NewRefDes = fpData.RefDesString;
826  change.Front = fpData.Front;
827 
828  if( fpData.RefDesString.IsEmpty() )
829  fpData.Action = EmptyRefDes;
830 
831  if( ( change.Action == EmptyRefDes ) || ( change.Action == InvalidRefDes ) )
832  {
833  m_changeArray.push_back( change );
834  aBadRefDes.push_back( fpData );
835  continue;
836  }
837 
838  if( change.Action == UpdateRefDes )
839  {
840  refdestype = fpData.RefDesType;
841  prefixpresent = ( 0 == fpData.RefDesType.find( aPrefix ) );
842 
843  if( addprefix && !prefixpresent )
844  fpData.RefDesType.insert( 0, aPrefix ); // Add prefix once only
845 
846  if( aRemovePrefix && prefixpresent ) // If there is a prefix remove it
847  fpData.RefDesType.erase( 0, prefixsize );
848 
849  for( i = 0; i < m_refDesTypes.size(); i++ ) // See if it is in the types array
850  {
851  if( m_refDesTypes[i].RefDesType == fpData.RefDesType ) // Found it!
852  break;
853  }
854 
855  // Wasn't in the types array so add it
856  if( i == m_refDesTypes.size() )
857  {
858  newtype.RefDesType = fpData.RefDesType;
859  newtype.RefDesCount = ( aStartRefDes == 0 ? 1 : aStartRefDes );
860  m_refDesTypes.push_back( newtype );
861  }
862 
863  change.NewRefDes = m_refDesTypes[i].RefDesType
864  + std::to_string( m_refDesTypes[i].RefDesCount++ );
865  }
866 
867  m_changeArray.push_back( change );
868  }
869 }
870 
871 
873 {
874  size_t i;
875 
876  for( i = 0; i < m_changeArray.size(); i++ )
877  {
878  if( aFootprint->m_Uuid == m_changeArray[i].Uuid )
879  return ( &m_changeArray[i] );
880  }
881 
882  ShowReport( _( "Footprint not found in changelist" ) + wxS( " " ) + aFootprint->GetReference(),
884 
885  return nullptr; // Should never happen
886 }
bool ReannotateBoard(void)
Actually reannotate the board.
std::vector< wxRadioButton * > m_sortButtons
Class DIALOG_BOARD_REANNOTATE_BASE.
bool DescendingSecond
void ShowReport(wxString aMessage, SEVERITY aSeverity)
Break report into strings separated by and sent to the reporter.
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
int StrNumCmp(const wxString &aString1, const wxString &aString2, bool aIgnoreCase)
Compare two strings with alphanumerical content.
Definition: string.cpp:518
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[]
SEVERITY
Definition: ui_common.h:100
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.
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.
APP_SETTINGS_BASE * KifaceSettings() const
Definition: kiface_i.h:92
DIALOG_BOARD_REANNOTATE(PCB_EDIT_FRAME *aParentFrame)
void BuildChangeArray(std::vector< RefDesInfo > &aFootprints, unsigned int aStartRefDes, wxString aPrefix, bool aRemovePrefix, std::vector< RefDesInfo > &aBadRefDes)
Scan through the footprint arrays and create the from -> to array.
#define MINGRID
std::vector< wxString > m_excludeArray
#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
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:233
KIFACE_I & Kiface()
Global KIFACE_I "get" accessor.
#define ASCENDINGSECOND
std::vector< RefDesTypeStr > m_refDesTypes
const wxString & GetReference() const
Definition: footprint.h:421
KIGFX::GAL * GetGAL() const
Return a pointer to the GAL instance used in the panel.
#define _(s)
#define ASCENDINGFIRST
#define SetSortCodes(DirArray, Code)
wxString ActionMessage[]
int BackDirectionsArray[]
int FrontDirectionsArray[]
void OnCloseClick(wxCommandEvent &event) override
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:97
#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:475
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:198
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
wxString CoordTowxString(int aX, int aY)
Convert coordinates to wxString.
static void BuildChoiceList(wxArrayString *aGridsList, APP_SETTINGS_BASE *aCfg, EDA_DRAW_FRAME *aParent)
Definition: grid_menu.cpp:79
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:100
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
TOOL_MANAGER * GetToolManager() const
Return the MVC controller.
Definition: tools_holder.h:54
BOARD * GetBoard() const
std::vector< wxRadioButton * > AnnotateWhat
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:296
bool IsEmpty() const
Definition: board.h:281
bool SortYFirst
#define SORTXFIRST
unsigned int RefDesCount
void MakeSampleText(wxString &aMessage)
Make the text to summarize what is about to happen.
#define VALIDPREFIX