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