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 <sch_edit_frame.h>
32 #include <sch_legacy_plugin.h>
33 #include <sch_sheet.h>
34 #include <sch_sheet_path.h>
35 #include <sch_view.h>
38 
39 
40 void SCH_EDIT_FRAME::InitSheet( SCH_SHEET* aSheet, const wxString& aFilename )
41 {
42  aSheet->SetScreen( new SCH_SCREEN( &Kiway() ) );
43  aSheet->GetScreen()->SetModify();
45  aSheet->GetScreen()->SetFileName( aFilename );
46 }
47 
48 
50  bool* aClearAnnotationNewItems )
51 {
52  if( aSheet == NULL || aHierarchy == NULL )
53  return false;
54 
55  SCH_SHEET_LIST hierarchy( g_RootSheet ); // This is the schematic sheet hierarchy.
56 
57  // Get the new texts
58  DIALOG_SCH_SHEET_PROPS dlg( this, aSheet );
59 
60  if( dlg.ShowModal() == wxID_CANCEL )
61  return false;
62 
63  wxFileName fileName = dlg.GetFileName();
64  fileName.SetExt( SchematicFileExtension );
65 
66  wxString msg;
67  bool loadFromFile = false;
68  bool clearAnnotation = false;
69  SCH_SCREEN* useScreen = NULL;
70 
71  // Relative file names are relative to the path of the current sheet. This allows for
72  // nesting of schematic files in subfolders.
73  if( !fileName.IsAbsolute() )
74  {
75  const SCH_SCREEN* currentScreen = aHierarchy->LastScreen();
76 
77  wxCHECK_MSG( currentScreen, false, "Invalid sheet path object." );
78 
79  wxFileName currentSheetFileName = currentScreen->GetFileName();
80 
81  wxCHECK_MSG( fileName.Normalize( wxPATH_NORM_ALL, currentSheetFileName.GetPath() ), false,
82  "Cannot normalize new sheet schematic file path." );
83  }
84 
85  wxString newFilename = fileName.GetFullPath();
86 
87  // Search for a schematic file having the same filename
88  // already in use in the hierarchy or on disk, in order to reuse it.
89  if( !g_RootSheet->SearchHierarchy( newFilename, &useScreen ) )
90  {
91  loadFromFile = wxFileExists( newFilename );
92  wxLogDebug( "Sheet requested file \"%s\", %s",
93  newFilename,
94  ( loadFromFile ) ? "found" : "not found" );
95  }
96 
97  // Inside Eeschema, filenames are stored using unix notation
98  newFilename.Replace( wxT( "\\" ), wxT( "/" ) );
99 
100  SCH_PLUGIN::SCH_PLUGIN_RELEASER pi( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_LEGACY ) );
101 
102  if( aSheet->GetScreen() == NULL ) // New sheet.
103  {
104  if( useScreen || loadFromFile ) // Load from existing file.
105  {
106  clearAnnotation = true;
107 
108  wxString existsMsg;
109  wxString linkMsg;
110  existsMsg.Printf( _( "\"%s\" already exists." ), fileName.GetFullName() );
111  linkMsg.Printf( _( "Link \"%s\" to this file?" ), dlg.GetSheetName() );
112  msg.Printf( wxT( "%s\n\n%s" ), existsMsg, linkMsg );
113 
114  if( !IsOK( this, msg ) )
115  return false;
116 
117  }
118  else // New file.
119  {
120  InitSheet( aSheet, newFilename );
121  }
122  }
123  else // Existing sheet.
124  {
125  bool isUndoable = true;
126  bool renameFile = false;
127  wxString replaceMsg;
128  wxString newMsg;
129  wxString noUndoMsg;
130 
131  // Changing the filename of a sheet can modify the full hierarchy structure
132  // and can be not always undoable.
133  // So prepare messages for user notifications:
134  replaceMsg.Printf( _( "Change \"%s\" link from \"%s\" to \"%s\"?" ),
135  dlg.GetSheetName(), aSheet->GetFileName(), fileName.GetFullName() );
136  newMsg.Printf( _( "Create new file \"%s\" with contents of \"%s\"?" ),
137  fileName.GetFullName(), aSheet->GetFileName() );
138  noUndoMsg = _( "This action cannot be undone." );
139 
140  // We are always using here a case insensitive comparison
141  // to avoid issues under Windows, although under Unix
142  // filenames are case sensitive.
143  // But many users create schematic under both Unix and Windows
144  // **
145  // N.B. 1: aSheet->GetFileName() will return a relative path
146  // aSheet->GetScreen()->GetFileName() returns a full path
147  //
148  // N.B. 2: newFilename uses the unix notation for separator.
149  // so we must use it also to compare the old filename to the new filename
150  wxString oldFilename = aSheet->GetScreen()->GetFileName();
151  oldFilename.Replace( wxT( "\\" ), wxT( "/" ) );
152 
153  if( newFilename.CmpNoCase( oldFilename ) != 0 )
154  {
155  // Sheet file name changes cannot be undone.
156  isUndoable = false;
157 
158  if( useScreen || loadFromFile ) // Load from existing file.
159  {
160  clearAnnotation = true;
161 
162  msg.Printf( wxT( "%s\n\n%s" ), replaceMsg, noUndoMsg );
163 
164  if( !IsOK( this, msg ) )
165  return false;
166 
167  if( loadFromFile )
168  aSheet->SetScreen( NULL );
169  }
170  else // Save to new file name.
171  {
172  if( aSheet->GetScreenCount() > 1 )
173  {
174  msg.Printf( wxT( "%s\n\n%s" ), newMsg, noUndoMsg );
175 
176  if( !IsOK( this, msg ) )
177  return false;
178  }
179 
180  renameFile = true;
181  }
182  }
183 
185 
186  if( isUndoable )
187  SaveCopyInUndoList( aSheet, UR_CHANGED );
188 
189  if( renameFile )
190  {
191  // If the the associated screen is shared by more than one sheet, do not
192  // change the filename of the corresponding screen here.
193  // (a new screen will be created later)
194  // if it is not shared, update the filename
195  if( aSheet->GetScreenCount() <= 1 )
196  aSheet->GetScreen()->SetFileName( newFilename );
197 
198  try
199  {
200  pi->Save( newFilename, aSheet->GetScreen(), &Kiway() );
201  }
202  catch( const IO_ERROR& ioe )
203  {
204  msg.Printf( _( "Error occurred saving schematic file \"%s\"." ), newFilename );
205  DisplayErrorMessage( this, msg, ioe.What() );
206 
207  msg.Printf( _( "Failed to save schematic \"%s\"" ), newFilename );
208  AppendMsgPanel( wxEmptyString, msg, CYAN );
209 
210  return false;
211  }
212 
213  // If the the associated screen is shared by more than one sheet, remove the
214  // screen and reload the file to a new screen. Failure to do this will trash
215  // the screen reference counting in complex hierarchies.
216  if( aSheet->GetScreenCount() > 1 )
217  {
218  aSheet->SetScreen( NULL );
219  loadFromFile = true;
220  }
221  }
222  }
223 
224  wxFileName userFileName = dlg.GetFileName();
225  userFileName.SetExt( SchematicFileExtension );
226  aSheet->SetFileName( userFileName.GetFullPath( wxPATH_UNIX ) );
227 
228  if( useScreen )
229  {
230  aSheet->SetScreen( useScreen );
231  }
232  else if( loadFromFile )
233  {
234  try
235  {
236  aSheet = pi->Load( newFilename, &Kiway(), aSheet );
237 
238  if( !pi->GetError().IsEmpty() )
239  {
240  DisplayErrorMessage( this,
241  _( "The entire schematic could not be loaded.\n"
242  "Errors occurred loading hierarchical sheets." ),
243  pi->GetError() );
244  }
245  }
246  catch( const IO_ERROR& ioe )
247  {
248  msg.Printf( _( "Error occurred loading schematic file \"%s\"." ), newFilename );
249  DisplayErrorMessage( this, msg, ioe.What() );
250 
251  msg.Printf( _( "Failed to load schematic \"%s\"" ), newFilename );
252  AppendMsgPanel( wxEmptyString, msg, CYAN );
253 
254  return false;
255  }
256  }
257 
258  aSheet->SetFileNameSize( dlg.GetFileNameTextSize() );
259  aSheet->SetName( dlg.GetSheetName() );
260  aSheet->SetSheetNameSize( dlg.GetSheetNameTextSize() );
261 
262  if( aSheet->GetName().IsEmpty() )
263  aSheet->SetName( wxString::Format( wxT( "Sheet%8.8lX" ),
264  (long unsigned) aSheet->GetTimeStamp() ) );
265 
266  // Make sure the sheet changes do not cause any recursion.
267  SCH_SHEET_LIST sheetHierarchy( aSheet );
268 
269  // Make sure files have fully qualified path and file name.
270  wxFileName destFn = aHierarchy->Last()->GetFileName();
271 
272  if( destFn.IsRelative() )
273  destFn.MakeAbsolute( Prj().GetProjectPath() );
274 
275  if( hierarchy.TestForRecursion( sheetHierarchy, destFn.GetFullPath( wxPATH_UNIX ) ) )
276  {
277  msg.Printf( _( "The sheet changes cannot be made because the destination sheet already "
278  "has the sheet \"%s\" or one of it's subsheets as a parent somewhere in "
279  "the schematic hierarchy." ),
280  newFilename );
281  DisplayError( this, msg );
282  return false;
283  }
284 
285  // Check to make sure the symbols have been remapped to the symbol library table.
286  SCH_SCREENS newScreens( aSheet );
287 
288  if( newScreens.HasNoFullyDefinedLibIds() )
289  {
290  msg.Printf(_( "The schematic \"%s\" has not been remapped to the symbol\nlibrary table. "
291  " The project this schematic belongs to must first be remapped\nbefore it "
292  "can be imported into the current project." ), fileName.GetFullName() );
293 
294  DisplayInfoMessage( this, msg );
295  return false;
296  }
297 
298  if( aClearAnnotationNewItems )
299  *aClearAnnotationNewItems = clearAnnotation;
300 
302  m_canvas->SetIgnoreMouseEvents( false );
303 
304  GetCanvas()->GetView()->Update( aSheet );
305 
306  OnModify();
307 
308  return true;
309 }
310 
311 
315 
317 {
318  // Delayed initialization (need the preferences to be loaded)
319  if( m_lastSheetPinTextSize.x == -1 )
320  {
323  }
324  return m_lastSheetPinTextSize;
325 }
326 
327 
328 int SCH_EDIT_FRAME::EditSheetPin( SCH_SHEET_PIN* aSheetPin, bool aRedraw )
329 {
330  if( aSheetPin == NULL )
331  return wxID_CANCEL;
332 
333  DIALOG_SCH_EDIT_SHEET_PIN dlg( this, aSheetPin );
334 
335  if( dlg.ShowModal() == wxID_CANCEL )
336  return wxID_CANCEL;
337 
338  if( aRedraw )
339  RefreshItem( aSheetPin );
340 
341  return wxID_OK;
342 }
343 
344 
346 {
347  wxString line;
348  SCH_SHEET_PIN* sheetPin;
349 
350  sheetPin = new SCH_SHEET_PIN( aSheet, wxPoint( 0, 0 ), line );
351  sheetPin->SetFlags( IS_NEW );
352  sheetPin->SetTextSize( GetLastSheetPinTextSize() );
353  sheetPin->SetShape( m_lastSheetPinType );
354 
355  int response = EditSheetPin( sheetPin, false );
356 
357  if( sheetPin->GetText().IsEmpty() || (response == wxID_CANCEL) )
358  {
359  delete sheetPin;
360  return NULL;
361  }
362 
363  m_lastSheetPinType = sheetPin->GetShape();
364  m_lastSheetPinTextSize = sheetPin->GetTextSize();
365 
366  sheetPin->SetPosition( GetCrossHairPosition() );
367 
368  return sheetPin;
369 }
370 
371 
373 {
374  EDA_ITEM* item;
375  SCH_SHEET_PIN* sheetPin;
376  SCH_HIERLABEL* label = NULL;
377 
378  if( !aSheet->GetScreen() )
379  return NULL;
380 
381  item = aSheet->GetScreen()->GetDrawItems();
382 
383  for( ; item != NULL; item = item->Next() )
384  {
385  if( item->Type() != SCH_HIER_LABEL_T )
386  continue;
387 
388  label = (SCH_HIERLABEL*) item;
389 
390  /* A global label has been found: check if there a corresponding sheet label. */
391  if( !aSheet->HasPin( label->GetText() ) )
392  break;
393 
394  label = NULL;
395  }
396 
397  if( label == NULL )
398  {
399  DisplayInfoMessage( this, _( "No new hierarchical labels found." ) );
400  return NULL;
401  }
402 
403  sheetPin = new SCH_SHEET_PIN( aSheet, wxPoint( 0, 0 ), label->GetText() );
404  sheetPin->SetFlags( IS_NEW );
405  sheetPin->SetTextSize( GetLastSheetPinTextSize() );
406  m_lastSheetPinType = label->GetShape();
407  sheetPin->SetShape( label->GetShape() );
408  sheetPin->SetPosition( GetCrossHairPosition() );
409 
410  return sheetPin;
411 }
Class SCH_SHEET_LIST.
void SetShape(PINSHEETLABEL_SHAPE aShape)
Definition: sch_text.h:123
const wxString & GetFileName() const
Definition: sch_screen.h:124
void SetFileNameSize(int aSize)
Definition: sch_sheet.h:266
bool SearchHierarchy(const wxString &aFilename, SCH_SCREEN **aScreen)
Search the existing hierarchy for an instance of screen loaded from aFileName.
Definition: sch_sheet.cpp:544
KIWAY & Kiway() const
Function Kiway returns a reference to the KIWAY that this object has an opportunity to participate in...
Definition: kiway_player.h:61
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Function DisplayErrorMessage displays an error message with aMessage.
Definition: confirm.cpp:258
SCH_SHEET * Last() const
Function Last returns a pointer to the last sheet of the list One can see the others sheet as the "pa...
This file is part of the common library.
void SetFileName(const wxString &aFilename)
Definition: sch_sheet.h:454
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
SCH_SHEET_PIN * CreateSheetPin(SCH_SHEET *aSheet)
Create a new SCH_SHEET_PIN object and add it to aSheet at the current cursor position.
Definition: sheet.cpp:345
virtual const wxString GetText() const
Function GetText returns the string associated with the text object.
Definition: eda_text.h:147
void SetTextSize(const wxSize &aNewSize)
Definition: eda_text.h:231
virtual void MoveCursorToCrossHair()
Function MoveCursorToCrossHair warps the cursor to the current cross hair position.
SCH_SCREEN * GetScreen()
Definition: sch_sheet.h:268
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:49
EDA_ITEM * Next() const
Definition: base_struct.h:212
Subclass of DIALOG_SCH_EDIT_SHEET_PIN_BASE, which is generated by wxFormBuilder.
#define IS_NEW
New item, just created.
Definition: base_struct.h:114
int GetScreenCount() const
Return the number of times the associated screen for the sheet is being used.
Definition: sch_sheet.cpp:127
void SetFlags(STATUS_FLAGS aMask)
Definition: base_struct.h:259
SCH_SHEET * g_RootSheet
Definition: eeschema.cpp:56
wxString GetName() const
Definition: sch_sheet.h:259
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 SetSheetNameSize(int aSize)
Definition: sch_sheet.h:263
void SetName(const wxString &aName)
Definition: sch_sheet.h:260
PROJECT & Prj() const
Function Prj returns a reference to the PROJECT "associated with" this KIWAY.
const wxSize & GetTextSize() const
Definition: eda_text.h:232
const std::string SchematicFileExtension
SCH_DRAW_PANEL * GetCanvas() const override
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:210
SCH_SHEET_PIN * ImportSheetPin(SCH_SHEET *aSheet)
Automatically create a sheet pin from the hierarchical labels in the schematic referenced by aSheet.
Definition: sheet.cpp:372
const wxSize & GetLastSheetPinTextSize()
Initializing accessor for the pin text size.
Definition: sheet.cpp:316
Define a sheet pin (label) used in sheets to create hierarchical schematics.
Definition: sch_sheet.h:56
bool HasNoFullyDefinedLibIds()
Test all of the schematic symbols to see if all LIB_ID objects library nickname is not set.
int m_UndoRedoCountMax
default Undo/Redo command Max depth, to be handed
Definition: draw_frame.h:123
void InitSheet(SCH_SHEET *aSheet, const wxString &aFilename)
Definition: sheet.cpp:40
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:1539
KIGFX::SCH_VIEW * GetView() const
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition: sch_sheet.h:201
wxString GetFileName(void) const
Return the filename corresponding to this sheet.
Definition: sch_sheet.cpp:627
Helper object to release a SCH_PLUGIN in the context of a potential thrown exception through its dest...
Definition: sch_io_mgr.h:523
EDA_DRAW_PANEL * m_canvas
The area to draw on.
Definition: draw_frame.h:128
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:187
PINSHEETLABEL_SHAPE
Definition: sch_text.h:47
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 RefreshItem(EDA_ITEM *aItem, bool isAddOrDelete=false)
Mark an item for refresh.
void SetIgnoreMouseEvents(bool aIgnore)
Implementing DIALOG_SCH_SHEET_PROPS_BASE.
void SetMaxUndoItems(int aMax)
Definition: base_screen.h:313
Class EDA_ITEM is a base class for most all the KiCad significant classes, used in schematics and boa...
Definition: base_struct.h:157
bool HasPin(const wxString &aName)
Checks if the sheet already has a sheet pin named aName.
Definition: sch_sheet.cpp:209
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:122
void SetModify()
Definition: base_screen.h:324
Definition of the SCH_SHEET_PATH and SCH_SHEET_LIST classes for Eeschema.
static wxPoint m_lastSheetPinPosition
Last sheet pin position.
int EditSheetPin(SCH_SHEET_PIN *aSheetPin, bool aRedraw)
Display the dialog for editing the parameters of aSheetPin.
Definition: sheet.cpp:328
bool TestForRecursion(const SCH_SHEET_LIST &aSrcSheetHierarchy, const wxString &aDestFileName) const
Function TestForRecursion.
void DisplayInfoMessage(wxWindow *aParent, const wxString &aMessage, const wxString &aExtraInfo)
Function DisplayInfoMessage displays an informational message box with aMessage.
Definition: confirm.cpp:276
void DisplayError(wxWindow *parent, const wxString &text, int displaytime)
Function DisplayError displays an error or warning message box with aMessage.
Definition: confirm.cpp:244
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)
Function IsOK displays a yes/no dialog with aMessage and returns the user response.
Definition: confirm.cpp:294
Container class that holds multiple SCH_SCREEN objects in a hierarchy.
Definition: sch_screen.h:493
SCH_SCREEN * LastScreen() const
Function LastScreen.
int GetDefaultTextSize()
Default size for text in general.
PINSHEETLABEL_SHAPE GetShape() const
Definition: sch_text.h:121
wxPoint GetCrossHairPosition(bool aInvertY=false) const
Return the current cross hair position in logical (drawing) coordinates.
KICAD_T Type() const
Function Type()
Definition: base_struct.h:204
SCH_ITEM * GetDrawItems() const
Definition: sch_screen.h:153