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( useScreen || loadFromFile ) // Load from existing file.
573  {
574  clearAnnotation = true;
575 
576  wxString existsMsg;
577  wxString linkMsg;
578  existsMsg.Printf( _( "\"%s\" already exists." ), fileName.GetFullName() );
579  linkMsg.Printf( _( "Link \"%s\" to this file?" ), dlg.GetSheetName() );
580  msg.Printf( wxT( "%s\n\n%s" ), existsMsg, linkMsg );
581 
582  if( !IsOK( this, msg ) )
583  return false;
584 
585  }
586  else // New file.
587  {
588  InitSheet( aSheet, newFilename );
589  }
590  }
591  else // Existing sheet.
592  {
593  bool isUndoable = true;
594  wxString replaceMsg;
595  wxString newMsg;
596  wxString noUndoMsg;
597 
598  isExistingSheet = true;
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.CmpNoCase( 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_SCH_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 NULL;
819 
820  for( EDA_ITEM* item = aSheet->GetScreen()->GetDrawItems(); item != NULL; item = item->Next() )
821  {
822  if( item->Type() != SCH_HIER_LABEL_T )
823  continue;
824 
825  SCH_HIERLABEL* label = (SCH_HIERLABEL*) item;
826 
827  /* A global label has been found: check if there a corresponding sheet label. */
828  if( !aSheet->HasPin( label->GetText() ) )
829  return label;
830  }
831 
832  return nullptr;
833 }
834 
835 
836 /*
837  * Copy the current page or block to the clipboard, to export drawings to other applications
838  * (word processing ...) This is not suitable for copy command within Eeschema or Pcbnew.
839  */
841 {
842  wxRect DrawArea;
843  BASE_SCREEN* screen = GetScreen();
844 
845  DrawArea.SetSize( GetPageSizeIU() );
846 
847  // Calculate a reasonable dc size, in pixels, and the dc scale to fit
848  // the drawings into the dc size
849  // scale is the ratio resolution (in PPI) / internal units
850  double ppi = 300; // Use 300 pixels per inch to create bitmap images on start
851  double inch2Iu = 1000.0 * IU_PER_MILS;
852  double scale = ppi / inch2Iu;
853 
854  wxSize dcsize = DrawArea.GetSize();
855 
856  int maxdim = std::max( dcsize.x, dcsize.y );
857 
858  // the max size in pixels of the bitmap used to byuild the sheet copy
859  const int maxbitmapsize = 3000;
860 
861  while( int( maxdim * scale ) > maxbitmapsize )
862  {
863  ppi = ppi / 1.5;
864  scale = ppi / inch2Iu;
865  }
866 
867  dcsize.x *= scale;
868  dcsize.y *= scale;
869 
870  // Set draw offset, zoom... to values needed to draw in the memory DC
871  // after saving initial values:
872  wxPoint tmp_startvisu = screen->m_StartVisu;
873  double tmpzoom = screen->GetZoom();
874  wxPoint old_org = screen->m_DrawOrg;
875  screen->m_DrawOrg.x = screen->m_DrawOrg.y = 0;
876  screen->m_StartVisu.x = screen->m_StartVisu.y = 0;
877 
878  screen->SetZoom( 1 ); // we use zoom = 1 in draw functions.
879 
880  wxMemoryDC dc;
881  wxBitmap image( dcsize );
882  dc.SelectObject( image );
883 
884  GRResetPenAndBrush( &dc );
885  GRForceBlackPen( false );
886  screen->m_IsPrinting = true;
887  dc.SetUserScale( scale, scale );
888 
889  dc.Clear();
890  PrintPage( &dc );
891  screen->m_IsPrinting = false;
892 
893  if( wxTheClipboard->Open() )
894  {
895  // This data objects are held by the clipboard, so do not delete them in the app.
896  wxBitmapDataObject* clipbrd_data = new wxBitmapDataObject( image );
897  wxTheClipboard->SetData( clipbrd_data );
898  wxTheClipboard->Close();
899  }
900 
901  // Deselect Bitmap from DC in order to delete the MemoryDC
902  dc.SelectObject( wxNullBitmap );
903 
904  GRForceBlackPen( false );
905 
906  screen->m_StartVisu = tmp_startvisu;
907  screen->m_DrawOrg = old_org;
908  screen->SetZoom( tmpzoom );
909 }
910 
911 
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:120
void SetShape(PINSHEETLABEL_SHAPE aShape)
Definition: sch_text.h:123
const wxString & GetFileName() const
Definition: sch_screen.h:118
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:269
bool SearchHierarchy(const wxString &aFilename, SCH_SCREEN **aScreen)
Search the existing hierarchy for an instance of screen loaded from aFileName.
Definition: sch_sheet.cpp:542
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:258
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:457
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:676
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:840
void Remove(SCH_ITEM *aItem)
Remove aItem from the schematic associated with this screen.
Definition: sch_screen.cpp:189
void SetTextSize(const wxSize &aNewSize)
Definition: eda_text.h:220
static const KICAD_T SheetsOnly[]
Definition: ee_collectors.h:45
SCH_SCREEN * GetScreen()
Definition: sch_sheet.h:271
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...
const wxString & GetDescr() const
Return the description of the library referenced by this row.
Subclass of DIALOG_SCH_EDIT_SHEET_PIN_BASE, which is generated by wxFormBuilder.
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
SCH_SHEET * g_RootSheet
Definition: eeschema.cpp:47
wxString GetName() const
Definition: sch_sheet.h:262
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:266
void SetName(const wxString &aName)
Definition: sch_sheet.h:263
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:221
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: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: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:204
wxString GetFileName(void) const
Return the filename corresponding to this sheet.
Definition: sch_sheet.cpp:629
Helper object to release a SCH_PLUGIN in the context of a potential thrown exception through its dest...
Definition: sch_io_mgr.h:520
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:190
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:815
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:149
#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:597
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:134
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 ...
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:50
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:116
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:276
void DisplayError(wxWindow *parent, const wxString &text, int displaytime)
Display an error or warning message box with aMessage.
Definition: confirm.cpp:243
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:294
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
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:486
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:121
void SetTimeStamp(timestamp_t aNewTimeStamp)
Definition: base_struct.h:215
SCH_ITEM * GetDrawItems() const
Definition: sch_screen.h:147