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, wxT( "\n Sheet requested file '%s', %s" ),
460  newAbsoluteFilename,
461  loadFromFile ? wxT( "found" ) : wxT( "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:693
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.cpp:320
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:172
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:110
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:93
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:102
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:1210
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:113
void SetBorderWidth(int aWidth)
Definition: sch_sheet.h:108
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:139
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:107
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:114
bool AllowCaseSensitiveFileNameClashes(const wxString &aSchematicFileName)
Check aSchematicFileName for a potential file name case sensitivity clashes.
Definition: sheet.cpp:553
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:111
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:226
wxString GetFileName() const
Return the filename corresponding to this sheet.
Definition: sch_sheet.h:314
wxPoint GetPosition() const override
Definition: sch_sheet.h:379
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:153
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
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 void SetValue(long long int aValue)
Set new value (in Internal Units) for the text field, taking care of units conversion.
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:1190
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:1165
A color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:103
void OnAddField(wxCommandEvent &event) override