KiCad PCB EDA Suite
dialog_board_reannotate.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2020 Brian Piccioni [email protected]
5  * Copyright (C) 2020-2021 KiCad Developers, see AUTHORS.txt for contributors.
6  * @author Brian Piccioni <[email protected]>
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, you may find one here:
20  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21  * or you may search the http://www.gnu.org website for the version 2 license,
22  * or you may write to the Free Software Foundation, Inc.,
23  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24  */
25 
26 #include <algorithm>
27 #include <base_units.h>
28 #include <bitmaps.h>
29 #include <board_commit.h>
30 #include <confirm.h>
31 #include <ctype.h>
33 #include <fstream>
34 #include <string_utils.h> // StrNumCmp
35 #include <kiface_base.h>
36 #include <mail_type.h>
37 #include <pcbnew_settings.h>
38 #include <refdes_utils.h>
39 #include <sstream>
40 #include <tool/tool_manager.h>
41 #include <tool/grid_menu.h>
42 #include <wx/valtext.h>
43 
44 
48 
49 //
50 // This converts the index into a sort code. Note that Back sort code will have left and
51 // right swapped.
52 //
54  SORTYFIRST + ASCENDINGFIRST + ASCENDINGSECOND, // "Top to bottom, left to right", // 100
55  SORTYFIRST + ASCENDINGFIRST + DESCENDINGSECOND, // "Top to bottom, right to left", // 101
56  SORTYFIRST + DESCENDINGFIRST + ASCENDINGSECOND, // "Back to Front, left to right", // 110
57  SORTYFIRST + DESCENDINGFIRST + DESCENDINGSECOND, // "Back to Front, right to left", // 111
58  SORTXFIRST + ASCENDINGFIRST + ASCENDINGSECOND, // "Left to right, Front to Back", // 000
59  SORTXFIRST + ASCENDINGFIRST + DESCENDINGSECOND, // "Left to right, Back to Front", // 001
60  SORTXFIRST + DESCENDINGFIRST + ASCENDINGSECOND, // "Right to left, Front to Back", // 010
61  SORTXFIRST + DESCENDINGFIRST + DESCENDINGSECOND // "Right to left, Back to Front", // 011
62 };
63 
64 
65 //
66 // Back Left/Right is opposite because it is a mirror image (coordinates are from the top)
67 //
69  SORTYFIRST + ASCENDINGFIRST + DESCENDINGSECOND, // "Top to bottom, left to right", // 101
70  SORTYFIRST + ASCENDINGFIRST + ASCENDINGSECOND, // "Top to bottom, right to left", // 100
71  SORTYFIRST + DESCENDINGFIRST + DESCENDINGSECOND, // "Bottom to top, left to right", // 111
72  SORTYFIRST + DESCENDINGFIRST + ASCENDINGSECOND, // "Bottom to top, right to left", // 110
73  SORTXFIRST + DESCENDINGFIRST + ASCENDINGSECOND, // "Left to right, top to bottom", // 010
74  SORTXFIRST + DESCENDINGFIRST + DESCENDINGSECOND, // "Left to right, bottom to top", // 011
75  SORTXFIRST + ASCENDINGFIRST + ASCENDINGSECOND, // "Right to left, top to bottom", // 000
76  SORTXFIRST + ASCENDINGFIRST + DESCENDINGSECOND // "Right to left, bottom to top", // 001
77 };
78 
79 #define SetSortCodes( DirArray, Code ) \
80  { \
81  SortYFirst = ( ( DirArray[Code] & SORTYFIRST ) != 0 ); \
82  DescendingFirst = ( ( DirArray[Code] & DESCENDINGFIRST ) != 0 ); \
83  DescendingSecond = ( ( DirArray[Code] & DESCENDINGSECOND ) != 0 ); \
84  }
85 
86 
87 wxString AnnotateString[] = {
88  _( "All" ), // AnnotateAll
89  _( "Only front" ), // AnnotateFront
90  _( "Only back" ), // AnnotateBack
91  _( "Only selected" ) // AnnotateSelected
92 };
93 
94 
95 wxString ActionMessage[] = {
96  "", // UpdateRefDes
97  _( "Empty" ), // EmptyRefDes
98  _( "Invalid" ), // InvalidRefDes
99  _( "Excluded" ) // Exclude
100 };
101 
102 
104  : DIALOG_BOARD_REANNOTATE_BASE( aParentFrame ),
105  m_footprints( aParentFrame->GetBoard()->Footprints() )
106 {
107  m_frame = aParentFrame;
109  InitValues();
110 
111  m_FrontRefDesStart->SetValidator( wxTextValidator( wxFILTER_DIGITS ) );
112  m_BackRefDesStart->SetValidator( wxTextValidator( wxFILTER_DIGITS ) );
113 
114  m_sdbSizerOK->SetLabel( _( "Reannotate PCB" ) );
115  m_sdbSizerCancel->SetLabel( _( "Close" ) );
116  m_sdbSizer->Layout();
117 
118  m_settings = aParentFrame->config();
119  wxArrayString gridslist;
120  GRID_MENU::BuildChoiceList( &gridslist, m_settings, aParentFrame );
121 
122  if( -1 == m_gridIndex ) // If no default loaded
123  m_gridIndex = m_settings->m_Window.grid.last_size_idx; // Get the current grid size
124 
127 
128  m_GridChoice->Set( gridslist );
129  m_GridChoice->SetSelection( m_gridIndex );
130 
131  for( wxRadioButton* button : m_sortButtons )
132  button->SetValue( false );
133 
134  m_sortButtons[m_sortCode]->SetValue( true );
135 
137 
138  if( !m_selection.Empty() )
140 
141  for( wxRadioButton* button : AnnotateWhat )
142  button->SetValue( false );
143 
144  m_annotationChoice = ( m_sortCode >= (int) AnnotateWhat.size() ) ?
147 
148  AnnotateWhat[m_annotationChoice]->SetValue( true );
149 
158 
159  m_ExcludeList->SetToolTip( m_ExcludeListText->GetToolTipText() );
160  m_GridChoice->SetToolTip( m_SortGridText->GetToolTipText() );
161 
162  m_MessageWindow->SetFileName( Prj().GetProjectPath() + wxT( "report.txt" ) );
163 
165 }
166 
167 
169 {
170  GetParameters(); // Get the current menu settings
172  cfg->m_Reannotate.sort_on_fp_location = m_locationChoice->GetSelection() == 0;
175  cfg->m_Reannotate.exclude_locked = m_ExcludeLocked->GetValue();
176 
181 
184  cfg->m_Reannotate.front_prefix = m_FrontPrefix->GetValue();
185  cfg->m_Reannotate.back_prefix = m_BackPrefix->GetValue();
186  cfg->m_Reannotate.exclude_list = m_ExcludeList->GetValue();
188 }
189 
190 
192 {
194  m_locationChoice->SetSelection( cfg->m_Reannotate.sort_on_fp_location ? 0 : 1 );
197  m_ExcludeLocked->SetValue( cfg->m_Reannotate.exclude_locked );
198 
203 
206  m_FrontPrefix->SetValue( cfg->m_Reannotate.front_prefix );
207  m_BackPrefix->SetValue( cfg->m_Reannotate.back_prefix );
208  m_ExcludeList->SetValue( cfg->m_Reannotate.exclude_list );
210 }
211 
212 
213 void DIALOG_BOARD_REANNOTATE::OnCloseClick( wxCommandEvent& event )
214 {
215  EndDialog( wxID_OK );
216 }
217 
218 
219 void DIALOG_BOARD_REANNOTATE::FilterPrefix( wxTextCtrl* aPrefix )
220 {
221  std::string tmps = VALIDPREFIX;
222 
223  if( aPrefix->GetValue().empty() )
224  return; //Should never happen
225 
226  char lastc = aPrefix->GetValue().Last();
227 
228  if( isalnum( (int) lastc ) )
229  return;
230 
231  if( std::string::npos != tmps.find( lastc ) )
232  return;
233 
234  tmps = aPrefix->GetValue();
235  aPrefix->Clear();
236  tmps.pop_back();
237  aPrefix->AppendText( tmps );
238 }
239 
240 
242  unsigned int aStartRefDes )
243 {
244  unsigned int requiredLastRef = ( aStartRefDes == 0 ? 1 : aStartRefDes ) - 1;
245 
246  for( size_t i = 0; i < m_refDesTypes.size(); i++ ) // See if it is in the types array
247  {
248  if( m_refDesTypes[i].RefDesType == aRefDesPrefix ) // Found it!
249  {
250  m_refDesTypes[i].LastUsedRefDes = std::max( m_refDesTypes[i].LastUsedRefDes,
251  requiredLastRef );
252 
253  return &m_refDesTypes[i];
254  }
255  }
256 
257  // Wasn't in the types array so add it
258  RefDesTypeStr newtype;
259  newtype.RefDesType = aRefDesPrefix;
260  newtype.LastUsedRefDes = requiredLastRef;
261  m_refDesTypes.push_back( newtype );
262 
263  return &m_refDesTypes.back();
264 }
265 
266 
267 void DIALOG_BOARD_REANNOTATE::FilterFrontPrefix( wxCommandEvent& event )
268 {
270 }
271 
272 
273 void DIALOG_BOARD_REANNOTATE::FilterBackPrefix( wxCommandEvent& event )
274 {
276 }
277 
278 
279 void DIALOG_BOARD_REANNOTATE::OnApplyClick( wxCommandEvent& event )
280 {
281  wxString warning;
282 
283  if( m_frame->GetBoard()->IsEmpty() )
284  {
285  ShowReport( _( "No PCB to reannotate!" ), RPT_SEVERITY_ERROR );
286  return;
287  }
288 
289  GetParameters(); // Figure out how this is to be done
290  MakeSampleText( warning );
291 
292  if( !IsOK( m_frame, warning ) )
293  return;
294 
295  if( ReannotateBoard() )
296  {
297  ShowReport( _( "PCB successfully reannotated" ), RPT_SEVERITY_ACTION );
298  ShowReport( _( "PCB annotation changes should be synchronized with schematic using "
299  "the \"Update Schematic from PCB\" tool." ), RPT_SEVERITY_WARNING );
300  }
301 
302  m_MessageWindow->SetLazyUpdate( false );
303  m_MessageWindow->Flush( false );
304  m_frame->GetCanvas()->Refresh(); // Redraw
305  m_frame->OnModify(); // Need to save file on exit.
306 }
307 
308 
309 void DIALOG_BOARD_REANNOTATE::MakeSampleText( wxString& aMessage )
310 {
311  wxString tmp;
312 
313  aMessage.Printf( _( "\n%s footprints will be reannotated." ),
315 
316  if( !m_ExcludeList->GetValue().empty() )
317  {
318  aMessage += wxString::Format( _( "\nAny reference types %s will not be annotated." ),
319  m_ExcludeList->GetValue() );
320  }
321 
322  if( m_ExcludeLocked->GetValue() )
323  aMessage += wxString::Format( _( "\nLocked footprints will not be annotated" ) );
324 
325  if( !m_AnnotateBack->GetValue() )
326  {
327  aMessage += wxString::Format( _( "\nFront footprints will start at %s" ),
328  m_FrontRefDesStart->GetValue() );
329  }
330 
331  if( !m_AnnotateFront->GetValue() )
332  {
333  bool frontPlusOne = ( 0 == wxAtoi( m_BackRefDesStart->GetValue() ) )
334  && !m_AnnotateBack->GetValue();
335 
336  aMessage += wxString::Format( _( "\nBack footprints will start at %s." ),
337  frontPlusOne ? _( "the last front footprint + 1" ) :
338  m_BackRefDesStart->GetValue() );
339  }
340 
341  if( !m_FrontPrefix->GetValue().empty() )
342  {
343  if( m_RemoveFrontPrefix->GetValue() )
344  {
345  aMessage += wxString::Format( _( "\nFront footprints starting with '%s' will have "
346  "the prefix removed." ),
347  m_FrontPrefix->GetValue() );
348  }
349  else
350  {
351  aMessage += wxString::Format( _( "\nFront footprints will have '%s' inserted as a "
352  "prefix." ),
353  m_FrontPrefix->GetValue() );
354  }
355  }
356 
357  if( !m_BackPrefix->GetValue().empty() )
358  {
359  if( m_RemoveBackPrefix->GetValue() )
360  {
361  aMessage += wxString::Format( _( "\nBack footprints starting with '%s' will have the "
362  "prefix removed." ),
363  m_BackPrefix->GetValue() );
364  }
365  else
366  {
367  aMessage += wxString::Format( _( "\nBack footprints will have '%s' inserted as a "
368  "prefix." ),
369  m_BackPrefix->GetValue() );
370  }
371  }
372 
373  bool fpLocation = m_locationChoice->GetSelection() == 0;
374 
375  aMessage += wxString::Format( _( "\nPrior to sorting by %s, the coordinates of which will be "
376  "rounded to a %s, %s grid." ),
377  fpLocation ? _( "footprint location" )
378  : _( "reference designator location" ),
381 
382  ShowReport( aMessage, RPT_SEVERITY_INFO );
383 }
384 
385 
387 {
388  m_sortCode = 0; // Convert radio button to sort direction code
389 
390  for( wxRadioButton* sortbuttons : m_sortButtons )
391  {
392  if( sortbuttons->GetValue() )
393  break;
394 
395  m_sortCode++;
396  }
397 
398  if( m_sortCode >= (int) m_sortButtons.size() )
399  m_sortCode = 0;
400 
401  m_frontPrefixString = m_FrontPrefix->GetValue();
402  m_backPrefixString = m_BackPrefix->GetValue();
403 
404  // Get the chosen sort grid for rounding
405  m_gridIndex = m_GridChoice->GetSelection();
406 
407  if( m_gridIndex >= ( int ) m_settings->m_Window.grid.sizes.size() )
408  {
413  }
414  else
415  {
419  }
420 
421  int i = 0;
422 
423  for( wxRadioButton* button : AnnotateWhat )
424  {
425  if( button->GetValue() )
426  break;
427  else
428  i++;
429  }
430 
431  m_annotationChoice = ( i >= (int) AnnotateWhat.size() ) ? AnnotationChoice::AnnotateAll : i;
432 
434 }
435 
436 
437 int DIALOG_BOARD_REANNOTATE::RoundToGrid( int aCoord, int aGrid )
438 {
439  if( 0 == aGrid )
440  aGrid = MINGRID;
441 
442  int rounder;
443  rounder = aCoord % aGrid;
444  aCoord -= rounder;
445 
446  if( abs( rounder ) > ( aGrid / 2 ) )
447  aCoord += ( aCoord < 0 ? -aGrid : aGrid );
448 
449  return ( aCoord );
450 }
451 
452 
455 static bool ChangeArrayCompare( const RefDesChange& aA, const RefDesChange& aB )
456 {
457  return ( StrNumCmp( aA.OldRefDesString, aB.OldRefDesString ) < 0 );
458 }
459 
460 
463 static bool ModuleCompare( const RefDesInfo& aA, const RefDesInfo& aB )
464 {
465  int X0 = aA.roundedx, X1 = aB.roundedx, Y0 = aA.roundedy, Y1 = aB.roundedy;
466 
467  if( SortYFirst ) //If sorting by Y then X, swap X and Y
468  {
469  std::swap( X0, Y0 );
470  std::swap( X1, Y1 );
471  }
472 
473  // If descending, same compare just swap directions
474  if( DescendingFirst )
475  std::swap( X0, X1 );
476 
477  if( DescendingSecond )
478  std::swap( Y0, Y1 );
479 
480  if( X0 < X1 )
481  return ( true ); // yes, its smaller
482 
483  if( X0 > X1 )
484  return ( false ); // No its not
485 
486  if( Y0 < Y1 )
487  return ( true ); // same but equal
488 
489  return ( false );
490 }
491 
492 
494 {
495  return wxString::Format( "%s, %s",
497  MessageTextFromValue( m_units, aY ) );
498 }
499 
500 
501 void DIALOG_BOARD_REANNOTATE::ShowReport( const 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 
516 {
517  int i = 1;
518  wxString message;
519 
520  message.Printf( _( "\n\nThere are %i types of reference designations\n"
521  "**********************************************************\n" ),
522  (int) m_refDesTypes.size() );
523 
524  for( RefDesTypeStr Type : m_refDesTypes ) // Show all the types of refdes
525  message += Type.RefDesType + ( 0 == ( i++ % 16 ) ? "\n" : " " );
526 
527  if( !m_excludeArray.empty() )
528  {
529  wxString excludes;
530 
531  for( wxString& exclude : m_excludeArray ) // Show the refdes we are excluding
532  excludes += exclude + " ";
533 
534  message += wxString::Format( _( "\nExcluding: %s from reannotation\n\n" ), excludes );
535  }
536 
537  message += _( "\n Change Array\n***********************\n" );
538 
539  for( const RefDesChange& change : m_changeArray )
540  {
541  message += wxString::Format(
542  "%s -> %s %s %s\n", change.OldRefDesString, change.NewRefDes,
543  ActionMessage[change.Action],
544  UpdateRefDes != change.Action ? _( " will be ignored" ) : wxT( "" ) );
545  }
546 
547  ShowReport( message, RPT_SEVERITY_INFO );
548 }
549 
550 
551 void DIALOG_BOARD_REANNOTATE::LogFootprints( const wxString& aMessage,
552  const std::vector<RefDesInfo>& aFootprints )
553 {
554  wxString message = aMessage;
555 
556  if( aFootprints.empty() )
557  message += _( "\nNo footprints" );
558  else
559  {
560  int i = 1;
561  bool fpLocations = m_locationChoice->GetSelection() == 0;
562 
563  message += wxString::Format( _( "\n*********** Sort on %s ***********" ),
564  fpLocations ? _( "Footprint Coordinates" )
565  : _( "Reference Designator Coordinates" ) );
566 
567  message += wxString::Format( _( "\nSort Code %d" ), m_sortCode );
568 
569  for( const RefDesInfo& mod : aFootprints )
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 
585 {
586  std::vector<RefDesInfo> BadRefDes;
587  wxString message, badrefdes;
588  STRING_FORMATTER stringformatter;
589  RefDesChange* newref;
591 
592  if( !BuildFootprintList( BadRefDes ) )
593  {
594  ShowReport( "Selected options resulted in errors! Change them and try again.",
596  return false;
597  }
598 
599  if( !BadRefDes.empty() )
600  {
601  message.Printf(
602  _( "\nPCB has %d empty or invalid reference designations."
603  "\nRecommend running DRC with 'Test footprints against schematic' checked.\n" ),
604  (int) BadRefDes.size() );
605 
606  for( const RefDesInfo& mod : BadRefDes )
607  {
608  badrefdes += wxString::Format( _( "\nRefDes: %s Footprint: %s:%s at %s on PCB." ),
609  mod.RefDesString,
610  mod.FPID.GetLibNickname().wx_str(),
611  mod.FPID.GetLibItemName().wx_str(),
612  CoordTowxString( mod.x, mod.y ) );
613  }
614 
615  ShowReport( message + badrefdes + "\n", RPT_SEVERITY_WARNING );
616  message += _( "Reannotate anyway?" );
617 
618  if( !IsOK( m_frame, message ) )
619  return false;
620  }
621 
622  BOARD_COMMIT commit( m_frame );
623 
624  for( FOOTPRINT* footprint : m_footprints )
625  {
626  newref = GetNewRefDes( footprint );
627 
628  if( nullptr == newref )
629  return false;
630 
631  commit.Modify( footprint ); // Make a copy for undo
632  footprint->SetReference( newref->NewRefDes ); // Update the PCB reference
633  m_frame->GetCanvas()->GetView()->Update( footprint ); // Touch the footprint
634  }
635 
636  commit.Push( "Geographic reannotation" );
637  return true;
638 }
639 
640 
641 bool DIALOG_BOARD_REANNOTATE::BuildFootprintList( std::vector<RefDesInfo>& aBadRefDes )
642 {
643  bool annotateSelected;
644  bool annotateFront = m_AnnotateFront->GetValue(); // Unless only doing back
645  bool annotateBack = m_AnnotateBack->GetValue(); // Unless only doing front
646  bool skipLocked = m_ExcludeLocked->GetValue();
647 
648  int errorcount = 0;
649  size_t firstnum = 0;
650 
651  m_frontFootprints.clear();
652  m_backFootprints.clear();
653  m_excludeArray.clear();
655 
656  std::vector<KIID> selected;
657 
658  if( m_AnnotateSelection->GetValue() )
659  {
660  for( EDA_ITEM* item : m_selection )
661  {
662  // Get the timestamps of selected footprints
663  if( item->Type() == PCB_FOOTPRINT_T )
664  selected.push_back( item->m_Uuid );
665  }
666  }
667 
668  annotateSelected = !selected.empty();
669 
670  wxString exclude;
671 
672  // Break exclude list into words.
673  for( auto thischar : m_ExcludeList->GetValue() )
674  {
675  if( ( ' ' == thischar ) || ( ',' == thischar ) )
676  {
677  m_excludeArray.push_back( exclude );
678  exclude.clear();
679  }
680  else
681  exclude += thischar;
682 
683  if( !exclude.empty() )
684  m_excludeArray.push_back( exclude );
685  }
686 
687  RefDesInfo fpData;
688  bool useModuleLocation = m_locationChoice->GetSelection() == 0;
689 
690  for( FOOTPRINT* footprint : m_footprints )
691  {
692  fpData.Uuid = footprint->m_Uuid;
693  fpData.RefDesString = footprint->GetReference();
694  fpData.FPID = footprint->GetFPID();
695  fpData.x = useModuleLocation ? footprint->GetPosition().x
696  : footprint->Reference().GetPosition().x;
697  fpData.y = useModuleLocation ? footprint->GetPosition().y
698  : footprint->Reference().GetPosition().y;
699  fpData.roundedx = RoundToGrid( fpData.x, m_sortGridx ); // Round to sort
700  fpData.roundedy = RoundToGrid( fpData.y, m_sortGridy );
701  fpData.Front = footprint->GetLayer() == F_Cu;
702  fpData.Action = UpdateRefDes; // Usually good
703 
704  if( fpData.RefDesString.IsEmpty() )
705  {
706  fpData.Action = EmptyRefDes;
707  }
708  else
709  {
710  firstnum = fpData.RefDesString.find_first_of( "0123456789" );
711 
712  if( std::string::npos == firstnum )
713  fpData.Action = InvalidRefDes; // do not change ref des such as 12 or +1, or L
714  }
715 
716  // Get the type (R, C, etc)
717  fpData.RefDesType = fpData.RefDesString.substr( 0, firstnum );
718 
719  for( wxString excluded : m_excludeArray )
720  {
721  if( excluded == fpData.RefDesType ) // Am I supposed to exclude this type?
722  {
723  fpData.Action = Exclude; // Yes
724  break;
725  }
726  }
727 
728  if(( fpData.Front && annotateBack ) || // If a front fp and doing backs only
729  ( !fpData.Front && annotateFront ) || // If a back fp and doing front only
730  ( footprint->IsLocked() && skipLocked ) ) // If excluding locked and it is locked
731  {
732  fpData.Action = Exclude;
733  }
734 
735  if( annotateSelected )
736  { // If only annotating selected c
737  fpData.Action = Exclude; // Assume it isn't selected
738 
739  for( KIID sel : selected )
740  {
741  if( fpData.Uuid == sel )
742  { // Found in selected footprints
743  fpData.Action = UpdateRefDes; // Update it
744  break;
745  }
746  }
747  }
748 
749  if( fpData.Front )
750  m_frontFootprints.push_back( fpData );
751  else
752  m_backFootprints.push_back( fpData );
753  }
754 
755  // Determine the sort order for the front.
757 
758  // Sort the front footprints.
759  sort( m_frontFootprints.begin(), m_frontFootprints.end(), ModuleCompare );
760 
761  // Determine the sort order for the back.
763 
764  // Sort the back footprints.
765  sort( m_backFootprints.begin(), m_backFootprints.end(), ModuleCompare );
766 
767  m_refDesTypes.clear();
768  m_changeArray.clear();
769 
771 
772  if( !m_frontFootprints.empty() )
773  {
774  BuildChangeArray( m_frontFootprints, wxAtoi( m_FrontRefDesStart->GetValue() ),
775  m_FrontPrefix->GetValue(), m_RemoveFrontPrefix->GetValue(), aBadRefDes );
776  }
777 
778  if( !m_backFootprints.empty() )
779  {
780  BuildChangeArray( m_backFootprints, wxAtoi( m_BackRefDesStart->GetValue() ),
781  m_BackPrefix->GetValue(), m_RemoveBackPrefix->GetValue(), aBadRefDes );
782  }
783 
784  if( !m_changeArray.empty() )
785  sort( m_changeArray.begin(), m_changeArray.end(), ChangeArrayCompare );
786 
787  LogChangePlan();
788 
789  size_t changearraysize = m_changeArray.size();
790 
791  for( size_t i = 0; i < changearraysize; i++ ) // Scan through for duplicates if update or skip
792  {
793  if( ( m_changeArray[i].Action != EmptyRefDes )
794  && ( m_changeArray[i].Action != InvalidRefDes ) )
795  {
796  for( size_t j = i + 1; j < changearraysize; j++ )
797  {
798  if( m_changeArray[i].NewRefDes == m_changeArray[j].NewRefDes )
799  {
800  ShowReport( "Duplicate instances of " + m_changeArray[j].NewRefDes,
802 
803  if( errorcount++ > MAXERROR )
804  {
805  ShowReport( _( "Aborted: too many errors" ), RPT_SEVERITY_ERROR );
806  break;
807  }
808  }
809  }
810  }
811 
812  if( errorcount > MAXERROR )
813  break;
814  }
815 
816  return ( 0 == errorcount );
817 }
818 
820 {
821  std::vector<RefDesInfo> excludedFootprints;
822 
823  for( RefDesInfo fpData : m_frontFootprints )
824  {
825  if( fpData.Action == Exclude )
826  excludedFootprints.push_back( fpData );
827  }
828 
829  for( RefDesInfo fpData : m_backFootprints )
830  {
831  if( fpData.Action == Exclude )
832  excludedFootprints.push_back( fpData );
833  }
834 
835  for( RefDesInfo fpData : excludedFootprints )
836  {
837  if( fpData.Action == Exclude )
838  {
839  RefDesTypeStr* refDesInfo = GetOrBuildRefDesInfo( fpData.RefDesType );
840  refDesInfo->UnavailableRefs.insert( UTIL::GetRefDesNumber( fpData.RefDesString ) );
841  }
842  }
843 }
844 
845 
846 void DIALOG_BOARD_REANNOTATE::BuildChangeArray( std::vector<RefDesInfo>& aFootprints,
847  unsigned int aStartRefDes, const wxString& aPrefix,
848  bool aRemovePrefix,
849  std::vector<RefDesInfo>& aBadRefDes )
850 {
851  size_t prefixsize = aPrefix.size();
852 
853  bool haveprefix = ( 0 != prefixsize ); // Do I have a prefix?
854  bool addprefix = haveprefix & !aRemovePrefix; // Yes- and I'm not removing it
855  aRemovePrefix &= haveprefix; // Only remove if I have a prefix
856 
857  bool prefixpresent; // Prefix found
858 
859  wxString logstring = ( aFootprints.front().Front ) ? _( "\n\nFront Footprints" )
860  : _( "\n\nBack Footprints" );
861  LogFootprints( logstring, aFootprints );
862 
863  if( 0 != aStartRefDes ) // Initialize the change array if present
864  {
865  for( size_t i = 0; i < m_refDesTypes.size(); i++ )
866  m_refDesTypes[i].LastUsedRefDes = aStartRefDes;
867  }
868 
869 
870  for( RefDesInfo fpData : aFootprints )
871  {
872  RefDesChange change;
873 
874  change.Uuid = fpData.Uuid;
875  change.Action = fpData.Action;
876  change.OldRefDesString = fpData.RefDesString;
877  change.NewRefDes = fpData.RefDesString;
878  change.Front = fpData.Front;
879 
880  if( fpData.RefDesString.IsEmpty() )
881  fpData.Action = EmptyRefDes;
882 
883  if( ( change.Action == EmptyRefDes ) || ( change.Action == InvalidRefDes ) )
884  {
885  m_changeArray.push_back( change );
886  aBadRefDes.push_back( fpData );
887  continue;
888  }
889 
890  if( change.Action == UpdateRefDes )
891  {
892  prefixpresent = ( 0 == fpData.RefDesType.find( aPrefix ) );
893 
894  if( addprefix && !prefixpresent )
895  fpData.RefDesType.insert( 0, aPrefix ); // Add prefix once only
896 
897  if( aRemovePrefix && prefixpresent ) // If there is a prefix remove it
898  fpData.RefDesType.erase( 0, prefixsize );
899 
900  RefDesTypeStr* refDesInfo = GetOrBuildRefDesInfo( fpData.RefDesType, aStartRefDes );
901  unsigned int newRefDesNumber = refDesInfo->LastUsedRefDes + 1;
902 
903  while( refDesInfo->UnavailableRefs.count( newRefDesNumber ) )
904  newRefDesNumber++;
905 
906  change.NewRefDes = refDesInfo->RefDesType + std::to_string( newRefDesNumber );
907  refDesInfo->LastUsedRefDes = newRefDesNumber;
908  }
909 
910  m_changeArray.push_back( change );
911  }
912 }
913 
914 
916 {
917  size_t i;
918 
919  for( i = 0; i < m_changeArray.size(); i++ )
920  {
921  if( aFootprint->m_Uuid == m_changeArray[i].Uuid )
922  return ( &m_changeArray[i] );
923  }
924 
925  ShowReport( _( "Footprint not found in changelist" ) + wxS( " " ) + aFootprint->GetReference(),
927 
928  return nullptr; // Should never happen
929 }
bool ReannotateBoard(void)
Actually reannotate the board.
std::vector< wxRadioButton * > m_sortButtons
Class DIALOG_BOARD_REANNOTATE_BASE.
bool DescendingSecond
void OnModify() override
Must be called after a board change to set the modified flag.
wxString MessageTextFromValue(EDA_UNITS aUnits, int aValue, bool aAddUnitLabel, EDA_DATA_TYPE aType)
Convert a value to a string using double notation.
Definition: base_units.cpp:104
void SetLazyUpdate(bool aLazyUpdate)
Forces updating the HTML page, after the report is built in lazy mode If aSort = true,...
COMMIT & Modify(EDA_ITEM *aItem)
Create an undo entry for an item that has been already modified.
Definition: commit.h:103
wxString user_grid_y
Definition: app_settings.h:54
void FilterPrefix(wxTextCtrl *aPrefix)
Check to make sure the prefix (if there is one) is properly constructed.
#define MAXERROR
virtual APP_SETTINGS_BASE * config() const
Returns the settings object used in SaveSettings(), and is overloaded in KICAD_MANAGER_FRAME.
wxString user_grid_x
Definition: app_settings.h:53
Implementation of conversion functions that require both schematic and board internal units.
This file is part of the common library.
wxString AnnotateString[]
KIFACE_BASE & Kiface()
Global KIFACE_BASE "get" accessor.
PCB_DRAW_PANEL_GAL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
void FilterBackPrefix(wxCommandEvent &event) override
void Flush(bool aSort=false)
Set the visible severity filter.
void LogFootprints(const wxString &aMessage, const std::vector< RefDesInfo > &aFootprints)
Create a list of the footprints and their coordinates.
Collection of utility functions for component reference designators (refdes)
static bool ModuleCompare(const RefDesInfo &aA, const RefDesInfo &aB)
Compare function to sort footprints.
bool BuildFootprintList(std::vector< RefDesInfo > &aBadRefDes)
Build the footprint lists, sort it, filter for excludes, then build the change list.
DIALOG_BOARD_REANNOTATE(PCB_EDIT_FRAME *aParentFrame)
#define MINGRID
std::vector< wxString > m_excludeArray
void ShowReport(const wxString &aMessage, SEVERITY aSeverity)
Break report into strings separated by and sent to the reporter.
#define SORTYFIRST
RefDesChange * GetNewRefDes(FOOTPRINT *aFootprint)
std::vector< RefDesChange > m_changeArray
bool DescendingFirst
Definition: kiid.h:44
virtual void Update(const VIEW_ITEM *aItem, int aUpdateFlags) const override
For dynamic VIEWs, inform the associated VIEW that the graphical representation of this item has chan...
Definition: pcb_view.cpp:92
unsigned int LastUsedRefDes
void OnApplyClick(wxCommandEvent &event) override
std::vector< RefDesInfo > m_backFootprints
std::vector< wxString > sizes
Definition: app_settings.h:52
Store information read from a netlist along with the flags used to update the NETLIST in the BOARD.
Definition: pcb_netlist.h:206
std::vector< RefDesInfo > m_frontFootprints
void InitValues(void)
Copy saved app settings to the dialog.
GRID_SETTINGS grid
Definition: app_settings.h:89
PROJECT & Prj() const
Return a reference to the PROJECT associated with this KIWAY.
int RoundToGrid(int aCoord, int aGrid)
Round an int coordinate to a suitable grid.
FOOTPRINTS & Footprints()
Definition: board.h:233
#define ASCENDINGSECOND
std::vector< RefDesTypeStr > m_refDesTypes
const wxString & GetReference() const
Definition: footprint.h:463
KIGFX::GAL * GetGAL() const
Return a pointer to the GAL instance used in the panel.
#define _(s)
#define ASCENDINGFIRST
#define SetSortCodes(DirArray, Code)
wxString ActionMessage[]
int BackDirectionsArray[]
int FrontDirectionsArray[]
SEVERITY
void OnCloseClick(wxCommandEvent &event) override
std::set< unsigned int > UnavailableRefs
wxBitmap KiBitmap(BITMAPS aBitmap, int aHeightTag)
Construct a wxBitmap from an image identifier Returns the image from the active theme if the image ha...
Definition: bitmap.cpp:105
virtual KIGFX::PCB_VIEW * GetView() const override
Return a pointer to the #VIEW instance used in the panel.
bool Empty() const
Checks if there is anything selected.
Definition: selection.h:98
#define DESCENDINGSECOND
void Report(const wxString &aText, SEVERITY aSeverity, REPORTER::LOCATION aLocation=REPORTER::LOC_BODY)
Reports the string.
class FOOTPRINT, a footprint
Definition: typeinfo.h:88
BOARD * GetBoard()
const KIID m_Uuid
Definition: eda_item.h:475
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
const VECTOR2D & GetGridSize() const
Return the grid size.
EDA_UNITS m_units
Definition: dialog_shim.h:200
void BuildUnavailableRefsList()
Build list of unavailable references. E.g. unselected footprints or locked footprints.
void finishDialogSettings()
In all dialogs, we must call the same functions to fix minimal dlg size, the default position and per...
static bool ChangeArrayCompare(const RefDesChange &aA, const RefDesChange &aB)
Compare function used to compare ChangeArray element for sort.
WINDOW_SETTINGS m_Window
Definition: app_settings.h:181
void FilterFrontPrefix(wxCommandEvent &event) override
RefDesTypeStr * GetOrBuildRefDesInfo(const wxString &aRefDesPrefix, unsigned int aStartRefDes=0)
Get the structure representing the information currently held for aRefDesPrefix or create one if it d...
Definition: layer_ids.h:71
wxString CoordTowxString(int aX, int aY)
Convert coordinates to wxString.
void BuildChangeArray(std::vector< RefDesInfo > &aFootprints, unsigned int aStartRefDes, const wxString &aPrefix, bool aRemovePrefix, std::vector< RefDesInfo > &aBadRefDes)
Scan through the footprint arrays and create the from -> to array.
static void BuildChoiceList(wxArrayString *aGridsList, APP_SETTINGS_BASE *aCfg, EDA_DRAW_FRAME *aParent)
Definition: grid_menu.cpp:79
virtual void Refresh(bool aEraseBackground=true, const wxRect *aRect=nullptr) override
Update the board display after modifying it by a python script (note: it is automatically called by a...
The main frame for Pcbnew.
PCBNEW_SETTINGS * GetPcbNewSettings() const
The selection tool: currently supports:
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:100
virtual void Push(const wxString &aMessage=wxT("A commit"), bool aCreateUndoEntry=true, bool aSetDirtyBit=true) override
Revert the commit by restoring the modified items state.
DIALOG_REANNOTATE m_Reannotate
APP_SETTINGS_BASE * KifaceSettings() const
Definition: kiface_base.h:92
TOOL_MANAGER * GetToolManager() const
Return the MVC controller.
Definition: tools_holder.h:54
int StrNumCmp(const wxString &aString1, const wxString &aString2, bool aIgnoreCase)
Compare two strings with alphanumerical content.
BOARD * GetBoard() const
std::vector< wxRadioButton * > AnnotateWhat
void SetFileName(const wxString &aReportFileName)
double DoubleValueFromString(EDA_UNITS aUnits, const wxString &aTextValue, EDA_DATA_TYPE aType)
Function DoubleValueFromString converts aTextValue to a double.
Definition: base_units.cpp:307
void LogChangePlan(void)
Create an audit trail of the changes.
#define DESCENDINGFIRST
Implement an OUTPUTFORMATTER to a memory buffer.
Definition: richio.h:414
bool IsOK(wxWindow *aParent, const wxString &aMessage)
Display a yes/no dialog with aMessage and returns the user response.
Definition: confirm.cpp:323
bool IsEmpty() const
Definition: board.h:281
bool SortYFirst
#define SORTXFIRST
int GetRefDesNumber(const wxString &aRefDes)
Get the numeric suffix from a refdes - e.g.
void MakeSampleText(wxString &aMessage)
Make the text to summarize what is about to happen.
#define VALIDPREFIX