kicad/eeschema/sheet.cpp

378 lines
12 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// Name: sheet.cpp
// Purpose:
// Author: jean-pierre Charras
// Modified by: Wayne Stambaugh
// Created: 08/02/2006 18:37:02
// RCS-ID:
// Copyright: License GNU
// License:
/////////////////////////////////////////////////////////////////////////////
#include "fctsys.h"
#include "gr_basic.h"
#include "common.h"
#include "macros.h"
#include "class_drawpanel.h"
#include "confirm.h"
#include "gestfich.h"
#include "wxEeschemaStruct.h"
#include "class_sch_screen.h"
#include "general.h"
#include "protos.h"
#include "sch_sheet.h"
#include "dialogs/dialog_sch_sheet_props.h"
#include <boost/foreach.hpp>
static int s_PreviousSheetWidth;
static int s_PreviousSheetHeight;
static wxPoint s_OldPos; /* Former position for cancellation or move ReSize */
/**
* Function EditSheet
* is used to edit an existing sheet or add a new sheet to the schematic.
* <p>
* When \a aSheet is a new sheet:
* <ul>
* <li>and the file name already exists in the schematic hierarchy, the screen associated with
* the sheet found in the hierarchy is associated with \a aSheet.</li>
* <li>and the file name already exists on the system, then \a aSheet is loaded with the
* existing file.</li>
* <li>and the file name does not exist in the schematic hierarchy or on the file system, then
* a new screen is created and associated with \a aSheet.</li>
* </ul> </p> <p>
* When \a aSheet is an existing sheet:
* <ul>
* <li>and the file name already exists in the schematic hierarchy, the current associated screen
* is replace by the one found in the hierarchy.</li>
* <li>and the file name already exists on the system, the current associated screen file name
* is changed and the file is loaded.</li>
* <li>and the file name does not exist in the schematic hierarchy or on the file system, the
* current associated screen file name is changed and saved to disk.</li>
* </ul> </p>
*/
bool SCH_EDIT_FRAME::EditSheet( SCH_SHEET* aSheet, wxDC* aDC )
{
if( aSheet == NULL )
return false;
/* Get the new texts */
DIALOG_SCH_SHEET_PROPS dlg( this );
wxString units = GetUnitsLabel( g_UserUnit );
dlg.SetFileName( aSheet->GetFileName() );
dlg.SetFileNameTextSize( ReturnStringFromValue( g_UserUnit,
aSheet->m_FileNameSize,
m_InternalUnits ) );
dlg.SetFileNameTextSizeUnits( units );
dlg.SetSheetName( aSheet->m_SheetName );
dlg.SetSheetNameTextSize( ReturnStringFromValue( g_UserUnit,
aSheet->m_SheetNameSize,
m_InternalUnits ) );
dlg.SetSheetNameTextSizeUnits( units );
/* 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.
*/
dlg.Layout();
dlg.Fit();
dlg.SetMinSize( dlg.GetSize() );
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;
}
wxString msg;
wxString tmp;
bool loadFromFile = false;
SCH_SCREEN* useScreen = NULL;
if( !g_RootSheet->SearchHierarchy( fileName.GetFullPath(), &useScreen ) )
loadFromFile = fileName.FileExists();
if( aSheet->GetScreen() == NULL ) // New sheet.
{
if( ( useScreen != NULL ) || loadFromFile ) // Load from existing file.
{
msg.Printf( _( "A file named \"%s\" already exists" ),
GetChars( fileName.GetFullName() ) );
if( useScreen != NULL )
msg += _( " in the current schematic hierarchy" );
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() );
aSheet->GetScreen()->SetFileName( fileName.GetFullPath() );
}
}
else // Existing sheet.
{
bool isUndoable = true;
bool renameFile = false;
if( fileName.GetFullName().CmpNoCase( aSheet->GetFileName() ) != 0 )
{
// Sheet file name changes cannot be undone.
isUndoable = false;
msg = _( "Changing the sheet file name cannot be undone. " );
if( ( useScreen != NULL ) || loadFromFile ) // Load from existing file.
{
tmp.Printf( _( "A file named \"%s\" already exists" ),
GetChars( fileName.GetFullName() ) );
msg += tmp;
if( useScreen != NULL )
msg += _( " in the current schematic hierarchy" );
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;
}
}
aSheet->Draw( DrawPanel, aDC, wxPoint( 0, 0 ), g_XorMode );
DrawPanel->m_IgnoreMouseEvents = true;
if( isUndoable )
SaveCopyInUndoList( aSheet, UR_CHANGED );
if( renameFile )
{
aSheet->GetScreen()->SetFileName( fileName.GetFullName() );
SaveEEFile( aSheet->GetScreen(), FILE_SAVE_AS );
// 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( fileName.GetFullPath() );
if( useScreen )
aSheet->SetScreen( useScreen );
else if( loadFromFile )
aSheet->Load( this );
aSheet->m_FileNameSize = ReturnValueFromString( g_UserUnit,
dlg.GetFileNameTextSize(),
m_InternalUnits );
aSheet->m_SheetName = dlg.GetSheetName();
aSheet->m_SheetNameSize = ReturnValueFromString( g_UserUnit,
dlg.GetSheetNameTextSize(),
m_InternalUnits );
if( aSheet->m_SheetName.IsEmpty() )
aSheet->m_SheetName.Printf( wxT( "Sheet%8.8lX" ), GetTimeStamp() );
DrawPanel->MouseToCursorSchema();
DrawPanel->m_IgnoreMouseEvents = false;
aSheet->Draw( DrawPanel, aDC, wxPoint( 0, 0 ), GR_DEFAULT_DRAWMODE );
return true;
}
/* Move selected sheet with the cursor.
* Callback function use by ManageCurseur.
*/
static void MoveOrResizeSheet( EDA_DRAW_PANEL* aPanel, wxDC* aDC, bool aErase )
{
wxPoint moveVector;
BASE_SCREEN* screen = aPanel->GetScreen();
SCH_SHEET* sheet = (SCH_SHEET*) screen->GetCurItem();
if( aErase )
sheet->Draw( aPanel, aDC, wxPoint( 0, 0 ), g_XorMode );
if( sheet->m_Flags & IS_RESIZED )
{
wxSize newSize( MAX( s_PreviousSheetWidth, screen->m_Curseur.x - sheet->m_Pos.x ),
MAX( s_PreviousSheetHeight, screen->m_Curseur.y - sheet->m_Pos.y ) );
sheet->Resize( newSize );
}
else /* Move Sheet */
{
moveVector = screen->m_Curseur - sheet->m_Pos;
sheet->Move( moveVector );
}
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_SHEET* sheet = (SCH_SHEET*) screen->GetCurItem();
if( sheet == NULL )
return;
if( sheet->m_Flags & IS_NEW )
{
sheet->Draw( aPanel, aDC, wxPoint( 0, 0 ), g_XorMode );
SAFE_DELETE( sheet );
}
else if( (sheet->m_Flags & (IS_RESIZED|IS_MOVED)) )
{
wxPoint curspos = screen->m_Curseur;
aPanel->GetScreen()->m_Curseur = s_OldPos;
MoveOrResizeSheet( aPanel, aDC, true );
sheet->Draw( aPanel, aDC, wxPoint( 0, 0 ), GR_DEFAULT_DRAWMODE );
sheet->m_Flags = 0;
screen->m_Curseur = curspos;
}
else
{
sheet->m_Flags = 0;
}
screen->SetCurItem( NULL );
aPanel->ManageCurseur = NULL;
aPanel->ForceCloseManageCurseur = NULL;
SAFE_DELETE( g_ItemToUndoCopy );
}
#define SHEET_MIN_WIDTH 500
#define SHEET_MIN_HEIGHT 150
/* Create hierarchy sheet. */
SCH_SHEET* SCH_EDIT_FRAME::CreateSheet( wxDC* aDC )
{
m_itemToRepeat = NULL;
SAFE_DELETE( g_ItemToUndoCopy );
SCH_SHEET* sheet = new SCH_SHEET( GetScreen()->m_Curseur );
sheet->m_Flags = IS_NEW | IS_RESIZED;
sheet->m_TimeStamp = GetTimeStamp();
sheet->SetParent( GetScreen() );
sheet->SetScreen( NULL );
s_PreviousSheetWidth = SHEET_MIN_WIDTH;
s_PreviousSheetHeight = SHEET_MIN_HEIGHT;
// 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 );
DrawPanel->ManageCurseur = MoveOrResizeSheet;
DrawPanel->ForceCloseManageCurseur = ExitSheet;
DrawPanel->ManageCurseur( DrawPanel, aDC, false );
return sheet;
}
void SCH_EDIT_FRAME::ReSizeSheet( SCH_SHEET* aSheet, wxDC* aDC )
{
if( aSheet == NULL || aSheet->m_Flags & IS_NEW )
return;
if( aSheet->Type() != SCH_SHEET_T )
{
DisplayError( this, wxT( "SCH_EDIT_FRAME::ReSizeSheet: Bad SructType" ) );
return;
}
OnModify( );
aSheet->m_Flags |= IS_RESIZED;
s_OldPos = aSheet->m_Pos + aSheet->m_Size;
s_PreviousSheetWidth = SHEET_MIN_WIDTH;
s_PreviousSheetHeight = SHEET_MIN_HEIGHT;
BOOST_FOREACH( SCH_SHEET_PIN sheetLabel, aSheet->GetSheetPins() )
{
s_PreviousSheetWidth = MAX( s_PreviousSheetWidth,
( sheetLabel.GetLength() + 1 ) * sheetLabel.m_Size.x );
s_PreviousSheetHeight = MAX( s_PreviousSheetHeight,
sheetLabel.m_Pos.y - aSheet->m_Pos.y );
}
DrawPanel->ManageCurseur = MoveOrResizeSheet;
DrawPanel->ForceCloseManageCurseur = ExitSheet;
DrawPanel->ManageCurseur( DrawPanel, aDC, true );
if( (aSheet->m_Flags & IS_NEW) == 0 ) // not already in edit, save a copy for undo/redo
{
delete g_ItemToUndoCopy;
g_ItemToUndoCopy = DuplicateStruct( aSheet, true );
}
}
void SCH_EDIT_FRAME::StartMoveSheet( SCH_SHEET* aSheet, wxDC* aDC )
{
if( ( aSheet == NULL ) || ( aSheet->Type() != SCH_SHEET_T ) )
return;
DrawPanel->CursorOff( aDC );
GetScreen()->m_Curseur = aSheet->m_Pos;
DrawPanel->MouseToCursorSchema();
s_OldPos = aSheet->m_Pos;
aSheet->m_Flags |= IS_MOVED;
DrawPanel->ManageCurseur = MoveOrResizeSheet;
DrawPanel->ForceCloseManageCurseur = ExitSheet;
DrawPanel->ManageCurseur( DrawPanel, aDC, true );
DrawPanel->CursorOn( aDC );
if( (aSheet->m_Flags & IS_NEW) == 0 ) // not already in edit, save a copy for undo/redo
{
delete g_ItemToUndoCopy;
g_ItemToUndoCopy = DuplicateStruct( aSheet, true );
}
}