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 <stambaughw@gmail.com>
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_i.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 
64  // Show/hide columns according to user's preference
65  auto cfg = dynamic_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() );
66  wxASSERT( cfg );
67 
68  if( cfg )
69  {
70  m_shownColumns = cfg->m_Appearance.edit_sheet_visible_columns;
72  }
73 
74  wxToolTip::Enable( true );
75  m_stdDialogButtonSizerOK->SetDefault();
76 
77  // Configure button logos
78  m_bpAdd->SetBitmap( KiBitmap( BITMAPS::small_plus ) );
79  m_bpDelete->SetBitmap( KiBitmap( BITMAPS::small_trash ) );
80  m_bpMoveUp->SetBitmap( KiBitmap( BITMAPS::small_up ) );
81  m_bpMoveDown->SetBitmap( KiBitmap( BITMAPS::small_down ) );
82 
83  // Set font sizes
84  wxFont infoFont = wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT );
85  infoFont.SetSymbolicSize( wxFONTSIZE_SMALL );
86  m_hierarchicalPathLabel->SetFont( infoFont );
87 
88  // wxFormBuilder doesn't include this event...
89  m_grid->Connect( wxEVT_GRID_CELL_CHANGING,
90  wxGridEventHandler( DIALOG_SHEET_PROPERTIES::OnGridCellChanging ),
91  nullptr, this );
92 
94 }
95 
96 
98 {
99  auto cfg = dynamic_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() );
100  wxASSERT( cfg );
101 
102  if( cfg )
103  cfg->m_Appearance.edit_sheet_visible_columns = m_grid->GetShownColumns();
104 
105  // Prevents crash bug in wxGrid's d'tor
107 
108  m_grid->Disconnect( wxEVT_GRID_CELL_CHANGING,
109  wxGridEventHandler( DIALOG_SHEET_PROPERTIES::OnGridCellChanging ),
110  nullptr, this );
111 
112  // Delete the GRID_TRICKS.
113  m_grid->PopEventHandler( true );
114 }
115 
116 
118 {
119  if( !wxDialog::TransferDataToWindow() )
120  return false;
121 
122  // Push a copy of each field into m_updateFields
123  for( SCH_FIELD& field : m_sheet->GetFields() )
124  {
125  SCH_FIELD field_copy( field );
126 
127 #ifdef __WINDOWS__
128  // Filenames are stored using unix notation
129  if( field_copy.GetId() == SHEETFILENAME )
130  {
131  wxString filename = field_copy.GetText();
132  filename.Replace( wxT( "/" ), wxT( "\\" ) );
133  field_copy.SetText( filename );
134  }
135 #endif
136 
137  // change offset to be symbol-relative
138  field_copy.Offset( -m_sheet->GetPosition() );
139 
140  m_fields->push_back( field_copy );
141  }
142 
143  // notify the grid
144  wxGridTableMessage msg( m_fields, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, m_fields->size() );
145  m_grid->ProcessTableMessage( msg );
146  AdjustGridColumns( m_grid->GetRect().GetWidth() );
147 
148  // border width
150 
151  // set up color swatches
152  KIGFX::COLOR4D borderColor = m_sheet->GetBorderColor();
153  KIGFX::COLOR4D backgroundColor = m_sheet->GetBackgroundColor();
154 
155  m_borderSwatch->SetDefaultColor( COLOR4D::UNSPECIFIED );
156  m_backgroundSwatch->SetDefaultColor( COLOR4D::UNSPECIFIED );
157 
158  m_borderSwatch->SetSwatchColor( borderColor, false );
159  m_backgroundSwatch->SetSwatchColor( backgroundColor, false );
160 
164 
166  SCH_SHEET_PATH instance = m_frame->GetCurrentSheet();
167 
168  instance.push_back( m_sheet );
169 
170  wxString nextPageNumber = m_sheet->GetPageNumber( instance );
171 
172  m_pageNumberTextCtrl->ChangeValue( nextPageNumber );
173 
174  Layout();
175 
176  return true;
177 }
178 
179 
181 {
182  wxString msg;
183  LIB_ID id;
184 
185  if( !m_grid->CommitPendingChanges() || !m_grid->Validate() )
186  return false;
187 
188  // Check for missing field names.
189  for( size_t i = SHEET_MANDATORY_FIELDS; i < m_fields->size(); ++i )
190  {
191  SCH_FIELD& field = m_fields->at( i );
192  wxString fieldName = field.GetName( false );
193 
194  if( fieldName.IsEmpty() )
195  {
196  DisplayErrorMessage( this, _( "Fields must have a name." ) );
197 
199  m_delayedFocusRow = i;
200 
201  return false;
202  }
203  }
204 
205  return true;
206 }
207 
208 
209 static bool positioningChanged( const SCH_FIELD& a, const SCH_FIELD& b )
210 {
211  if( a.GetPosition() != b.GetPosition() )
212  return true;
213 
214  if( a.GetHorizJustify() != b.GetHorizJustify() )
215  return true;
216 
217  if( a.GetVertJustify() != b.GetVertJustify() )
218  return true;
219 
220  if( a.GetTextAngle() != b.GetTextAngle() )
221  return true;
222 
223  return false;
224 }
225 
226 
227 static bool positioningChanged( FIELDS_GRID_TABLE<SCH_FIELD>* a, std::vector<SCH_FIELD>& b )
228 {
229  for( size_t i = 0; i < SHEET_MANDATORY_FIELDS; ++i )
230  {
231  if( positioningChanged( a->at( i ), b.at( i ) ) )
232  return true;
233  }
234 
235  return false;
236 }
237 
238 
240 {
241  if( !wxDialog::TransferDataFromWindow() ) // Calls our Validate() method.
242  return false;
243 
244  // Sheet file names can be relative or absolute.
245  wxString sheetFileName = m_fields->at( SHEETFILENAME ).GetText();
246 
247  // Ensure filepath is not empty. (In normal use will be caught by grid validators,
248  // but unedited data from existing files can be bad.)
249  if( sheetFileName.IsEmpty() )
250  {
251  DisplayError( this, _( "A sheet must have a valid file name." ) );
252  return false;
253  }
254 
255  // Ensure the filename extension is OK. In normal use will be caught by grid validators,
256  // but unedited data from existing files can be bad.
257  wxFileName fn( sheetFileName );
258 
259  if( fn.GetExt().CmpNoCase( KiCadSchematicFileExtension ) != 0 )
260  {
261  DisplayError( this, _( "Sheet file must have a '.kicad_sch' extension." ) );
262  return false;
263  }
264 
265  wxString newRelativeFilename = fn.GetFullPath();
266 
267  // Inside Eeschema, filenames are stored using unix notation
268  newRelativeFilename.Replace( wxT( "\\" ), wxT( "/" ) );
269 
270  wxString oldFilename = m_sheet->GetFields()[ SHEETFILENAME ].GetText();
271  oldFilename.Replace( wxT( "\\" ), wxT( "/" ) );
272 
273  bool filename_changed = oldFilename != newRelativeFilename;
274 
275  if( filename_changed || m_sheet->IsNew() )
276  {
277  SCH_SCREEN* currentScreen = m_frame->GetCurrentSheet().LastScreen();
278 
279  wxCHECK( currentScreen, false );
280 
281  bool clearFileName = false;
282 
283  // This can happen for the root sheet when opening Eeschema in the stand alone mode.
284  if( currentScreen->GetFileName().IsEmpty() )
285  {
286  clearFileName = true;
287  currentScreen->SetFileName( m_frame->Prj().AbsolutePath( wxT( "noname.kicad_sch" ) ) );
288  }
289 
290  wxFileName tmp( fn );
291  wxFileName screenFileName = currentScreen->GetFileName();
292 
293  if( fn.IsAbsolute() && fn.MakeRelativeTo( screenFileName.GetPath() ) )
294  {
295  wxMessageDialog makeRelDlg( this, _( "Use relative path for sheet file?" ),
296  _( "Sheet File Path" ),
297  wxYES_NO | wxYES_DEFAULT | wxICON_QUESTION | wxCENTER );
298 
299  makeRelDlg.SetExtendedMessage( _( "Using relative hierarchical sheet file name paths "
300  "improves schematic portability across systems and "
301  "platforms. Using absolute paths can result in "
302  "portability issues." ) );
303  makeRelDlg.SetYesNoLabels( wxMessageDialog::ButtonLabel( _( "Use Relative Path" ) ),
304  wxMessageDialog::ButtonLabel( _( "Use Absolute Path" ) ) );
305 
306  if( makeRelDlg.ShowModal() == wxID_YES )
307  {
308  wxLogTrace( tracePathsAndFiles, "\n Converted absolute path: '%s'"
309  "\n to relative path: '%s'",
310  tmp.GetPath(),
311  fn.GetPath() );
312  m_fields->at( SHEETFILENAME ).SetText( fn.GetFullPath() );
313  newRelativeFilename = fn.GetFullPath();
314  }
315  }
316 
317  if( !onSheetFilenameChanged( newRelativeFilename ) )
318  {
319  if( clearFileName )
320  currentScreen->SetFileName( wxEmptyString );
321 
322  return false;
323  }
324 
325  if( clearFileName )
326  currentScreen->SetFileName( wxEmptyString );
327 
328  // One last validity check (and potential repair) just to be sure to be sure
329  SCH_SHEET_LIST repairedList( &m_frame->Schematic().Root(), true );
330  }
331 
332  wxString newSheetname = m_fields->at( SHEETNAME ).GetText();
333 
334  if( newSheetname.IsEmpty() )
335  newSheetname = _( "Untitled Sheet" );
336 
337  m_fields->at( SHEETNAME ).SetText( newSheetname );
338 
339  // change all field positions from relative to absolute
340  for( unsigned i = 0; i < m_fields->size(); ++i )
341  m_fields->at( i ).Offset( m_sheet->GetPosition() );
342 
345 
347 
349 
350  COLOR_SETTINGS* colorSettings = m_frame->GetColorSettings();
351 
352  if( colorSettings->GetOverrideSchItemColors()
355  {
356  wxPanel temp( this );
357  temp.Hide();
358  PANEL_EESCHEMA_COLOR_SETTINGS prefs( m_frame, &temp );
359  wxString checkboxLabel = prefs.m_optOverrideColors->GetLabel();
360 
361  KIDIALOG dlg( this, _( "Note: item colors are overridden in the current color theme." ),
363  dlg.ShowDetailedText( wxString::Format( _( "To see individual item colors uncheck '%s'\n"
364  "in Preferences > Eeschema > Colors." ),
365  checkboxLabel ) );
366  dlg.DoNotShowCheckbox( __FILE__, __LINE__ );
367  dlg.ShowModal();
368  }
369 
372 
374  SCH_SHEET_PATH instance = m_frame->GetCurrentSheet();
375 
376  instance.push_back( m_sheet );
377 
378  if( m_sheet->IsNew() )
379  m_sheet->AddInstance( instance.Path() );
380 
381  m_sheet->SetPageNumber( instance, m_pageNumberTextCtrl->GetValue() );
382 
384 
385  // Refresh all sheets in case ordering changed.
386  for( SCH_ITEM* item : m_frame->GetScreen()->Items().OfType( SCH_SHEET_T ) )
387  m_frame->UpdateItem( item );
388 
389  m_frame->OnModify();
390 
391  return true;
392 }
393 
394 
395 bool DIALOG_SHEET_PROPERTIES::onSheetFilenameChanged( const wxString& aNewFilename )
396 {
397  wxString msg;
398 
399  // Sheet file names are relative to the path of the current sheet. This allows for
400  // nesting of schematic files in subfolders. Screen file names are always absolute.
401  wxFileName sheetFileName( aNewFilename );
402 
403  if( sheetFileName.GetExt().IsEmpty() )
404  {
405  sheetFileName.SetExt( KiCadSchematicFileExtension );
406  }
407  else if( sheetFileName.GetExt().CmpNoCase( KiCadSchematicFileExtension ) != 0 )
408  {
409  msg = wxString::Format( _( "The file '%s' does not appear to be a valid schematic file." ),
410  sheetFileName.GetFullName() );
411  wxMessageDialog badSchFileDialog( this, msg, _( "Invalid Schematic File" ),
412  wxOK | wxCENTRE | wxICON_EXCLAMATION );
413  badSchFileDialog.ShowModal();
414  return false;
415  }
416 
417  wxFileName screenFileName( sheetFileName );
418  wxFileName tmp( sheetFileName );
419 
420  SCH_SCREEN* currentScreen = m_frame->GetCurrentSheet().LastScreen();
421 
422  wxCHECK( currentScreen, false );
423 
424  // SCH_SCREEN file names are always absolute.
425  wxFileName currentScreenFileName = currentScreen->GetFileName();
426 
427  if( !screenFileName.Normalize( wxPATH_NORM_ALL, currentScreenFileName.GetPath() ) )
428  {
429  msg = wxString::Format( _( "Cannot normalize new sheet schematic file path:\n"
430  "'%s'\n"
431  "against parent sheet schematic file path:\n"
432  "'%s'." ),
433  sheetFileName.GetPath(),
434  currentScreenFileName.GetPath() );
435  DisplayError( this, msg );
436  return false;
437  }
438 
439  wxString newAbsoluteFilename = screenFileName.GetFullPath();
440 
441  // Inside Eeschema, filenames are stored using unix notation
442  newAbsoluteFilename.Replace( wxT( "\\" ), wxT( "/" ) );
443 
444  bool renameFile = false;
445  bool loadFromFile = false;
446  bool clearAnnotation = false;
447  bool restoreSheet = false;
448  bool isExistingSheet = false;
449  SCH_SCREEN* useScreen = nullptr;
450 
451  // Search for a schematic file having the same filename already in use in the hierarchy
452  // or on disk, in order to reuse it.
453  if( !m_frame->Schematic().Root().SearchHierarchy( newAbsoluteFilename, &useScreen ) )
454  {
455  loadFromFile = wxFileExists( newAbsoluteFilename );
456 
457  wxLogTrace( tracePathsAndFiles, "\n Sheet requested file '%s', %s",
458  newAbsoluteFilename,
459  loadFromFile ? "found" : "not found" );
460  }
461 
462  if( m_sheet->GetScreen() == nullptr ) // New just created sheet.
463  {
464  if( !m_frame->AllowCaseSensitiveFileNameClashes( newAbsoluteFilename ) )
465  return false;
466 
467  if( useScreen || loadFromFile ) // Load from existing file.
468  {
469  clearAnnotation = true;
470 
471  if( !IsOK( this, wxString::Format( _( "'%s' already exists." ),
472  sheetFileName.GetFullName() )
473  + wxT( "\n\n" )
474  + wxString::Format( _( "Link '%s' to this file?" ),
475  newAbsoluteFilename ) ) )
476  {
477  return false;
478  }
479  }
480  else // New file.
481  {
482  m_frame->InitSheet( m_sheet, newAbsoluteFilename );
483  }
484  }
485  else // Existing sheet.
486  {
487  bool isUndoable = true;
488  isExistingSheet = true;
489 
490  if( !m_frame->AllowCaseSensitiveFileNameClashes( newAbsoluteFilename ) )
491  return false;
492 
493  // We are always using here a case insensitive comparison to avoid issues
494  // under Windows, although under Unix filenames are case sensitive.
495  // But many users create schematic under both Unix and Windows
496  // **
497  // N.B. 1: aSheet->GetFileName() will return a relative path
498  // aSheet->GetScreen()->GetFileName() returns a full path
499  //
500  // N.B. 2: newFilename uses the unix notation for separator.
501  // so we must use it also to compare the old and new filenames
502  wxString oldAbsoluteFilename = m_sheet->GetScreen()->GetFileName();
503  oldAbsoluteFilename.Replace( wxT( "\\" ), wxT( "/" ) );
504 
505  if( newAbsoluteFilename.Cmp( oldAbsoluteFilename ) != 0 )
506  {
507  // Sheet file name changes cannot be undone.
508  isUndoable = false;
509 
510  if( useScreen || loadFromFile ) // Load from existing file.
511  {
512  clearAnnotation = true;
513 
514  if( !IsOK( this, wxString::Format( _( "Change '%s' link from '%s' to '%s'?" ),
515  newAbsoluteFilename,
516  m_sheet->GetFileName(),
517  sheetFileName.GetFullName() )
518  + wxT( "\n\n" )
519  + _( "This action cannot be undone." ) ) )
520  {
521  return false;
522  }
523 
524  if( loadFromFile )
525  m_sheet->SetScreen( nullptr );
526  }
527  else // Save to new file name.
528  {
529  if( m_sheet->GetScreenCount() > 1 )
530  {
531  if( !IsOK( this, wxString::Format( _( "Create new file '%s' with contents of '%s'?" ),
532  sheetFileName.GetFullName(),
533  m_sheet->GetFileName() )
534  + wxT( "\n\n" )
535  + _( "This action cannot be undone." ) ) )
536  {
537  return false;
538  }
539  }
540 
541  renameFile = true;
542  }
543  }
544 
545  if( isUndoable )
547 
548  if( renameFile )
549  {
550  SCH_PLUGIN::SCH_PLUGIN_RELEASER pi( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
551 
552  // If the associated screen is shared by more than one sheet, do not
553  // change the filename of the corresponding screen here.
554  // (a new screen will be created later)
555  // if it is not shared, update the filename
556  if( m_sheet->GetScreenCount() <= 1 )
557  m_sheet->GetScreen()->SetFileName( newAbsoluteFilename );
558 
559  try
560  {
561  pi->Save( newAbsoluteFilename, m_sheet, &m_frame->Schematic() );
562  }
563  catch( const IO_ERROR& ioe )
564  {
565  msg = wxString::Format( _( "Error occurred saving schematic file '%s'." ),
566  newAbsoluteFilename );
567  DisplayErrorMessage( this, msg, ioe.What() );
568 
569  msg = wxString::Format( _( "Failed to save schematic '%s'" ),
570  newAbsoluteFilename );
571  m_frame->SetMsgPanel( wxEmptyString, msg );
572  return false;
573  }
574 
575  // If the associated screen is shared by more than one sheet, remove the
576  // screen and reload the file to a new screen. Failure to do this will trash
577  // the screen reference counting in complex hierarchies.
578  if( m_sheet->GetScreenCount() > 1 )
579  {
580  m_sheet->SetScreen( nullptr );
581  loadFromFile = true;
582  }
583  }
584  }
585 
586  SCH_SHEET_PATH& currentSheet = m_frame->GetCurrentSheet();
587 
588  if( useScreen )
589  {
590  // Create a temporary sheet for recursion testing to prevent a possible recursion error.
591  std::unique_ptr< SCH_SHEET> tmpSheet = std::make_unique<SCH_SHEET>();
592  tmpSheet->GetFields()[SHEETNAME] = m_fields->at( SHEETNAME );
593  tmpSheet->GetFields()[SHEETFILENAME].SetText( sheetFileName.GetFullPath() );
594  tmpSheet->SetScreen( useScreen );
595 
596  // No need to check for valid library IDs if we are using an existing screen.
597  if( m_frame->CheckSheetForRecursion( tmpSheet.get(), &currentSheet ) )
598  {
599  if( restoreSheet )
600  currentSheet.LastScreen()->Append( m_sheet );
601 
602  return false;
603  }
604 
605  // It's safe to set the sheet screen now.
606  m_sheet->SetScreen( useScreen );
607  }
608  else if( loadFromFile )
609  {
610  if( isExistingSheet )
611  {
612  // Temporarily remove the sheet from the current schematic page so that recursion
613  // and symbol library link tests can be performed with the modified sheet settings.
614  restoreSheet = true;
615  currentSheet.LastScreen()->Remove( m_sheet );
616  }
617 
618  if( !m_frame->LoadSheetFromFile( m_sheet, &currentSheet, newAbsoluteFilename )
619  || m_frame->CheckSheetForRecursion( m_sheet, &currentSheet ) )
620  {
621  if( restoreSheet )
622  currentSheet.LastScreen()->Append( m_sheet );
623 
624  return false;
625  }
626 
627  if( restoreSheet )
628  currentSheet.LastScreen()->Append( m_sheet );
629  }
630 
632  *m_clearAnnotationNewItems = clearAnnotation;
633 
634  return true;
635 }
636 
637 
639 {
640  bool success = true;
641  wxGridCellEditor* editor = m_grid->GetCellEditor( event.GetRow(), event.GetCol() );
642  wxControl* control = editor->GetControl();
643  wxTextEntry* textControl = dynamic_cast<wxTextEntry*>( control );
644 
645  // Short-circuit the validator's more generic "can't be empty" message for the
646  // two mandatory fields:
647  if( event.GetRow() == SHEETNAME && event.GetCol() == FDC_VALUE )
648  {
649  if( textControl && textControl->IsEmpty() )
650  {
651  wxMessageBox( _( "A sheet must have a name." ) );
652  success = false;
653  }
654  }
655  else if( event.GetRow() == SHEETFILENAME && event.GetCol() == FDC_VALUE && textControl )
656  {
657  if( textControl->IsEmpty() )
658  {
659  wxMessageBox( _( "A sheet must have a file specified." ) );
660  success = false;
661  }
662  }
663 
664  if( success && control && control->GetValidator() )
665  success = control->GetValidator()->Validate( control );
666 
667  if( !success )
668  {
669  event.Veto();
670  m_delayedFocusRow = event.GetRow();
671  m_delayedFocusColumn = event.GetCol();
672  }
673 
674  editor->DecRef();
675 }
676 
677 
678 void DIALOG_SHEET_PROPERTIES::OnAddField( wxCommandEvent& event )
679 {
680  if( !m_grid->CommitPendingChanges() )
681  return;
682 
683  int fieldID = m_fields->size();
684  SCH_FIELD newField( wxPoint( 0, 0 ), fieldID, m_sheet,
685  SCH_SHEET::GetDefaultFieldName( fieldID ) );
686 
687  newField.SetTextAngle( m_fields->at( SHEETNAME ).GetTextAngle() );
688 
689  m_fields->push_back( newField );
690 
691  // notify the grid
692  wxGridTableMessage msg( m_fields, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, 1 );
693  m_grid->ProcessTableMessage( msg );
694 
695  m_grid->MakeCellVisible( m_fields->size() - 1, 0 );
696  m_grid->SetGridCursor( m_fields->size() - 1, 0 );
697 
698  m_grid->EnableCellEditControl();
699  m_grid->ShowCellEditControl();
700 }
701 
702 
703 void DIALOG_SHEET_PROPERTIES::OnDeleteField( wxCommandEvent& event )
704 {
705  int curRow = m_grid->GetGridCursorRow();
706 
707  if( curRow < 0 )
708  return;
709  else if( curRow < SHEET_MANDATORY_FIELDS )
710  {
711  DisplayError( this, wxString::Format( _( "The first %d fields are mandatory." ),
713  return;
714  }
715 
716  m_grid->CommitPendingChanges( true /* quiet mode */ );
717 
718  m_fields->erase( m_fields->begin() + curRow );
719 
720  // notify the grid
721  wxGridTableMessage msg( m_fields, wxGRIDTABLE_NOTIFY_ROWS_DELETED, curRow, 1 );
722  m_grid->ProcessTableMessage( msg );
723 
724  if( m_grid->GetNumberRows() > 0 )
725  {
726  m_grid->MakeCellVisible( std::max( 0, curRow-1 ), m_grid->GetGridCursorCol() );
727  m_grid->SetGridCursor( std::max( 0, curRow-1 ), m_grid->GetGridCursorCol() );
728  }
729 }
730 
731 
732 void DIALOG_SHEET_PROPERTIES::OnMoveUp( wxCommandEvent& event )
733 {
734  if( !m_grid->CommitPendingChanges() )
735  return;
736 
737  int i = m_grid->GetGridCursorRow();
738 
739  if( i > SHEET_MANDATORY_FIELDS )
740  {
741  SCH_FIELD tmp = m_fields->at( (unsigned) i );
742  m_fields->erase( m_fields->begin() + i, m_fields->begin() + i + 1 );
743  m_fields->insert( m_fields->begin() + i - 1, tmp );
744  m_grid->ForceRefresh();
745 
746  m_grid->SetGridCursor( i - 1, m_grid->GetGridCursorCol() );
747  m_grid->MakeCellVisible( m_grid->GetGridCursorRow(), m_grid->GetGridCursorCol() );
748  }
749  else
750  wxBell();
751 }
752 
753 
754 void DIALOG_SHEET_PROPERTIES::OnMoveDown( wxCommandEvent& event )
755 {
756  if( !m_grid->CommitPendingChanges() )
757  return;
758 
759  int i = m_grid->GetGridCursorRow();
760 
761  if( i >= SHEET_MANDATORY_FIELDS && i < m_grid->GetNumberRows() - 1 )
762  {
763  SCH_FIELD tmp = m_fields->at( (unsigned) i );
764  m_fields->erase( m_fields->begin() + i, m_fields->begin() + i + 1 );
765  m_fields->insert( m_fields->begin() + i + 1, tmp );
766  m_grid->ForceRefresh();
767 
768  m_grid->SetGridCursor( i + 1, m_grid->GetGridCursorCol() );
769  m_grid->MakeCellVisible( m_grid->GetGridCursorRow(), m_grid->GetGridCursorCol() );
770  }
771  else
772  wxBell();
773 }
774 
775 
777 {
778  m_width = aWidth;
779  // Account for scroll bars
780  aWidth -= ( m_grid->GetSize().x - m_grid->GetClientSize().x );
781 
782  m_grid->AutoSizeColumn( 0 );
783 
784  int fixedColsWidth = m_grid->GetColSize( 0 );
785 
786  for( int i = 2; i < m_grid->GetNumberCols(); i++ )
787  fixedColsWidth += m_grid->GetColSize( i );
788 
789  m_grid->SetColSize( 1, aWidth - fixedColsWidth );
790 }
791 
792 
793 void DIALOG_SHEET_PROPERTIES::OnUpdateUI( wxUpdateUIEvent& event )
794 {
795  wxString shownColumns = m_grid->GetShownColumns();
796 
797  if( shownColumns != m_shownColumns )
798  {
799  m_shownColumns = shownColumns;
800 
801  if( !m_grid->IsCellEditControlShown() )
802  AdjustGridColumns( m_grid->GetRect().GetWidth() );
803  }
804 
805  // Propagate changes in sheetname to displayed hierarchical path
806  wxString hierarchicalPath = _( "Hierarchical path: " );
807 
808  hierarchicalPath += m_frame->GetCurrentSheet().PathHumanReadable( false );
809 
810  if( hierarchicalPath.Last() != '/' )
811  hierarchicalPath.Append( '/' );
812 
813  wxGridCellEditor* editor = m_grid->GetCellEditor( SHEETNAME, FDC_VALUE );
814  wxControl* control = editor->GetControl();
815  wxTextEntry* textControl = dynamic_cast<wxTextEntry*>( control );
816  wxString sheetName;
817 
818  if( textControl )
819  sheetName = textControl->GetValue();
820  else
821  sheetName = m_grid->GetCellValue( SHEETNAME, FDC_VALUE );
822 
824  m_dummySheetNameField.SetText( sheetName );
825  hierarchicalPath += m_dummySheetNameField.GetShownText();
826 
827  editor->DecRef();
828 
829  if( m_hierarchicalPathLabel->GetLabel() != hierarchicalPath )
830  m_hierarchicalPathLabel->SetLabel( hierarchicalPath );
831 
832  // Handle a delayed focus
833  if( m_delayedFocusRow >= 0 )
834  {
835  m_grid->SetFocus();
836  m_grid->MakeCellVisible( m_delayedFocusRow, m_delayedFocusColumn );
837  m_grid->SetGridCursor( m_delayedFocusRow, m_delayedFocusColumn );
838 
839 
840  m_grid->EnableCellEditControl( true );
841  m_grid->ShowCellEditControl();
842 
843  m_delayedFocusRow = -1;
845  }
846 }
847 
848 
849 void DIALOG_SHEET_PROPERTIES::OnSizeGrid( wxSizeEvent& event )
850 {
851  auto new_size = event.GetSize().GetX();
852 
853  if( m_width != new_size )
854  {
855  AdjustGridColumns( new_size );
856  }
857 
858  // Always propagate for a grid repaint (needed if the height changes, as well as width)
859  event.Skip();
860 }
861 
862 
863 void DIALOG_SHEET_PROPERTIES::OnInitDlg( wxInitDialogEvent& event )
864 {
866 
867  // Now all widgets have the size fixed, call FinishDialogSettings
869 }
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:252
A container for handling SCH_SHEET_PATH objects in a flattened hierarchy.
EE_TYPE OfType(KICAD_T aType) const
Definition: sch_rtree.h:216
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:259
Functions for manipulating tab traversal in forms and dialogs.
const wxString & GetFileName() const
Definition: sch_screen.h:137
void DoNotShowCheckbox(wxString file, int line)
Checks the 'do not show again' setting for the dialog.
Definition: confirm.cpp:55
void SetSwatchColor(KIGFX::COLOR4D aColor, bool aSendEvent)
Set the current swatch color directly.
bool Remove(SCH_ITEM *aItem)
Remove aItem from the schematic associated with this screen.
Definition: sch_screen.cpp:260
bool SearchHierarchy(const wxString &aFilename, SCH_SCREEN **aScreen)
Search the existing hierarchy for an instance of screen loaded from aFileName.
Definition: sch_sheet.cpp:630
void UpdateItem(EDA_ITEM *aItem, bool isAddOrDelete=false)
Mark an item for refresh.
KIGFX::COLOR4D GetSwatchColor() const
EDA_TEXT_VJUSTIFY_T GetVertJustify() const
Definition: eda_text.h:206
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition: confirm.cpp:265
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:96
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
void ShowHideColumns(const wxString &shownColumns)
Show/hide the grid columns based on a tokenized string of shown column indexes.
Definition: wx_grid.cpp:129
void SetDefaultColor(KIGFX::COLOR4D aColor)
Sets the color that will be chosen with the "Reset to Default" button in the chooser.
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:713
KIID_PATH Path() const
Get the sheet path as an KIID_PATH.
void SetScreen(SCH_SCREEN *aScreen)
Set the SCH_SCREEN associated with this sheet to aScreen.
Definition: sch_sheet.cpp:157
const wxChar *const tracePathsAndFiles
Flag to enable path and file name debug output.
double GetTextAngle() const
Definition: eda_text.h:181
int GetId() const
Definition: sch_field.h:113
void InitSheet(SCH_SHEET *aSheet, const wxString &aNewFilename)
Definition: sheet.cpp:97
KIGFX::COLOR4D GetBorderColor() const
Definition: sch_sheet.h:111
Schematic editor (Eeschema) main window.
void SetSwatchBackground(KIGFX::COLOR4D aBackground)
Set the swatch background color.
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:65
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:103
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:1136
wxString GetShownText(int aDepth=0) const override
Return the string actually shown after processing of the base text.
Definition: sch_field.cpp:106
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:271
bool IsNew() const
Definition: eda_item.h:119
KIGFX::COLOR4D GetBackgroundColor() const
Definition: sch_sheet.h:114
void SetBorderWidth(int aWidth)
Definition: sch_sheet.h:109
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:97
std::vector< SCH_FIELD > & GetFields()
Definition: sch_sheet.h:88
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:180
int GetBorderWidth() const
Definition: sch_sheet.h:108
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:115
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:205
virtual void SetText(const wxString &aText)
Definition: eda_text.cpp:114
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.
KIFACE_I & Kiface()
Global KIFACE_I "get" accessor.
SCHEMATIC & Schematic() const
void SetBorderColor(KIGFX::COLOR4D aColor)
Definition: sch_sheet.h:112
Definition of file extensions used in Kicad.
static bool positioningChanged(const SCH_FIELD &a, const SCH_FIELD &b)
void ClearFieldsAutoplaced()
Definition: sch_item.h:436
Class DIALOG_SHEET_PROPERTIES_BASE.
#define _(s)
wxLogTrace helper definitions.
static const wxString GetDefaultFieldName(int aFieldNdx)
Definition: sch_sheet.cpp:47
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:182
wxString GetFileName() const
Return the filename corresponding to this sheet.
Definition: sch_sheet.h:315
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:473
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:110
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:144
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:548
EE_RTREE & Items()
Definition: sch_screen.h:102
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:174
void OnSizeGrid(wxSizeEvent &event) override
void SetFileName(const wxString &aFileName)
Set the file name for this screen to aFileName.
Definition: sch_screen.cpp:108
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:197
bool IsOK(wxWindow *aParent, const wxString &aMessage)
Display a yes/no dialog with aMessage and returns the user response.
Definition: confirm.cpp:296
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition: eda_text.h:133
Custom text control validator definitions.
SCH_SHEET_LIST & GetFullHierarchy() const
Return the full schematic flattened hierarchical sheet list.
Definition: schematic.cpp:358
wxString GetPageNumber(const SCH_SHEET_PATH &aInstance) const
Return the sheet page number for aInstance.
Definition: sch_sheet.cpp:1118
bool AddInstance(const KIID_PATH &aInstance)
Add a new instance aSheetPath to the instance list.
Definition: sch_sheet.cpp:1094
A color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:103
void OnAddField(wxCommandEvent &event) override