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