files-io.cpp 13.5 KB
Newer Older
1 2 3
/*
 * This program source code file is part of KiCad, a free EDA CAD application.
 *
4 5
 * Copyright (C) 2013 Jean-Pierre Charras, jp.charras at wanadoo.fr
 * Copyright (C) 2013 Wayne Stambaugh <stambaughw@verizon.net>
6
 * Copyright (C) 2013 CERN (www.cern.ch)
7
 * Copyright (C) 1992-2013 KiCad Developers, see AUTHORS.txt for contributors.
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
 *
 * 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
 */

/**
28
 * @file eeschema/files-io.cpp
29
 */
30

31 32 33 34 35
#include <fctsys.h>
#include <class_drawpanel.h>
#include <confirm.h>
#include <gestfich.h>
#include <wxEeschemaStruct.h>
36
#include <pgm_base.h>
37 38 39 40 41

#include <eeschema_id.h>
#include <class_library.h>
#include <libeditframe.h>
#include <sch_sheet.h>
42
#include <sch_sheet_path.h>
43
#include <sch_component.h>
44
#include <wildcards_and_files_ext.h>
45 46


47
bool SCH_EDIT_FRAME::SaveEEFile( SCH_SCREEN* aScreen, bool aSaveUnderNewName, bool aCreateBackupFile )
48 49
{
    wxString msg;
50 51
    wxFileName schematicFileName;
    bool success;
52

53 54
    if( aScreen == NULL )
        aScreen = GetScreen();
55

56
    // If no name exists in the window yet - save as new.
57
    if( aScreen->GetFileName().IsEmpty() )
58
        aSaveUnderNewName = true;
59

60
    // Construct the name of the file to be saved
61
    schematicFileName = Prj().AbsolutePath( aScreen->GetFileName() );
62 63

    if( aSaveUnderNewName )
64
    {
65 66
        wxFileDialog dlg( this, _( "Schematic Files" ),
                wxPathOnly( Prj().GetProjectFullName() ),
67 68 69 70 71 72 73
                schematicFileName.GetFullName(), SchematicFileWildcard,
                wxFD_SAVE | wxFD_OVERWRITE_PROMPT );

        if( dlg.ShowModal() == wxID_CANCEL )
            return false;

        schematicFileName = dlg.GetPath();
74

75 76 77
        if( schematicFileName.GetExt() != SchematicFileExtension )
            schematicFileName.SetExt( SchematicFileExtension );
    }
78

79 80
    if( !IsWritable( schematicFileName ) )
        return false;
81

82
    // Create backup if requested
83 84 85
    if( aCreateBackupFile && schematicFileName.FileExists() )
    {
        wxFileName backupFileName = schematicFileName;
86

87
        // Rename the old file to a '.bak' one:
88
        backupFileName.SetExt( SchematicBackupFileExtension );
89

90 91
        if( backupFileName.FileExists() )
            wxRemoveFile( backupFileName.GetFullPath() );
92

93 94
        if( !wxRenameFile( schematicFileName.GetFullPath(), backupFileName.GetFullPath() ) )
        {
95
            msg.Printf( _( "Could not save backup of file '%s'" ),
96 97 98
                    GetChars( schematicFileName.GetFullPath() ) );
            DisplayError( this, msg );
        }
99 100
    }

101
    // Save
102 103 104
    wxLogTrace( traceAutoSave,
                wxT( "Saving file <" ) + schematicFileName.GetFullPath() + wxT( ">" ) );

105 106 107
    FILE* f = wxFopen( schematicFileName.GetFullPath(), wxT( "wt" ) );

    if( !f )
108
    {
109
        msg.Printf( _( "Failed to create file '%s'" ),
110
                    GetChars( schematicFileName.GetFullPath() ) );
111 112 113 114
        DisplayError( this, msg );
        return false;
    }

115
    success = aScreen->Save( f );
116

117
    if( success )
118
    {
119
        // Delete auto save file.
120 121 122 123 124 125 126 127 128 129 130 131
        wxFileName autoSaveFileName = schematicFileName;
        autoSaveFileName.SetName( wxT( "$" ) + schematicFileName.GetName() );

        if( autoSaveFileName.FileExists() )
        {
            wxLogTrace( traceAutoSave,
                        wxT( "Removing auto save file <" ) + autoSaveFileName.GetFullPath() +
                        wxT( ">" ) );

            wxRemoveFile( autoSaveFileName.GetFullPath() );
        }

132 133 134
        // Update the screen and frame info.
        if( aSaveUnderNewName )
            aScreen->SetFileName( schematicFileName.GetFullPath() );
135
        aScreen->ClrSave();
136
        aScreen->ClrModify();
137

138 139
        msg.Printf( _( "File %s saved" ), GetChars( aScreen->GetFileName() ) );
        SetStatusText( msg, 0 );
jean-pierre charras's avatar
jean-pierre charras committed
140
    }
141 142 143 144
    else
    {
        DisplayError( this, _( "File write operation failed." ) );
    }
145 146 147 148 149 150 151

    fclose( f );

    return success;
}


152
void SCH_EDIT_FRAME::Save_File( wxCommandEvent& event )
153
{
dickelbeck's avatar
dickelbeck committed
154 155 156 157
    int id = event.GetId();

    switch( id )
    {
158 159
    case ID_UPDATE_ONE_SHEET:
        SaveEEFile( NULL );
dickelbeck's avatar
dickelbeck committed
160 161
        break;

162
    case ID_SAVE_ONE_SHEET_UNDER_NEW_NAME:
163 164
        if( SaveEEFile( NULL, true ) )
        {
165
            CreateArchiveLibraryCacheFile( true );
166
        }
dickelbeck's avatar
dickelbeck committed
167 168
        break;
    }
169 170

    UpdateTitle();
171 172 173
}


174
bool SCH_EDIT_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, int aCtl )
175
{
176
    // implement the pseudo code from KIWAY_PLAYER.h:
177

178
    SCH_SCREENS screenList;
179

180 181
    // This is for python:
    if( aFileSet.size() != 1 )
182
    {
183 184 185
        UTF8 msg = StrPrintf( "Eeschema:%s() takes only a single filename", __func__ );
        DisplayError( this, msg );
        return false;
186 187
    }

188
    wxString    fullFileName( aFileSet[0] );
189

190 191 192
    // We insist on caller sending us an absolute path, if it does not, we say it's a bug.
    wxASSERT_MSG( wxFileName( fullFileName ).IsAbsolute(),
        wxT( "bug in single_top.cpp or project manager." ) );
193

194
    if( !LockFile( fullFileName ) )
195 196 197 198 199 200 201 202
    {
        wxString msg = wxString::Format( _(
                "Schematic file '%s' is already open." ),
                GetChars( fullFileName )
                );
        DisplayError( this, msg );
        return false;
    }
203

204 205 206 207
    // save any currently open and modified project files.
    for( SCH_SCREEN* screen = screenList.GetFirst(); screen; screen = screenList.GetNext() )
    {
        if( screen->IsModify() )
208
        {
209 210 211 212 213 214 215 216
            int response = YesNoCancelDialog( this, _(
                "The current schematic has been modified.  Do you wish to save the changes?" ),
                wxEmptyString,
                _( "Save and Load" ),
                _( "Load Without Saving" )
                );

            if( response == wxID_CANCEL )
217
            {
218
                return false;
219
            }
220 221 222 223 224 225 226 227 228 229
            else if( response == wxID_YES )
            {
                wxCommandEvent dummy;
                OnSaveProject( dummy );
            }
            else
            {
                // response == wxID_NO, fall thru
            }
            break;
230 231 232
        }
    }

233 234
    wxFileName pro = fullFileName;
    pro.SetExt( ProjectFileExtension );
235

236
    bool is_new = !wxFileName::IsFileReadable( fullFileName );
237

238 239
    // If its a non-existent schematic and caller thinks it exists
    if( is_new && !( aCtl & KICTL_CREATE ) )
240
    {
241 242 243 244 245 246
        // notify user that fullFileName does not exist, ask if user wants to create it.
        wxString ask = wxString::Format( _(
                "Schematic '%s' does not exist.  Do you wish to create it?" ),
                GetChars( fullFileName )
                );
        if( !IsOK( this, ask ) )
247
            return false;
248 249
    }

250
    // unload current project file before loading new
251
    {
252 253
        delete g_RootSheet;
        g_RootSheet = NULL;
254

255
        CreateScreens();
256
    }
257

258
    GetScreen()->SetFileName( fullFileName );
259
    g_RootSheet->SetFileName( fullFileName );
260

261
    SetStatusText( wxEmptyString );
262
    ClearMsgPanel();
263

264 265 266 267 268
    wxString msg = wxString::Format( _(
            "Ready\nProject dir: '%s'\n" ),
            GetChars( wxPathOnly( Prj().GetProjectFullName() ) )
            );
    SetStatusText( msg );
269

270 271
    // PROJECT::SetProjectFullName() is an impactful function.  It should only be
    // called under carefully considered circumstances.
272

273 274 275 276
    // The calling code should know not to ask me here to change projects unless
    // it knows what consequences that will have on other KIFACEs running and using
    // this same PROJECT.  It can be very harmful if that calling code is stupid.
    Prj().SetProjectFullName( pro.GetFullPath() );
277

278
    LoadProjectFile();
279

280 281 282 283 284 285 286
    // load the libraries here, not in SCH_SCREEN::Draw() which is a context
    // that will not tolerate DisplayError() dialog since we're already in an
    // event handler in there.
    // And when a schematic file is loaded, we need these libs to initialize
    // some parameters (links to PART LIB, dangling ends ...)
    Prj().SchLibs();

287
    if( is_new )
288
    {
289 290
        // mark new, unsaved file as modified.
        GetScreen()->SetModify();
291
    }
292 293 294
    else
    {
        g_RootSheet->SetScreen( NULL );
295

296
        DBG( printf( "%s: loading schematic %s\n", __func__, TO_UTF8( fullFileName ) );)
297

298 299
        bool diag = g_RootSheet->Load( this );
        (void) diag;
300

301
        SetScreen( m_CurrentSheet->LastScreen() );
302

303
        GetScreen()->ClrModify();
304

305 306
        UpdateFileHistory( fullFileName );
    }
307

308
    GetScreen()->SetGrid( ID_POPUP_GRID_LEVEL_1000 + m_LastGridSizeId );
309
    Zoom_Automatique( false );
310
    SetSheetNumberAndCount();
311

312 313 314
    m_canvas->Refresh( true );

    return true;
315
}
316 317


318 319
bool SCH_EDIT_FRAME::AppendOneEEProject()
{
320 321
    wxString    fullFileName;
    wxString    msg;
322

323
    SCH_SCREEN* screen = GetScreen();
324 325 326 327 328 329 330 331

    if( !screen )
    {
        wxLogError( wxT("Document not ready, cannot import") );
        return false;
    }

    // open file chooser dialog
332 333 334
    wxString path = wxPathOnly( Prj().GetProjectFullName() );

    wxFileDialog dlg( this, _( "Import Schematic" ), path,
335 336 337 338 339 340
                      wxEmptyString, SchematicFileWildcard,
                      wxFD_OPEN | wxFD_FILE_MUST_EXIST );

    if( dlg.ShowModal() == wxID_CANCEL )
        return false;

341
    fullFileName = dlg.GetPath();
342

343
    wxFileName fn = fullFileName;
344 345 346 347

    if( fn.IsRelative() )
    {
        fn.MakeAbsolute();
348
        fullFileName = fn.GetFullPath();
349 350
    }

351 352 353 354 355 356 357 358
    wxString cache_name = PART_LIBS::CacheName( fullFileName );
    if( !!cache_name )
    {
        PART_LIBS*  libs = Prj().SchLibs();

        if( PART_LIB* lib = libs->AddLibrary( cache_name ) )
            lib->SetCache();
    }
359

360
    wxLogDebug( wxT( "Importing schematic " ) + fullFileName );
361 362

    // load the project
363
    bool success = LoadOneEEFile( screen, fullFileName, true );
364

365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380
    if( success )
    {
        // load sub-sheets
        EDA_ITEM* bs = screen->GetDrawItems();
        while( bs )
        {
            // do not append hierarchical sheets
            if( bs->Type() ==  SCH_SHEET_T )
            {
                screen->Remove( (SCH_SHEET*) bs );
            }
            // clear annotation and init new time stamp for the new components
            else if( bs->Type() == SCH_COMPONENT_T )
            {
                ( (SCH_COMPONENT*) bs )->SetTimeStamp( GetNewTimeStamp() );
                ( (SCH_COMPONENT*) bs )->ClearAnnotation( NULL );
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
                // Clear flags, which are set by these previous modifications:
                bs->ClearFlags();
            }

            bs = bs->Next();
        }
    }

    // redraw base screen (ROOT) if necessary
    GetScreen()->SetGrid( ID_POPUP_GRID_LEVEL_1000 + m_LastGridSizeId );
    Zoom_Automatique( false );
    SetSheetNumberAndCount();
    m_canvas->Refresh( true );
    return success;
}


void SCH_EDIT_FRAME::OnAppendProject( wxCommandEvent& event )
{
    wxString msg = _( "This operation cannot be undone. "
            "Besides, take into account that hierarchical sheets will not be appended.\n\n"
            "Do you want to save the current document before proceeding?" );

    if( IsOK( this, msg ) )
        OnSaveProject( event );

    AppendOneEEProject();
}


412
void SCH_EDIT_FRAME::OnSaveProject( wxCommandEvent& aEvent )
413
{
414
    SCH_SCREEN* screen;
415
    SCH_SCREENS screenList;
416

417 418
    // I want to see it in the debugger, show me the string!  Can't do that with wxFileName.
    wxString    fileName = Prj().AbsolutePath( g_RootSheet->GetFileName() );
419

420
    wxFileName  fn = fileName;
421

422 423 424 425 426 427 428 429
    if( !fn.IsDirWritable() )
    {
        wxString msg = wxString::Format( _(
                "Directory '%s' is not writable" ),
                GetChars( fn.GetPath() )
                );

        DisplayError( this, msg );
430
        return;
431
    }
432

433
    for( screen = screenList.GetFirst(); screen; screen = screenList.GetNext() )
434
        SaveEEFile( screen );
435

436
    CreateArchiveLibraryCacheFile();
437 438

    UpdateTitle();
439
}
440 441 442 443


bool SCH_EDIT_FRAME::doAutoSave()
{
444 445
    wxFileName  tmpFileName = g_RootSheet->GetFileName();
    wxFileName  fn = tmpFileName;
446 447
    wxFileName  tmp;
    SCH_SCREENS screens;
448

449 450 451 452 453 454 455
    bool autoSaveOk = true;

    tmp.AssignDir( fn.GetPath() );

    if( !IsWritable( tmp ) )
        return false;

456
    for( SCH_SCREEN* screen = screens.GetFirst(); screen; screen = screens.GetNext() )
457 458 459 460 461 462 463 464 465 466 467 468
    {
        // Only create auto save files for the schematics that have been modified.
        if( !screen->IsSave() )
            continue;

        tmpFileName = fn = screen->GetFileName();

        // Auto save file name is the normal file name prefixed with $.
        fn.SetName( wxT( "$" ) + fn.GetName() );

        screen->SetFileName( fn.GetFullPath() );

469
        if( SaveEEFile( screen, false, NO_BACKUP_FILE ) )
470 471 472 473 474 475 476 477 478 479 480 481
            screen->SetModify();
        else
            autoSaveOk = false;

        screen->SetFileName( tmpFileName.GetFullPath() );
    }

    if( autoSaveOk )
        m_autoSaveState = false;

    return autoSaveOk;
}