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 right swapped.
46 //
48  SORTYFIRST + ASCENDINGFIRST + ASCENDINGSECOND, //"Top to bottom, left to right", // 100
49  SORTYFIRST + ASCENDINGFIRST + DESCENDINGSECOND, //"Top to bottom, right to left", // 101
50  SORTYFIRST + DESCENDINGFIRST + ASCENDINGSECOND, //"Back to Front, left to right", // 110
51  SORTYFIRST + DESCENDINGFIRST + DESCENDINGSECOND, //"Back to Front, right to left", // 111
52  SORTXFIRST + ASCENDINGFIRST + ASCENDINGSECOND, //"Left to right, Front to Back", // 000
53  SORTXFIRST + ASCENDINGFIRST + DESCENDINGSECOND, //"Left to right, Back to Front", // 001
54  SORTXFIRST + DESCENDINGFIRST + ASCENDINGSECOND, //"Right to left, Front to Back", // 010
55  SORTXFIRST + DESCENDINGFIRST + DESCENDINGSECOND //"Right to left, Back to Front", // 011
56 };
57 
58 
59 //
60 // Back Left/Right is opposite because it is a mirror image (coordinates are from the top)
61 //
63  SORTYFIRST + ASCENDINGFIRST + DESCENDINGSECOND, //"Top to bottom, left to right", // 101
64  SORTYFIRST + ASCENDINGFIRST + ASCENDINGSECOND, //"Top to bottom, right to left", // 100
65  SORTYFIRST + DESCENDINGFIRST + DESCENDINGSECOND, //"Bottom to top, left to right", // 111
66  SORTYFIRST + DESCENDINGFIRST + ASCENDINGSECOND, //"Bottom to top, right to left", // 110
67  SORTXFIRST + DESCENDINGFIRST + ASCENDINGSECOND, //"Left to right, top to bottom", // 010
68  SORTXFIRST + DESCENDINGFIRST + DESCENDINGSECOND, //"Left to right, bottom to top", // 011
69  SORTXFIRST + ASCENDINGFIRST + ASCENDINGSECOND, //"Right to left, top to bottom", // 000
70  SORTXFIRST + ASCENDINGFIRST + DESCENDINGSECOND //"Right to left, bottom to top", // 001
71 };
72 
73 #define SetSortCodes( DirArray, Code ) \
74  { \
75  SortYFirst = ( ( DirArray[Code] & SORTYFIRST ) != 0 ); \
76  DescendingFirst = ( ( DirArray[Code] & DESCENDINGFIRST ) != 0 ); \
77  DescendingSecond = ( ( DirArray[Code] & DESCENDINGSECOND ) != 0 ); \
78  }
79 
80 
81 wxString AnnotateString[] = {
82  _( "All" ), //AnnotateAll
83  _( "Only front" ), //AnnotateFront
84  _( "Only back" ), //AnnotateBack
85  _( "Only selected" ) //AnnotateSelected
86 };
87 
88 
89 wxString ActionMessage[] = {
90  "", //UpdateRefDes
91  _( "Empty" ), //EmptyRefDes
92  _( "Invalid" ), //InvalidRefDes
93  _( "Excluded" ) //Exclude
94 };
95 
96 
98  : DIALOG_BOARD_REANNOTATE_BASE( aParentFrame ),
99  m_modules( aParentFrame->GetBoard()->Modules() )
100 {
102  InitValues();
103 
104  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 
121  m_Settings = aParentFrame->config();
122  wxArrayString gridslist;
124  aParentFrame->GetUserUnits() != EDA_UNITS::INCHES );
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 
140  m_selection = m_frame->GetToolManager()->GetTool<SELECTION_TOOL>()->GetSelection();
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  if( m_MessageWindow->GetFileName().empty() )
167  { //Set the reporter window filename to something sensible
168  wxFileName fn = m_frame->GetBoard()->GetFileName();
169  fn.SetName( "annotationreport" );
170  fn.SetExt( "txt " );
171  wxString fullname = fn.GetFullPath();
172  m_MessageWindow->SetFileName( fullname );
173  }
174 
175  m_MessageWindow->SetPrintInfo( false ); //Suppress the "Info: " prefix
176 }
177 
178 
180 {
181  GetParameters(); //Get the current menu settings
183  cfg->m_Reannotate.sort_on_module_location = m_locationChoice->GetSelection() == 0;
187  cfg->m_Reannotate.exclude_locked = m_ExcludeLocked->GetValue();
188 
193 
196  cfg->m_Reannotate.front_prefix = m_FrontPrefix->GetValue();
197  cfg->m_Reannotate.back_prefix = m_BackPrefix->GetValue();
198  cfg->m_Reannotate.exclude_list = m_ExcludeList->GetValue();
200 }
201 
204 {
206  m_locationChoice->SetSelection( cfg->m_Reannotate.sort_on_module_location ? 0 : 1 );
210  m_ExcludeLocked->SetValue( cfg->m_Reannotate.exclude_locked );
211 
216 
219  m_FrontPrefix->SetValue( cfg->m_Reannotate.front_prefix );
220  m_BackPrefix->SetValue( cfg->m_Reannotate.back_prefix );
221  m_ExcludeList->SetValue( cfg->m_Reannotate.exclude_list );
223 }
224 
225 
226 void DIALOG_BOARD_REANNOTATE::OnCloseClick( wxCommandEvent& event )
227 {
228  EndDialog( wxID_OK );
229 }
230 
231 
232 //
234 void DIALOG_BOARD_REANNOTATE::FilterPrefix( wxTextCtrl* aPrefix )
235 {
236  std::string tmps = VALIDPREFIX;
237 
238  if( aPrefix->GetValue().empty() )
239  return; //Should never happen
240 
241  char lastc = aPrefix->GetValue().Last();
242 
243  if( isalnum( (int) lastc ) )
244  return;
245 
246  if( std::string::npos != tmps.find( lastc ) )
247  return;
248 
249  tmps = aPrefix->GetValue();
250  aPrefix->Clear();
251  tmps.pop_back();
252  aPrefix->AppendText( tmps );
253 }
254 
255 
256 void DIALOG_BOARD_REANNOTATE::FilterFrontPrefix( wxCommandEvent& event )
257 {
259 }
260 
261 
262 void DIALOG_BOARD_REANNOTATE::FilterBackPrefix( wxCommandEvent& event )
263 {
265 }
266 
267 
268 void DIALOG_BOARD_REANNOTATE::OnApplyClick( wxCommandEvent& event )
269 {
270  wxString warning;
271 
272  if( m_frame->GetBoard()->IsEmpty() )
273  {
274  ShowReport( _( "No PCB to reannotate!" ), RPT_SEVERITY_ERROR );
275  return;
276  }
277 
278  GetParameters(); //Figure out how this is to be done
279  MakeSampleText( warning );
280 
281  if( !IsOK( m_frame, warning ) )
282  return;
283 
284  if( ReannotateBoard() )
285  ShowReport( _( "PCB and schematic successfully reannotated" ), RPT_SEVERITY_ACTION );
286 
287  m_MessageWindow->SetLazyUpdate( false );
288  m_MessageWindow->Flush( false );
289  m_frame->GetCanvas()->Refresh(); //Redraw
290  m_frame->OnModify(); //Need to save file on exit.
291 }
292 
293 
294 //
296 void DIALOG_BOARD_REANNOTATE::MakeSampleText( wxString& aMessage )
297 {
298  wxString tmp;
299 
300  aMessage.Printf( _( "\n%s footprints will be reannotated." ),
302 
303  if( !m_ExcludeList->GetValue().empty() )
304  {
305  aMessage += wxString::Format( _( "\nAny reference types %s will not be annotated." ),
306  m_ExcludeList->GetValue() );
307  }
308 
309  if( m_ExcludeLocked->GetValue() )
310  aMessage += wxString::Format( _( "\nLocked footprints will not be annotated" ) );
311 
312  if( !m_AnnotateBack->GetValue() )
313  {
314  aMessage += wxString::Format( _( "\nFront footprints will start at %s" ),
315  m_FrontRefDesStart->GetValue() );
316  }
317 
318  if( !m_AnnotateFront->GetValue() )
319  {
320  bool frontPlusOne = ( 0 == wxAtoi( m_BackRefDesStart->GetValue() ) )
321  && !m_AnnotateBack->GetValue();
322 
323  aMessage += wxString::Format( _( "\nBack footprints will start at %s." ),
324  frontPlusOne ? _( "the last front footprint + 1" ) :
325  m_BackRefDesStart->GetValue() );
326  }
327 
328  if( !m_FrontPrefix->GetValue().empty() )
329  {
330  if( m_RemoveFrontPrefix->GetValue() )
331  {
332  aMessage += wxString::Format( _( "\nFront footprints starting with '%s' will have "
333  "the prefix removed." ),
334  m_FrontPrefix->GetValue() );
335  }
336  else
337  {
338  aMessage += wxString::Format( _( "\nFront footprints will have '%s' inserted as a "
339  "prefix." ),
340  m_FrontPrefix->GetValue() );
341  }
342  }
343 
344  if( !m_BackPrefix->GetValue().empty() )
345  {
346  if( m_RemoveBackPrefix->GetValue() )
347  {
348  aMessage += wxString::Format( _( "\nBack footprints starting with '%s' will have the "
349  "prefix removed." ),
350  m_BackPrefix->GetValue() );
351  }
352  else
353  {
354  aMessage += wxString::Format( _( "\nBack footprints will have '%s' inserted as a "
355  "prefix." ),
356  m_BackPrefix->GetValue() );
357  }
358  }
359 
360  bool moduleLocation = m_locationChoice->GetSelection() == 0;
361 
362  aMessage += wxString::Format( _( "\nPrior to sorting by %s, the coordinates of which will be "
363  "rounded to a %s, %s grid. " ),
364  moduleLocation ? _( "footprint location" )
365  : _( "reference designator location" ),
368 
369  if( m_UpdateSchematic->GetValue() )
370  aMessage += _( "\nThe schematic will be updated." );
371  else
372  aMessage += _( "\nThe schematic will not be updated." );
373 
374  ShowReport( aMessage, RPT_SEVERITY_INFO );
375 }
376 
377 
379 {
380  m_SortCode = 0; //Convert radio button to sort direction code
381 
382  for( wxRadioButton* sortbuttons : m_sortButtons )
383  {
384  if( sortbuttons->GetValue() )
385  break;
386 
387  m_SortCode++;
388  }
389 
390  if( m_SortCode >= (int) m_sortButtons.size() )
391  m_SortCode = 0;
392 
393  m_FrontPrefixString = m_FrontPrefix->GetValue();
394  m_BackPrefixString = m_BackPrefix->GetValue();
395 
396  //Get the chosen sort grid for rounding
397  m_GridIndex = m_GridChoice->GetSelection();
398 
399  if( m_GridIndex >= ( int ) m_Settings->m_Window.grid.sizes.size() )
400  {
405  }
406  else
407  {
411  }
412 
413  int i = 0;
414 
415  for( wxRadioButton* button : AnnotateWhat )
416  {
417  if( button->GetValue() )
418  break;
419  else
420  i++;
421  }
422 
423  m_AnnotationChoice = ( i >= (int) AnnotateWhat.size() ) ? AnnotationChoice::AnnotateAll : i;
424 
426 }
427 
428 
429 //
431 int DIALOG_BOARD_REANNOTATE::RoundToGrid( int aCoord, int aGrid )
432 {
433  if( 0 == aGrid )
434  aGrid = MINGRID;
435 
436  int rounder;
437  rounder = aCoord % aGrid;
438  aCoord -= rounder;
439 
440  if( abs( rounder ) > ( aGrid / 2 ) )
441  aCoord += ( aCoord < 0 ? -aGrid : aGrid );
442 
443  return ( aCoord );
444 }
445 
446 
447 //
450 static bool ChangeArrayCompare( const RefDesChange& aA, const RefDesChange& aB )
451 {
452  return ( aA.OldRefDesString < aB.OldRefDesString );
453 }
454 
455 
456 //
459 static bool ModuleCompare( const RefDesInfo& aA, const RefDesInfo& aB )
460 {
461  int X0 = aA.roundedx, X1 = aB.roundedx, Y0 = aA.roundedy, Y1 = aB.roundedy;
462 
463  if( SortYFirst ) //If sorting by Y then X, swap X and Y
464  {
465  std::swap( X0, Y0 );
466  std::swap( X1, Y1 );
467  }
468 
469  //If descending, same compare just swap directions
470  if( DescendingFirst )
471  std::swap( X0, X1 );
472 
473  if( DescendingSecond )
474  std::swap( Y0, Y1 );
475 
476  if( X0 < X1 )
477  return ( true ); //yes, its smaller
478 
479  if( X0 > X1 )
480  return ( false ); //No its not
481 
482  if( Y0 < Y1 )
483  return ( true ); //same but equal
484 
485  return ( false );
486 }
487 
488 
489 //
493 {
494  return wxString::Format( "%s, %s", MessageTextFromValue( m_Units, aX, false ),
495  MessageTextFromValue( m_Units, aY, false ) );
496 }
497 
498 
499 //
501 void DIALOG_BOARD_REANNOTATE::ShowReport( wxString aMessage, SEVERITY aSeverity )
502 {
503  size_t pos = 0, prev = 0;
504 
505  do
506  {
507  pos = aMessage.ToStdString().find( '\n', prev );
508  m_MessageWindow->Report( aMessage.ToStdString().substr( prev, pos - prev ), aSeverity );
509  prev = pos + 1;
510  } while( std::string::npos != pos );
511 
512 }
513 
514 
515 //
518 {
519  int i = 1;
520  wxString message;
521 
522  message.Printf( _( "\n\nThere are %i types of reference designations\n"
523  "**********************************************************\n" ),
524  (int) m_RefDesTypes.size() );
525 
526  for( RefDesTypeStr Type : m_RefDesTypes ) //Show all the types of refdes
527  message += Type.RefDesType + ( 0 == ( i++ % 16 ) ? "\n" : " " );
528 
529  if( !m_ExcludeArray.empty() )
530  {
531  message += _( "\nExcluding: " );
532  for( wxString& Exclude : m_ExcludeArray ) //Show the refdes we are excluding
533  message += Exclude + " ";
534  message += _( " from reannotation\n\n" );
535  }
536 
537  message += _( "\n Change Array\n***********************\n" );
538 
539  for( RefDesChange Change : m_ChangeArray )
540  {
541  message += wxString::Format( "%s -> %s %s %s\n", Change.OldRefDesString, Change.NewRefDes,
542  ActionMessage[Change.Action],
543  UpdateRefDes != Change.Action ? _( " will be ignored" ) : wxString("") );
544  }
545 
546  ShowReport( message, RPT_SEVERITY_INFO );
547 }
548 
549 
550 //
552 void DIALOG_BOARD_REANNOTATE::LogModules( wxString& aMessage, std::vector<RefDesInfo>& aModules )
553 {
554  wxString message = aMessage;
555 
556  if( aModules.empty() )
557  message += _( "\nNo modules" );
558  else
559  {
560  int i = 1;
561  bool moduleLocations = m_locationChoice->GetSelection() == 0;
562 
563  message += wxString::Format( _( "\n*********** Sort on %s ***********" ),
564  moduleLocations ? _( "Footprint Coordinates" )
565  : _( "Reference Designator Coordinates" ) );
566 
567  message += wxString::Format( _( "\nSort Code %d" ), m_SortCode );
568 
569  for( const RefDesInfo& mod : aModules )
570  {
571  message += wxString::Format( _( "\n%d %s Uuid: [%s], X, Y: %s, Rounded X, Y, %s" ),
572  i++,
573  mod.RefDesString,
574  mod.Uuid.AsString(),
575  CoordTowxString( mod.x, mod.y ),
576  CoordTowxString( mod.roundedx, mod.roundedy ) );
577  }
578  }
579 
580  ShowReport( message, RPT_SEVERITY_INFO );
581 }
582 
583 //
587 {
588  std::string payload;
589  std::vector<RefDesInfo> BadRefDes;
590  wxString message, badrefdes;
591  STRING_FORMATTER stringformatter;
592  RefDesChange* newref;
593  NETLIST netlist;
594 
595  if( !BuildModuleList( BadRefDes ) )
596  {
597  ShowReport( "Selected options resulted in errors! Change them and try again.",
599  return false;
600  }
601 
602  if( !BadRefDes.empty() )
603  {
604  message.Printf(
605  _( "\nPCB has %d empty or invalid reference designations."
606  "\nRecommend you run DRC with 'Test footprints against schematic' checked.\n" ),
607  (int) BadRefDes.size() );
608 
609  for( const RefDesInfo& mod : BadRefDes )
610  {
611  badrefdes += wxString::Format( _( "\nRefDes: %s Module: %s:%s at %s on PCB." ),
612  mod.RefDesString,
613  mod.FPID.GetLibNickname().wx_str(),
614  mod.FPID.GetLibItemName().wx_str(),
615  CoordTowxString( mod.x, mod.y ) );
616  }
617 
618  ShowReport( message + badrefdes + "\n", RPT_SEVERITY_WARNING );
619  message += _( "Reannotate anyway?" );
620 
621  if( !IsOK( m_frame, message ) )
622  return ( false );
623  }
624 
625  payload.clear(); //If not updating schematic no netlist error
626 
627  if( m_UpdateSchematic->GetValue() )
628  { //If updating schematic send a netlist
629 
630  for( MODULE* mod : m_modules )
631  { // Create a netlist
632  newref = GetNewRefDes( mod );
633 
634  if( nullptr == newref )
635  return false; //Not found in changelist
636 
637  //add to the netlist
638  netlist.AddComponent( new COMPONENT( mod->GetFPID(), newref->NewRefDes,
639  mod->GetValue(), mod->GetPath() ) );
640  }
641 
642  netlist.Format( "pcb_netlist", &stringformatter, 0,
644 
645  payload = stringformatter.GetString(); //create netlist
646 
647  //Send netlist to eeSchema
648  bool attemptreannotate = m_frame->ReannotateSchematic( payload );
649 
650  if( !attemptreannotate )
651  { //Didn't get a valid reply
652  ShowReport( _( "\nReannotate failed!\n" ), RPT_SEVERITY_WARNING );
653  return false;
654  }
655 
656  } //If updating schematic
657 
658  bool reannotateok = payload.size( ) == 0;
659 
660  ShowReport( payload, reannotateok ? RPT_SEVERITY_ACTION : RPT_SEVERITY_ERROR );
661  BOARD_COMMIT commit( m_frame );
662 
663  if( reannotateok )
664  { //Only update if no errors
665 
666  for( MODULE* mod : m_modules )
667  { // Create a netlist
668  newref = GetNewRefDes( mod );
669 
670  if( nullptr == newref )
671  return false;
672 
673  commit.Modify( mod ); //Make a copy for undo
674  mod->SetReference( newref->NewRefDes ); //Update the PCB reference
675  m_frame->GetCanvas()->GetView()->Update( mod ); //Touch the module
676  }
677  }
678 
679  commit.Push( "Geographic reannotation" );
680  return reannotateok;
681 }
682 
683 
684 //
687 bool DIALOG_BOARD_REANNOTATE::BuildModuleList( std::vector<RefDesInfo>& aBadRefDes )
688 {
689  bool annotateselected;
690  bool annotatefront = m_AnnotateFront->GetValue(); //Unless only doing back
691  bool annotateback = m_AnnotateBack->GetValue(); //Unless only doing front
692  bool skiplocked = m_ExcludeLocked->GetValue();
693 
694  int errorcount = 0;
695  unsigned int backstartrefdes;
696  size_t firstnum = 0;
697 
698  m_FrontModules.clear();
699  m_BackModules.clear();
700  m_ExcludeArray.clear();
702 
703  std::vector<KIID> selected;
704 
705  if( m_AnnotateSelection->GetValue() )
706  {
707  for( EDA_ITEM* item : m_selection )
708  { //Get the timestamps of selected modules
709  if( item->Type() == PCB_MODULE_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 thismodule;
734  bool useModuleLocation = m_locationChoice->GetSelection() == 0;
735 
736  for( MODULE* mod : m_modules )
737  {
738  thismodule.Uuid = mod->m_Uuid;
739  thismodule.RefDesString = mod->GetReference();
740  thismodule.FPID = mod->GetFPID();
741  thismodule.x = useModuleLocation ? mod->GetPosition().x
742  : mod->Reference().GetPosition().x;
743  thismodule.y = useModuleLocation ? mod->GetPosition().y
744  : mod->Reference().GetPosition().y;
745  thismodule.roundedx = RoundToGrid( thismodule.x, m_SortGridx ); //Round to sort
746  thismodule.roundedy = RoundToGrid( thismodule.y, m_SortGridy );
747  thismodule.Front = mod->GetLayer() == F_Cu;
748  thismodule.Action = UpdateRefDes; //Usually good
749 
750  if( thismodule.RefDesString.IsEmpty() )
751  thismodule.Action = EmptyRefDes;
752  else
753  {
754  firstnum = thismodule.RefDesString.find_first_of( "0123456789" );
755 
756  if( std::string::npos == firstnum )
757  thismodule.Action = InvalidRefDes; //do not change ref des such as 12 or +1, or L
758  }
759 
760  //Get the type (R, C, etc)
761  thismodule.RefDesType = thismodule.RefDesString.substr( 0, firstnum );
762 
763  for( wxString excluded : m_ExcludeArray )
764  {
765  if( excluded == thismodule.RefDesType ) //Am I supposed to exclude this type?
766  {
767  thismodule.Action = Exclude; //Yes
768  break;
769  }
770  }
771 
772  if( ( thismodule.Front && annotateback ) || //If a front module and doing backs only
773  ( !thismodule.Front && annotatefront ) || //If a back module and doing front only
774  ( mod->IsLocked() && skiplocked ) ) //If excluding locked and it is locked
775  thismodule.Action = Exclude;
776 
777  if( annotateselected )
778  { //If onnly annotating selected c
779  thismodule.Action = Exclude; //Assume it isn't selected
780 
781  for( KIID sel : selected )
782  {
783  if( thismodule.Uuid == sel )
784  { //Found in selected modules
785  thismodule.Action = UpdateRefDes; //Update it
786  break;
787  }
788  }
789  }
790 
791  if( thismodule.Front )
792  m_FrontModules.push_back( thismodule );
793  else
794  m_BackModules.push_back( thismodule );
795  }
796 
797  SetSortCodes( FrontDirectionsArray, m_SortCode ); //Determine the sort order for the front
798  sort( m_FrontModules.begin(), m_FrontModules.end(),ModuleCompare ); //Sort the front modules
799 
800  SetSortCodes( BackDirectionsArray, m_SortCode ); //Determine the sort order for the back
801  sort( m_BackModules.begin(), m_BackModules.end(), ModuleCompare ); //Sort the back modules
802 
803  m_RefDesTypes.clear();
804  m_ChangeArray.clear();
805  backstartrefdes = wxAtoi( m_BackRefDesStart->GetValue() );
806 
807  if( !m_FrontModules.empty() )
808  BuildChangeArray( m_FrontModules, wxAtoi( m_FrontRefDesStart->GetValue() ),
809  m_FrontPrefix->GetValue(), m_RemoveFrontPrefix->GetValue(), aBadRefDes );
810 
811  if( !m_BackModules.empty() )
812  BuildChangeArray( m_BackModules, backstartrefdes, m_BackPrefix->GetValue(),
813  m_RemoveBackPrefix->GetValue(), aBadRefDes );
814 
815  if( !m_ChangeArray.empty() )
816  sort( m_ChangeArray.begin(), m_ChangeArray.end(), ChangeArrayCompare );
817 
818  LogChangePlan();
819 
820  size_t changearraysize = m_ChangeArray.size();
821 
822  for( size_t i = 0; i < changearraysize; i++ ) //Scan through for duplicates if update or skip
823  {
824  if( ( m_ChangeArray[i].Action != EmptyRefDes )
825  && ( m_ChangeArray[i].Action != InvalidRefDes ) )
826  {
827  for( size_t j = i + 1; j < changearraysize; j++ )
828  {
829  if( m_ChangeArray[i].NewRefDes == m_ChangeArray[j].NewRefDes )
830  {
831  ShowReport( "Duplicate instances of " + m_ChangeArray[j].NewRefDes,
833 
834  if( errorcount++ > MAXERROR )
835  {
836  ShowReport( _( "Aborted: too many errors " ), RPT_SEVERITY_ERROR );
837  break;
838  }
839  }
840  }
841  }
842 
843  if( errorcount > MAXERROR )
844  break;
845  }
846 
847  return ( 0 == errorcount );
848 }
849 
850 
851 //
853 void DIALOG_BOARD_REANNOTATE::BuildChangeArray( std::vector<RefDesInfo>& aModules,
854  unsigned int aStartRefDes, wxString aPrefix,
855  bool aRemovePrefix,
856  std::vector<RefDesInfo>& aBadRefDes )
857 {
858  size_t i;
859  RefDesChange change;
860  RefDesTypeStr newtype;
861 
862  wxString refdestype;
863  size_t prefixsize = aPrefix.size();
864 
865  bool haveprefix = ( 0 != prefixsize ); //Do I have a prefix?
866  bool addprefix = haveprefix & !aRemovePrefix; //Yes- and I'm not removing it
867  aRemovePrefix &= haveprefix; //Only remove if I have a prefix
868 
869  bool prefixpresent; //Prefix found
870 
871  wxString logstring = ( aModules.front().Front ) ? _( "\n\nFront Modules" )
872  : _( "\n\nBack Modules" );
873  LogModules( logstring, aModules );
874 
875  if( 0 != aStartRefDes ) //Initialize the change array if present
876  for( i = 0; i < m_RefDesTypes.size(); i++ )
877  m_RefDesTypes[i].RefDesCount = aStartRefDes;
878 
879  for( RefDesInfo Mod : aModules )
880  { //For each module
881  change.Uuid = Mod.Uuid;
882  change.Action = Mod.Action;
883  change.OldRefDesString = Mod.RefDesString;
884  change.NewRefDes = Mod.RefDesString;
885  change.Front = Mod.Front;
886 
887  if( Mod.RefDesString.IsEmpty() )
888  Mod.Action = EmptyRefDes;
889 
890  if( ( change.Action == EmptyRefDes ) || ( change.Action == InvalidRefDes ) )
891  {
892  m_ChangeArray.push_back( change );
893  aBadRefDes.push_back( Mod );
894  continue;
895  }
896 
897  if( change.Action == UpdateRefDes )
898  {
899  refdestype = Mod.RefDesType;
900  prefixpresent = ( 0 == Mod.RefDesType.find( aPrefix ) );
901 
902  if( addprefix && !prefixpresent )
903  Mod.RefDesType.insert( 0, aPrefix ); //Add prefix once only
904 
905  if( aRemovePrefix && prefixpresent ) //If there is a prefix remove it
906  Mod.RefDesType.erase( 0, prefixsize );
907 
908  for( i = 0; i < m_RefDesTypes.size(); i++ ) //See if it is in the types array
909  if( m_RefDesTypes[i].RefDesType == Mod.RefDesType ) //Found it!
910  break;
911 
912  if( i == m_RefDesTypes.size() )
913  { //Wasn't in the types array so add it
914  newtype.RefDesType = Mod.RefDesType;
915  newtype.RefDesCount = ( aStartRefDes == 0 ? 1 : aStartRefDes );
916  m_RefDesTypes.push_back( newtype );
917  }
918 
919  change.NewRefDes = m_RefDesTypes[i].RefDesType
920  + std::to_string( m_RefDesTypes[i].RefDesCount++ );
921  }
922  m_ChangeArray.push_back( change ); //Add to the change array
923  }
924 }
925 
926 //
929 {
930  size_t i;
931 
932  for( i = 0; i < m_ChangeArray.size(); i++ )
933  if( aMod->m_Uuid == m_ChangeArray[i].Uuid )
934  return ( &m_ChangeArray[i] );
935 
936  ShowReport( _( "Module not found in changelist " ) + aMod->GetReference(), RPT_SEVERITY_ERROR );
937  return nullptr; //Should never happen
938 }
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
Function OnModify must be called after a board change to set the modified flag.
void SetLazyUpdate(bool aLazyUpdate)
Sets the lasy update.
COMMIT & Modify(EDA_ITEM *aItem)
Modifies a given item in the model.
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
virtual void Update(VIEW_ITEM *aItem, int aUpdateFlags) override
For dynamic VIEWs, informs the associated VIEW that the graphical representation of this item has cha...
Definition: pcb_view.cpp:93
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:45
SELECTION_TOOL.
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)
Forces updating the HTML page, after the report is built in lazy mode If aSort = true,...
static bool ModuleCompare(const RefDesInfo &aA, const RefDesInfo &aB)
Compare function to sort modules.
double DoubleValueFromString(EDA_UNITS aUnits, const wxString &aTextValue, bool aUseMils, EDA_DATA_TYPE aType)
Function DoubleValueFromString converts aTextValue to a double.
Definition: base_units.cpp:346
bool BuildModuleList(std::vector< RefDesInfo > &aBadRefDes)
Build the module lists, sort it, filter for excludes, then build the change list.
APP_SETTINGS_BASE * KifaceSettings() const
Definition: kiface_i.h:103
std::vector< RefDesChange > m_ChangeArray
const BITMAP_OPAQUE reannotate_down_left_xpm[1]
PCBNEW_SETTINGS * GetPcbNewSettings()
DIALOG_BOARD_REANNOTATE(PCB_EDIT_FRAME *aParentFrame)
const BITMAP_OPAQUE reannotate_down_right_xpm[1]
const wxString & GetFileName() const
Definition: class_board.h:242
const BITMAP_OPAQUE reannotate_right_down_xpm[1]
std::vector< RefDesInfo > m_FrontModules
const wxString GetReference() const
Function GetReference.
Definition: class_module.h:451
void AddComponent(COMPONENT *aComponent)
Function AddComponent adds aComponent to the NETLIST.
#define MINGRID
bool TestStandalone(void)
Test if standalone mode.
#define SORTYFIRST
wxBitmap KiBitmap(BITMAP_DEF aBitmap)
Construct a wxBitmap from a memory record, held in a BITMAP_DEF.
Definition: bitmap.cpp:80
bool DescendingFirst
Definition: common.h:68
class MODULE, a footprint
Definition: typeinfo.h:89
void OnApplyClick(wxCommandEvent &event) override
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:194
RefDesChange * GetNewRefDes(MODULE *aMod)
MODULES & Modules()
Definition: class_board.h:247
void InitValues(void)
Copy saved app settings to the dialog.
GRID_SETTINGS grid
Definition: app_settings.h:89
#define CTL_OMIT_FILTERS
Definition: pcb_netlist.h:283
void SetPrintInfo(bool aPrintInfo)
If true prints Info: at the beginning of each Info severity line (Default)
int RoundToGrid(int aCoord, int aGrid)
Round an int coordinate to a suitable grid.
KIFACE_I & Kiface()
Global KIFACE_I "get" accessor.
#define ASCENDINGSECOND
void SetFileName(wxString &aReportFileName)
Set the report full file name to the string
static void BuildChoiceList(wxArrayString *aGridsList, APP_SETTINGS_BASE *aCfg, bool mmFirst)
Definition: grid_menu.cpp:80
KIGFX::GAL * GetGAL() const
Function GetGAL() Returns 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:475
#define SetSortCodes(DirArray, Code)
const BITMAP_OPAQUE reannotate_up_right_xpm[1]
wxString ActionMessage[]
int BackDirectionsArray[]
int FrontDirectionsArray[]
void OnCloseClick(wxCommandEvent &event) override
std::vector< RefDesInfo > m_BackModules
#define CTL_OMIT_NETS
Omit pads net names (useless in library)
virtual KIGFX::PCB_VIEW * GetView() const override
Function GetView() Returns 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:121
#define DESCENDINGSECOND
void Report(const wxString &aText, SEVERITY aSeverity, REPORTER::LOCATION aLocation=REPORTER::LOC_BODY)
Reports the string.
BOARD * GetBoard()
const KIID m_Uuid
Definition: base_struct.h:162
const VECTOR2D & GetGridSize() const
Returns the grid size.
wxString MessageTextFromValue(EDA_UNITS aUnits, int aValue, bool aUseMils, EDA_DATA_TYPE aType)
Definition: base_units.cpp:124
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:171
void FilterFrontPrefix(wxCommandEvent &event) override
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, CPTREE &aTree)
Function Format outputs a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:201
std::vector< RefDesTypeStr > m_RefDesTypes
#define _(s)
Definition: 3d_actions.cpp:33
void LogModules(wxString &aMessage, std::vector< RefDesInfo > &aModules)
Create a list of the modules and their coordinates.
void Format(const char *aDocName, OUTPUTFORMATTER *aOut, int aNestLevel, int aCtl=0)
wxString CoordTowxString(int aX, int aY)
Convert coordinates to wxString.
PCB_EDIT_FRAME is the main frame for Pcbnew.
EDA_ITEM is a base class for most all the KiCad significant classes, used in schematics and boards.
Definition: base_struct.h:159
virtual void Push(const wxString &aMessage=wxT("A commit"), bool aCreateUndoEntry=true, bool aSetDirtyBit=true) override
Executes the changes.
PCB_SCREEN * GetScreen() const override
Return a pointer to a BASE_SCREEN or one of its derivatives.
DIALOG_REANNOTATE m_Reannotate
std::vector< wxString > m_ExcludeArray
void BuildChangeArray(std::vector< RefDesInfo > &aModules, unsigned int aStartRefDes, wxString aPrefix, bool aRemovePrefix, std::vector< RefDesInfo > &aBadRefDes)
Scan through the module arrays and create the from -> to array.
bool ReannotateSchematic(std::string &aNetlist)
Sends a command to Eeschema to re-annotate the schematic.
TOOL_MANAGER * GetToolManager() const
Return the MVC controller.
Definition: tools_holder.h:78
BOARD * GetBoard() const
const BITMAP_OPAQUE reannotate_left_up_xpm[1]
std::vector< wxRadioButton * > AnnotateWhat
void LogChangePlan(void)
Create an audit trail of the changes.
#define DESCENDINGFIRST
STRING_FORMATTER implements OUTPUTFORMATTER to a memory buffer.
Definition: richio.h:445
bool IsOK(wxWindow *aParent, const wxString &aMessage)
Display a yes/no dialog with aMessage and returns the user response.
Definition: confirm.cpp:283
bool IsEmpty() const
Definition: class_board.h:290
const BITMAP_OPAQUE reannotate_right_up_xpm[1]
bool SortYFirst
#define SORTXFIRST
EDA_UNITS GetUserUnits() const
Return the user units currently in use.
unsigned int RefDesCount
void MakeSampleText(wxString &aMessage)
Make the text to summarize what is about to happen.
#define VALIDPREFIX