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  int i;
108  wxString msg;
109  wxString topLevelSheetPath;
110  wxFileName tmp;
111  wxFileName currentSheetFileName;
112  bool libTableChanged = false;
113  SCH_SCREEN* currentScreen = aHierarchy->LastScreen();
114  SCH_PLUGIN::SCH_PLUGIN_RELEASER pi( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_LEGACY ) );
115  std::unique_ptr< SCH_SHEET> newSheet( new SCH_SHEET );
116 
117  wxFileName fileName( aFileName );
118 
119  if( !fileName.IsAbsolute() )
120  {
121  wxCHECK_MSG( fileName.MakeAbsolute(), false,
122  wxString::Format( "Cannot make file name \"%s\" path absolute.",
123  aFileName ) );
124  }
125 
126  wxString fullFilename = fileName.GetFullPath();
127 
128  try
129  {
130  if( aSheet->GetScreen() != nullptr )
131  {
132  newSheet.reset( pi->Load( fullFilename, &Kiway() ) );
133  }
134  else
135  {
136  newSheet->SetFileName( fullFilename );
137  pi->Load( fullFilename, &Kiway(), newSheet.get() );
138  }
139 
140  if( !pi->GetError().IsEmpty() )
141  {
142  msg = _( "The entire schematic could not be loaded. Errors occurred attempting "
143  "to load hierarchical sheet schematics." );
144 
145  wxMessageDialog msgDlg1( this, msg, _( "Schematic Load Error" ),
146  wxOK | wxCANCEL | wxCANCEL_DEFAULT |
147  wxCENTER | wxICON_QUESTION );
148  msgDlg1.SetOKLabel( wxMessageDialog::ButtonLabel( _( "Use partial schematic" ) ) );
149  msgDlg1.SetExtendedMessage( pi->GetError() );
150 
151  if( msgDlg1.ShowModal() == wxID_CANCEL )
152  return false;
153  }
154  }
155  catch( const IO_ERROR& ioe )
156  {
157  msg.Printf( _( "Error occurred loading schematic file \"%s\"." ), fullFilename );
158  DisplayErrorMessage( this, msg, ioe.What() );
159 
160  msg.Printf( _( "Failed to load schematic \"%s\"" ), fullFilename );
161  AppendMsgPanel( wxEmptyString, msg, CYAN );
162 
163  return false;
164  }
165 
166  tmp = fileName;
167 
168  // If the loaded schematic is in a different folder from the current project and
169  // it contains hierarchical sheets, the hierarchical sheet paths need to be updated.
170  if( fileName.GetPath( wxPATH_GET_SEPARATOR ) != Prj().GetProjectPath()
171  && newSheet->CountSheets() )
172  {
173  // Give the user the option to choose relative path if possible.
174  if( tmp.MakeRelativeTo( Prj().GetProjectPath() ) )
175  {
176  wxMessageDialog msgDlg2(
177  this,
178  "Do you want to use a relative path to the loaded "
179  "schematic?", "Select Path Type",
180  wxYES_NO | wxCANCEL | wxYES_DEFAULT | wxICON_QUESTION | wxCENTER );
181  msgDlg2.SetYesNoLabels( wxMessageDialog::ButtonLabel( "Use Relative Path" ),
182  wxMessageDialog::ButtonLabel( "Use Absolute Path" ) );
183  int rsp = msgDlg2.ShowModal();
184 
185  if( rsp == wxID_CANCEL )
186  {
187  return false;
188  }
189  else if( rsp == wxID_NO )
190  {
191  topLevelSheetPath = fileName.GetPathWithSep();
192  }
193  else
194  {
195  topLevelSheetPath = tmp.GetPathWithSep();
196  }
197  }
198  else
199  {
200  topLevelSheetPath = tmp.GetPathWithSep();
201  }
202 
203  if( wxFileName::GetPathSeparator() == '\\' )
204  topLevelSheetPath.Replace( "\\", "/" );
205  }
206 
207  // Make sure any new sheet changes do not cause any recursion issues.
208  SCH_SHEET_LIST hierarchy( g_RootSheet ); // This is the schematic sheet hierarchy.
209  SCH_SHEET_LIST sheetHierarchy( newSheet.get() ); // This is the hierarchy of the loaded file.
210 
211  if( checkSheetForRecursion( newSheet.get(), aHierarchy )
212  || checkForNoFullyDefinedLibIds( newSheet.get() ) )
213  return false;
214 
215  // Make a valiant attempt to warn the user of all possible scenarios where there could
216  // be broken symbol library links.
217  wxArrayString names;
218  wxArrayString newLibNames;
219  SCH_SCREENS newScreens( newSheet.get() ); // All screens associated with the import.
220  SCH_SCREENS prjScreens( g_RootSheet );
221 
222  newScreens.GetLibNicknames( names );
223 
224  wxMessageDialog::ButtonLabel okButtonLabel( _( "Continue Load" ) );
225  wxMessageDialog::ButtonLabel cancelButtonLabel( _( "Cancel Load" ) );
226 
227  if( fileName.GetPath( wxPATH_GET_SEPARATOR ) == Prj().GetProjectPath()
228  && !prjScreens.HasSchematic( fullFilename ) )
229  {
230  // A schematic in the current project path that isn't part of the current project.
231  // It's possible the user copied this schematic from another project so the library
232  // links may not be avaible. Even this is check is no guarantee that all symbol
233  // library links are valid but it's better than nothing.
234  for( const auto& name : names )
235  {
236  if( !Prj().SchSymbolLibTable()->HasLibrary( name ) )
237  newLibNames.Add( name );
238  }
239 
240  if( !newLibNames.IsEmpty() )
241  {
242  msg = _( "There are library names in the loaded schematic that are missing "
243  "from the project library table. This may result in broken symbol "
244  "library links for the loaded schematic. Do you wish to continue?" );
245  wxMessageDialog msgDlg3( this, msg, _( "Continue Load Schematic" ),
246  wxOK | wxCANCEL | wxCANCEL_DEFAULT |
247  wxCENTER | wxICON_QUESTION );
248  msgDlg3.SetOKCancelLabels( okButtonLabel, cancelButtonLabel );
249 
250  if( msgDlg3.ShowModal() == wxID_CANCEL )
251  return false;
252  }
253  }
254  else if( fileName.GetPath( wxPATH_GET_SEPARATOR ) != Prj().GetProjectPath() )
255  {
256  // A schematic loaded from a path other than the current project path.
257 
258  // If there are symbol libraries in the imported schematic that are not in the
259  // symbol library table of this project, there could be a lot of broken symbol
260  // library links. Attempt to add the missing libraries to the project symbol
261  // library table.
262  wxArrayString duplicateLibNames;
263 
264  for( const auto& name : names )
265  {
266  if( !Prj().SchSymbolLibTable()->HasLibrary( name ) )
267  newLibNames.Add( name );
268  else
269  duplicateLibNames.Add( name );
270  }
271 
272  SYMBOL_LIB_TABLE table;
273  wxFileName symLibTableFn( fileName.GetPath(),
275 
276  // If there are any new or duplicate libraries, check to see if it's possible that
277  // there could be any missing libraries that would cause broken symbol library links.
278  if( !newLibNames.IsEmpty() || !duplicateLibNames.IsEmpty() )
279  {
280  if( !symLibTableFn.Exists() || !symLibTableFn.IsFileReadable() )
281  {
282  msg.Printf( _( "The project library table \"%s\" does not exist or cannot "
283  "be read. This may result in broken symbol links for the "
284  "schematic. Do you wish to continue?" ),
285  fileName.GetFullPath() );
286  wxMessageDialog msgDlg4( this, msg, _( "Continue Load Schematic" ),
287  wxOK | wxCANCEL | wxCANCEL_DEFAULT |
288  wxCENTER | wxICON_QUESTION );
289  msgDlg4.SetOKCancelLabels( okButtonLabel, cancelButtonLabel );
290 
291  if( msgDlg4.ShowModal() == wxID_CANCEL )
292  return false;
293  }
294  else
295  {
296  try
297  {
298  table.Load( symLibTableFn.GetFullPath() );
299  }
300  catch( const IO_ERROR& ioe )
301  {
302  msg.Printf( _( "An error occurred loading the symbol library table "
303  "\"%s\"." ),
304  symLibTableFn.GetFullPath() );
305  DisplayErrorMessage( NULL, msg, ioe.What() );
306  return false;
307  }
308  }
309  }
310 
311  // Check to see if any of the symbol libraries found in the appended schematic do
312  // not exist in the current project are missing from the appended project symbol
313  // library table.
314  if( !newLibNames.IsEmpty() )
315  {
316  bool missingLibNames = table.IsEmpty();
317 
318  if( !missingLibNames )
319  {
320  for( const auto& newLibName : newLibNames )
321  {
322  if( !table.HasLibrary( newLibName ) )
323  {
324  missingLibNames = true;
325  break;
326  }
327  }
328  }
329 
330  if( missingLibNames )
331  {
332  msg = _( "There are library names in the loaded schematic that are missing "
333  "from the loaded schematic project library table. This may result "
334  "in broken symbol library links for the schematic. "
335  "Do you wish to continue?" );
336  wxMessageDialog msgDlg5( this, msg, _( "Continue Load Schematic" ),
337  wxOK | wxCANCEL | wxCANCEL_DEFAULT |
338  wxCENTER | wxICON_QUESTION );
339  msgDlg5.SetOKCancelLabels( okButtonLabel, cancelButtonLabel );
340 
341  if( msgDlg5.ShowModal() == wxID_CANCEL )
342  return false;
343  }
344  }
345 
346  // The library name already exists in the current project. Check to see if the
347  // duplicate name is the same library in the current project. If it's not, it's
348  // most likely that the symbol library links will be broken.
349  if( !duplicateLibNames.IsEmpty() && !table.IsEmpty() )
350  {
351  bool libNameConflict = false;
352 
353  for( const auto& duplicateLibName : duplicateLibNames )
354  {
355  const SYMBOL_LIB_TABLE_ROW* thisRow = nullptr;
356  const SYMBOL_LIB_TABLE_ROW* otherRow = nullptr;
357 
358  if( Prj().SchSymbolLibTable()->HasLibrary( duplicateLibName ) )
359  thisRow = Prj().SchSymbolLibTable()->FindRow( duplicateLibName );
360 
361  if( table.HasLibrary( duplicateLibName ) )
362  otherRow = table.FindRow( duplicateLibName );
363 
364  // It's in the global library table so there is no conflict.
365  if( thisRow && !otherRow )
366  continue;
367 
368  if( !thisRow || !otherRow )
369  continue;
370 
371  wxFileName otherUriFileName;
372  wxString thisURI = thisRow->GetFullURI( true );
373  wxString otherURI = otherRow->GetFullURI( false);
374 
375  if( otherURI.Contains( "${KIPRJMOD}" ) || otherURI.Contains( "$(KIPRJMOD)" ) )
376  {
377  // Cannot use relative paths here, "${KIPRJMOD}../path-to-cache-lib" does
378  // not expand to a valid symbol library path.
379  otherUriFileName.SetPath( fileName.GetPath() );
380  otherUriFileName.SetFullName( otherURI.AfterLast( '}' ) );
381  otherURI = otherUriFileName.GetFullPath();
382  }
383 
384  if( thisURI != otherURI )
385  {
386  libNameConflict = true;
387  break;
388  }
389  }
390 
391  if( libNameConflict )
392  {
393  msg = _( "A duplicate library name that references a different library exists "
394  "in the current library table. This conflict cannot be resolved and "
395  "may result in broken symbol library links for the schematic. "
396  "Do you wish to continue?" );
397  wxMessageDialog msgDlg6( this, msg, _( "Continue Load Schematic" ),
398  wxOK | wxCANCEL | wxCANCEL_DEFAULT |
399  wxCENTER | wxICON_QUESTION );
400  msgDlg6.SetOKCancelLabels( okButtonLabel, cancelButtonLabel );
401 
402  if( msgDlg6.ShowModal() == wxID_CANCEL )
403  return false;
404  }
405  }
406 
407  // All (most?) of the possible broken symbol library link cases are covered. Map the
408  // new appended schematic project symbol library table entries to the current project
409  // symbol library table.
410  if( !newLibNames.IsEmpty() && !table.IsEmpty() )
411  {
412  for( const auto& libName : newLibNames )
413  {
414  if( !table.HasLibrary( libName )
415  || Prj().SchSymbolLibTable()->HasLibrary( libName ) )
416  continue;
417 
418  // Don't expand environment variable because KIPRJMOD will not be correct
419  // for a different project.
420  wxString uri = table.GetFullURI( libName, false );
421  wxFileName newLib;
422 
423  if( uri.Contains( "${KIPRJMOD}" ) || uri.Contains( "$(KIPRJMOD)" ) )
424  {
425  // Cannot use relative paths here, "${KIPRJMOD}../path-to-cache-lib" does
426  // not expand to a valid symbol library path.
427  newLib.SetPath( fileName.GetPath() );
428  newLib.SetFullName( uri.AfterLast( '}' ) );
429  uri = newLib.GetFullPath();
430  }
431  else
432  {
433  uri = table.GetFullURI( libName );
434  }
435 
436  // Add the library from the imported project to the current project
437  // symbol library table.
438  const SYMBOL_LIB_TABLE_ROW* row = table.FindRow( libName );
439 
440  auto newRow = new SYMBOL_LIB_TABLE_ROW( libName, uri, row->GetType(),
441  row->GetOptions(), row->GetDescr() );
442 
443  Prj().SchSymbolLibTable()->InsertRow( newRow );
444  libTableChanged = true;
445  }
446  }
447  }
448 
449  // Check for duplicate sheet names in the current page.
450  wxArrayString duplicateSheetNames;
451  EE_TYPE_COLLECTOR sheets;
452 
453  sheets.Collect( currentScreen->GetDrawItems(), EE_COLLECTOR::SheetsOnly );
454 
455  for( i = 0; i < sheets.GetCount(); ++i )
456  {
457  if( newSheet->GetScreen()->GetSheet( ( ( SCH_SHEET* ) sheets[i] )->GetName() ) )
458  duplicateSheetNames.Add( ( ( SCH_SHEET* ) sheets[i] )->GetName() );
459  }
460 
461  if( !duplicateSheetNames.IsEmpty() )
462  {
463  msg.Printf( "Duplicate sheet names exist on the current page. Do you want to "
464  "automatically rename the duplicate sheet names?" );
465 
466  if( !IsOK( this, msg ) )
467  return false;
468  }
469 
470  // Rename all duplicate sheet names.
471  SCH_SCREEN* newScreen = newSheet->GetScreen();
472  wxCHECK_MSG( newScreen, false, "No screen defined for sheet." );
473 
474  for( const auto& duplicateName : duplicateSheetNames )
475  {
476  SCH_SHEET* renamedSheet = newScreen->GetSheet( duplicateName );
477 
478  wxCHECK2_MSG( renamedSheet, continue,
479  "Sheet " + duplicateName + " not found in imported schematic." );
480 
481  timestamp_t newtimestamp = GetNewTimeStamp();
482  renamedSheet->SetTimeStamp( newtimestamp );
483  renamedSheet->SetName( wxString::Format( "Sheet%8.8lX", (unsigned long) newtimestamp ) );
484  }
485 
486  // Set all sheets loaded into the correct sheet file paths.
487  EE_TYPE_COLLECTOR newTopLevelSheets;
488 
489  newTopLevelSheets.Collect( newSheet->GetScreen()->GetDrawItems(), EE_COLLECTOR::SheetsOnly );
490 
491  for( i = 0; i < newTopLevelSheets.GetCount(); ++i )
492  {
493  SCH_SHEET* tmpSheet = dynamic_cast< SCH_SHEET* >( newTopLevelSheets[i] );
494  wxCHECK2( tmpSheet != nullptr, continue );
495  tmpSheet->SetFileName( topLevelSheetPath + tmpSheet->GetFileName() );
496  }
497 
498  if( libTableChanged )
499  Prj().SchSymbolLibTable()->Save( Prj().GetProjectPath() +
501 
502  // It is finally safe to add or append the imported schematic.
503  if( aSheet->GetScreen() == nullptr )
504  aSheet->SetScreen( newScreen );
505  else
506  aSheet->GetScreen()->Append( newScreen );
507 
508  SCH_SCREENS allScreens;
509  allScreens.ReplaceDuplicateTimeStamps();
510 
511  SCH_SCREENS screens( aSheet );
512  screens.UpdateSymbolLinks( true );
513 
514  return true;
515 }
516 
517 
519  bool* aClearAnnotationNewItems )
520 {
521  if( aSheet == NULL || aHierarchy == NULL )
522  return false;
523 
524  // Get the new texts
525  DIALOG_SCH_SHEET_PROPS dlg( this, aSheet );
526 
527  if( dlg.ShowModal() == wxID_CANCEL )
528  return false;
529 
530  wxFileName fileName = dlg.GetFileName();
531  fileName.SetExt( SchematicFileExtension );
532 
533  wxString msg;
534  bool renameFile = false;
535  bool loadFromFile = false;
536  bool clearAnnotation = false;
537  bool restoreSheet = false;
538  bool isExistingSheet = false;
539  SCH_SCREEN* useScreen = NULL;
540 
541  // Relative file names are relative to the path of the current sheet. This allows for
542  // nesting of schematic files in subfolders.
543  if( !fileName.IsAbsolute() )
544  {
545  const SCH_SCREEN* currentScreen = aHierarchy->LastScreen();
546 
547  wxCHECK_MSG( currentScreen, false, "Invalid sheet path object." );
548 
549  wxFileName currentSheetFileName = currentScreen->GetFileName();
550 
551  wxCHECK_MSG( fileName.Normalize( wxPATH_NORM_ALL, currentSheetFileName.GetPath() ), false,
552  "Cannot normalize new sheet schematic file path." );
553  }
554 
555  wxString newFilename = fileName.GetFullPath();
556 
557  // Search for a schematic file having the same filename already in use in the hierarchy
558  // or on disk, in order to reuse it.
559  if( !g_RootSheet->SearchHierarchy( newFilename, &useScreen ) )
560  {
561  loadFromFile = wxFileExists( newFilename );
562  wxLogDebug( "Sheet requested file \"%s\", %s",
563  newFilename,
564  ( loadFromFile ) ? "found" : "not found" );
565  }
566 
567  // Inside Eeschema, filenames are stored using unix notation
568  newFilename.Replace( wxT( "\\" ), wxT( "/" ) );
569 
570  if( aSheet->GetScreen() == NULL ) // New sheet.
571  {
572  if( !allowCaseSensitiveFileNameClashes( newFilename ) )
573  return false;
574 
575  if( useScreen || loadFromFile ) // Load from existing file.
576  {
577  clearAnnotation = true;
578 
579  wxString existsMsg;
580  wxString linkMsg;
581  existsMsg.Printf( _( "\"%s\" already exists." ), fileName.GetFullName() );
582  linkMsg.Printf( _( "Link \"%s\" to this file?" ), dlg.GetSheetName() );
583  msg.Printf( wxT( "%s\n\n%s" ), existsMsg, linkMsg );
584 
585  if( !IsOK( this, msg ) )
586  return false;
587 
588  }
589  else // New file.
590  {
591  InitSheet( aSheet, newFilename );
592  }
593  }
594  else // Existing sheet.
595  {
596  bool isUndoable = true;
597  wxString replaceMsg;
598  wxString newMsg;
599  wxString noUndoMsg;
600 
601  isExistingSheet = true;
602 
603  if( !allowCaseSensitiveFileNameClashes( newFilename ) )
604  return false;
605 
606  // Changing the filename of a sheet can modify the full hierarchy structure
607  // and can be not always undoable.
608  // So prepare messages for user notifications:
609  replaceMsg.Printf( _( "Change \"%s\" link from \"%s\" to \"%s\"?" ),
610  dlg.GetSheetName(), aSheet->GetFileName(), fileName.GetFullName() );
611  newMsg.Printf( _( "Create new file \"%s\" with contents of \"%s\"?" ),
612  fileName.GetFullName(), aSheet->GetFileName() );
613  noUndoMsg = _( "This action cannot be undone." );
614 
615  // We are always using here a case insensitive comparison
616  // to avoid issues under Windows, although under Unix
617  // filenames are case sensitive.
618  // But many users create schematic under both Unix and Windows
619  // **
620  // N.B. 1: aSheet->GetFileName() will return a relative path
621  // aSheet->GetScreen()->GetFileName() returns a full path
622  //
623  // N.B. 2: newFilename uses the unix notation for separator.
624  // so we must use it also to compare the old filename to the new filename
625  wxString oldFilename = aSheet->GetScreen()->GetFileName();
626  oldFilename.Replace( wxT( "\\" ), wxT( "/" ) );
627 
628  if( newFilename.Cmp( oldFilename ) != 0 )
629  {
630  // Sheet file name changes cannot be undone.
631  isUndoable = false;
632 
633  if( useScreen || loadFromFile ) // Load from existing file.
634  {
635  clearAnnotation = true;
636 
637  msg.Printf( wxT( "%s\n\n%s" ), replaceMsg, noUndoMsg );
638 
639  if( !IsOK( this, msg ) )
640  return false;
641 
642  if( loadFromFile )
643  aSheet->SetScreen( NULL );
644  }
645  else // Save to new file name.
646  {
647  if( aSheet->GetScreenCount() > 1 )
648  {
649  msg.Printf( wxT( "%s\n\n%s" ), newMsg, noUndoMsg );
650 
651  if( !IsOK( this, msg ) )
652  return false;
653  }
654 
655  renameFile = true;
656  }
657  }
658 
659  if( isUndoable )
660  SaveCopyInUndoList( aSheet, UR_CHANGED );
661 
662  if( renameFile )
663  {
664  SCH_PLUGIN::SCH_PLUGIN_RELEASER pi( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_LEGACY ) );
665 
666  // If the the associated screen is shared by more than one sheet, do not
667  // change the filename of the corresponding screen here.
668  // (a new screen will be created later)
669  // if it is not shared, update the filename
670  if( aSheet->GetScreenCount() <= 1 )
671  aSheet->GetScreen()->SetFileName( newFilename );
672 
673  try
674  {
675  pi->Save( newFilename, aSheet->GetScreen(), &Kiway() );
676  }
677  catch( const IO_ERROR& ioe )
678  {
679  msg.Printf( _( "Error occurred saving schematic file \"%s\"." ), newFilename );
680  DisplayErrorMessage( this, msg, ioe.What() );
681 
682  msg.Printf( _( "Failed to save schematic \"%s\"" ), newFilename );
683  AppendMsgPanel( wxEmptyString, msg, CYAN );
684 
685  return false;
686  }
687 
688  // If the the associated screen is shared by more than one sheet, remove the
689  // screen and reload the file to a new screen. Failure to do this will trash
690  // the screen reference counting in complex hierarchies.
691  if( aSheet->GetScreenCount() > 1 )
692  {
693  aSheet->SetScreen( NULL );
694  loadFromFile = true;
695  }
696  }
697  }
698 
699  wxFileName userFileName = dlg.GetFileName();
700  userFileName.SetExt( SchematicFileExtension );
701 
702  if( useScreen )
703  {
704  // Create a temporary sheet for recursion testing to prevent a possible recursion error.
705  std::unique_ptr< SCH_SHEET> tmpSheet( new SCH_SHEET );
706  tmpSheet->SetName( dlg.GetSheetName() );
707  tmpSheet->SetFileName( userFileName.GetFullPath() );
708  tmpSheet->SetScreen( useScreen );
709 
710  // No need to check for valid library IDs if we are using an existing screen.
711  if( checkSheetForRecursion( tmpSheet.get(), aHierarchy ) )
712  {
713  if( restoreSheet )
714  aHierarchy->LastScreen()->Append( aSheet );
715 
716  return false;
717  }
718 
719  // It's safe to set the sheet screen now.
720  aSheet->SetScreen( useScreen );
721  }
722  else if( loadFromFile )
723  {
724  if( isExistingSheet )
725  {
726  // Temporarily remove the sheet from the current schematic page so that recursion
727  // and symbol library link tests can be performed with the modified sheet settings.
728  restoreSheet = true;
729  aHierarchy->LastScreen()->Remove( aSheet );
730  }
731 
732  if( !LoadSheetFromFile( aSheet, aHierarchy, newFilename ) )
733  {
734  if( restoreSheet )
735  aHierarchy->LastScreen()->Append( aSheet );
736 
737  return false;
738  }
739 
740  if( restoreSheet )
741  aHierarchy->LastScreen()->Append( aSheet );
742  }
743 
744  wxString tmpFn = userFileName.GetFullPath();
745 
746  if( wxFileName::GetPathSeparator() == '\\' )
747  tmpFn.Replace( "\\", "/" );
748 
749  aSheet->SetFileName( tmpFn );
750  aSheet->SetFileNameSize( dlg.GetFileNameTextSize() );
751  aSheet->SetName( dlg.GetSheetName() );
752  aSheet->SetSheetNameSize( dlg.GetSheetNameTextSize() );
753 
754  if( aSheet->GetName().IsEmpty() )
755  aSheet->SetName( wxString::Format( wxT( "Sheet%8.8lX" ),
756  (long unsigned) aSheet->GetTimeStamp() ) );
757 
758  if( aClearAnnotationNewItems )
759  *aClearAnnotationNewItems = clearAnnotation;
760 
761  GetCanvas()->GetView()->Update( aSheet );
762 
763  OnModify();
764 
765  return true;
766 }
767 
768 
772 
774 {
775  // Delayed initialization (need the preferences to be loaded)
776  if( m_lastSheetPinTextSize.x == -1 )
777  {
780  }
781  return m_lastSheetPinTextSize;
782 }
783 
784 
786 {
787  wxString text;
788  SCH_SHEET_PIN* sheetPin;
789 
790  if( aLabel )
791  {
792  text = aLabel->GetText();
793  m_lastSheetPinType = aLabel->GetShape();
794  }
795 
796  sheetPin = new SCH_SHEET_PIN( aSheet, wxPoint( 0, 0 ), text );
797  sheetPin->SetFlags( IS_NEW );
798  sheetPin->SetTextSize( GetLastSheetPinTextSize() );
799  sheetPin->SetShape( m_lastSheetPinType );
800 
801  if( !aLabel )
802  {
803  DIALOG_EDIT_SHEET_PIN dlg( this, sheetPin );
804 
805  if( dlg.ShowModal() != wxID_OK || sheetPin->GetText().IsEmpty() )
806  {
807  delete sheetPin;
808  return nullptr;
809  }
810  }
811 
812  m_lastSheetPinType = sheetPin->GetShape();
813  m_lastSheetPinTextSize = sheetPin->GetTextSize();
814 
815  sheetPin->SetPosition( (wxPoint) GetCanvas()->GetViewControls()->GetCursorPosition() );
816 
817  return sheetPin;
818 }
819 
820 
822 {
823  if( !aSheet->GetScreen() )
824  return NULL;
825 
826  for( EDA_ITEM* item = aSheet->GetScreen()->GetDrawItems(); item != NULL; item = item->Next() )
827  {
828  if( item->Type() != SCH_HIER_LABEL_T )
829  continue;
830 
831  SCH_HIERLABEL* label = (SCH_HIERLABEL*) item;
832 
833  /* A global label has been found: check if there a corresponding sheet label. */
834  if( !aSheet->HasPin( label->GetText() ) )
835  return label;
836  }
837 
838  return nullptr;
839 }
840 
841 
843 {
844  wxRect DrawArea;
845  BASE_SCREEN* screen = GetScreen();
846 
847  DrawArea.SetSize( GetPageSizeIU() );
848 
849  // Calculate a reasonable dc size, in pixels, and the dc scale to fit
850  // the drawings into the dc size
851  // scale is the ratio resolution (in PPI) / internal units
852  double ppi = 300; // Use 300 pixels per inch to create bitmap images on start
853  double inch2Iu = 1000.0 * IU_PER_MILS;
854  double scale = ppi / inch2Iu;
855 
856  wxSize dcsize = DrawArea.GetSize();
857 
858  int maxdim = std::max( dcsize.x, dcsize.y );
859 
860  // the max size in pixels of the bitmap used to byuild the sheet copy
861  const int maxbitmapsize = 3000;
862 
863  while( int( maxdim * scale ) > maxbitmapsize )
864  {
865  ppi = ppi / 1.5;
866  scale = ppi / inch2Iu;
867  }
868 
869  dcsize.x *= scale;
870  dcsize.y *= scale;
871 
872  // Set draw offset, zoom... to values needed to draw in the memory DC
873  // after saving initial values:
874  wxPoint tmp_startvisu = screen->m_StartVisu;
875  double tmpzoom = screen->GetZoom();
876  wxPoint old_org = screen->m_DrawOrg;
877  screen->m_DrawOrg.x = screen->m_DrawOrg.y = 0;
878  screen->m_StartVisu.x = screen->m_StartVisu.y = 0;
879 
880  screen->SetZoom( 1 ); // we use zoom = 1 in draw functions.
881 
882  wxMemoryDC dc;
883  wxBitmap image( dcsize );
884  dc.SelectObject( image );
885 
886  GRResetPenAndBrush( &dc );
887  GRForceBlackPen( false );
888  screen->m_IsPrinting = true;
889  dc.SetUserScale( scale, scale );
890 
891  dc.Clear();
892  PrintPage( &dc );
893  screen->m_IsPrinting = false;
894 
895  if( wxTheClipboard->Open() )
896  {
897  // This data objects are held by the clipboard, so do not delete them in the app.
898  wxBitmapDataObject* clipbrd_data = new wxBitmapDataObject( image );
899  wxTheClipboard->SetData( clipbrd_data );
900  wxTheClipboard->Close();
901  }
902 
903  // Deselect Bitmap from DC in order to delete the MemoryDC
904  dc.SelectObject( wxNullBitmap );
905 
906  GRForceBlackPen( false );
907 
908  screen->m_StartVisu = tmp_startvisu;
909  screen->m_DrawOrg = old_org;
910  screen->SetZoom( tmpzoom );
911 }
912 
913 
914 bool SCH_EDIT_FRAME::allowCaseSensitiveFileNameClashes( const wxString& aSchematicFileName )
915 {
916  wxString msg;
917  SCH_SCREENS screens;
918  wxFileName fn = aSchematicFileName;
919 
920  wxCHECK( fn.IsAbsolute(), false );
921 
923  && screens.CanCauseCaseSensitivityIssue( aSchematicFileName ) )
924  {
925  msg.Printf( _( "The file name \"%s\" can cause issues with an existing file name\n"
926  "already defined in the schematic on systems that support case\n"
927  "insensitive file names. This will cause issues if you copy this\n"
928  "project to an operating system that supports case insensitive file\n"
929  "names.\n\nDo you wish to continue?" ),
930  fn.GetName() );
931 
932  wxRichMessageDialog dlg( this, msg, _( "Warning" ),
933  wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION );
934  dlg.ShowCheckBox( _( "Do not show this message again." ) );
935  dlg.SetYesNoLabels( wxMessageDialog::ButtonLabel( _( "Create New Sheet" ) ),
936  wxMessageDialog::ButtonLabel( _( "Discard New Sheet" ) ) );
937 
938  if( dlg.ShowModal() == wxID_NO )
939  return false;
940 
941  m_showSheetFileNameCaseSensitivityDlg = !dlg.IsCheckBoxChecked();
942  }
943 
944  return true;
945 }
void DisplayError(wxWindow *aParent, const wxString &aText, int aDisplayTime)
Display an error or warning message box with aMessage.
Definition: confirm.cpp:236
Class 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 Collect(EDA_ITEM *aItem, const KICAD_T aScanList[])
Function Collect scans a DLIST using this class's Inspector method, which does the collection.
void GRResetPenAndBrush(wxDC *DC)
Definition: gr_basic.cpp:119
void SetShape(PINSHEETLABEL_SHAPE aShape)
Definition: sch_text.h:128
const wxString & GetFileName() const
Definition: sch_screen.h:123
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:279
bool SearchHierarchy(const wxString &aFilename, SCH_SCREEN **aScreen)
Search the existing hierarchy for an instance of screen loaded from aFileName.
Definition: sch_sheet.cpp:559
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:467
SCH_ITEM * Next() const
Definition: sch_item.h:153
SCH_SHEET * GetSheet(const wxString &aName)
Returns a sheet object pointer that is named aName.
Definition: sch_screen.cpp:674
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:104
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:842
void Remove(SCH_ITEM *aItem)
Remove aItem from the schematic associated with this screen.
Definition: sch_screen.cpp:190
void SetTextSize(const wxSize &aNewSize)
Definition: eda_text.h:222
static const KICAD_T SheetsOnly[]
Definition: ee_collectors.h:45
SCH_SCREEN * GetScreen()
Definition: sch_sheet.h:281
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:518
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:127
int GetCount() const
Function GetCount returns the number of objects in the list.
Definition: collector.h:113
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:217
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:265
bool allowCaseSensitiveFileNameClashes(const wxString &aSchematicFileName)
Check aSchematicFileName for a potential file name case sensitivity clashes.
Definition: sheet.cpp:914
SCH_SHEET * g_RootSheet
Definition: eeschema.cpp:47
wxString GetName() const
Definition: sch_sheet.h:272
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:200
void SetSheetNameSize(int aSize)
Definition: sch_sheet.h:276
void SetName(const wxString &aName)
Definition: sch_sheet.h:273
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.
Class 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:216
const wxSize & GetLastSheetPinTextSize()
Initializing accessor for the pin text size.
Definition: sheet.cpp:773
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:86
Class 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.
#define _(s)
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:209
wxString GetFileName(void) const
Return the filename corresponding to this sheet.
Definition: sch_sheet.cpp:661
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:47
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:821
const char * name
Definition: DXF_plotter.cpp:61
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.h:154
#define max(a, b)
Definition: auxiliary.h:86
Class EE_TYPE_COLLECTOR merely gathers up all SCH_ITEMs of a given set of KICAD_T type(s).
size_t i
Definition: json11.cpp:649
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:136
Class EDA_ITEM is a base class for most all the KiCad significant classes, used in schematics and boa...
Definition: base_struct.h:163
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:53
void OnModify() override
Must be called after a schematic change in order to set the "modify" flag of the current screen and u...
void SetFileName(const wxString &aFileName)
Definition: sch_screen.h:121
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:785
virtual const wxString & GetText() const
Function GetText returns the string associated with the text object.
Definition: eda_text.h:124
Container class that holds multiple SCH_SCREEN objects in a hierarchy.
Definition: sch_screen.h:491
SCH_SCREEN * LastScreen() const
Function LastScreen.
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:126
void SetTimeStamp(timestamp_t aNewTimeStamp)
Definition: base_struct.h:215
SCH_ITEM * GetDrawItems() const
Definition: sch_screen.h:152