KiCad PCB EDA Suite
sheet.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) 2015 Jean-Pierre Charras, jp.charras at wanadoo.fr
5  * Copyright (C) 2004-2019 KiCad Developers, see change_log.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 
25 #include <fctsys.h>
26 #include <sch_draw_panel.h>
27 #include <confirm.h>
28 #include <kiface_i.h>
29 #include <project.h>
31 #include <tool/tool_manager.h>
32 #include <wx/clipbrd.h>
33 #include <sch_edit_frame.h>
34 #include <sch_legacy_plugin.h>
35 #include <sch_sheet.h>
36 #include <sch_sheet_path.h>
37 #include <sch_view.h>
38 #include <symbol_lib_table.h>
41 #include <tool/actions.h>
42 
43 
45 {
46  wxASSERT( aSheet && aHierarchy );
47 
48  wxString msg;
49  SCH_SHEET_LIST hierarchy( g_RootSheet ); // This is the full schematic sheet hierarchy.
50  SCH_SHEET_LIST sheetHierarchy( aSheet ); // This is the hierarchy of the loaded file.
51 
52  wxFileName destFile = aHierarchy->LastScreen()->GetFileName();
53 
54  // SCH_SCREEN object file paths are expected to be absolute. If this assert fires,
55  // something is seriously broken.
56  wxASSERT( destFile.IsAbsolute() );
57 
58  if( hierarchy.TestForRecursion( sheetHierarchy, destFile.GetFullPath() ) )
59  {
60  msg.Printf( _( "The sheet changes cannot be made because the destination sheet already "
61  "has the sheet \"%s\" or one of it's subsheets as a parent somewhere in "
62  "the schematic hierarchy." ),
63  destFile.GetFullPath() );
64  DisplayError( this, msg );
65  return true;
66  }
67 
68  return false;
69 }
70 
71 
73 {
74  wxASSERT( aSheet && aSheet->GetScreen() );
75 
76  wxString msg;
77  SCH_SCREENS newScreens( aSheet );
78 
79  if( newScreens.HasNoFullyDefinedLibIds() )
80  {
81  msg.Printf( _( "The schematic \"%s\" has not had it's symbol library links remapped "
82  "to the symbol library table. The project this schematic belongs to "
83  "must first be remapped before it can be imported into the current "
84  "project." ), aSheet->GetScreen()->GetFileName() );
85  DisplayInfoMessage( this, msg );
86  return true;
87  }
88 
89  return false;
90 }
91 
92 
93 void SCH_EDIT_FRAME::InitSheet( SCH_SHEET* aSheet, const wxString& aNewFilename )
94 {
95  aSheet->SetScreen( new SCH_SCREEN( &Kiway() ) );
96  aSheet->GetScreen()->SetModify();
98  aSheet->GetScreen()->SetFileName( aNewFilename );
99 }
100 
101 
103  const wxString& aFileName )
104 {
105  wxASSERT( aSheet && aHierarchy );
106 
107  wxString msg;
108  wxString topLevelSheetPath;
109  wxFileName tmp;
110  wxFileName currentSheetFileName;
111  bool libTableChanged = false;
112  SCH_SCREEN* currentScreen = aHierarchy->LastScreen();
113  SCH_PLUGIN::SCH_PLUGIN_RELEASER pi( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_LEGACY ) );
114  std::unique_ptr< SCH_SHEET> newSheet( new SCH_SHEET );
115 
116  wxFileName fileName( aFileName );
117 
118  if( !fileName.IsAbsolute() )
119  {
120  wxCHECK_MSG( fileName.MakeAbsolute(), false,
121  wxString::Format( "Cannot make file name \"%s\" path absolute.",
122  aFileName ) );
123  }
124 
125  wxString fullFilename = fileName.GetFullPath();
126 
127  try
128  {
129  if( aSheet->GetScreen() != nullptr )
130  {
131  newSheet.reset( pi->Load( fullFilename, &Kiway() ) );
132  }
133  else
134  {
135  newSheet->SetFileName( fullFilename );
136  pi->Load( fullFilename, &Kiway(), newSheet.get() );
137  }
138 
139  if( !pi->GetError().IsEmpty() )
140  {
141  msg = _( "The entire schematic could not be loaded. Errors occurred attempting "
142  "to load hierarchical sheet schematics." );
143 
144  wxMessageDialog msgDlg1( this, msg, _( "Schematic Load Error" ),
145  wxOK | wxCANCEL | wxCANCEL_DEFAULT |
146  wxCENTER | wxICON_QUESTION );
147  msgDlg1.SetOKLabel( wxMessageDialog::ButtonLabel( _( "Use partial schematic" ) ) );
148  msgDlg1.SetExtendedMessage( pi->GetError() );
149 
150  if( msgDlg1.ShowModal() == wxID_CANCEL )
151  return false;
152  }
153  }
154  catch( const IO_ERROR& ioe )
155  {
156  msg.Printf( _( "Error occurred loading schematic file \"%s\"." ), fullFilename );
157  DisplayErrorMessage( this, msg, ioe.What() );
158 
159  msg.Printf( _( "Failed to load schematic \"%s\"" ), fullFilename );
160  AppendMsgPanel( wxEmptyString, msg, CYAN );
161 
162  return false;
163  }
164 
165  tmp = fileName;
166 
167  // If the loaded schematic is in a different folder from the current project and
168  // it contains hierarchical sheets, the hierarchical sheet paths need to be updated.
169  if( fileName.GetPath( wxPATH_GET_SEPARATOR ) != Prj().GetProjectPath()
170  && newSheet->CountSheets() )
171  {
172  // Give the user the option to choose relative path if possible.
173  if( tmp.MakeRelativeTo( Prj().GetProjectPath() ) )
174  {
175  wxMessageDialog msgDlg2(
176  this,
177  "Do you want to use a relative path to the loaded "
178  "schematic?", "Select Path Type",
179  wxYES_NO | wxCANCEL | wxYES_DEFAULT | wxICON_QUESTION | wxCENTER );
180  msgDlg2.SetYesNoLabels( wxMessageDialog::ButtonLabel( "Use Relative Path" ),
181  wxMessageDialog::ButtonLabel( "Use Absolute Path" ) );
182  int rsp = msgDlg2.ShowModal();
183 
184  if( rsp == wxID_CANCEL )
185  {
186  return false;
187  }
188  else if( rsp == wxID_NO )
189  {
190  topLevelSheetPath = fileName.GetPathWithSep();
191  }
192  else
193  {
194  topLevelSheetPath = tmp.GetPathWithSep();
195  }
196  }
197  else
198  {
199  topLevelSheetPath = tmp.GetPathWithSep();
200  }
201 
202  if( wxFileName::GetPathSeparator() == '\\' )
203  topLevelSheetPath.Replace( "\\", "/" );
204  }
205 
206  // Make sure any new sheet changes do not cause any recursion issues.
207  SCH_SHEET_LIST hierarchy( g_RootSheet ); // This is the schematic sheet hierarchy.
208  SCH_SHEET_LIST sheetHierarchy( newSheet.get() ); // This is the hierarchy of the loaded file.
209 
210  if( checkSheetForRecursion( newSheet.get(), aHierarchy )
211  || checkForNoFullyDefinedLibIds( newSheet.get() ) )
212  return false;
213 
214  // Make a valiant attempt to warn the user of all possible scenarios where there could
215  // be broken symbol library links.
216  wxArrayString names;
217  wxArrayString newLibNames;
218  SCH_SCREENS newScreens( newSheet.get() ); // All screens associated with the import.
219  SCH_SCREENS prjScreens( g_RootSheet );
220 
221  newScreens.GetLibNicknames( names );
222 
223  wxMessageDialog::ButtonLabel okButtonLabel( _( "Continue Load" ) );
224  wxMessageDialog::ButtonLabel cancelButtonLabel( _( "Cancel Load" ) );
225 
226  if( fileName.GetPath( wxPATH_GET_SEPARATOR ) == Prj().GetProjectPath()
227  && !prjScreens.HasSchematic( fullFilename ) )
228  {
229  // A schematic in the current project path that isn't part of the current project.
230  // It's possible the user copied this schematic from another project so the library
231  // links may not be avaible. Even this is check is no guarantee that all symbol
232  // library links are valid but it's better than nothing.
233  for( const auto& name : names )
234  {
235  if( !Prj().SchSymbolLibTable()->HasLibrary( name ) )
236  newLibNames.Add( name );
237  }
238 
239  if( !newLibNames.IsEmpty() )
240  {
241  msg = _( "There are library names in the loaded schematic that are missing "
242  "from the project library table. This may result in broken symbol "
243  "library links for the loaded schematic. Do you wish to continue?" );
244  wxMessageDialog msgDlg3( this, msg, _( "Continue Load Schematic" ),
245  wxOK | wxCANCEL | wxCANCEL_DEFAULT |
246  wxCENTER | wxICON_QUESTION );
247  msgDlg3.SetOKCancelLabels( okButtonLabel, cancelButtonLabel );
248 
249  if( msgDlg3.ShowModal() == wxID_CANCEL )
250  return false;
251  }
252  }
253  else if( fileName.GetPath( wxPATH_GET_SEPARATOR ) != Prj().GetProjectPath() )
254  {
255  // A schematic loaded from a path other than the current project path.
256 
257  // If there are symbol libraries in the imported schematic that are not in the
258  // symbol library table of this project, there could be a lot of broken symbol
259  // library links. Attempt to add the missing libraries to the project symbol
260  // library table.
261  wxArrayString duplicateLibNames;
262 
263  for( const auto& name : names )
264  {
265  if( !Prj().SchSymbolLibTable()->HasLibrary( name ) )
266  newLibNames.Add( name );
267  else
268  duplicateLibNames.Add( name );
269  }
270 
271  SYMBOL_LIB_TABLE table;
272  wxFileName symLibTableFn( fileName.GetPath(),
274 
275  // If there are any new or duplicate libraries, check to see if it's possible that
276  // there could be any missing libraries that would cause broken symbol library links.
277  if( !newLibNames.IsEmpty() || !duplicateLibNames.IsEmpty() )
278  {
279  if( !symLibTableFn.Exists() || !symLibTableFn.IsFileReadable() )
280  {
281  msg.Printf( _( "The project library table \"%s\" does not exist or cannot "
282  "be read. This may result in broken symbol links for the "
283  "schematic. Do you wish to continue?" ),
284  fileName.GetFullPath() );
285  wxMessageDialog msgDlg4( this, msg, _( "Continue Load Schematic" ),
286  wxOK | wxCANCEL | wxCANCEL_DEFAULT |
287  wxCENTER | wxICON_QUESTION );
288  msgDlg4.SetOKCancelLabels( okButtonLabel, cancelButtonLabel );
289 
290  if( msgDlg4.ShowModal() == wxID_CANCEL )
291  return false;
292  }
293  else
294  {
295  try
296  {
297  table.Load( symLibTableFn.GetFullPath() );
298  }
299  catch( const IO_ERROR& ioe )
300  {
301  msg.Printf( _( "An error occurred loading the symbol library table "
302  "\"%s\"." ),
303  symLibTableFn.GetFullPath() );
304  DisplayErrorMessage( NULL, msg, ioe.What() );
305  return false;
306  }
307  }
308  }
309 
310  // Check to see if any of the symbol libraries found in the appended schematic do
311  // not exist in the current project are missing from the appended project symbol
312  // library table.
313  if( !newLibNames.IsEmpty() )
314  {
315  bool missingLibNames = table.IsEmpty();
316 
317  if( !missingLibNames )
318  {
319  for( const auto& newLibName : newLibNames )
320  {
321  if( !table.HasLibrary( newLibName ) )
322  {
323  missingLibNames = true;
324  break;
325  }
326  }
327  }
328 
329  if( missingLibNames )
330  {
331  msg = _( "There are library names in the loaded schematic that are missing "
332  "from the loaded schematic project library table. This may result "
333  "in broken symbol library links for the schematic. "
334  "Do you wish to continue?" );
335  wxMessageDialog msgDlg5( this, msg, _( "Continue Load Schematic" ),
336  wxOK | wxCANCEL | wxCANCEL_DEFAULT |
337  wxCENTER | wxICON_QUESTION );
338  msgDlg5.SetOKCancelLabels( okButtonLabel, cancelButtonLabel );
339 
340  if( msgDlg5.ShowModal() == wxID_CANCEL )
341  return false;
342  }
343  }
344 
345  // The library name already exists in the current project. Check to see if the
346  // duplicate name is the same library in the current project. If it's not, it's
347  // most likely that the symbol library links will be broken.
348  if( !duplicateLibNames.IsEmpty() && !table.IsEmpty() )
349  {
350  bool libNameConflict = false;
351 
352  for( const auto& duplicateLibName : duplicateLibNames )
353  {
354  const SYMBOL_LIB_TABLE_ROW* thisRow = nullptr;
355  const SYMBOL_LIB_TABLE_ROW* otherRow = nullptr;
356 
357  if( Prj().SchSymbolLibTable()->HasLibrary( duplicateLibName ) )
358  thisRow = Prj().SchSymbolLibTable()->FindRow( duplicateLibName );
359 
360  if( table.HasLibrary( duplicateLibName ) )
361  otherRow = table.FindRow( duplicateLibName );
362 
363  // It's in the global library table so there is no conflict.
364  if( thisRow && !otherRow )
365  continue;
366 
367  if( !thisRow || !otherRow )
368  continue;
369 
370  wxFileName otherUriFileName;
371  wxString thisURI = thisRow->GetFullURI( true );
372  wxString otherURI = otherRow->GetFullURI( false);
373 
374  if( otherURI.Contains( "${KIPRJMOD}" ) || otherURI.Contains( "$(KIPRJMOD)" ) )
375  {
376  // Cannot use relative paths here, "${KIPRJMOD}../path-to-cache-lib" does
377  // not expand to a valid symbol library path.
378  otherUriFileName.SetPath( fileName.GetPath() );
379  otherUriFileName.SetFullName( otherURI.AfterLast( '}' ) );
380  otherURI = otherUriFileName.GetFullPath();
381  }
382 
383  if( thisURI != otherURI )
384  {
385  libNameConflict = true;
386  break;
387  }
388  }
389 
390  if( libNameConflict )
391  {
392  msg = _( "A duplicate library name that references a different library exists "
393  "in the current library table. This conflict cannot be resolved and "
394  "may result in broken symbol library links for the schematic. "
395  "Do you wish to continue?" );
396  wxMessageDialog msgDlg6( this, msg, _( "Continue Load Schematic" ),
397  wxOK | wxCANCEL | wxCANCEL_DEFAULT |
398  wxCENTER | wxICON_QUESTION );
399  msgDlg6.SetOKCancelLabels( okButtonLabel, cancelButtonLabel );
400 
401  if( msgDlg6.ShowModal() == wxID_CANCEL )
402  return false;
403  }
404  }
405 
406  // All (most?) of the possible broken symbol library link cases are covered. Map the
407  // new appended schematic project symbol library table entries to the current project
408  // symbol library table.
409  if( !newLibNames.IsEmpty() && !table.IsEmpty() )
410  {
411  for( const auto& libName : newLibNames )
412  {
413  if( !table.HasLibrary( libName )
414  || Prj().SchSymbolLibTable()->HasLibrary( libName ) )
415  continue;
416 
417  // Don't expand environment variable because KIPRJMOD will not be correct
418  // for a different project.
419  wxString uri = table.GetFullURI( libName, false );
420  wxFileName newLib;
421 
422  if( uri.Contains( "${KIPRJMOD}" ) || uri.Contains( "$(KIPRJMOD)" ) )
423  {
424  // Cannot use relative paths here, "${KIPRJMOD}../path-to-cache-lib" does
425  // not expand to a valid symbol library path.
426  newLib.SetPath( fileName.GetPath() );
427  newLib.SetFullName( uri.AfterLast( '}' ) );
428  uri = newLib.GetFullPath();
429  }
430  else
431  {
432  uri = table.GetFullURI( libName );
433  }
434 
435  // Add the library from the imported project to the current project
436  // symbol library table.
437  const SYMBOL_LIB_TABLE_ROW* row = table.FindRow( libName );
438 
439  auto newRow = new SYMBOL_LIB_TABLE_ROW( libName, uri, row->GetType(),
440  row->GetOptions(), row->GetDescr() );
441 
442  Prj().SchSymbolLibTable()->InsertRow( newRow );
443  libTableChanged = true;
444  }
445  }
446  }
447 
448  // Check for duplicate sheet names in the current page.
449  wxArrayString duplicateSheetNames;
450 
451  for( auto item : currentScreen->Items().OfType( SCH_SHEET_T ) )
452  {
453  auto sheet = static_cast<SCH_SHEET*>( item );
454 
455  if( newSheet->GetScreen()->GetSheet( sheet->GetName() ) )
456  duplicateSheetNames.Add( sheet->GetName() );
457  }
458 
459  if( !duplicateSheetNames.IsEmpty() )
460  {
461  msg.Printf( "Duplicate sheet names exist on the current page. Do you want to "
462  "automatically rename the duplicate sheet names?" );
463 
464  if( !IsOK( this, msg ) )
465  return false;
466  }
467 
468  // Rename all duplicate sheet names.
469  SCH_SCREEN* newScreen = newSheet->GetScreen();
470  wxCHECK_MSG( newScreen, false, "No screen defined for sheet." );
471 
472  for( const auto& duplicateName : duplicateSheetNames )
473  {
474  SCH_SHEET* renamedSheet = newScreen->GetSheet( duplicateName );
475 
476  wxCHECK2_MSG( renamedSheet, continue,
477  "Sheet " + duplicateName + " not found in imported schematic." );
478 
479  timestamp_t newtimestamp = GetNewTimeStamp();
480  renamedSheet->SetTimeStamp( newtimestamp );
481  renamedSheet->SetName( wxString::Format( "Sheet%8.8lX", (unsigned long) newtimestamp ) );
482  }
483 
484  // Set all sheets loaded into the correct sheet file paths.
485 
486  for( auto aItem : currentScreen->Items().OfType( SCH_SHEET_T ) )
487  {
488  auto sheet = static_cast<SCH_SHEET*>( aItem );
489  sheet->SetFileName( topLevelSheetPath + sheet->GetFileName() );
490  }
491 
492  if( libTableChanged )
493  Prj().SchSymbolLibTable()->Save( Prj().GetProjectPath() +
495 
496  // It is finally safe to add or append the imported schematic.
497  if( aSheet->GetScreen() == nullptr )
498  aSheet->SetScreen( newScreen );
499  else
500  aSheet->GetScreen()->Append( newScreen );
501 
502  SCH_SCREENS allScreens;
503  allScreens.ReplaceDuplicateTimeStamps();
504 
505  SCH_SCREENS screens( aSheet );
506  screens.UpdateSymbolLinks( true );
507 
508  return true;
509 }
510 
511 
513  bool* aClearAnnotationNewItems )
514 {
515  if( aSheet == NULL || aHierarchy == NULL )
516  return false;
517 
518  // Get the new texts
519  DIALOG_SCH_SHEET_PROPS dlg( this, aSheet );
520 
521  if( dlg.ShowModal() == wxID_CANCEL )
522  return false;
523 
524  wxFileName fileName = dlg.GetFileName();
525  fileName.SetExt( SchematicFileExtension );
526 
527  wxString msg;
528  bool renameFile = false;
529  bool loadFromFile = false;
530  bool clearAnnotation = false;
531  bool restoreSheet = false;
532  bool isExistingSheet = false;
533  SCH_SCREEN* useScreen = NULL;
534 
535  // Relative file names are relative to the path of the current sheet. This allows for
536  // nesting of schematic files in subfolders.
537  if( !fileName.IsAbsolute() )
538  {
539  const SCH_SCREEN* currentScreen = aHierarchy->LastScreen();
540 
541  wxCHECK_MSG( currentScreen, false, "Invalid sheet path object." );
542 
543  wxFileName currentSheetFileName = currentScreen->GetFileName();
544 
545  wxCHECK_MSG( fileName.Normalize( wxPATH_NORM_ALL, currentSheetFileName.GetPath() ), false,
546  "Cannot normalize new sheet schematic file path." );
547  }
548 
549  wxString newFilename = fileName.GetFullPath();
550 
551  // Search for a schematic file having the same filename already in use in the hierarchy
552  // or on disk, in order to reuse it.
553  if( !g_RootSheet->SearchHierarchy( newFilename, &useScreen ) )
554  {
555  loadFromFile = wxFileExists( newFilename );
556  wxLogDebug( "Sheet requested file \"%s\", %s",
557  newFilename,
558  ( loadFromFile ) ? "found" : "not found" );
559  }
560 
561  // Inside Eeschema, filenames are stored using unix notation
562  newFilename.Replace( wxT( "\\" ), wxT( "/" ) );
563 
564  if( aSheet->GetScreen() == NULL ) // New sheet.
565  {
566  if( !allowCaseSensitiveFileNameClashes( newFilename ) )
567  return false;
568 
569  if( useScreen || loadFromFile ) // Load from existing file.
570  {
571  clearAnnotation = true;
572 
573  wxString existsMsg;
574  wxString linkMsg;
575  existsMsg.Printf( _( "\"%s\" already exists." ), fileName.GetFullName() );
576  linkMsg.Printf( _( "Link \"%s\" to this file?" ), dlg.GetSheetName() );
577  msg.Printf( wxT( "%s\n\n%s" ), existsMsg, linkMsg );
578 
579  if( !IsOK( this, msg ) )
580  return false;
581 
582  }
583  else // New file.
584  {
585  InitSheet( aSheet, newFilename );
586  }
587  }
588  else // Existing sheet.
589  {
590  bool isUndoable = true;
591  wxString replaceMsg;
592  wxString newMsg;
593  wxString noUndoMsg;
594 
595  isExistingSheet = true;
596 
597  if( !allowCaseSensitiveFileNameClashes( newFilename ) )
598  return false;
599 
600  // Changing the filename of a sheet can modify the full hierarchy structure
601  // and can be not always undoable.
602  // So prepare messages for user notifications:
603  replaceMsg.Printf( _( "Change \"%s\" link from \"%s\" to \"%s\"?" ),
604  dlg.GetSheetName(), aSheet->GetFileName(), fileName.GetFullName() );
605  newMsg.Printf( _( "Create new file \"%s\" with contents of \"%s\"?" ),
606  fileName.GetFullName(), aSheet->GetFileName() );
607  noUndoMsg = _( "This action cannot be undone." );
608 
609  // We are always using here a case insensitive comparison
610  // to avoid issues under Windows, although under Unix
611  // filenames are case sensitive.
612  // But many users create schematic under both Unix and Windows
613  // **
614  // N.B. 1: aSheet->GetFileName() will return a relative path
615  // aSheet->GetScreen()->GetFileName() returns a full path
616  //
617  // N.B. 2: newFilename uses the unix notation for separator.
618  // so we must use it also to compare the old filename to the new filename
619  wxString oldFilename = aSheet->GetScreen()->GetFileName();
620  oldFilename.Replace( wxT( "\\" ), wxT( "/" ) );
621 
622  if( newFilename.Cmp( oldFilename ) != 0 )
623  {
624  // Sheet file name changes cannot be undone.
625  isUndoable = false;
626 
627  if( useScreen || loadFromFile ) // Load from existing file.
628  {
629  clearAnnotation = true;
630 
631  msg.Printf( wxT( "%s\n\n%s" ), replaceMsg, noUndoMsg );
632 
633  if( !IsOK( this, msg ) )
634  return false;
635 
636  if( loadFromFile )
637  aSheet->SetScreen( NULL );
638  }
639  else // Save to new file name.
640  {
641  if( aSheet->GetScreenCount() > 1 )
642  {
643  msg.Printf( wxT( "%s\n\n%s" ), newMsg, noUndoMsg );
644 
645  if( !IsOK( this, msg ) )
646  return false;
647  }
648 
649  renameFile = true;
650  }
651  }
652 
653  if( isUndoable )
654  SaveCopyInUndoList( aSheet, UR_CHANGED );
655 
656  if( renameFile )
657  {
658  SCH_PLUGIN::SCH_PLUGIN_RELEASER pi( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_LEGACY ) );
659 
660  // If the the associated screen is shared by more than one sheet, do not
661  // change the filename of the corresponding screen here.
662  // (a new screen will be created later)
663  // if it is not shared, update the filename
664  if( aSheet->GetScreenCount() <= 1 )
665  aSheet->GetScreen()->SetFileName( newFilename );
666 
667  try
668  {
669  pi->Save( newFilename, aSheet->GetScreen(), &Kiway() );
670  }
671  catch( const IO_ERROR& ioe )
672  {
673  msg.Printf( _( "Error occurred saving schematic file \"%s\"." ), newFilename );
674  DisplayErrorMessage( this, msg, ioe.What() );
675 
676  msg.Printf( _( "Failed to save schematic \"%s\"" ), newFilename );
677  AppendMsgPanel( wxEmptyString, msg, CYAN );
678 
679  return false;
680  }
681 
682  // If the the associated screen is shared by more than one sheet, remove the
683  // screen and reload the file to a new screen. Failure to do this will trash
684  // the screen reference counting in complex hierarchies.
685  if( aSheet->GetScreenCount() > 1 )
686  {
687  aSheet->SetScreen( NULL );
688  loadFromFile = true;
689  }
690  }
691  }
692 
693  wxFileName userFileName = dlg.GetFileName();
694  userFileName.SetExt( SchematicFileExtension );
695 
696  if( useScreen )
697  {
698  // Create a temporary sheet for recursion testing to prevent a possible recursion error.
699  std::unique_ptr< SCH_SHEET> tmpSheet( new SCH_SHEET );
700  tmpSheet->SetName( dlg.GetSheetName() );
701  tmpSheet->SetFileName( userFileName.GetFullPath() );
702  tmpSheet->SetScreen( useScreen );
703 
704  // No need to check for valid library IDs if we are using an existing screen.
705  if( checkSheetForRecursion( tmpSheet.get(), aHierarchy ) )
706  {
707  if( restoreSheet )
708  aHierarchy->LastScreen()->Append( aSheet );
709 
710  return false;
711  }
712 
713  // It's safe to set the sheet screen now.
714  aSheet->SetScreen( useScreen );
715  }
716  else if( loadFromFile )
717  {
718  if( isExistingSheet )
719  {
720  // Temporarily remove the sheet from the current schematic page so that recursion
721  // and symbol library link tests can be performed with the modified sheet settings.
722  restoreSheet = true;
723  aHierarchy->LastScreen()->Remove( aSheet );
724  }
725 
726  if( !LoadSheetFromFile( aSheet, aHierarchy, newFilename ) )
727  {
728  if( restoreSheet )
729  aHierarchy->LastScreen()->Append( aSheet );
730 
731  return false;
732  }
733 
734  if( restoreSheet )
735  aHierarchy->LastScreen()->Append( aSheet );
736  }
737 
738  wxString tmpFn = userFileName.GetFullPath();
739 
740  if( wxFileName::GetPathSeparator() == '\\' )
741  tmpFn.Replace( "\\", "/" );
742 
743  aSheet->SetFileName( tmpFn );
744  aSheet->SetFileNameSize( dlg.GetFileNameTextSize() );
745  aSheet->SetName( dlg.GetSheetName() );
746  aSheet->SetSheetNameSize( dlg.GetSheetNameTextSize() );
747 
748  if( aSheet->GetName().IsEmpty() )
749  aSheet->SetName( wxString::Format( wxT( "Sheet%8.8lX" ),
750  (long unsigned) aSheet->GetTimeStamp() ) );
751 
752  if( aClearAnnotationNewItems )
753  *aClearAnnotationNewItems = clearAnnotation;
754 
755  GetCanvas()->GetView()->Update( aSheet );
756 
757  OnModify();
758 
759  return true;
760 }
761 
762 
766 
768 {
769  // Delayed initialization (need the preferences to be loaded)
770  if( m_lastSheetPinTextSize.x == -1 )
771  {
774  }
775  return m_lastSheetPinTextSize;
776 }
777 
778 
780 {
781  wxString text;
782  SCH_SHEET_PIN* sheetPin;
783 
784  if( aLabel )
785  {
786  text = aLabel->GetText();
787  m_lastSheetPinType = aLabel->GetShape();
788  }
789 
790  sheetPin = new SCH_SHEET_PIN( aSheet, wxPoint( 0, 0 ), text );
791  sheetPin->SetFlags( IS_NEW );
792  sheetPin->SetTextSize( GetLastSheetPinTextSize() );
793  sheetPin->SetShape( m_lastSheetPinType );
794 
795  if( !aLabel )
796  {
797  DIALOG_EDIT_SHEET_PIN dlg( this, sheetPin );
798 
799  if( dlg.ShowModal() != wxID_OK || sheetPin->GetText().IsEmpty() )
800  {
801  delete sheetPin;
802  return nullptr;
803  }
804  }
805 
806  m_lastSheetPinType = sheetPin->GetShape();
807  m_lastSheetPinTextSize = sheetPin->GetTextSize();
808 
809  sheetPin->SetPosition( (wxPoint) GetCanvas()->GetViewControls()->GetCursorPosition() );
810 
811  return sheetPin;
812 }
813 
814 
816 {
817  if( !aSheet->GetScreen() )
818  return nullptr;
819 
820  for( auto item : aSheet->GetScreen()->Items().OfType( SCH_HIER_LABEL_T ) )
821  {
822  auto label = static_cast<SCH_HIERLABEL*>( item );
823 
824  /* A global label has been found: check if there a corresponding sheet label. */
825  if( !aSheet->HasPin( label->GetText() ) )
826  return label;
827  }
828 
829  return nullptr;
830 }
831 
832 
834 {
835  wxRect DrawArea;
836  BASE_SCREEN* screen = GetScreen();
837 
838  DrawArea.SetSize( GetPageSizeIU() );
839 
840  // Calculate a reasonable dc size, in pixels, and the dc scale to fit
841  // the drawings into the dc size
842  // scale is the ratio resolution (in PPI) / internal units
843  double ppi = 300; // Use 300 pixels per inch to create bitmap images on start
844  double inch2Iu = 1000.0 * IU_PER_MILS;
845  double scale = ppi / inch2Iu;
846 
847  wxSize dcsize = DrawArea.GetSize();
848 
849  int maxdim = std::max( dcsize.x, dcsize.y );
850 
851  // the max size in pixels of the bitmap used to byuild the sheet copy
852  const int maxbitmapsize = 3000;
853 
854  while( int( maxdim * scale ) > maxbitmapsize )
855  {
856  ppi = ppi / 1.5;
857  scale = ppi / inch2Iu;
858  }
859 
860  dcsize.x *= scale;
861  dcsize.y *= scale;
862 
863  // Set draw offset, zoom... to values needed to draw in the memory DC
864  // after saving initial values:
865  wxPoint tmp_startvisu = screen->m_StartVisu;
866  double tmpzoom = screen->GetZoom();
867  wxPoint old_org = screen->m_DrawOrg;
868  screen->m_DrawOrg.x = screen->m_DrawOrg.y = 0;
869  screen->m_StartVisu.x = screen->m_StartVisu.y = 0;
870 
871  screen->SetZoom( 1 ); // we use zoom = 1 in draw functions.
872 
873  wxMemoryDC dc;
874  wxBitmap image( dcsize );
875  dc.SelectObject( image );
876 
877  GRResetPenAndBrush( &dc );
878  GRForceBlackPen( false );
879  screen->m_IsPrinting = true;
880  dc.SetUserScale( scale, scale );
881 
882  dc.Clear();
883  PrintPage( &dc );
884  screen->m_IsPrinting = false;
885 
886  if( wxTheClipboard->Open() )
887  {
888  // This data objects are held by the clipboard, so do not delete them in the app.
889  wxBitmapDataObject* clipbrd_data = new wxBitmapDataObject( image );
890  wxTheClipboard->SetData( clipbrd_data );
891  wxTheClipboard->Close();
892  }
893 
894  // Deselect Bitmap from DC in order to delete the MemoryDC
895  dc.SelectObject( wxNullBitmap );
896 
897  GRForceBlackPen( false );
898 
899  screen->m_StartVisu = tmp_startvisu;
900  screen->m_DrawOrg = old_org;
901  screen->SetZoom( tmpzoom );
902 }
903 
904 
905 bool SCH_EDIT_FRAME::allowCaseSensitiveFileNameClashes( const wxString& aSchematicFileName )
906 {
907  wxString msg;
908  SCH_SCREENS screens;
909  wxFileName fn = aSchematicFileName;
910 
911  wxCHECK( fn.IsAbsolute(), false );
912 
914  && screens.CanCauseCaseSensitivityIssue( aSchematicFileName ) )
915  {
916  msg.Printf( _( "The file name \"%s\" can cause issues with an existing file name\n"
917  "already defined in the schematic on systems that support case\n"
918  "insensitive file names. This will cause issues if you copy this\n"
919  "project to an operating system that supports case insensitive file\n"
920  "names.\n\nDo you wish to continue?" ),
921  fn.GetName() );
922 
923  wxRichMessageDialog dlg( this, msg, _( "Warning" ),
924  wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION );
925  dlg.ShowCheckBox( _( "Do not show this message again." ) );
926  dlg.SetYesNoLabels( wxMessageDialog::ButtonLabel( _( "Create New Sheet" ) ),
927  wxMessageDialog::ButtonLabel( _( "Discard New Sheet" ) ) );
928 
929  if( dlg.ShowModal() == wxID_NO )
930  return false;
931 
932  m_showSheetFileNameCaseSensitivityDlg = !dlg.IsCheckBoxChecked();
933  }
934 
935  return true;
936 }
void DisplayError(wxWindow *aParent, const wxString &aText, int aDisplayTime)
Display an error or warning message box with aMessage.
Definition: confirm.cpp:236
SCH_SHEET_LIST.
KIGFX::SCH_VIEW * GetView() const override
Function GetView() Returns a pointer to the VIEW instance used in the panel.
static const wxString & GetSymbolLibTableFileName()
void GRResetPenAndBrush(wxDC *DC)
Definition: gr_basic.cpp:120
void SetShape(PINSHEETLABEL_SHAPE aShape)
Definition: sch_text.h:245
const wxString & GetFileName() const
Definition: sch_screen.h:157
SYMBOL_LIB_TABLE_ROW * FindRow(const wxString &aNickName)
Return an SYMBOL_LIB_TABLE_ROW if aNickName is found in this table or in any chained fallBack table f...
void SetFileNameSize(int aSize)
Definition: sch_sheet.h:276
bool SearchHierarchy(const wxString &aFilename, SCH_SCREEN **aScreen)
Search the existing hierarchy for an instance of screen loaded from aFileName.
Definition: sch_sheet.cpp:540
Hold a record identifying a symbol library accessed by the appropriate symbol library SCH_PLUGIN obje...
KIWAY & Kiway() const
Function Kiway returns a reference to the KIWAY that this object has an opportunity to participate in...
Definition: kiway_holder.h:56
const wxString & GetOptions() const
Return the options string, which may hold a password or anything else needed to instantiate the under...
bool HasLibrary(const wxString &aNickname, bool aCheckEnabled=false) const
Test for the existence of aNickname in the library table.
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition: confirm.cpp:249
This file is part of the common library.
bool checkSheetForRecursion(SCH_SHEET *aSheet, SCH_SHEET_PATH *aHierarchy)
Verify that aSheet will not cause a recursion error in aHierarchy.
Definition: sheet.cpp:44
void SetFileName(const wxString &aFilename)
Definition: sch_sheet.h:464
SCH_SHEET * GetSheet(const wxString &aName)
Returns a sheet object pointer that is named aName.
Definition: sch_screen.cpp:696
double GetZoom() const
Function GetZoom returns the current "zoom factor", which is a measure of "internal units per device ...
Definition: base_screen.h:240
static wxSize m_lastSheetPinTextSize
Last sheet pin text size.
void SetScreen(SCH_SCREEN *aScreen)
Set the SCH_SCREEN associated with this sheet to aScreen.
Definition: sch_sheet.cpp:111
EE_TYPE OfType(KICAD_T aType)
Definition: sch_rtree.h:219
void InitSheet(SCH_SHEET *aSheet, const wxString &aNewFilename)
Definition: sheet.cpp:93
void DrawCurrentSheetToClipboard()
Use the wxWidgets print code to draw an image of the current sheet onto the clipboard.
Definition: sheet.cpp:833
void Remove(SCH_ITEM *aItem)
Remove aItem from the schematic associated with this screen.
Definition: sch_screen.cpp:235
void SetTextSize(const wxSize &aNewSize)
Definition: eda_text.h:222
SCH_SCREEN * GetScreen()
Definition: sch_sheet.h:278
VTBL_ENTRY const wxString GetProjectPath() const
Function GetProjectPath returns the full path of the project.
Definition: project.cpp:102
bool EditSheet(SCH_SHEET *aSheet, SCH_SHEET_PATH *aHierarchy, bool *aClearAnnotationNewItems)
Edit an existing sheet or add a new sheet to the schematic.
Definition: sheet.cpp:512
const wxSize GetPageSizeIU() const override
Works off of GetPageSettings() to return the size of the paper page in the internal units of this par...
bool m_showSheetFileNameCaseSensitivityDlg
const wxString & GetDescr() const
Return the description of the library referenced by this row.
const wxString GetFullURI(bool aSubstituted=false) const
Return the full location specifying URI for the LIB, either in original UI form or in environment var...
#define IS_NEW
New item, just created.
Definition: base_struct.h:120
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:134
wxPoint m_StartVisu
Coordinates in drawing units of the current view position (upper left corner of device)
Definition: base_screen.h:117
timestamp_t GetNewTimeStamp()
Definition: common.cpp:215
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:102
void SetFlags(STATUS_FLAGS aMask)
Definition: base_struct.h:255
bool allowCaseSensitiveFileNameClashes(const wxString &aSchematicFileName)
Check aSchematicFileName for a potential file name case sensitivity clashes.
Definition: sheet.cpp:905
wxString GetName() const
Definition: sch_sheet.h:269
#define NULL
static PINSHEETLABEL_SHAPE m_lastSheetPinType
Last sheet pin type.
virtual const wxString What() const
A composite of Problem() and Where()
Definition: exceptions.cpp:33
void GRForceBlackPen(bool flagforce)
Function GRForceBlackPen.
Definition: gr_basic.cpp:201
void SetSheetNameSize(int aSize)
Definition: sch_sheet.h:273
void SetName(const wxString &aName)
Definition: sch_sheet.h:270
PROJECT & Prj() const
Function Prj returns a reference to the PROJECT "associated with" this KIWAY.
wxString GetFullURI(const wxString &aLibNickname, bool aExpandEnvVars=true) const
Return the full URI of the library mapped to aLibNickname.
BASE_SCREEN handles how to draw a screen (a board, a schematic ...)
Definition: base_screen.h:74
const wxSize & GetTextSize() const
Definition: eda_text.h:223
const std::string SchematicFileExtension
SCH_DRAW_PANEL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
const wxString GetType() const override
Return the type of symbol library table represented by this row.
Definition: colors.h:59
void SaveCopyInUndoList(SCH_ITEM *aItemToCopy, UNDO_REDO_T aTypeCommand, bool aAppend=false, const wxPoint &aTransformPoint=wxPoint(0, 0))
Create a copy of the current schematic item, and put it in the undo list.
Definition of file extensions used in Kicad.
timestamp_t GetTimeStamp() const
Definition: base_struct.h:213
const wxSize & GetLastSheetPinTextSize()
Initializing accessor for the pin text size.
Definition: sheet.cpp:767
Define a sheet pin (label) used in sheets to create hierarchical schematics.
Definition: sch_sheet.h:75
bool HasNoFullyDefinedLibIds()
Test all of the schematic symbols to see if all LIB_ID objects library nickname is not set.
virtual bool SetZoom(double iu_per_du)
Function SetZoom adjusts the current zoom factor.
Definition: base_screen.cpp:88
SCH_SHEET_PATH.
virtual void Update(VIEW_ITEM *aItem, int aUpdateFlags)
For dynamic VIEWs, informs the associated VIEW that the graphical representation of this item has cha...
Definition: view.cpp:1540
void Load(const wxString &aFileName)
Load the library table using the path defined by aFileName aFallBackTable.
bool m_IsPrinting
Definition: base_screen.h:139
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition: sch_sheet.h:206
wxString GetFileName(void) const
Return the filename corresponding to this sheet.
Definition: sch_sheet.cpp:616
Helper object to release a SCH_PLUGIN in the context of a potential thrown exception through its dest...
Definition: sch_io_mgr.h:497
void AppendMsgPanel(const wxString &textUpper, const wxString &textLower, COLOR4D color, int pad=6)
Append a message to the message panel.
void SetPosition(const wxPoint &aPosition) override
Function SetPosition set the schematic item position to aPosition.
Definition: sch_sheet.h:195
virtual void PrintPage(wxDC *aDC) override
Plot or print the current sheet to the clipboard.
PINSHEETLABEL_SHAPE
Definition: sch_text.h:164
const int scale
void UpdateSymbolLinks(bool aForce=false)
Initialize or reinitialize the weak reference to the LIB_PART for each SCH_COMPONENT found in the ful...
SCH_HIERLABEL * ImportHierLabel(SCH_SHEET *aSheet)
Import a hierarchical label with no attached sheet pin.
Definition: sheet.cpp:815
SCH_SCREEN * LastScreen()
Function LastScreen.
const char * name
Definition: DXF_plotter.cpp:60
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, CPTREE &aTree)
Function Format outputs a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:205
void Append(SCH_ITEM *aItem)
Definition: sch_screen.cpp:175
#define _(s)
Definition: 3d_actions.cpp:31
EE_RTREE & Items()
Definition: sch_screen.h:127
Implementing DIALOG_SCH_SHEET_PROPS_BASE.
wxPoint m_DrawOrg
offsets for drawing the circuit on the screen
Definition: base_screen.h:112
void SetMaxUndoItems(int aMax)
Definition: base_screen.h:213
#define IU_PER_MILS
Definition: plotter.cpp:137
bool HasPin(const wxString &aName)
Checks if the sheet already has a sheet pin named aName.
Definition: sch_sheet.cpp:209
int ReplaceDuplicateTimeStamps()
Test all sheet and component objects in the schematic for duplicate time stamps and replaces them as ...
bool CanCauseCaseSensitivityIssue(const wxString &aSchematicFileName) const
Check aSchematicFileName for a potential file name case sensitivity issue.
uint32_t timestamp_t
timestamp_t is our type to represent unique IDs for all kinds of elements; historically simply the ti...
Definition: common.h:52
void OnModify() override
Must be called after a schematic change in order to set the "modify" flag of the current screen and u...
SCH_SHEET * g_RootSheet
void SetFileName(const wxString &aFileName)
Definition: sch_screen.h:155
bool checkForNoFullyDefinedLibIds(SCH_SHEET *aSheet)
Verify that the symbol library links aSheet and all of it's child sheets have been remapped to the sy...
Definition: sheet.cpp:72
void SetModify()
Definition: base_screen.h:224
Definition of the SCH_SHEET_PATH and SCH_SHEET_LIST classes for Eeschema.
static wxPoint m_lastSheetPinPosition
Last sheet pin position.
bool TestForRecursion(const SCH_SHEET_LIST &aSrcSheetHierarchy, const wxString &aDestFileName) const
Function TestForRecursion.
void DisplayInfoMessage(wxWindow *aParent, const wxString &aMessage, const wxString &aExtraInfo)
Display an informational message box with aMessage.
Definition: confirm.cpp:264
Struct IO_ERROR is a class used to hold an error message and may be used when throwing exceptions con...
Definition: ki_exception.h:76
bool IsOK(wxWindow *aParent, const wxString &aMessage)
Display a yes/no dialog with aMessage and returns the user response.
Definition: confirm.cpp:280
SCH_SHEET_PIN * CreateSheetPin(SCH_SHEET *aSheet, SCH_HIERLABEL *aLabel)
Create a new SCH_SHEET_PIN object and add it to aSheet at the current cursor position.
Definition: sheet.cpp:779
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition: eda_text.h:123
Container class that holds multiple SCH_SCREEN objects in a hierarchy.
Definition: sch_screen.h:498
bool IsEmpty(bool aIncludeFallback=true)
Return true if the table is empty.
int GetDefaultTextSize()
Default size for text in general.
PINSHEETLABEL_SHAPE GetShape() const
Definition: sch_text.h:243
void SetTimeStamp(timestamp_t aNewTimeStamp)
Definition: base_struct.h:212