1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
|
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2015 Jean-Pierre Charras, jp.charras at wanadoo.fr
* Copyright (C) 2004-2015 KiCad Developers, see change_log.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
/**
* @file sheet.cpp
*/
#include <fctsys.h>
#include <class_drawpanel.h>
#include <confirm.h>
#include <schframe.h>
#include <base_units.h>
#include <kiface_i.h>
#include <sch_sheet.h>
#include <sch_sheet_path.h>
#include <dialogs/dialog_sch_sheet_props.h>
#include <wildcards_and_files_ext.h>
#include <project.h>
bool SCH_EDIT_FRAME::EditSheet( SCH_SHEET* aSheet, SCH_SHEET_PATH* aHierarchy )
{
if( aSheet == NULL || aHierarchy == NULL )
return false;
SCH_SHEET_LIST hierarchy; // This is the schematic sheet hierarchy.
// Get the new texts
DIALOG_SCH_SHEET_PROPS dlg( this );
wxString units = GetUnitsLabel( g_UserUnit );
dlg.SetFileName( aSheet->GetFileName() );
dlg.SetFileNameTextSize( StringFromValue( g_UserUnit, aSheet->GetFileNameSize() ) );
dlg.SetFileNameTextSizeUnits( units );
dlg.SetSheetName( aSheet->GetName() );
dlg.SetSheetNameTextSize( StringFromValue( g_UserUnit, aSheet->GetSheetNameSize() ) );
dlg.SetSheetNameTextSizeUnits( units );
dlg.SetSheetTimeStamp( wxString::Format( wxT("%8.8lX"),
(unsigned long) aSheet->GetTimeStamp() ) );
/* This ugly hack fixes a bug in wxWidgets 2.8.7 and likely earlier
* versions for the flex grid sizer in wxGTK that prevents the last
* column from being sized correctly. It doesn't cause any problems
* on win32 so it doesn't need to wrapped in ugly #ifdef __WXGTK__
* #endif.
* Still presen in wxWidgets 3.0.2
*/
dlg.Layout();
dlg.Fit();
dlg.SetMinSize( dlg.GetSize() );
dlg.GetSizer()->Fit( &dlg );
if( dlg.ShowModal() == wxID_CANCEL )
return false;
wxFileName fileName = dlg.GetFileName();
fileName.SetExt( SchematicFileExtension );
if( !fileName.IsOk() )
{
DisplayError( this, _( "File name is not valid!" ) );
return false;
}
// Duplicate sheet names are not valid.
const SCH_SHEET* sheet = hierarchy.FindSheetByName( dlg.GetSheetName() );
if( sheet && (sheet != aSheet) )
{
DisplayError( this, wxString::Format( _( "A sheet named \"%s\" already exists." ),
GetChars( dlg.GetSheetName() ) ) );
return false;
}
wxString msg;
bool loadFromFile = false;
SCH_SCREEN* useScreen = NULL;
wxString newFilename = fileName.GetFullPath();
// Search for a schematic file having the same filename
// already in use in the hierarchy or on disk, in order to reuse it.
if( !g_RootSheet->SearchHierarchy( newFilename, &useScreen ) )
{
// if user entered a relative path, allow that to stay, but do the
// file existence test with an absolute (full) path. This transformation
// is local to this scope, but is the same one used at load time later.
wxString absolute = Prj().AbsolutePath( newFilename );
loadFromFile = wxFileExists( absolute );
}
// Inside Eeschema, filenames are stored using unix notation
newFilename.Replace( wxT( "\\" ), wxT( "/" ) );
if( aSheet->GetScreen() == NULL ) // New sheet.
{
if( useScreen || loadFromFile ) // Load from existing file.
{
if( useScreen != NULL )
{
msg.Printf( _( "A file named '%s' already exists in the current schematic "
"hierarchy." ), GetChars( newFilename ) );
}
else
{
msg.Printf( _( "A file named '%s' already exists." ), GetChars( newFilename ) );
}
msg += _( "\n\nDo you want to create a sheet with the contents of this file?" );
if( !IsOK( this, msg ) )
{
return false;
}
}
else // New file.
{
aSheet->SetScreen( new SCH_SCREEN( &Kiway() ) );
aSheet->GetScreen()->SetMaxUndoItems( m_UndoRedoCountMax );
aSheet->GetScreen()->SetFileName( newFilename );
}
}
else // Existing sheet.
{
bool isUndoable = true;
bool renameFile = false;
// We are always using here a case insensitive comparison
// to avoid issues under Windows, although under Unix
// filenames are case sensitive.
// But many users create schematic under both Unix and Windows
if( newFilename.CmpNoCase( aSheet->GetFileName() ) != 0 )
{
// Sheet file name changes cannot be undone.
isUndoable = false;
msg = _( "Changing the sheet file name cannot be undone. " );
if( useScreen || loadFromFile ) // Load from existing file.
{
wxString tmp;
if( useScreen != NULL )
{
tmp.Printf( _( "A file named <%s> already exists in the current schematic "
"hierarchy." ), GetChars( newFilename ) );
}
else
{
tmp.Printf( _( "A file named <%s> already exists." ),
GetChars( newFilename ) );
}
msg += tmp;
msg += _( "\n\nDo you want to replace the sheet with the contents of this file?" );
if( !IsOK( this, msg ) )
return false;
if( loadFromFile )
aSheet->SetScreen( NULL );
}
else // Save to new file name.
{
if( aSheet->GetScreenCount() > 1 )
{
msg += _( "This sheet uses shared data in a complex hierarchy.\n\n" );
msg += _( "Do you wish to convert it to a simple hierarchical sheet?" );
if( !IsOK( NULL, msg ) )
return false;
}
renameFile = true;
}
}
m_canvas->SetIgnoreMouseEvents( true );
if( isUndoable )
SaveCopyInUndoList( aSheet, UR_CHANGED );
if( renameFile )
{
aSheet->GetScreen()->SetFileName( newFilename );
SaveEEFile( aSheet->GetScreen() );
// If the the associated screen is shared by more than one sheet, remove the
// screen and reload the file to a new screen. Failure to do this will trash
// the screen reference counting in complex hierarchies.
if( aSheet->GetScreenCount() > 1 )
{
aSheet->SetScreen( NULL );
loadFromFile = true;
}
}
}
aSheet->SetFileName( newFilename );
if( useScreen )
aSheet->SetScreen( useScreen );
else if( loadFromFile )
aSheet->Load( this );
aSheet->SetFileNameSize( ValueFromString( g_UserUnit, dlg.GetFileNameTextSize() ) );
aSheet->SetName( dlg.GetSheetName() );
aSheet->SetSheetNameSize( ValueFromString( g_UserUnit, dlg.GetSheetNameTextSize() ) );
if( aSheet->GetName().IsEmpty() )
aSheet->SetName( wxString::Format( wxT( "Sheet%8.8lX" ),
(long unsigned) aSheet->GetTimeStamp() ) );
// Make sure the sheet changes do not cause any recursion.
SCH_SHEET_LIST sheetHierarchy( aSheet );
// Make sure files have fully qualified path and file name.
wxFileName destFn = aHierarchy->Last()->GetFileName();
if( destFn.IsRelative() )
destFn.MakeAbsolute( Prj().GetProjectPath() );
if( hierarchy.TestForRecursion( sheetHierarchy, destFn.GetFullPath( wxPATH_UNIX ) ) )
{
msg.Printf( _( "The sheet changes cannot be made because the destination sheet already "
"has the sheet <%s> or one of it's subsheets as a parent somewhere in "
"the schematic hierarchy." ),
GetChars( newFilename ) );
DisplayError( this, msg );
return false;
}
m_canvas->MoveCursorToCrossHair();
m_canvas->SetIgnoreMouseEvents( false );
OnModify();
return true;
}
/* Move selected sheet with the cursor.
* Callback function used by m_mouseCaptureCallback.
* Note also now this function is aclled only when resizing the sheet
* But the (very small code) relative to sheet move is still present here
*/
static void resizeSheetWithMouseCursor( EDA_DRAW_PANEL* aPanel, wxDC* aDC, const wxPoint& aPosition,
bool aErase )
{
BASE_SCREEN* screen = aPanel->GetScreen();
SCH_SHEET* sheet = (SCH_SHEET*) screen->GetCurItem();
if( aErase )
sheet->Draw( aPanel, aDC, wxPoint( 0, 0 ), g_XorMode );
wxPoint pos = sheet->GetPosition();
int width = aPanel->GetParent()->GetCrossHairPosition().x - sheet->GetPosition().x;
int height = aPanel->GetParent()->GetCrossHairPosition().y - sheet->GetPosition().y;
// If the sheet doesn't have any pins, clamp the minimum size to the default values.
width = ( width < MIN_SHEET_WIDTH ) ? MIN_SHEET_WIDTH : width;
height = ( height < MIN_SHEET_HEIGHT ) ? MIN_SHEET_HEIGHT : height;
if( sheet->HasPins() )
{
int gridSizeX = KiROUND( screen->GetGridSize().x );
int gridSizeY = KiROUND( screen->GetGridSize().y );
// If the sheet has pins, use the pin positions to clamp the minimum height.
height = ( height < sheet->GetMinHeight() + gridSizeY ) ?
sheet->GetMinHeight() + gridSizeY : height;
width = ( width < sheet->GetMinWidth() + gridSizeX ) ?
sheet->GetMinWidth() + gridSizeX : width;
}
wxPoint grid = aPanel->GetParent()->GetNearestGridPosition(
wxPoint( pos.x + width, pos.y + height ) );
sheet->Resize( wxSize( grid.x - pos.x, grid.y - pos.y ) );
sheet->Draw( aPanel, aDC, wxPoint( 0, 0 ), g_XorMode );
}
// Complete sheet move.
static void ExitSheet( EDA_DRAW_PANEL* aPanel, wxDC* aDC )
{
SCH_SCREEN* screen = (SCH_SCREEN*) aPanel->GetScreen();
SCH_ITEM* item = screen->GetCurItem();
SCH_EDIT_FRAME* parent = (SCH_EDIT_FRAME*) aPanel->GetParent();
if( (item == NULL) || (item->Type() != SCH_SHEET_T) || (parent == NULL) )
return;
parent->SetRepeatItem( NULL );
item->Draw( aPanel, aDC, wxPoint( 0, 0 ), g_XorMode );
if( item->IsNew() )
{
delete item;
}
else if( item->IsMoving() || item->IsResized() )
{
screen->Remove( item );
delete item;
item = parent->GetUndoItem();
wxCHECK_RET( item != NULL, wxT( "Cannot restore undefined last sheet item." ) );
screen->Append( item );
// the owner of item is no more parent, this is the draw list of screen:
parent->SetUndoItem( NULL );
item->Draw( aPanel, aDC, wxPoint( 0, 0 ), GR_DEFAULT_DRAWMODE );
item->ClearFlags();
}
else
{
item->ClearFlags();
}
screen->SetCurItem( NULL );
}
// Create hierarchy sheet.
SCH_SHEET* SCH_EDIT_FRAME::CreateSheet( wxDC* aDC )
{
SetRepeatItem( NULL );
SCH_SHEET* sheet = new SCH_SHEET( GetCrossHairPosition() );
sheet->SetFlags( IS_NEW | IS_RESIZED );
sheet->SetTimeStamp( GetNewTimeStamp() );
sheet->SetParent( GetScreen() );
sheet->SetScreen( NULL );
// need to check if this is being added to the GetDrawItems().
// also need to update the hierarchy, if we are adding
// a sheet to a screen that already has multiple instances (!)
GetScreen()->SetCurItem( sheet );
m_canvas->SetMouseCapture( resizeSheetWithMouseCursor, ExitSheet );
m_canvas->CallMouseCapture( aDC, wxDefaultPosition, false );
m_canvas->CrossHairOff( aDC );
SetCrossHairPosition( sheet->GetResizePosition() );
m_canvas->MoveCursorToCrossHair();
m_canvas->CrossHairOn( aDC );
return sheet;
}
void SCH_EDIT_FRAME::ReSizeSheet( SCH_SHEET* aSheet, wxDC* aDC )
{
if( aSheet == NULL || aSheet->IsNew() )
return;
wxCHECK_RET( aSheet->Type() == SCH_SHEET_T,
wxString::Format( wxT( "Cannot perform sheet resize on %s object." ),
GetChars( aSheet->GetClass() ) ) );
m_canvas->CrossHairOff( aDC );
SetCrossHairPosition( aSheet->GetResizePosition() );
m_canvas->MoveCursorToCrossHair();
m_canvas->CrossHairOn( aDC );
SetUndoItem( aSheet );
aSheet->SetFlags( IS_RESIZED );
m_canvas->SetMouseCapture( resizeSheetWithMouseCursor, ExitSheet );
m_canvas->CallMouseCapture( aDC, wxDefaultPosition, true );
if( aSheet->IsNew() ) // not already in edit, save a copy for undo/redo
SetUndoItem( aSheet );
}
void SCH_EDIT_FRAME::RotateHierarchicalSheet( SCH_SHEET* aSheet, bool aRotCCW )
{
if( aSheet == NULL )
return;
// Save old sheet in undo list if not already in edit, or moving.
if( aSheet->GetFlags() == 0 )
SaveCopyInUndoList( aSheet, UR_CHANGED );
// Rotate the sheet on itself. Sheets do not have a anchor point.
// Rotation is made around it center
wxPoint rotPoint = aSheet->GetBoundingBox().Centre();
// rotate CCW, or CW. to rotate CW, rotate 3 times
aSheet->Rotate( rotPoint );
if( !aRotCCW )
{
aSheet->Rotate( rotPoint );
aSheet->Rotate( rotPoint );
}
GetCanvas()->Refresh();
OnModify();
}
void SCH_EDIT_FRAME::MirrorSheet( SCH_SHEET* aSheet, bool aFromXaxis )
{
if( aSheet == NULL )
return;
// Save old sheet in undo list if not already in edit, or moving.
if( aSheet->GetFlags() == 0 )
SaveCopyInUndoList( aSheet, UR_CHANGED );
// Mirror the sheet on itself. Sheets do not have a anchor point.
// Mirroring is made around it center
wxPoint mirrorPoint = aSheet->GetBoundingBox().Centre();
if( aFromXaxis ) // mirror relative to Horizontal axis
aSheet->MirrorX( mirrorPoint.y );
else // Mirror relative to vertical axis
aSheet->MirrorY( mirrorPoint.x );
GetCanvas()->Refresh();
OnModify();
}
|