KiCad PCB EDA Suite
dialog_sheet_properties.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) 2009 Wayne Stambaugh <[email protected]>
5  * Copyright (C) 2014-2021 KiCad Developers, see CHANGELOG.txt for contributors.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, you may find one here:
19  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20  * or you may search the http://www.gnu.org website for the version 2 license,
21  * or you may write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24 
26 #include <kiface_base.h>
27 #include <wx/string.h>
28 #include <wx/log.h>
29 #include <wx/tooltip.h>
30 #include <confirm.h>
31 #include <validators.h>
33 #include <widgets/tab_traversal.h>
34 #include <sch_edit_frame.h>
35 #include <sch_sheet.h>
36 #include <schematic.h>
37 #include <bitmaps.h>
38 #include <eeschema_settings.h>
40 #include <trace_helpers.h>
42 
44  bool* aClearAnnotationNewItems ) :
46  m_frame( aParent ),
47  m_clearAnnotationNewItems( aClearAnnotationNewItems ),
48  m_borderWidth( aParent, m_borderWidthLabel, m_borderWidthCtrl, m_borderWidthUnits ),
49  m_dummySheet( *aSheet ),
50  m_dummySheetNameField( wxDefaultPosition, SHEETNAME, &m_dummySheet )
51 {
52  m_sheet = aSheet;
53  m_fields = new FIELDS_GRID_TABLE<SCH_FIELD>( this, aParent, m_grid, m_sheet );
54  m_width = 100; // Will be later set to a better value
57 
58  // Give a bit more room for combobox editors
59  m_grid->SetDefaultRowSize( m_grid->GetDefaultRowSize() + 4 );
60 
62  m_grid->PushEventHandler( new FIELDS_GRID_TRICKS( m_grid, this ) );
63  m_grid->SetSelectionMode( wxGrid::wxGridSelectRows );
64 
65  // Show/hide columns according to user's preference
66  auto cfg = dynamic_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() );
67  wxASSERT( cfg );
68 
69  if( cfg )
70  {
71  m_shownColumns = cfg->m_Appearance.edit_sheet_visible_columns;
73  }
74 
75  wxToolTip::Enable( true );
76  m_stdDialogButtonSizerOK->SetDefault();
77 
78  // Configure button logos
79  m_bpAdd->SetBitmap( KiBitmap( BITMAPS::small_plus ) );
80  m_bpDelete->SetBitmap( KiBitmap( BITMAPS::small_trash ) );
81  m_bpMoveUp->SetBitmap( KiBitmap( BITMAPS::small_up ) );
82  m_bpMoveDown->SetBitmap( KiBitmap( BITMAPS::small_down ) );
83 
84  // Set font sizes
85  m_hierarchicalPathLabel->SetFont( KIUI::GetInfoFont( this ) );
86 
87  // wxFormBuilder doesn't include this event...
88  m_grid->Connect( wxEVT_GRID_CELL_CHANGING,
89  wxGridEventHandler( DIALOG_SHEET_PROPERTIES::OnGridCellChanging ),
90  nullptr, this );
91 
93 }
94 
95 
97 {
98  auto cfg = dynamic_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() );
99  wxASSERT( cfg );
100 
101  if( cfg )
102  cfg->m_Appearance.edit_sheet_visible_columns = m_grid->GetShownColumns();
103 
104  // Prevents crash bug in wxGrid's d'tor
106 
107  m_grid->Disconnect( wxEVT_GRID_CELL_CHANGING,
108  wxGridEventHandler( DIALOG_SHEET_PROPERTIES::OnGridCellChanging ),
109  nullptr, this );
110 
111  // Delete the GRID_TRICKS.
112  m_grid->PopEventHandler( true );
113 }
114 
115 
117 {
118  if( !wxDialog::TransferDataToWindow() )
119  return false;
120 
121  // Push a copy of each field into m_updateFields
122  for( SCH_FIELD& field : m_sheet->GetFields() )
123  {
124  SCH_FIELD field_copy( field );
125 
126 #ifdef __WINDOWS__
127  // Filenames are stored using unix notation
128  if( field_copy.GetId() == SHEETFILENAME )
129  {
130  wxString filename = field_copy.GetText();
131  filename.Replace( wxT( "/" ), wxT( "\\" ) );
132  field_copy.SetText( filename );
133  }
134 #endif
135 
136  // change offset to be symbol-relative
137  field_copy.Offset( -m_sheet->GetPosition() );
138 
139  m_fields->push_back( field_copy );
140  }
141 
142  // notify the grid
143  wxGridTableMessage msg( m_fields, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, m_fields->size() );
144  m_grid->ProcessTableMessage( msg );
145  AdjustGridColumns( m_grid->GetRect().GetWidth() );
146 
147  // border width
149 
150  // set up color swatches
151  KIGFX::COLOR4D borderColor = m_sheet->GetBorderColor();
152  KIGFX::COLOR4D backgroundColor = m_sheet->GetBackgroundColor();
153 
154  m_borderSwatch->SetDefaultColor( COLOR4D::UNSPECIFIED );
155  m_backgroundSwatch->SetDefaultColor( COLOR4D::UNSPECIFIED );
156 
157  m_borderSwatch->SetSwatchColor( borderColor, false );
158  m_backgroundSwatch->SetSwatchColor( backgroundColor, false );
159 
163 
165  SCH_SHEET_PATH instance = m_frame->GetCurrentSheet();
166 
167  instance.push_back( m_sheet );
168 
169  wxString nextPageNumber = m_sheet->GetPageNumber( instance );
170 
171  m_pageNumberTextCtrl->ChangeValue( nextPageNumber );
172 
173  Layout();
174 
175  return true;
176 }
177 
178 
180 {
181  wxString msg;
182  LIB_ID id;
183 
184  if( !m_grid->CommitPendingChanges() || !m_grid->Validate() )
185  return false;
186 
187  // Check for missing field names.
188  for( size_t i = SHEET_MANDATORY_FIELDS; i < m_fields->size(); ++i )
189  {
190  SCH_FIELD& field = m_fields->at( i );
191  wxString fieldName = field.GetName( false );
192 
193  if( fieldName.IsEmpty() )
194  {
195  DisplayErrorMessage( this, _( "Fields must have a name." ) );
196 
198  m_delayedFocusRow = i;
199 
200  return false;
201  }
202  }
203 
204  return true;
205 }
206 
207 
208 static bool positioningChanged( const SCH_FIELD& a, const SCH_FIELD& b )
209 {
210  if( a.GetPosition() != b.GetPosition() )
211  return true;
212 
213  if( a.GetHorizJustify() != b.GetHorizJustify() )
214  return true;
215 
216  if( a.GetVertJustify() != b.GetVertJustify() )
217  return true;
218 
219  if( a.GetTextAngle() != b.GetTextAngle() )
220  return true;
221 
222  return false;
223 }
224 
225 
226 static bool positioningChanged( FIELDS_GRID_TABLE<SCH_FIELD>* a, std::vector<SCH_FIELD>& b )
227 {
228  for( size_t i = 0; i < SHEET_MANDATORY_FIELDS; ++i )
229  {
230  if( positioningChanged( a->at( i ), b.at( i ) ) )
231  return true;
232  }
233 
234  return false;
235 }
236 
237 
239 {
240  if( !wxDialog::TransferDataFromWindow() ) // Calls our Validate() method.
241  return false;
242 
243  // Sheet file names can be relative or absolute.
244  wxString sheetFileName = m_fields->at( SHEETFILENAME ).GetText();
245 
246  // Ensure filepath is not empty. (In normal use will be caught by grid validators,
247  // but unedited data from existing files can be bad.)
248  if( sheetFileName.IsEmpty() )
249  {
250  DisplayError( this, _( "A sheet must have a valid file name." ) );
251  return false;
252  }
253 
254  // Ensure the filename extension is OK. In normal use will be caught by grid validators,
255  // but unedited data from existing files can be bad.
256  wxFileName fn( sheetFileName );
257 
258  if( fn.GetExt().CmpNoCase( KiCadSchematicFileExtension ) != 0 )
259  {
260  DisplayError( this, _( "Sheet file must have a '.kicad_sch' extension." ) );
261  return false;
262  }
263 
264  wxString newRelativeFilename = fn.GetFullPath();
265 
266  // Inside Eeschema, filenames are stored using unix notation
267  newRelativeFilename.Replace( wxT( "\\" ), wxT( "/" ) );
268 
269  wxString oldFilename = m_sheet->GetFields()[ SHEETFILENAME ].GetText();
270  oldFilename.Replace( wxT( "\\" ), wxT( "/" ) );
271 
272  bool filename_changed = oldFilename != newRelativeFilename;
273 
274  if( filename_changed || m_sheet->IsNew() )
275  {
276  SCH_SCREEN* currentScreen = m_frame->GetCurrentSheet().LastScreen();
277 
278  wxCHECK( currentScreen, false );
279 
280  bool clearFileName = false;
281 
282  // This can happen for the root sheet when opening Eeschema in the stand alone mode.
283  if( currentScreen->GetFileName().IsEmpty() )
284  {
285  clearFileName = true;
286  currentScreen->SetFileName( m_frame->Prj().AbsolutePath( wxT( "noname.kicad_sch" ) ) );
287  }
288 
289  wxFileName tmp( fn );
290  wxFileName screenFileName = currentScreen->GetFileName();
291 
292  if( fn.IsAbsolute() && fn.MakeRelativeTo( screenFileName.GetPath() ) )
293  {
294  wxMessageDialog makeRelDlg( this, _( "Use relative path for sheet file?" ),
295  _( "Sheet File Path" ),
296  wxYES_NO | wxYES_DEFAULT | wxICON_QUESTION | wxCENTER );
297 
298  makeRelDlg.SetExtendedMessage( _( "Using relative hierarchical sheet file name paths "
299  "improves schematic portability across systems and "
300  "platforms. Using absolute paths can result in "
301  "portability issues." ) );
302  makeRelDlg.SetYesNoLabels( wxMessageDialog::ButtonLabel( _( "Use Relative Path" ) ),
303  wxMessageDialog::ButtonLabel( _( "Use Absolute Path" ) ) );
304 
305  if( makeRelDlg.ShowModal() == wxID_YES )
306  {
307  wxLogTrace( tracePathsAndFiles, "\n Converted absolute path: '%s'"
308  "\n to relative path: '%s'",
309  tmp.GetPath(),
310  fn.GetPath() );
311  m_fields->at( SHEETFILENAME ).SetText( fn.GetFullPath() );
312  newRelativeFilename = fn.GetFullPath();
313  }
314  }
315 
316  if( !onSheetFilenameChanged( newRelativeFilename ) )
317  {
318  if( clearFileName )
319  currentScreen->SetFileName( wxEmptyString );
320 
321  return false;
322  }
323 
324  if( clearFileName )
325  currentScreen->SetFileName( wxEmptyString );
326 
327  // One last validity check (and potential repair) just to be sure to be sure
328  SCH_SHEET_LIST repairedList( &m_frame->Schematic().Root(), true );
329  }
330 
331  wxString newSheetname = m_fields->at( SHEETNAME ).GetText();
332 
333  if( newSheetname.IsEmpty() )
334  newSheetname = _( "Untitled Sheet" );
335 
336  m_fields->at( SHEETNAME ).SetText( newSheetname );
337 
338  // change all field positions from relative to absolute
339  for( unsigned i = 0; i < m_fields->size(); ++i )
340  m_fields->at( i ).Offset( m_sheet->GetPosition() );
341 
344 
346 
348 
349  COLOR_SETTINGS* colorSettings = m_frame->GetColorSettings();
350 
351  if( colorSettings->GetOverrideSchItemColors()
354  {
355  wxPanel temp( this );
356  temp.Hide();
357  PANEL_EESCHEMA_COLOR_SETTINGS prefs( m_frame, &temp );
358  wxString checkboxLabel = prefs.m_optOverrideColors->GetLabel();
359 
360  KIDIALOG dlg( this, _( "Note: item colors are overridden in the current color theme." ),
362  dlg.ShowDetailedText( wxString::Format( _( "To see individual item colors uncheck '%s'\n"
363  "in Preferences > Eeschema > Colors." ),
364  checkboxLabel ) );
365  dlg.DoNotShowCheckbox( __FILE__, __LINE__ );
366  dlg.ShowModal();
367  }
368 
371 
373  SCH_SHEET_PATH instance = m_frame->GetCurrentSheet();
374 
375  instance.push_back( m_sheet );
376 
377  if( m_sheet->IsNew() )
378  m_sheet->AddInstance( instance );
379 
380  m_sheet->SetPageNumber( instance, m_pageNumberTextCtrl->GetValue() );
381 
383 
384  // Refresh all sheets in case ordering changed.
385  for( SCH_ITEM* item : m_frame->GetScreen()->Items().OfType( SCH_SHEET_T ) )
386  m_frame->UpdateItem( item );
387 
388  m_frame->OnModify();
389 
390  return true;
391 }
392 
393 
394 bool DIALOG_SHEET_PROPERTIES::onSheetFilenameChanged( const wxString& aNewFilename )
395 {
396  wxString msg;
397 
398  // Sheet file names are relative to the path of the current sheet. This allows for
399  // nesting of schematic files in subfolders. Screen file names are always absolute.
400  wxFileName sheetFileName( aNewFilename );
401 
402  if( sheetFileName.GetExt().IsEmpty() )
403  {
404  sheetFileName.SetExt( KiCadSchematicFileExtension );
405  }
406  else if( sheetFileName.GetExt().CmpNoCase( KiCadSchematicFileExtension ) != 0 )
407  {
408  msg = wxString::Format( _( "The file '%s' does not appear to be a valid schematic file." ),
409  sheetFileName.GetFullName() );
410  wxMessageDialog badSchFileDialog( this, msg, _( "Invalid Schematic File" ),
411  wxOK | wxCENTRE | wxICON_EXCLAMATION );
412  badSchFileDialog.ShowModal();
413  return false;
414  }
415 
416  SCH_SHEET_LIST fullHierarchy = m_frame->Schematic().GetFullHierarchy();
417  std::vector<SCH_SHEET_INSTANCE> sheetInstances = fullHierarchy.GetSheetInstances();
418  wxFileName screenFileName( sheetFileName );
419  wxFileName tmp( sheetFileName );
420 
421  SCH_SCREEN* currentScreen = m_frame->GetCurrentSheet().LastScreen();
422 
423  wxCHECK( currentScreen, false );
424 
425  // SCH_SCREEN file names are always absolute.
426  wxFileName currentScreenFileName = currentScreen->GetFileName();
427 
428  if( !screenFileName.Normalize( wxPATH_NORM_ALL, currentScreenFileName.GetPath() ) )
429  {
430  msg = wxString::Format( _( "Cannot normalize new sheet schematic file path:\n"
431  "'%s'\n"
432  "against parent sheet schematic file path:\n"
433  "'%s'." ),
434  sheetFileName.GetPath(),
435  currentScreenFileName.GetPath() );
436  DisplayError( this, msg );
437  return false;
438  }
439 
440  wxString newAbsoluteFilename = screenFileName.GetFullPath();
441 
442  // Inside Eeschema, filenames are stored using unix notation
443  newAbsoluteFilename.Replace( wxT( "\\" ), wxT( "/" ) );
444 
445  bool renameFile = false;
446  bool loadFromFile = false;
447  bool clearAnnotation = false;
448  bool restoreSheet = false;
449  bool isExistingSheet = false;
450  SCH_SCREEN* useScreen = nullptr;
451  SCH_SCREEN* oldScreen = nullptr;
452 
453  // Search for a schematic file having the same filename already in use in the hierarchy
454  // or on disk, in order to reuse it.
455  if( !m_frame->Schematic().Root().SearchHierarchy( newAbsoluteFilename, &useScreen ) )
456  {
457  loadFromFile = wxFileExists( newAbsoluteFilename );
458 
459  wxLogTrace( tracePathsAndFiles, "\n Sheet requested file '%s', %s",
460  newAbsoluteFilename,
461  loadFromFile ? "found" : "not found" );
462  }
463 
464  if( m_sheet->GetScreen() == nullptr ) // New just created sheet.
465  {
466  if( !m_frame->AllowCaseSensitiveFileNameClashes( newAbsoluteFilename ) )
467  return false;
468 
469  if( useScreen || loadFromFile ) // Load from existing file.
470  {
471  clearAnnotation = true;
472 
473  if( !IsOK( this, wxString::Format( _( "'%s' already exists." ),
474  sheetFileName.GetFullName() )
475  + wxT( "\n\n" )
476  + wxString::Format( _( "Link '%s' to this file?" ),
477  newAbsoluteFilename ) ) )
478  {
479  return false;
480  }
481  }
482  else // New file.
483  {
484  m_frame->InitSheet( m_sheet, newAbsoluteFilename );
485  }
486  }
487  else // Existing sheet.
488  {
489  bool isUndoable = true;
490  isExistingSheet = true;
491 
492  if( !m_frame->AllowCaseSensitiveFileNameClashes( newAbsoluteFilename ) )
493  return false;
494 
495  // We are always using here a case insensitive comparison to avoid issues
496  // under Windows, although under Unix filenames are case sensitive.
497  // But many users create schematic under both Unix and Windows
498  // **
499  // N.B. 1: aSheet->GetFileName() will return a relative path
500  // aSheet->GetScreen()->GetFileName() returns a full path
501  //
502  // N.B. 2: newFilename uses the unix notation for separator.
503  // so we must use it also to compare the old and new filenames
504  wxString oldAbsoluteFilename = m_sheet->GetScreen()->GetFileName();
505  oldAbsoluteFilename.Replace( wxT( "\\" ), wxT( "/" ) );
506 
507  if( newAbsoluteFilename.Cmp( oldAbsoluteFilename ) != 0 )
508  {
509  // Sheet file name changes cannot be undone.
510  isUndoable = false;
511 
512  if( useScreen || loadFromFile ) // Load from existing file.
513  {
514  clearAnnotation = true;
515 
516  if( !IsOK( this, wxString::Format( _( "Change '%s' link from '%s' to '%s'?" ),
517  newAbsoluteFilename,
518  m_sheet->GetFileName(),
519  sheetFileName.GetFullName() )
520  + wxT( "\n\n" )
521  + _( "This action cannot be undone." ) ) )
522  {
523  return false;
524  }
525 
526  if( loadFromFile )
527  m_sheet->SetScreen( nullptr );
528  }
529  else // Save to new file name.
530  {
531  if( m_sheet->GetScreenCount() > 1 )
532  {
533  if( !IsOK( this, wxString::Format( _( "Create new file '%s' with contents of '%s'?" ),
534  sheetFileName.GetFullName(),
535  m_sheet->GetFileName() )
536  + wxT( "\n\n" )
537  + _( "This action cannot be undone." ) ) )
538  {
539  return false;
540  }
541  }
542 
543  renameFile = true;
544  }
545  }
546 
547  // If we are renaming files, the undo/redo list becomes invalid and must be cleared
548  if( isUndoable )
550  else
552 
553  if( renameFile )
554  {
555  SCH_PLUGIN::SCH_PLUGIN_RELEASER pi( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
556 
557  // If the associated screen is shared by more than one sheet, do not
558  // change the filename of the corresponding screen here.
559  // (a new screen will be created later)
560  // if it is not shared, update the filename
561  if( m_sheet->GetScreenCount() <= 1 )
562  m_sheet->GetScreen()->SetFileName( newAbsoluteFilename );
563 
564  try
565  {
566  pi->Save( newAbsoluteFilename, m_sheet, &m_frame->Schematic() );
567  }
568  catch( const IO_ERROR& ioe )
569  {
570  msg = wxString::Format( _( "Error occurred saving schematic file '%s'." ),
571  newAbsoluteFilename );
572  DisplayErrorMessage( this, msg, ioe.What() );
573 
574  msg = wxString::Format( _( "Failed to save schematic '%s'" ),
575  newAbsoluteFilename );
576  m_frame->SetMsgPanel( wxEmptyString, msg );
577  return false;
578  }
579 
580  // If the associated screen is shared by more than one sheet, remove the
581  // screen and reload the file to a new screen. Failure to do this will trash
582  // the screen reference counting in complex hierarchies.
583  if( m_sheet->GetScreenCount() > 1 )
584  {
585  oldScreen = m_sheet->GetScreen();
586  m_sheet->SetScreen( nullptr );
587  loadFromFile = true;
588  }
589  }
590  }
591 
592  SCH_SHEET_PATH& currentSheet = m_frame->GetCurrentSheet();
593 
594  if( useScreen )
595  {
596  // Create a temporary sheet for recursion testing to prevent a possible recursion error.
597  std::unique_ptr< SCH_SHEET> tmpSheet = std::make_unique<SCH_SHEET>();
598  tmpSheet->GetFields()[SHEETNAME] = m_fields->at( SHEETNAME );
599  tmpSheet->GetFields()[SHEETFILENAME].SetText( sheetFileName.GetFullPath() );
600  tmpSheet->SetScreen( useScreen );
601 
602  // No need to check for valid library IDs if we are using an existing screen.
603  if( m_frame->CheckSheetForRecursion( tmpSheet.get(), &currentSheet ) )
604  {
605  if( restoreSheet )
606  currentSheet.LastScreen()->Append( m_sheet );
607 
608  return false;
609  }
610 
611  // It's safe to set the sheet screen now.
612  m_sheet->SetScreen( useScreen );
613  }
614  else if( loadFromFile )
615  {
616  if( isExistingSheet )
617  {
618  // Temporarily remove the sheet from the current schematic page so that recursion
619  // and symbol library link tests can be performed with the modified sheet settings.
620  restoreSheet = true;
621  currentSheet.LastScreen()->Remove( m_sheet );
622  }
623 
624  if( !m_frame->LoadSheetFromFile( m_sheet, &currentSheet, newAbsoluteFilename )
625  || m_frame->CheckSheetForRecursion( m_sheet, &currentSheet ) )
626  {
627  if( restoreSheet )
628  {
629  // If we cleared the previous screen, restore it before returning to the user
630  if( oldScreen )
631  m_sheet->SetScreen( oldScreen );
632 
633  currentSheet.LastScreen()->Append( m_sheet );
634  }
635 
636  return false;
637  }
638 
639  if( restoreSheet )
640  currentSheet.LastScreen()->Append( m_sheet );
641 
642  // The full hiearchy needs to be reloaded because any sub-sheet that occurred on
643  // file load will have new SCH_SHEET object pointers.
644  fullHierarchy = m_frame->Schematic().GetFullHierarchy();
645  fullHierarchy.UpdateSheetInstances( sheetInstances );
646  }
647 
649  *m_clearAnnotationNewItems = clearAnnotation;
650 
651  return true;
652 }
653 
654 
656 {
657  bool success = true;
658  wxGridCellEditor* editor = m_grid->GetCellEditor( event.GetRow(), event.GetCol() );
659  wxControl* control = editor->GetControl();
660  wxTextEntry* textControl = dynamic_cast<wxTextEntry*>( control );
661 
662  // Short-circuit the validator's more generic "can't be empty" message for the
663  // two mandatory fields:
664  if( event.GetRow() == SHEETNAME && event.GetCol() == FDC_VALUE )
665  {
666  if( textControl && textControl->IsEmpty() )
667  {
668  wxMessageBox( _( "A sheet must have a name." ) );
669  success = false;
670  }
671  }
672  else if( event.GetRow() == SHEETFILENAME && event.GetCol() == FDC_VALUE && textControl )
673  {
674  if( textControl->IsEmpty() )
675  {
676  wxMessageBox( _( "A sheet must have a file specified." ) );
677  success = false;
678  }
679  }
680 
681  if( success && control && control->GetValidator() )
682  success = control->GetValidator()->Validate( control );
683 
684  if( !success )
685  {
686  event.Veto();
687  m_delayedFocusRow = event.GetRow();
688  m_delayedFocusColumn = event.GetCol();
689  }
690 
691  editor->DecRef();
692 }
693 
694 
695 void DIALOG_SHEET_PROPERTIES::OnAddField( wxCommandEvent& event )
696 {
697  if( !m_grid->CommitPendingChanges() )
698  return;
699 
700  int fieldID = m_fields->size();
701  SCH_FIELD newField( wxPoint( 0, 0 ), fieldID, m_sheet,
702  SCH_SHEET::GetDefaultFieldName( fieldID ) );
703 
704  newField.SetTextAngle( m_fields->at( SHEETNAME ).GetTextAngle() );
705 
706  m_fields->push_back( newField );
707 
708  // notify the grid
709  wxGridTableMessage msg( m_fields, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, 1 );
710  m_grid->ProcessTableMessage( msg );
711 
712  m_grid->MakeCellVisible( m_fields->size() - 1, 0 );
713  m_grid->SetGridCursor( m_fields->size() - 1, 0 );
714 
715  m_grid->EnableCellEditControl();
716  m_grid->ShowCellEditControl();
717 }
718 
719 
720 void DIALOG_SHEET_PROPERTIES::OnDeleteField( wxCommandEvent& event )
721 {
722  wxArrayInt selectedRows = m_grid->GetSelectedRows();
723 
724  if( selectedRows.empty() && m_grid->GetGridCursorRow() >= 0 )
725  selectedRows.push_back( m_grid->GetGridCursorRow() );
726 
727  if( selectedRows.empty() )
728  return;
729 
730  for( int row : selectedRows )
731  {
732  if( row < SHEET_MANDATORY_FIELDS )
733  {
734  DisplayError( this, wxString::Format( _( "The first %d fields are mandatory." ),
736  return;
737  }
738  }
739 
740  m_grid->CommitPendingChanges( true /* quiet mode */ );
741 
742  // Reverse sort so deleting a row doesn't change the indexes of the other rows.
743  selectedRows.Sort( []( int* first, int* second ) { return *second - *first; } );
744 
745  for( int row : selectedRows )
746  {
747  m_fields->erase( m_fields->begin() + row );
748 
749  // notify the grid
750  wxGridTableMessage msg( m_fields, wxGRIDTABLE_NOTIFY_ROWS_DELETED, row, 1 );
751  m_grid->ProcessTableMessage( msg );
752 
753  if( m_grid->GetNumberRows() > 0 )
754  {
755  m_grid->MakeCellVisible( std::max( 0, row-1 ), m_grid->GetGridCursorCol() );
756  m_grid->SetGridCursor( std::max( 0, row-1 ), m_grid->GetGridCursorCol() );
757  }
758  }
759 }
760 
761 
762 void DIALOG_SHEET_PROPERTIES::OnMoveUp( wxCommandEvent& event )
763 {
764  if( !m_grid->CommitPendingChanges() )
765  return;
766 
767  int i = m_grid->GetGridCursorRow();
768 
769  if( i > SHEET_MANDATORY_FIELDS )
770  {
771  SCH_FIELD tmp = m_fields->at( (unsigned) i );
772  m_fields->erase( m_fields->begin() + i, m_fields->begin() + i + 1 );
773  m_fields->insert( m_fields->begin() + i - 1, tmp );
774  m_grid->ForceRefresh();
775 
776  m_grid->SetGridCursor( i - 1, m_grid->GetGridCursorCol() );
777  m_grid->MakeCellVisible( m_grid->GetGridCursorRow(), m_grid->GetGridCursorCol() );
778  }
779  else
780  wxBell();
781 }
782 
783 
784 void DIALOG_SHEET_PROPERTIES::OnMoveDown( wxCommandEvent& event )
785 {
786  if( !m_grid->CommitPendingChanges() )
787  return;
788 
789  int i = m_grid->GetGridCursorRow();
790 
791  if( i >= SHEET_MANDATORY_FIELDS && i < m_grid->GetNumberRows() - 1 )
792  {
793  SCH_FIELD tmp = m_fields->at( (unsigned) i );
794  m_fields->erase( m_fields->begin() + i, m_fields->begin() + i + 1 );
795  m_fields->insert( m_fields->begin() + i + 1, tmp );
796  m_grid->ForceRefresh();
797 
798  m_grid->SetGridCursor( i + 1, m_grid->GetGridCursorCol() );
799  m_grid->MakeCellVisible( m_grid->GetGridCursorRow(), m_grid->GetGridCursorCol() );
800  }
801  else
802  wxBell();
803 }
804 
805 
807 {
808  m_width = aWidth;
809  // Account for scroll bars
810  aWidth -= ( m_grid->GetSize().x - m_grid->GetClientSize().x );
811 
812  m_grid->AutoSizeColumn( 0 );
813 
814  int fixedColsWidth = m_grid->GetColSize( 0 );
815 
816  for( int i = 2; i < m_grid->GetNumberCols(); i++ )
817  fixedColsWidth += m_grid->GetColSize( i );
818 
819  int colSize = std::max( aWidth - fixedColsWidth, -1 );
820  colSize = ( colSize == 0 ) ? -1 : colSize; // don't hide the column!
821 
822  m_grid->SetColSize( 1, colSize );
823 }
824 
825 
826 void DIALOG_SHEET_PROPERTIES::OnUpdateUI( wxUpdateUIEvent& event )
827 {
828  wxString shownColumns = m_grid->GetShownColumns();
829 
830  if( shownColumns != m_shownColumns )
831  {
832  m_shownColumns = shownColumns;
833 
834  if( !m_grid->IsCellEditControlShown() )
835  AdjustGridColumns( m_grid->GetRect().GetWidth() );
836  }
837 
838  // Propagate changes in sheetname to displayed hierarchical path
839  wxString hierarchicalPath = _( "Hierarchical path: " );
840 
841  hierarchicalPath += m_frame->GetCurrentSheet().PathHumanReadable( false );
842 
843  if( hierarchicalPath.Last() != '/' )
844  hierarchicalPath.Append( '/' );
845 
846  wxGridCellEditor* editor = m_grid->GetCellEditor( SHEETNAME, FDC_VALUE );
847  wxControl* control = editor->GetControl();
848  wxTextEntry* textControl = dynamic_cast<wxTextEntry*>( control );
849  wxString sheetName;
850 
851  if( textControl )
852  sheetName = textControl->GetValue();
853  else
854  sheetName = m_grid->GetCellValue( SHEETNAME, FDC_VALUE );
855 
857  m_dummySheetNameField.SetText( sheetName );
858  hierarchicalPath += m_dummySheetNameField.GetShownText();
859 
860  editor->DecRef();
861 
862  if( m_hierarchicalPathLabel->GetLabel() != hierarchicalPath )
863  m_hierarchicalPathLabel->SetLabel( hierarchicalPath );
864 
865  // Handle a delayed focus
866  if( m_delayedFocusRow >= 0 )
867  {
868  m_grid->SetFocus();
869  m_grid->MakeCellVisible( m_delayedFocusRow, m_delayedFocusColumn );
870  m_grid->SetGridCursor( m_delayedFocusRow, m_delayedFocusColumn );
871 
872 
873  m_grid->EnableCellEditControl( true );
874  m_grid->ShowCellEditControl();
875 
876  m_delayedFocusRow = -1;
878  }
879 }
880 
881 
882 void DIALOG_SHEET_PROPERTIES::OnSizeGrid( wxSizeEvent& event )
883 {
884  auto new_size = event.GetSize().GetX();
885 
886  if( m_width != new_size )
887  {
888  AdjustGridColumns( new_size );
889  }
890 
891  // Always propagate for a grid repaint (needed if the height changes, as well as width)
892  event.Skip();
893 }
894 
895 
896 void DIALOG_SHEET_PROPERTIES::OnInitDlg( wxInitDialogEvent& event )
897 {
899 
900  // Now all widgets have the size fixed, call FinishDialogSettings
902 }
void SetSwatchColor(const KIGFX::COLOR4D &aColor, bool aSendEvent)
Set the current swatch color directly.
bool CheckSheetForRecursion(SCH_SHEET *aSheet, SCH_SHEET_PATH *aHierarchy)
Verify that aSheet will not cause a recursion error in aHierarchy.
Definition: sheet.cpp:47
void DisplayError(wxWindow *aParent, const wxString &aText, int aDisplayTime)
Display an error or warning message box with aMessage.
Definition: confirm.cpp:279
A container for handling SCH_SHEET_PATH objects in a flattened hierarchy.
EE_TYPE OfType(KICAD_T aType) const
Definition: sch_rtree.h:230
void OnDeleteField(wxCommandEvent &event) override
Instances are attached to a symbol or sheet and provide a place for the symbol's value,...
Definition: sch_field.h:49
void Offset(const wxPoint &aOffset)
Definition: eda_text.h:273
Functions for manipulating tab traversal in forms and dialogs.
const wxString & GetFileName() const
Definition: sch_screen.h:145
void DoNotShowCheckbox(wxString file, int line)
Checks the 'do not show again' setting for the dialog.
Definition: confirm.cpp:55
bool Remove(SCH_ITEM *aItem)
Remove aItem from the schematic associated with this screen.
Definition: sch_screen.cpp:273
bool SearchHierarchy(const wxString &aFilename, SCH_SCREEN **aScreen)
Search the existing hierarchy for an instance of screen loaded from aFileName.
Definition: sch_sheet.cpp:669
KIGFX::COLOR4D GetSwatchColor() const
EDA_TEXT_VJUSTIFY_T GetVertJustify() const
Definition: eda_text.h:220
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition: confirm.cpp:292
Helper class to create more flexible dialogs, including 'do not show again' checkbox handling.
Definition: confirm.h:45
void SetFields(const std::vector< SCH_FIELD > &aFields)
Set multiple schematic fields.
Definition: sch_sheet.h:98
void OnUpdateUI(wxUpdateUIEvent &event) override
The first 2 are mandatory, and must be instantiated in SCH_SHEET.
Definition: sch_sheet.h:47
This file is part of the common library.
void OnMoveDown(wxCommandEvent &event) override
KIFACE_BASE & Kiface()
Global KIFACE_BASE "get" accessor.
void ShowHideColumns(const wxString &shownColumns)
Show/hide the grid columns based on a tokenized string of shown column indexes.
Definition: wx_grid.cpp:136
void push_back(SCH_SHEET *aSheet)
Forwarded method from std::vector.
wxString PathHumanReadable(bool aUseShortRootName=true) const
Return the sheet path in a human readable form made from the sheet names.
wxPoint GetPosition() const override
Definition: sch_field.cpp:842
void SetScreen(SCH_SCREEN *aScreen)
Set the SCH_SCREEN associated with this sheet to aScreen.
Definition: sch_sheet.cpp:175
wxFont GetInfoFont(wxWindow *aWindow)
Definition: ui_common.cpp:144
const wxChar *const tracePathsAndFiles
Flag to enable path and file name debug output.
double GetTextAngle() const
Definition: eda_text.h:195
int GetId() const
Definition: sch_field.h:113
static const wxString GetDefaultFieldName(int aFieldNdx, bool aTranslated=true)
Definition: sch_sheet.cpp:53
void InitSheet(SCH_SHEET *aSheet, const wxString &aNewFilename)
Definition: sheet.cpp:97
KIGFX::COLOR4D GetBorderColor() const
Definition: sch_sheet.h:113
Schematic editor (Eeschema) main window.
void SetTable(wxGridTableBase *table, bool aTakeOwnership=false)
Hide wxGrid's SetTable() method with one which doesn't mess up the grid column widths when setting th...
Definition: wx_grid.cpp:72
A logical library item identifier and consists of various portions much like a URI.
Definition: lib_id.h:51
SCH_SCREEN * GetScreen() const
Definition: sch_sheet.h:105
void OnInitDlg(wxInitDialogEvent &event) override
void SetPageNumber(const SCH_SHEET_PATH &aInstance, const wxString &aPageNumber)
Set the page number for the sheet instance aInstance.
Definition: sch_sheet.cpp:1186
wxString GetShownText(int aDepth=0) const override
Return the string actually shown after processing of the base text.
Definition: sch_field.cpp:105
void UpdateSheetInstances(const std::vector< SCH_SHEET_INSTANCE > &aSheetInstances)
Update all of the sheet instance information using aSheetInstances.
virtual const wxString AbsolutePath(const wxString &aFileName) const
Fix up aFileName if it is relative to the project's directory to be an absolute path and filename.
Definition: project.cpp:269
bool IsNew() const
Definition: eda_item.h:118
KIGFX::COLOR4D GetBackgroundColor() const
Definition: sch_sheet.h:116
void SetBorderWidth(int aWidth)
Definition: sch_sheet.h:111
void DestroyTable(wxGridTableBase *aTable)
Work-around for a bug in wxGrid which crashes when deleting the table if the cell edit control was no...
Definition: wx_grid.cpp:104
std::vector< SCH_FIELD > & GetFields()
Definition: sch_sheet.h:90
SCH_SCREEN * GetScreen() const override
Return a pointer to a BASE_SCREEN or one of its derivatives.
int GetScreenCount() const
Return the number of times the associated screen for the sheet is being used.
Definition: sch_sheet.cpp:198
int GetBorderWidth() const
Definition: sch_sheet.h:110
bool LoadSheetFromFile(SCH_SHEET *aSheet, SCH_SHEET_PATH *aHierarchy, const wxString &aFileName)
Load a the KiCad schematic file aFileName into the sheet aSheet.
Definition: sheet.cpp:105
void SetBackgroundColor(KIGFX::COLOR4D aColor)
Definition: sch_sheet.h:117
bool AllowCaseSensitiveFileNameClashes(const wxString &aSchematicFileName)
Check aSchematicFileName for a potential file name case sensitivity clashes.
Definition: sheet.cpp:550
EDA_TEXT_HJUSTIFY_T GetHorizJustify() const
Definition: eda_text.h:219
virtual void SetText(const wxString &aText)
Definition: eda_text.cpp:124
void TestDanglingEnds()
Test all of the connectable objects in the schematic for unused connection points.
virtual const wxString What() const
A composite of Problem() and Where()
Definition: exceptions.cpp:30
void SetMsgPanel(const std::vector< MSG_PANEL_ITEM > &aList)
Clear the message panel and populates it with the contents of aList.
PROJECT & Prj() const
Return a reference to the PROJECT associated with this KIWAY.
SCHEMATIC & Schematic() const
void SetBorderColor(KIGFX::COLOR4D aColor)
Definition: sch_sheet.h:114
Definition of file extensions used in Kicad.
static bool positioningChanged(const SCH_FIELD &a, const SCH_FIELD &b)
void ClearFieldsAutoplaced()
Definition: sch_item.h:427
Class DIALOG_SHEET_PROPERTIES_BASE.
#define _(s)
void UpdateItem(EDA_ITEM *aItem, bool isAddOrDelete=false, bool aUpdateRtree=false)
Mark an item for refresh.
std::vector< SCH_SHEET_INSTANCE > GetSheetInstances() const
Fetch the instance information for all of the sheets in the hiearchy.
void SetDefaultColor(const KIGFX::COLOR4D &aColor)
Sets the color that will be chosen with the "Reset to Default" button in the chooser.
wxLogTrace helper definitions.
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
bool CommitPendingChanges(bool aQuietMode=false)
Close any open cell edit controls.
Definition: wx_grid.cpp:190
wxString GetFileName() const
Return the filename corresponding to this sheet.
Definition: sch_sheet.h:317
wxPoint GetPosition() const override
Definition: sch_sheet.h:382
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
COLOR_SETTINGS * GetColorSettings() const override
Returns a pointer to the active color theme settings.
void OnGridCellChanging(wxGridEvent &event)
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition: sch_sheet.h:54
DIALOG_SHEET_PROPERTIES(SCH_EDIT_FRAME *aParent, SCH_SHEET *aSheet, bool *aClearAnnotationNewItems)
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
Helper object to release a SCH_PLUGIN in the context of a potential thrown exception through its dest...
Definition: sch_io_mgr.h:479
void finishDialogSettings()
In all dialogs, we must call the same functions to fix minimal dlg size, the default position and per...
wxString GetShownColumns()
Get a tokenized string containing the shown column indexes.
Definition: wx_grid.cpp:117
COLOR4D GetColor(int aLayer) const
SCH_SHEET & Root() const
Definition: schematic.h:92
SCH_SCREEN * LastScreen()
void Append(SCH_ITEM *aItem)
Definition: sch_screen.cpp:146
void SaveCopyInUndoList(SCH_SCREEN *aScreen, SCH_ITEM *aItemToCopy, UNDO_REDO aTypeCommand, bool aAppend)
Create a copy of the current schematic item, and put it in the undo list.
void OnMoveUp(wxCommandEvent &event) override
virtual void SetValue(int aValue)
Set new value (in Internal Units) for the text field, taking care of units conversion.
wxString GetName(bool aUseDefaultName=true) const
Return the field name.
Definition: sch_field.cpp:678
EE_RTREE & Items()
Gets the full RTree, usually for iterating.
Definition: sch_screen.h:110
const std::string KiCadSchematicFileExtension
virtual long long int GetValue()
Return the current value in Internal Units.
int ShowModal() override
Definition: confirm.cpp:99
Color settings are a bit different than most of the settings objects in that there can be more than o...
SCH_SHEET_PATH & GetCurrentSheet() const
bool GetOverrideSchItemColors() const
void OnModify() override
Must be called after a schematic change in order to set the "modify" flag of the current screen and u...
virtual void SetTextAngle(double aAngle)
Definition: eda_text.h:188
void OnSizeGrid(wxSizeEvent &event) override
void SetFileName(const wxString &aFileName)
Set the file name for this screen to aFileName.
Definition: sch_screen.cpp:110
bool onSheetFilenameChanged(const wxString &aNewFilename)
FIELDS_GRID_TABLE< SCH_FIELD > * m_fields
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
Definition: ki_exception.h:75
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:182
bool IsOK(wxWindow *aParent, const wxString &aMessage)
Display a yes/no dialog with aMessage and returns the user response.
Definition: confirm.cpp:323
virtual void ClearUndoRedoList()
Clear the undo and redo list using ClearUndoORRedoList()
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition: eda_text.h:154
Custom text control validator definitions.
SCH_SHEET_LIST & GetFullHierarchy() const
Return the full schematic flattened hierarchical sheet list.
Definition: schematic.cpp:395
wxString GetPageNumber(const SCH_SHEET_PATH &aInstance) const
Return the sheet page number for aInstance.
Definition: sch_sheet.cpp:1166
void SetSwatchBackground(const KIGFX::COLOR4D &aBackground)
Set the swatch background color.
bool AddInstance(const SCH_SHEET_PATH &aInstance)
Add a new instance aSheetPath to the instance list.
Definition: sch_sheet.cpp:1141
A color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:103
void OnAddField(wxCommandEvent &event) override