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) 1992-2020 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 <kiface_i.h>
34 #include <mail_type.h>
35 #include <pcbnew_settings.h>
36 #include <sstream>
37 #include <tool/tool_manager.h>
38 #include <tool/grid_menu.h>
39 
43 
44 //
45 // This converts the index into a sort code. Note that Back sort code will have left and
46 // right swapped.
47 //
49  SORTYFIRST + ASCENDINGFIRST + ASCENDINGSECOND, //"Top to bottom, left to right", // 100
50  SORTYFIRST + ASCENDINGFIRST + DESCENDINGSECOND, //"Top to bottom, right to left", // 101
51  SORTYFIRST + DESCENDINGFIRST + ASCENDINGSECOND, //"Back to Front, left to right", // 110
52  SORTYFIRST + DESCENDINGFIRST + DESCENDINGSECOND, //"Back to Front, right to left", // 111
53  SORTXFIRST + ASCENDINGFIRST + ASCENDINGSECOND, //"Left to right, Front to Back", // 000
54  SORTXFIRST + ASCENDINGFIRST + DESCENDINGSECOND, //"Left to right, Back to Front", // 001
55  SORTXFIRST + DESCENDINGFIRST + ASCENDINGSECOND, //"Right to left, Front to Back", // 010
56  SORTXFIRST + DESCENDINGFIRST + DESCENDINGSECOND //"Right to left, Back to Front", // 011
57 };
58 
59 
60 //
61 // Back Left/Right is opposite because it is a mirror image (coordinates are from the top)
62 //
64  SORTYFIRST + ASCENDINGFIRST + DESCENDINGSECOND, //"Top to bottom, left to right", // 101
65  SORTYFIRST + ASCENDINGFIRST + ASCENDINGSECOND, //"Top to bottom, right to left", // 100
66  SORTYFIRST + DESCENDINGFIRST + DESCENDINGSECOND, //"Bottom to top, left to right", // 111
67  SORTYFIRST + DESCENDINGFIRST + ASCENDINGSECOND, //"Bottom to top, right to left", // 110
68  SORTXFIRST + DESCENDINGFIRST + ASCENDINGSECOND, //"Left to right, top to bottom", // 010
69  SORTXFIRST + DESCENDINGFIRST + DESCENDINGSECOND, //"Left to right, bottom to top", // 011
70  SORTXFIRST + ASCENDINGFIRST + ASCENDINGSECOND, //"Right to left, top to bottom", // 000
71  SORTXFIRST + ASCENDINGFIRST + DESCENDINGSECOND //"Right to left, bottom to top", // 001
72 };
73 
74 #define SetSortCodes( DirArray, Code ) \
75  { \
76  SortYFirst = ( ( DirArray[Code] & SORTYFIRST ) != 0 ); \
77  DescendingFirst = ( ( DirArray[Code] & DESCENDINGFIRST ) != 0 ); \
78  DescendingSecond = ( ( DirArray[Code] & DESCENDINGSECOND ) != 0 ); \
79  }
80 
81 
82 wxString AnnotateString[] = {
83  _( "All" ), //AnnotateAll
84  _( "Only front" ), //AnnotateFront
85  _( "Only back" ), //AnnotateBack
86  _( "Only selected" ) //AnnotateSelected
87 };
88 
89 
90 wxString ActionMessage[] = {
91  "", //UpdateRefDes
92  _( "Empty" ), //EmptyRefDes
93  _( "Invalid" ), //InvalidRefDes
94  _( "Excluded" ) //Exclude
95 };
96 
97 
99  : DIALOG_BOARD_REANNOTATE_BASE( aParentFrame ),
100  m_footprints( aParentFrame->GetBoard()->Footprints() )
101 {
103  InitValues();
104 
105  m_frame = aParentFrame;
107  m_standalone = !m_frame->TestStandalone(); //Do this here forces the menu on top
108 
109  if( m_standalone )
110  { //Only update the schematic if not in standalone mode
111  m_UpdateSchematic->Enable( false );
112  m_UpdateSchematic->SetValue( false );
113  }
114 
115  m_FrontRefDesStart->SetValidator( wxTextValidator( wxFILTER_DIGITS ) );
116  m_BackRefDesStart->SetValidator( wxTextValidator( wxFILTER_DIGITS ) );
117 
118  m_sdbSizerOK->SetLabel( _( "Reannotate PCB" ) );
119  m_sdbSizerCancel->SetLabel( _( "Close" ) );
120  m_sdbSizer->Layout();
121 
122  m_settings = aParentFrame->config();
123  wxArrayString gridslist;
124  GRID_MENU::BuildChoiceList( &gridslist, m_settings, aParentFrame );
125 
126  if( -1 == m_gridIndex ) //If no default loaded
127  m_gridIndex = m_settings->m_Window.grid.last_size_idx; //Get the current grid size
128 
131 
132  m_GridChoice->Set( gridslist ); //Show the choice in the dialog
133  m_GridChoice->SetSelection( m_gridIndex );
134 
135  for( wxRadioButton* button : m_sortButtons )
136  button->SetValue( false );
137 
138  m_sortButtons[m_sortCode]->SetValue( true );
139 
141 
142  if( !m_selection.Empty() )
144 
145  for( wxRadioButton* button : AnnotateWhat )
146  button->SetValue( false );
147 
148  m_annotationChoice = ( m_sortCode >= (int) AnnotateWhat.size() ) ?
151 
152  AnnotateWhat[m_annotationChoice]->SetValue( true );
153 
162 
163  m_ExcludeList->SetToolTip( m_ExcludeListText->GetToolTipText() );
164  m_GridChoice->SetToolTip( m_SortGridText->GetToolTipText() );
165 
166  m_MessageWindow->SetFileName( Prj().GetProjectPath() + wxT( "report.txt" ) );
167 
169 }
170 
171 
173 {
174  GetParameters(); //Get the current menu settings
176  cfg->m_Reannotate.sort_on_fp_location = m_locationChoice->GetSelection() == 0;
180  cfg->m_Reannotate.exclude_locked = m_ExcludeLocked->GetValue();
181 
186 
189  cfg->m_Reannotate.front_prefix = m_FrontPrefix->GetValue();
190  cfg->m_Reannotate.back_prefix = m_BackPrefix->GetValue();
191  cfg->m_Reannotate.exclude_list = m_ExcludeList->GetValue();
193 }
194 
195 
198 {
200  m_locationChoice->SetSelection( cfg->m_Reannotate.sort_on_fp_location ? 0 : 1 );
204  m_ExcludeLocked->SetValue( cfg->m_Reannotate.exclude_locked );
205 
210 
213  m_FrontPrefix->SetValue( cfg->m_Reannotate.front_prefix );
214  m_BackPrefix->SetValue( cfg->m_Reannotate.back_prefix );
215  m_ExcludeList->SetValue( cfg->m_Reannotate.exclude_list );
217 }
218 
219 
220 void DIALOG_BOARD_REANNOTATE::OnCloseClick( wxCommandEvent& event )
221 {
222  EndDialog( wxID_OK );
223 }
224 
225 
226 //
228 void DIALOG_BOARD_REANNOTATE::FilterPrefix( wxTextCtrl* aPrefix )
229 {
230  std::string tmps = VALIDPREFIX;
231 
232  if( aPrefix->GetValue().empty() )
233  return; //Should never happen
234 
235  char lastc = aPrefix->GetValue().Last();
236 
237  if( isalnum( (int) lastc ) )
238  return;
239 
240  if( std::string::npos != tmps.find( lastc ) )
241  return;
242 
243  tmps = aPrefix->GetValue();
244  aPrefix->Clear();
245  tmps.pop_back();
246  aPrefix->AppendText( tmps );
247 }
248 
249 
250 void DIALOG_BOARD_REANNOTATE::FilterFrontPrefix( wxCommandEvent& event )
251 {
253 }
254 
255 
256 void DIALOG_BOARD_REANNOTATE::FilterBackPrefix( wxCommandEvent& event )
257 {
259 }
260 
261 
262 void DIALOG_BOARD_REANNOTATE::OnApplyClick( wxCommandEvent& event )
263 {
264  wxString warning;
265 
266  if( m_frame->GetBoard()->IsEmpty() )
267  {
268  ShowReport( _( "No PCB to reannotate!" ), RPT_SEVERITY_ERROR );
269  return;
270  }
271 
272  GetParameters(); //Figure out how this is to be done
273  MakeSampleText( warning );
274 
275  if( !IsOK( m_frame, warning ) )
276  return;
277 
278  if( ReannotateBoard() )
279  ShowReport( _( "PCB and schematic successfully reannotated" ), RPT_SEVERITY_ACTION );
280 
281  m_MessageWindow->SetLazyUpdate( false );
282  m_MessageWindow->Flush( false );
283  m_frame->GetCanvas()->Refresh(); //Redraw
284  m_frame->OnModify(); //Need to save file on exit.
285 }
286 
287 
288 //
290 void DIALOG_BOARD_REANNOTATE::MakeSampleText( wxString& aMessage )
291 {
292  wxString tmp;
293 
294  aMessage.Printf( _( "\n%s footprints will be reannotated." ),
296 
297  if( !m_ExcludeList->GetValue().empty() )
298  {
299  aMessage += wxString::Format( _( "\nAny reference types %s will not be annotated." ),
300  m_ExcludeList->GetValue() );
301  }
302 
303  if( m_ExcludeLocked->GetValue() )
304  aMessage += wxString::Format( _( "\nLocked footprints will not be annotated" ) );
305 
306  if( !m_AnnotateBack->GetValue() )
307  {
308  aMessage += wxString::Format( _( "\nFront footprints will start at %s" ),
309  m_FrontRefDesStart->GetValue() );
310  }
311 
312  if( !m_AnnotateFront->GetValue() )
313  {
314  bool frontPlusOne = ( 0 == wxAtoi( m_BackRefDesStart->GetValue() ) )
315  && !m_AnnotateBack->GetValue();
316 
317  aMessage += wxString::Format( _( "\nBack footprints will start at %s." ),
318  frontPlusOne ? _( "the last front footprint + 1" ) :
319  m_BackRefDesStart->GetValue() );
320  }
321 
322  if( !m_FrontPrefix->GetValue().empty() )
323  {
324  if( m_RemoveFrontPrefix->GetValue() )
325  {
326  aMessage += wxString::Format( _( "\nFront footprints starting with '%s' will have "
327  "the prefix removed." ),
328  m_FrontPrefix->GetValue() );
329  }
330  else
331  {
332  aMessage += wxString::Format( _( "\nFront footprints will have '%s' inserted as a "
333  "prefix." ),
334  m_FrontPrefix->GetValue() );
335  }
336  }
337 
338  if( !m_BackPrefix->GetValue().empty() )
339  {
340  if( m_RemoveBackPrefix->GetValue() )
341  {
342  aMessage += wxString::Format( _( "\nBack footprints starting with '%s' will have the "
343  "prefix removed." ),
344  m_BackPrefix->GetValue() );
345  }
346  else
347  {
348  aMessage += wxString::Format( _( "\nBack footprints will have '%s' inserted as a "
349  "prefix." ),
350  m_BackPrefix->GetValue() );
351  }
352  }
353 
354  bool fpLocation = m_locationChoice->GetSelection() == 0;
355 
356  aMessage += wxString::Format( _( "\nPrior to sorting by %s, the coordinates of which will be "
357  "rounded to a %s, %s grid." ),
358  fpLocation ? _( "footprint location" )
359  : _( "reference designator location" ),
362 
363  if( m_UpdateSchematic->GetValue() )
364  aMessage += _( "\nThe schematic will be updated." );
365  else
366  aMessage += _( "\nThe schematic will not be updated." );
367 
368  ShowReport( aMessage, RPT_SEVERITY_INFO );
369 }
370 
371 
373 {
374  m_sortCode = 0; //Convert radio button to sort direction code
375 
376  for( wxRadioButton* sortbuttons : m_sortButtons )
377  {
378  if( sortbuttons->GetValue() )
379  break;
380 
381  m_sortCode++;
382  }
383 
384  if( m_sortCode >= (int) m_sortButtons.size() )
385  m_sortCode = 0;
386 
387  m_frontPrefixString = m_FrontPrefix->GetValue();
388  m_backPrefixString = m_BackPrefix->GetValue();
389 
390  //Get the chosen sort grid for rounding
391  m_gridIndex = m_GridChoice->GetSelection();
392 
393  if( m_gridIndex >= ( int ) m_settings->m_Window.grid.sizes.size() )
394  {
399  }
400  else
401  {
405  }
406 
407  int i = 0;
408 
409  for( wxRadioButton* button : AnnotateWhat )
410  {
411  if( button->GetValue() )
412  break;
413  else
414  i++;
415  }
416 
417  m_annotationChoice = ( i >= (int) AnnotateWhat.size() ) ? AnnotationChoice::AnnotateAll : i;
418 
420 }
421 
422 
423 //
425 int DIALOG_BOARD_REANNOTATE::RoundToGrid( int aCoord, int aGrid )
426 {
427  if( 0 == aGrid )
428  aGrid = MINGRID;
429 
430  int rounder;
431  rounder = aCoord % aGrid;
432  aCoord -= rounder;
433 
434  if( abs( rounder ) > ( aGrid / 2 ) )
435  aCoord += ( aCoord < 0 ? -aGrid : aGrid );
436 
437  return ( aCoord );
438 }
439 
440 
441 //
444 static bool ChangeArrayCompare( const RefDesChange& aA, const RefDesChange& aB )
445 {
446  return ( aA.OldRefDesString < aB.OldRefDesString );
447 }
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 
483 //
487 {
488  return wxString::Format( "%s, %s",
490  MessageTextFromValue( m_units, aY ) );
491 }
492 
493 
494 //
496 void DIALOG_BOARD_REANNOTATE::ShowReport( wxString aMessage, SEVERITY aSeverity )
497 {
498  size_t pos = 0, prev = 0;
499 
500  do
501  {
502  pos = aMessage.ToStdString().find( '\n', prev );
503  m_MessageWindow->Report( aMessage.ToStdString().substr( prev, pos - prev ), aSeverity );
504  prev = pos + 1;
505  } while( std::string::npos != pos );
506 
507 }
508 
509 
510 //
513 {
514  int i = 1;
515  wxString message;
516 
517  message.Printf( _( "\n\nThere are %i types of reference designations\n"
518  "**********************************************************\n" ),
519  (int) m_refDesTypes.size() );
520 
521  for( RefDesTypeStr Type : m_refDesTypes ) //Show all the types of refdes
522  message += Type.RefDesType + ( 0 == ( i++ % 16 ) ? "\n" : " " );
523 
524  if( !m_excludeArray.empty() )
525  {
526  wxString excludes;
527 
528  for( wxString& exclude : m_excludeArray ) //Show the refdes we are excluding
529  excludes += exclude + " ";
530 
531  message += wxString::Format( _( "\nExcluding: %s from reannotation\n\n" ), excludes );
532  }
533 
534  message += _( "\n Change Array\n***********************\n" );
535 
536  for( RefDesChange Change : m_changeArray )
537  {
538  message += wxString::Format( "%s -> %s %s %s\n", Change.OldRefDesString, Change.NewRefDes,
539  ActionMessage[Change.Action],
540  UpdateRefDes != Change.Action ? wxS( " " ) + _( "will be ignored" ) : wxString("") );
541  }
542 
543  ShowReport( message, RPT_SEVERITY_INFO );
544 }
545 
546 
547 //
549 void DIALOG_BOARD_REANNOTATE::LogFootprints( const wxString& aMessage,
550  const std::vector<RefDesInfo>& aFootprints )
551 {
552  wxString message = aMessage;
553 
554  if( aFootprints.empty() )
555  message += _( "\nNo footprints" );
556  else
557  {
558  int i = 1;
559  bool fpLocations = m_locationChoice->GetSelection() == 0;
560 
561  message += wxString::Format( _( "\n*********** Sort on %s ***********" ),
562  fpLocations ? _( "Footprint Coordinates" )
563  : _( "Reference Designator Coordinates" ) );
564 
565  message += wxString::Format( _( "\nSort Code %d" ), m_sortCode );
566 
567  for( const RefDesInfo& mod : aFootprints )
568  {
569  message += wxString::Format( _( "\n%d %s Uuid: [%s], X, Y: %s, Rounded X, Y, %s" ),
570  i++,
571  mod.RefDesString,
572  mod.Uuid.AsString(),
573  CoordTowxString( mod.x, mod.y ),
574  CoordTowxString( mod.roundedx, mod.roundedy ) );
575  }
576  }
577 
578  ShowReport( message, RPT_SEVERITY_INFO );
579 }
580 
581 
582 //
586 {
587  std::string payload;
588  std::vector<RefDesInfo> BadRefDes;
589  wxString message, badrefdes;
590  STRING_FORMATTER stringformatter;
591  RefDesChange* newref;
592  NETLIST netlist;
593 
594  if( !BuildFootprintList( BadRefDes ) )
595  {
596  ShowReport( "Selected options resulted in errors! Change them and try again.",
598  return false;
599  }
600 
601  if( !BadRefDes.empty() )
602  {
603  message.Printf(
604  _( "\nPCB has %d empty or invalid reference designations."
605  "\nRecommend you run DRC with 'Test footprints against schematic' checked.\n" ),
606  (int) BadRefDes.size() );
607 
608  for( const RefDesInfo& mod : BadRefDes )
609  {
610  badrefdes += wxString::Format( _( "\nRefDes: %s Footprint: %s:%s at %s on PCB." ),
611  mod.RefDesString,
612  mod.FPID.GetLibNickname().wx_str(),
613  mod.FPID.GetLibItemName().wx_str(),
614  CoordTowxString( mod.x, mod.y ) );
615  }
616 
617  ShowReport( message + badrefdes + "\n", RPT_SEVERITY_WARNING );
618  message += _( "Reannotate anyway?" );
619 
620  if( !IsOK( m_frame, message ) )
621  return ( false );
622  }
623 
624  payload.clear(); //If not updating schematic no netlist error
625 
626  if( m_UpdateSchematic->GetValue() )
627  { //If updating schematic send a netlist
628 
629  for( FOOTPRINT* footprint : m_footprints )
630  { // Create a netlist
631  newref = GetNewRefDes( footprint );
632 
633  if( nullptr == newref )
634  return false; //Not found in changelist
635 
636  //add to the netlist
637  netlist.AddComponent( new COMPONENT( footprint->GetFPID(), newref->NewRefDes,
638  footprint->GetValue(), footprint->GetPath(), { footprint->m_Uuid } ) );
639  }
640 
641  netlist.Format( "pcb_netlist", &stringformatter, 0,
643 
644  payload = stringformatter.GetString(); //create netlist
645 
646  //Send netlist to eeSchema
647  bool attemptreannotate = m_frame->ReannotateSchematic( payload );
648 
649  if( !attemptreannotate )
650  { //Didn't get a valid reply
651  ShowReport( _( "\nReannotate failed!\n" ), RPT_SEVERITY_WARNING );
652  return false;
653  }
654 
655  } //If updating schematic
656 
657  bool reannotateOk = payload.size( ) == 0;
658 
659  ShowReport( payload, reannotateOk ? RPT_SEVERITY_ACTION : RPT_SEVERITY_ERROR );
660  BOARD_COMMIT commit( m_frame );
661 
662  if( reannotateOk )//Only update if no errors
663  {
664 
665  for( FOOTPRINT* footprint : m_footprints )
666  {
667  newref = GetNewRefDes( footprint );
668 
669  if( nullptr == newref )
670  return false;
671 
672  commit.Modify( footprint ); // Make a copy for undo
673  footprint->SetReference( newref->NewRefDes ); // Update the PCB reference
674  m_frame->GetCanvas()->GetView()->Update( footprint ); // Touch the footprint
675  }
676  }
677 
678  commit.Push( "Geographic reannotation" );
679  return reannotateOk;
680 }
681 
682 
683 //
686 bool DIALOG_BOARD_REANNOTATE::BuildFootprintList( std::vector<RefDesInfo>& aBadRefDes )
687 {
688  bool annotateSelected;
689  bool annotateFront = m_AnnotateFront->GetValue(); //Unless only doing back
690  bool annotateBack = m_AnnotateBack->GetValue(); //Unless only doing front
691  bool skipLocked = m_ExcludeLocked->GetValue();
692 
693  int errorcount = 0;
694  unsigned int backstartrefdes;
695  size_t firstnum = 0;
696 
697  m_frontFootprints.clear();
698  m_backFootprints.clear();
699  m_excludeArray.clear();
701 
702  std::vector<KIID> selected;
703 
704  if( m_AnnotateSelection->GetValue() )
705  {
706  for( EDA_ITEM* item : m_selection )
707  {
708  //Get the timestamps of selected footprints
709  if( item->Type() == PCB_FOOTPRINT_T )
710  selected.push_back( item->m_Uuid );
711  }
712  }
713 
714  annotateSelected = !selected.empty();
715 
716  wxString exclude;
717 
718  for( auto thischar : m_ExcludeList->GetValue() )
719  { //Break exclude list into words
720 
721  if( ( ' ' == thischar ) || ( ',' == thischar ) )
722  {
723  m_excludeArray.push_back( exclude );
724  exclude.clear();
725  }
726  else
727  exclude += thischar;
728 
729  if( !exclude.empty() )
730  m_excludeArray.push_back( exclude );
731  }
732 
733  RefDesInfo fpData;
734  bool useModuleLocation = m_locationChoice->GetSelection() == 0;
735 
736  for( FOOTPRINT* footprint : m_footprints )
737  {
738  fpData.Uuid = footprint->m_Uuid;
739  fpData.RefDesString = footprint->GetReference();
740  fpData.FPID = footprint->GetFPID();
741  fpData.x = useModuleLocation ? footprint->GetPosition().x
742  : footprint->Reference().GetPosition().x;
743  fpData.y = useModuleLocation ? footprint->GetPosition().y
744  : footprint->Reference().GetPosition().y;
745  fpData.roundedx = RoundToGrid( fpData.x, m_sortGridx ); //Round to sort
746  fpData.roundedy = RoundToGrid( fpData.y, m_sortGridy );
747  fpData.Front = footprint->GetLayer() == F_Cu;
748  fpData.Action = UpdateRefDes; //Usually good
749 
750  if( fpData.RefDesString.IsEmpty() )
751  {
752  fpData.Action = EmptyRefDes;
753  }
754  else
755  {
756  firstnum = fpData.RefDesString.find_first_of( "0123456789" );
757 
758  if( std::string::npos == firstnum )
759  fpData.Action = InvalidRefDes; //do not change ref des such as 12 or +1, or L
760  }
761 
762  //Get the type (R, C, etc)
763  fpData.RefDesType = fpData.RefDesString.substr( 0, firstnum );
764 
765  for( wxString excluded : m_excludeArray )
766  {
767  if( excluded == fpData.RefDesType ) //Am I supposed to exclude this type?
768  {
769  fpData.Action = Exclude; //Yes
770  break;
771  }
772  }
773 
774  if(( fpData.Front && annotateBack ) || // If a front fp and doing backs only
775  ( !fpData.Front && annotateFront ) || // If a back fp and doing front only
776  ( footprint->IsLocked() && skipLocked ) ) // If excluding locked and it is locked
777  {
778  fpData.Action = Exclude;
779  }
780 
781  if( annotateSelected )
782  { // If onnly annotating selected c
783  fpData.Action = Exclude; // Assume it isn't selected
784 
785  for( KIID sel : selected )
786  {
787  if( fpData.Uuid == sel )
788  { // Found in selected footprints
789  fpData.Action = UpdateRefDes; // Update it
790  break;
791  }
792  }
793  }
794 
795  if( fpData.Front )
796  m_frontFootprints.push_back( fpData );
797  else
798  m_backFootprints.push_back( fpData );
799  }
800 
801  SetSortCodes( FrontDirectionsArray, m_sortCode ); //Determine the sort order for the front
802  sort( m_frontFootprints.begin(), m_frontFootprints.end(), ModuleCompare ); //Sort the front footprints
803 
804  SetSortCodes( BackDirectionsArray, m_sortCode ); //Determine the sort order for the back
805  sort( m_backFootprints.begin(), m_backFootprints.end(), ModuleCompare ); //Sort the back footprints
806 
807  m_refDesTypes.clear();
808  m_changeArray.clear();
809  backstartrefdes = wxAtoi( m_BackRefDesStart->GetValue() );
810 
811  if( !m_frontFootprints.empty() )
812  {
813  BuildChangeArray( m_frontFootprints, wxAtoi( m_FrontRefDesStart->GetValue() ),
814  m_FrontPrefix->GetValue(), m_RemoveFrontPrefix->GetValue(), aBadRefDes );
815  }
816 
817  if( !m_backFootprints.empty() )
818  {
819  BuildChangeArray( m_backFootprints, backstartrefdes, m_BackPrefix->GetValue(),
820  m_RemoveBackPrefix->GetValue(), aBadRefDes );
821  }
822 
823  if( !m_changeArray.empty() )
824  sort( m_changeArray.begin(), m_changeArray.end(), ChangeArrayCompare );
825 
826  LogChangePlan();
827 
828  size_t changearraysize = m_changeArray.size();
829 
830  for( size_t i = 0; i < changearraysize; i++ ) //Scan through for duplicates if update or skip
831  {
832  if( ( m_changeArray[i].Action != EmptyRefDes )
833  && ( m_changeArray[i].Action != InvalidRefDes ) )
834  {
835  for( size_t j = i + 1; j < changearraysize; j++ )
836  {
837  if( m_changeArray[i].NewRefDes == m_changeArray[j].NewRefDes )
838  {
839  ShowReport( "Duplicate instances of " + m_changeArray[j].NewRefDes,
841 
842  if( errorcount++ > MAXERROR )
843  {
844  ShowReport( _( "Aborted: too many errors" ), RPT_SEVERITY_ERROR );
845  break;
846  }
847  }
848  }
849  }
850 
851  if( errorcount > MAXERROR )
852  break;
853  }
854 
855  return ( 0 == errorcount );
856 }
857 
858 
859 //
861 void DIALOG_BOARD_REANNOTATE::BuildChangeArray( std::vector<RefDesInfo>& aFootprints,
862  unsigned int aStartRefDes, wxString aPrefix,
863  bool aRemovePrefix,
864  std::vector<RefDesInfo>& aBadRefDes )
865 {
866  size_t i;
867  RefDesChange change;
868  RefDesTypeStr newtype;
869 
870  wxString refdestype;
871  size_t prefixsize = aPrefix.size();
872 
873  bool haveprefix = ( 0 != prefixsize ); //Do I have a prefix?
874  bool addprefix = haveprefix & !aRemovePrefix; //Yes- and I'm not removing it
875  aRemovePrefix &= haveprefix; //Only remove if I have a prefix
876 
877  bool prefixpresent; //Prefix found
878 
879  wxString logstring = ( aFootprints.front().Front ) ? _( "\n\nFront Footprints" )
880  : _( "\n\nBack Footprints" );
881  LogFootprints( logstring, aFootprints );
882 
883  if( 0 != aStartRefDes ) //Initialize the change array if present
884  {
885  for( i = 0; i < m_refDesTypes.size(); i++ )
886  m_refDesTypes[i].RefDesCount = aStartRefDes;
887  }
888 
889  for( RefDesInfo fpData : aFootprints )
890  {
891  change.Uuid = fpData.Uuid;
892  change.Action = fpData.Action;
893  change.OldRefDesString = fpData.RefDesString;
894  change.NewRefDes = fpData.RefDesString;
895  change.Front = fpData.Front;
896 
897  if( fpData.RefDesString.IsEmpty() )
898  fpData.Action = EmptyRefDes;
899 
900  if( ( change.Action == EmptyRefDes ) || ( change.Action == InvalidRefDes ) )
901  {
902  m_changeArray.push_back( change );
903  aBadRefDes.push_back( fpData );
904  continue;
905  }
906 
907  if( change.Action == UpdateRefDes )
908  {
909  refdestype = fpData.RefDesType;
910  prefixpresent = ( 0 == fpData.RefDesType.find( aPrefix ) );
911 
912  if( addprefix && !prefixpresent )
913  fpData.RefDesType.insert( 0, aPrefix ); //Add prefix once only
914 
915  if( aRemovePrefix && prefixpresent ) //If there is a prefix remove it
916  fpData.RefDesType.erase( 0, prefixsize );
917 
918  for( i = 0; i < m_refDesTypes.size(); i++ ) //See if it is in the types array
919  {
920  if( m_refDesTypes[i].RefDesType == fpData.RefDesType ) //Found it!
921  break;
922  }
923 
924  if( i == m_refDesTypes.size() )
925  { //Wasn't in the types array so add it
926  newtype.RefDesType = fpData.RefDesType;
927  newtype.RefDesCount = ( aStartRefDes == 0 ? 1 : aStartRefDes );
928  m_refDesTypes.push_back( newtype );
929  }
930 
931  change.NewRefDes = m_refDesTypes[i].RefDesType
932  + std::to_string( m_refDesTypes[i].RefDesCount++ );
933  }
934  m_changeArray.push_back( change ); //Add to the change array
935  }
936 }
937 
938 
939 //
942 {
943  size_t i;
944 
945  for( i = 0; i < m_changeArray.size(); i++ )
946  {
947  if( aFootprint->m_Uuid == m_changeArray[i].Uuid )
948  return ( &m_changeArray[i] );
949  }
950 
951  ShowReport( _( "Footprint not found in changelist" ) + wxS( " " ) + aFootprint->GetReference(),
953 
954  return nullptr; //Should never happen
955 }
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:125
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
const BITMAP_OPAQUE reannotate_up_left_xpm[1]
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[]
SEVERITY
Definition: ui_common.h:83
#define CTL_OMIT_NETS
Definition: pcb_netlist.h:295
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
const BITMAP_OPAQUE reannotate_down_left_xpm[1]
DIALOG_BOARD_REANNOTATE(PCB_EDIT_FRAME *aParentFrame)
const BITMAP_OPAQUE reannotate_down_right_xpm[1]
const BITMAP_OPAQUE reannotate_right_down_xpm[1]
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.
void AddComponent(COMPONENT *aComponent)
Function AddComponent adds aComponent to the NETLIST.
#define MINGRID
bool TestStandalone(void)
Test if standalone mode.
std::vector< wxString > m_excludeArray
#define SORTYFIRST
RefDesChange * GetNewRefDes(FOOTPRINT *aFootprint)
wxBitmap KiBitmap(BITMAP_DEF aBitmap)
Construct a wxBitmap from a memory record, held in a BITMAP_DEF.
Definition: bitmap.cpp:82
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
NETLIST stores all of information read from a netlist along with the flags used to update the NETLIST...
Definition: pcb_netlist.h:207
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.
#define CTL_OMIT_FILTERS
Definition: pcb_netlist.h:296
int RoundToGrid(int aCoord, int aGrid)
Round an int coordinate to a suitable grid.
FOOTPRINTS & Footprints()
Definition: board.h:296
KIFACE_I & Kiface()
Global KIFACE_I "get" accessor.
#define ASCENDINGSECOND
std::vector< RefDesTypeStr > m_refDesTypes
const wxString & GetReference() const
Definition: footprint.h:423
KIGFX::GAL * GetGAL() const
Return a pointer to the GAL instance used in the panel.
#define ASCENDINGFIRST
const BITMAP_OPAQUE reannotate_left_down_xpm[1]
const std::string & GetString()
Definition: richio.h:435
#define SetSortCodes(DirArray, Code)
const BITMAP_OPAQUE reannotate_up_right_xpm[1]
wxString ActionMessage[]
int BackDirectionsArray[]
int FrontDirectionsArray[]
void OnCloseClick(wxCommandEvent &event) override
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:524
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
void Format(const char *aDocName, OUTPUTFORMATTER *aOut, int aNestLevel, int aCtl=0)
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:149
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
bool ReannotateSchematic(std::string &aNetlist)
Send a command to Eeschema to re-annotate the schematic.
TOOL_MANAGER * GetToolManager() const
Return the MVC controller.
Definition: tools_holder.h:55
BOARD * GetBoard() const
const BITMAP_OPAQUE reannotate_left_up_xpm[1]
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:338
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:297
bool IsEmpty() const
Definition: board.h:344
const BITMAP_OPAQUE reannotate_right_up_xpm[1]
bool SortYFirst
#define SORTXFIRST
unsigned int RefDesCount
void MakeSampleText(wxString &aMessage)
Make the text to summarize what is about to happen.
#define VALIDPREFIX