diff options
Diffstat (limited to 'eeschema/sheet.cpp')
-rw-r--r-- | eeschema/sheet.cpp | 452 |
1 files changed, 452 insertions, 0 deletions
diff --git a/eeschema/sheet.cpp b/eeschema/sheet.cpp new file mode 100644 index 0000000..95005c6 --- /dev/null +++ b/eeschema/sheet.cpp @@ -0,0 +1,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(); +} |