files.cpp 24.6 KB
Newer Older
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
/*
 * This program source code file is part of KiCad, a free EDA CAD application.
 *
 * Copyright (C) 2004-2010 Jean-Pierre Charras, jean-pierre.charras@gpisa-lab.inpg.fr
 * Copyright (C) 2011 Wayne Stambaugh <stambaughw@verizon.net>
 * Copyright (C) 2010 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
 */

26 27 28 29
/**
 * @file pcbnew/files.cpp
 * @brief Read and write board files.
 */
30

31 32 33 34 35 36 37 38 39 40 41
#include <fctsys.h>
#include <class_drawpanel.h>
#include <confirm.h>
#include <kicad_string.h>
#include <gestfich.h>
#include <wxPcbStruct.h>
#include <macros.h>
#include <pcbcommon.h>
#include <3d_viewer.h>
#include <richio.h>
#include <filter_reader.h>
42
#include <pgm_base.h>
43
#include <msgpanel.h>
44
#include <fp_lib_table.h>
45

46 47 48
#include <pcbnew.h>
#include <pcbnew_id.h>
#include <io_mgr.h>
49
#include <wildcards_and_files_ext.h>
50

51
#include <class_board.h>
52
#include <build_version.h>      // LEGACY_BOARD_FILE_VERSION
53 54 55
#include <module_editor_frame.h>
#include <modview_frame.h>

56

57 58 59
//#define     USE_INSTRUMENTATION     true
#define     USE_INSTRUMENTATION     false

60

61 62 63
static const wxChar backupSuffix[]  = wxT( "-bak" );
static const wxChar autosavePrefix[]= wxT( "_autosave-" );

64

65
void PCB_EDIT_FRAME::OnFileHistory( wxCommandEvent& event )
66
{
67
    wxString fn = GetFileFromHistory( event.GetId(), _( "Printed circuit board" ) );
68

69
    if( !!fn )
70
    {
71 72
        int open_ctl = 0;

73
        m_canvas->EndMouseCapture( ID_NO_TOOL_SELECTED, m_canvas->GetDefaultCursor() );
74
        ::wxSetWorkingDirectory( ::wxPathOnly( fn ) );
75 76 77 78 79 80 81 82 83

        // LoadOnePcbFile( fn, bool aAppend = false,  bool aForceFileDialog = false );
        if( !wxFileName::IsFileReadable( fn ) )
        {
            if( !AskBoardFileName( this, &open_ctl, &fn ) )
                return;
        }

        OpenProjectFiles( std::vector<wxString>( 1, fn ), open_ctl );
84 85 86
    }
}

87

88
void PCB_EDIT_FRAME::Files_io( wxCommandEvent& event )
89
{
90 91 92
    int        id = event.GetId();
    wxString   msg;

93 94 95
    // If an edition is in progress, stop it.
    // For something else than save, get rid of current tool.
    if( id == ID_SAVE_BOARD )
96
        m_canvas->EndMouseCapture( -1, m_canvas->GetDefaultCursor() );
97
    else
98
        m_canvas->EndMouseCapture( ID_NO_TOOL_SELECTED, m_canvas->GetDefaultCursor() );
99 100 101 102

    switch( id )
    {
    case ID_LOAD_FILE:
103 104 105 106 107 108 109 110 111 112 113
        {
            // LoadOnePcbFile( GetBoard()->GetFileName(), append=false, aForceFileDialog=true );

            int         open_ctl;
            wxString    fileName = GetBoard()->GetFileName();

            if( !AskBoardFileName( this, &open_ctl, &fileName ) )
                return;

            OpenProjectFiles( std::vector<wxString>( 1, fileName ), open_ctl );
        }
114 115
        break;

116 117
    case ID_MENU_READ_BOARD_BACKUP_FILE:
    case ID_MENU_RECOVER_BOARD_AUTOSAVE:
118
        {
119 120
            wxFileName currfn = GetBoard()->GetFileName();
            wxFileName fn = currfn;
121

122 123
            if( id == ID_MENU_RECOVER_BOARD_AUTOSAVE )
            {
124
                wxString rec_name = wxString( autosavePrefix ) + fn.GetName();
125 126 127 128
                fn.SetName( rec_name );
            }
            else
            {
129
                wxString backup_ext = fn.GetExt()+ backupSuffix;
130 131
                fn.SetExt( backup_ext );
            }
132

133 134
            if( !fn.FileExists() )
            {
135
                msg.Printf( _( "Recovery file '%s' not found." ),
136
                            GetChars( fn.GetFullPath() ) );
137
                DisplayInfoMessage( this, msg );
138
                break;
139
            }
140

141
            msg.Printf( _( "OK to load recovery or backup file '%s'" ),
142 143 144 145
                            GetChars(fn.GetFullPath() ) );

            if( !IsOK( this, msg ) )
                break;
146

147
            GetScreen()->ClrModify();    // do not prompt the user for changes
148 149 150

            // LoadOnePcbFile( fn.GetFullPath(), aAppend=false, aForceFileDialog=false );
            OpenProjectFiles( std::vector<wxString>( 1, fn.GetFullPath() ) );
151

152 153
            // Re-set the name since name or extension was changed
            GetBoard()->SetFileName( currfn.GetFullPath() );
154 155
            UpdateTitle();
        }
156 157 158
        break;

    case ID_APPEND_FILE:
159 160 161 162 163 164 165 166 167 168
        {
            // LoadOnePcbFile( wxEmptyString, aAppend = true, aForceFileDialog=false );
            int         open_ctl;
            wxString    fileName;

            if( !AskBoardFileName( this, &open_ctl, &fileName ) )
                break;

            OpenProjectFiles( std::vector<wxString>( 1, fileName ), open_ctl | KICTL_OPEN_APPEND );
        }
169 170 171
        break;

    case ID_NEW_BOARD:
172
        {
173 174
            if( ! Clear_Pcb( true ) )
                break;
175

176
            // Clear footprint library table for the new board.
177
            Prj().PcbFootprintLibs()->Clear();
178 179

            wxFileName fn;
180

181
            fn.AssignCwd();
182
            fn.SetName( wxT( "noname" ) );
183 184 185

            Prj().SetProjectFullName( fn.GetFullPath() );

186
            fn.SetExt( PcbFileExtension );
187

188
            GetBoard()->SetFileName( fn.GetFullPath() );
189
            UpdateTitle();
190
            ReCreateLayerBox();
191
        }
192 193 194
        break;

    case ID_SAVE_BOARD:
195
        SavePcbFile( GetBoard()->GetFileName() );
196 197
        break;

198
    case ID_SAVE_BOARD_AS:
199 200 201 202 203 204
        SavePcbFile( wxEmptyString );
        break;

    default:
        DisplayError( this, wxT( "File_io Internal Error" ) ); break;
    }
205 206 207
}


208
bool AskBoardFileName( wxWindow* aParent, int* aCtl, wxString* aFileName )
209
{
210 211
    // This is a subset of all PLUGINs which are trusted to be able to
    // load a BOARD.  Order is subject to change as KICAD plugin matures.
212
    // User may occasionally use the wrong plugin to load a *.brd file,
213
    // (since both legacy and eagle use *.brd extension),
214
    // but eventually *.kicad_pcb will be more common than legacy *.brd files.
215 216
    static const struct
    {
217 218
        const wxString&     filter;
        IO_MGR::PCB_FILE_T  pluginType;
219 220
    } loaders[] =
    {
221
        { PcbFileWildcard,          IO_MGR::KICAD },
222
        { LegacyPcbFileWildcard,    IO_MGR::LEGACY },
223
        { EaglePcbFileWildcard,     IO_MGR::EAGLE },
224
        { PCadPcbFileWildcard,      IO_MGR::PCAD },
225
    };
dickelbeck's avatar
dickelbeck committed
226

227 228 229 230
    wxFileName  fileName( *aFileName );
    wxString    fileFilters;

    for( unsigned i=0;  i<DIM( loaders );  ++i )
231
    {
232 233
        if( i > 0 )
            fileFilters += wxChar( '|' );
234

235 236
        fileFilters += wxGetTranslation( loaders[i].filter );
    }
237

238 239
    wxString    path;
    wxString    name;
240

241 242 243 244 245 246 247 248 249 250
    if( fileName.FileExists() )
    {
        path = fileName.GetPath();
        name = fileName.GetFullName();
    }
    else
    {
        path = wxGetCwd();
        // leave name empty
    }
251

252 253
    wxFileDialog dlg( aParent, _( "Open Board File" ), path, name, fileFilters,
                      wxFD_OPEN | wxFD_FILE_MUST_EXIST );
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
    if( dlg.ShowModal() != wxID_CANCEL )
    {
        int chosenFilter = dlg.GetFilterIndex();

        // if Eagle, tell OpenProjectFiles() to use Eagle plugin.  It's the only special
        // case because of the duplicate use of the *.brd file extension.  Other cases
        // are clear because of unique file extensions.
        *aCtl = chosenFilter == 2  ? KICTL_EAGLE_BRD : 0;
        *aFileName = dlg.GetPath();

        return true;
    }
    else
        return false;
}


bool PCB_EDIT_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, int aCtl )
{
    wxASSERT( aFileSet.size() == 1 );

    bool        doAppend = aCtl & KICTL_OPEN_APPEND;
    wxFileName  fileName( aFileSet[0] );

    // Make filename absolute, to avoid issues when the filename is relative,
    // for instance when stored in history list without path, and when building
    // the config filename ( which should have a path )
    if( fileName.IsRelative() )
        fileName.MakeAbsolute();

    if( GetScreen()->IsModify() && !doAppend )
    {
        int response = YesNoCancelDialog( this, _(
            "The current board has been modified.  Do "
            "you wish to save the changes?" ),
            wxEmptyString,
            _( "Save and Load" ),
            _( "Load Without Saving" )
            );

        if( response == wxID_CANCEL )
296
            return false;
297 298 299
        else if( response == wxID_YES )
            SavePcbFile( GetBoard()->GetFileName(), true );
    }
300

301 302 303 304 305 306
    if( doAppend )
    {
        GetBoard()->SetFileName( wxEmptyString );
        OnModify();
        GetBoard()->m_Status_Pcb = 0;
    }
307

308 309 310 311 312 313 314 315 316 317
    // The KIWAY_PLAYER::OpenProjectFiles() API knows nothing about plugins, so
    // determine how to load the BOARD here, with minor assistance from KICTL_EAGLE_BRD
    // bit flag.

    IO_MGR::PCB_FILE_T  pluginType;

    if( fileName.GetExt() == IO_MGR::GetFileExtension( IO_MGR::LEGACY ) )
    {
        // both legacy and eagle share a common file extension.
        pluginType = ( aCtl & KICTL_EAGLE_BRD ) ? IO_MGR::EAGLE : IO_MGR::LEGACY;
318
    }
319
    else if( fileName.GetExt() == IO_MGR::GetFileExtension( IO_MGR::LEGACY ) + backupSuffix )
320
    {
321
        pluginType = IO_MGR::LEGACY;
322
    }
323 324 325 326 327 328
    else if( fileName.GetExt() == IO_MGR::GetFileExtension( IO_MGR::IO_MGR::PCAD ) )
    {
        pluginType = IO_MGR::PCAD;
    }
    else
        pluginType = IO_MGR::KICAD;
dickelbeck's avatar
dickelbeck committed
329

330 331
    PLUGIN::RELEASER pi( IO_MGR::PluginFind( pluginType ) );

332
    if( !doAppend )
333
    {
334
        if( !Pgm().LockFile( fileName.GetFullPath() ) )
335 336 337 338
        {
            DisplayError( this, _( "This file is already open." ) );
            return false;
        }
dickelbeck's avatar
dickelbeck committed
339
        Clear_Pcb( false );     // pass false since we prompted above for a modified board
340
    }
dickelbeck's avatar
dickelbeck committed
341

342
    CheckForAutoSaveFile( fileName, fileName.GetExt() );
343

344
    GetBoard()->SetFileName( fileName.GetFullPath() );
345

346
    if( !doAppend )
347 348 349 350 351 352 353
    {
        // Update the option toolbar
        m_DisplayPcbTrackFill = DisplayOpt.DisplayPcbTrackFill;
        m_DisplayModText = DisplayOpt.DisplayModText;
        m_DisplayModEdge = DisplayOpt.DisplayModEdge;
        m_DisplayPadFill = DisplayOpt.DisplayPadFill;
        m_DisplayViaFill = DisplayOpt.DisplayViaFill;
354 355

        // load project settings before BOARD, in case BOARD file has overrides.
356
        LoadProjectSettings( GetBoard()->GetFileName() );
357
    }
358 359
    else
    {
360
        GetDesignSettings().m_NetClasses.Clear();
361 362
    }

363
    BOARD* loadedBoard = 0;   // it will be set to non-NULL if loaded OK
364 365 366

    try
    {
367
        PROPERTIES  props;
368 369
        char        xbuf[30];
        char        ybuf[30];
370

371
        // EAGLE_PLUGIN can use this info to center the BOARD, but it does not yet.
372 373 374 375 376
        sprintf( xbuf, "%d", GetPageSizeIU().x );
        sprintf( ybuf, "%d", GetPageSizeIU().y );

        props["page_width"]  = xbuf;
        props["page_height"] = ybuf;
377

378
#if USE_INSTRUMENTATION
379 380 381 382
        // measure the time to load a BOARD.
        unsigned startTime = GetRunningMicroSecs();
#endif

383
        // load or append either:
384
        loadedBoard = pi->Load( GetBoard()->GetFileName(), doAppend ? GetBoard() : NULL, &props );
385

386
#if USE_INSTRUMENTATION
387 388 389 390
        unsigned stopTime = GetRunningMicroSecs();
        printf( "PLUGIN::Load(): %u usecs\n", stopTime - startTime );
#endif

391 392 393
        // the Load plugin method makes a 'fresh' board, so we need to
        // set its own name
        GetBoard()->SetFileName( fileName.GetFullPath() );
394

395
        if( !doAppend )
Dick Hollenbeck's avatar
Dick Hollenbeck committed
396
        {
397 398
            if( pluginType == IO_MGR::LEGACY &&
                loadedBoard->GetFileFormatVersionAtLoad() < LEGACY_BOARD_FILE_VERSION )
Dick Hollenbeck's avatar
Dick Hollenbeck committed
399
            {
400
                DisplayInfoMessage( this,
401 402
                    _(  "This file was created by an older version of Pcbnew.\n"
                        "It will be stored in the new file format when you save this file again." ) );
Dick Hollenbeck's avatar
Dick Hollenbeck committed
403 404
            }

405
            SetBoard( loadedBoard );
Dick Hollenbeck's avatar
Dick Hollenbeck committed
406
        }
407
    }
408
    catch( const IO_ERROR& ioe )
409 410 411
    {
        wxString msg = wxString::Format( _( "Error loading board.\n%s" ),
                                         ioe.errorText.GetData() );
412
        wxMessageBox( msg, _( "Open Board File" ), wxOK | wxICON_ERROR );
413 414
    }

415 416 417 418 419 420 421 422
    if( loadedBoard )
    {
        // we should not ask PLUGINs to do these items:
        loadedBoard->BuildListOfNets();
        loadedBoard->SynchronizeNetsAndNetClasses();

        SetStatusText( wxEmptyString );
        BestZoom();
423 424 425

        // update the layer names in the listbox
        ReCreateLayerBox( false );
426 427
    }

428 429
    GetScreen()->ClrModify();

430
    if( doAppend )
431
    {
432
        // change the initial board name to <oldname>-append.brd
433
        wxString new_filename = GetBoard()->GetFileName().BeforeLast( '.' );
434

435
        if( !new_filename.EndsWith( wxT( "-append" ) ) )
436
            new_filename += wxT( "-append" );
437

438
        new_filename += wxT( "." ) + PcbFileExtension;
439

440
        OnModify();
441
        GetBoard()->SetFileName( new_filename );
442 443
    }

444 445 446 447 448 449 450 451 452 453 454
    // Fix the directory separator on Windows and
    // force the new file format for not Kicad boards,
    // to ensure the right format when saving the board
    bool converted =  pluginType != IO_MGR::LEGACY && pluginType != IO_MGR::KICAD;
    wxString fn;

    if( converted )
        fn = GetBoard()->GetFileName().BeforeLast( '.' );
    else
        fn = GetBoard()->GetFileName();

455
    fn.Replace( WIN_STRING_DIR_SEP, UNIX_STRING_DIR_SEP );
456 457 458 459

    if( converted )
        fn += wxT( "." ) + PcbFileExtension;

460
    GetBoard()->SetFileName( fn );
461

462
    UpdateTitle();
463 464 465

    if( !converted )
        UpdateFileHistory( GetBoard()->GetFileName() );
466

Dick Hollenbeck's avatar
Dick Hollenbeck committed
467
    // Rebuild the new pad list (for drc and ratsnet control ...)
468
    GetBoard()->m_Status_Pcb = 0;
469

Dick Hollenbeck's avatar
Dick Hollenbeck committed
470
    // Dick 5-Feb-2012: I do not agree with this.  The layer widget will show what
Dick Hollenbeck's avatar
Dick Hollenbeck committed
471 472
    // is visible or not, and it would be nice for the board to look like it
    // did when I saved it, immediately after loading.
Dick Hollenbeck's avatar
Dick Hollenbeck committed
473
#if 0
474
    /* Reset the items visibility flag when loading a new config
Dick Hollenbeck's avatar
Dick Hollenbeck committed
475
     * Because it could creates SERIOUS mistakes for the user,
476 477
     * if board items are not visible after loading a board...
     * Grid and ratsnest can be left to their previous state
478
     */
479 480
    bool showGrid = IsElementVisible( GRID_VISIBLE );
    bool showRats = IsElementVisible( RATSNEST_VISIBLE );
Dick Hollenbeck's avatar
Dick Hollenbeck committed
481

482
    SetVisibleAlls();
Dick Hollenbeck's avatar
Dick Hollenbeck committed
483

484 485
    SetElementVisibility( GRID_VISIBLE, showGrid );
    SetElementVisibility( RATSNEST_VISIBLE, showRats );
Dick Hollenbeck's avatar
Dick Hollenbeck committed
486
#endif
487

charras's avatar
charras committed
488
    // Update info shown by the horizontal toolbars
489
    GetDesignSettings().SetCurrentNetClass( NETCLASS::Default );
490
    ReFillLayerWidget();
491
    ReCreateLayerBox();
Dick Hollenbeck's avatar
Dick Hollenbeck committed
492

493 494 495
    // upate the layer widget to match board visibility states, both layers and render columns.
    syncLayerVisibilities();
    syncLayerWidgetLayer();
Dick Hollenbeck's avatar
Dick Hollenbeck committed
496
    syncRenderStates();
charras's avatar
charras committed
497

498 499 500 501 502
    // Update the RATSNEST items, which were not loaded at the time
    // BOARD::SetVisibleElements() was called from within any PLUGIN.
    // See case RATSNEST_VISIBLE: in BOARD::SetElementVisibility()
    GetBoard()->SetVisibleElements( GetBoard()->GetVisibleElements() );

503 504 505
    updateTraceWidthSelectBox();
    updateViaSizeSelectBox();

charras's avatar
charras committed
506
    // Display the loaded board:
charras's avatar
charras committed
507
    Zoom_Automatique( false );
charras's avatar
charras committed
508

509
    // Compile ratsnest and displays net info
510 511 512 513 514
    {
        wxBusyCursor dummy;    // Displays an Hourglass while building connectivity
        Compile_Ratsnest( NULL, true );
    }

515
    SetMsgPanel( GetBoard() );
516

517 518 519
    // Refresh the 3D view, if any
    if( m_Draw3DFrame )
        m_Draw3DFrame->NewDisplay();
charras's avatar
charras committed
520 521

#if 0 && defined(DEBUG)
dickelbeck's avatar
dickelbeck committed
522
    // Output the board object tree to stdout, but please run from command prompt:
523
    GetBoard()->Show( 0, std::cout );
524
#endif
charras's avatar
charras committed
525

526 527 528 529 530 531 532 533 534 535 536 537 538 539
    // from EDA_APPL which was first loaded BOARD only:
    {
        /* For an obscure reason the focus is lost after loading a board file
         * when starting up the process.
         * (seems due to the recreation of the layer manager after loading the file)
         * Give focus to main window and Drawpanel
         * must be done for these 2 windows (for an obscure reason ...)
         * Linux specific
         * This is more a workaround than a fix.
         */
        SetFocus();
        GetCanvas()->SetFocus();
    }

540
    return true;
541 542 543
}


544
bool PCB_EDIT_FRAME::SavePcbFile( const wxString& aFileName, bool aCreateBackupFile )
545
{
546 547
    wxFileName  backupFileName;
    wxFileName  pcbFileName;
dickelbeck's avatar
dickelbeck committed
548 549 550
    wxString    upperTxt;
    wxString    lowerTxt;
    wxString    msg;
551
    bool        saveok = true;
552
    bool        isSaveAs = false;
553

554
    IO_MGR::PCB_FILE_T pluginType;
555

556
    if( aFileName == wxEmptyString )
557
    {
558
        wxString    wildcard;
Dick Hollenbeck's avatar
Dick Hollenbeck committed
559 560 561
        wildcard << wxGetTranslation( PcbFileWildcard )
                        // << wxChar( '|' ) << wxGetTranslation( LegacyPcbFileWildcard )
                        ;
562

563
        isSaveAs = true;
564 565
        pcbFileName = GetBoard()->GetFileName();

566 567 568 569 570
        if( pcbFileName.GetName() == wxEmptyString )
        {
            pcbFileName.SetName( _( "Unnamed file" ) );
        }

571 572 573 574
        // Match the default wildcard filter choice, with the inital file extension shown.
        // That'll be the extension unless user changes filter dropdown listbox.
        pcbFileName.SetExt( KiCadPcbFileExtension );

575 576
        wxFileDialog dlg(   this, _( "Save Board File As" ), pcbFileName.GetPath(),
                            pcbFileName.GetFullName(),
577 578
                            wildcard, wxFD_SAVE
                            /* wxFileDialog is not equipped to handle multiple wildcards and
579
                                wxFD_OVERWRITE_PROMPT both together.
580 581 582
                                | wxFD_OVERWRITE_PROMPT
                            */
                            );
charras's avatar
charras committed
583

584
        if( dlg.ShowModal() != wxID_OK )
585
            return false;
charras's avatar
charras committed
586

Dick Hollenbeck's avatar
Dick Hollenbeck committed
587
#if 0   // no more LEGACY_PLUGIN::Save()
588 589 590
        int filterNdx = dlg.GetFilterIndex();

        pluginType = ( filterNdx == 1 ) ? IO_MGR::LEGACY : IO_MGR::KICAD;
Dick Hollenbeck's avatar
Dick Hollenbeck committed
591 592 593
#else
        pluginType = IO_MGR::KICAD;
#endif
594 595

        // Note: on Linux wxFileDialog is not reliable for noticing a changed filename.
596
        // We probably need to file a bug report or implement our own derivation.
597 598 599 600
        pcbFileName = dlg.GetPath();

        // enforce file extension, must match plugin's policy.
        pcbFileName.SetExt( IO_MGR::GetFileExtension( pluginType ) );
601 602 603 604 605

        // Since the file overwrite test was removed from wxFileDialog because it doesn't work
        // when multiple wildcards are defined, we have to check it ourselves to prevent an
        // existing board file from silently being over written.
        if( pcbFileName.FileExists()
606
          && !IsOK( this, wxString::Format( _( "The file '%s' already exists.\n\nDo you want "
607 608 609 610
                                               "to overwrite it?" ),
                                            GetChars( pcbFileName.GetFullPath() ) )) )
            return false;

611 612 613
#if 0   //  RHH 6-Jul-14: I see no plausible reason to do this.  We did not auto generate the
        // footprint table.  And the dialog which does suppport editing does the saving.

614
        // Save the project specific footprint library table.
615
        if( !Prj().PcbFootprintLibs()->IsEmpty( false ) )
616
        {
617
            wxString fp_lib_tbl = Prj().FootprintLibTblName();
618

619
            if( wxFileName::FileExists( fp_lib_tbl )
jean-pierre charras's avatar
jean-pierre charras committed
620
              && IsOK( this, _( "A footprint library table already exists in this path.\n\nDo "
621 622 623 624
                                "you want to overwrite it?" ) ) )
            {
                try
                {
625
                    Prj().PcbFootprintLibs()->Save( fp_lib_tbl );
626
                }
627
                catch( const IO_ERROR& ioe )
628
                {
629 630 631 632 633 634 635
                    wxString msg = wxString::Format( _(
                        "An error occurred attempting to save the "
                        "footprint library table '%s'\n\n%s" ),
                        GetChars( fp_lib_tbl ),
                        GetChars( ioe.errorText )
                        );
                    DisplayError( this, msg );
636 637 638
                }
            }
        }
639 640
#endif

641 642
    }
    else
643
    {
644
        pcbFileName = aFileName;
645

646 647 648 649 650 651 652
        if( pcbFileName.GetExt() == LegacyPcbFileExtension )
            pluginType = IO_MGR::LEGACY;
        {
            pluginType = IO_MGR::KICAD;
            pcbFileName.SetExt( KiCadPcbFileExtension );
        }
    }
653

654 655 656
    if( !IsWritable( pcbFileName ) )
        return false;

657
    if( aCreateBackupFile )
658
    {
Dick Hollenbeck's avatar
Dick Hollenbeck committed
659
        // Get the backup file name
660
        backupFileName = pcbFileName;
661
        backupFileName.SetExt( pcbFileName.GetExt() + backupSuffix );
662

663 664
        // If an old backup file exists, delete it.  If an old board file exists, rename
        // it to the backup file name.
665 666 667 668 669 670
        if( pcbFileName.FileExists() )
        {
            // Remove the old file xxx.000 if it exists.
            if( backupFileName.FileExists() )
                wxRemoveFile( backupFileName.GetFullPath() );

671
            // Rename the "old" file" from xxx.kicad_pcb to xxx.000
672 673 674 675 676 677 678 679
            if( !wxRenameFile( pcbFileName.GetFullPath(), backupFileName.GetFullPath() ) )
            {
                msg = _( "Warning: unable to create backup file " ) + backupFileName.GetFullPath();
                DisplayError( this, msg );
                saveok = false;
            }
        }
        else
680
        {
681
            backupFileName.Clear();
682 683 684
        }
    }

685 686 687 688 689 690
    GetBoard()->m_Status_Pcb &= ~CONNEXION_OK;

    GetBoard()->SynchronizeNetsAndNetClasses();

    // Select default Netclass before writing file.
    // Useful to save default values in headers
691
    GetDesignSettings().SetCurrentNetClass( NETCLASS::Default );
692

693 694
    try
    {
695
        PLUGIN::RELEASER    pi( IO_MGR::PluginFind( pluginType ) );
696

697 698
        /*
        if( (PLUGIN*)pi == NULL )
699 700
            THROW_IO_ERROR( wxString::Format( _( "cannot find file plug in for file format '%s'" ),
                                              GetChars( pcbFileName.GetExt() ) ) );
701
        */
702

703
        pi->Save( pcbFileName.GetFullPath(), GetBoard(), NULL );
704
    }
705
    catch( const IO_ERROR& ioe )
706
    {
707 708
        wxString msg = wxString::Format( _( "Error saving board.\n%s" ),
                                         ioe.errorText.GetData() );
709
        wxMessageBox( msg, _( "Save Board File" ), wxICON_ERROR | wxOK );
710 711 712
        saveok = false;
    }

713 714
    if( saveok )
    {
715
        GetBoard()->SetFileName( pcbFileName.GetFullPath() );
716
        UpdateTitle();
717 718 719 720 721 722 723

        // Put the saved file in File History, unless aCreateBackupFile
        // is false.
        // aCreateBackupFile == false is mainly used to write autosave files
        // and not need to have an autosave file in file history
        if( aCreateBackupFile )
            UpdateFileHistory( GetBoard()->GetFileName() );
724 725 726 727 728

        // It's possible that the save as wrote over an existing board file that was part of a
        // project so attempt reload the projects settings.
        if( isSaveAs )
            LoadProjectSettings( pcbFileName.GetFullPath() );
729 730
    }

Dick Hollenbeck's avatar
Dick Hollenbeck committed
731
    // Display the file names:
732
    m_messagePanel->EraseMsgBox();
733 734 735

    if( saveok )
    {
736 737
        // Delete auto save file on successful save.
        wxFileName autoSaveFileName = pcbFileName;
738 739

        autoSaveFileName.SetName( wxString( autosavePrefix ) + pcbFileName.GetName() );
740 741 742 743

        if( autoSaveFileName.FileExists() )
            wxRemoveFile( autoSaveFileName.GetFullPath() );

744
        upperTxt = _( "Backup file: " ) + backupFileName.GetFullPath();
745 746
    }

Dick Hollenbeck's avatar
Dick Hollenbeck committed
747
    if( saveok )
dickelbeck's avatar
dickelbeck committed
748
        lowerTxt = _( "Wrote board file: " );
749
    else
dickelbeck's avatar
dickelbeck committed
750
        lowerTxt = _( "Failed to create " );
751

752
    lowerTxt += pcbFileName.GetFullPath();
753

754 755
    ClearMsgPanel();
    AppendMsgPanel( upperTxt, lowerTxt, CYAN );
charras's avatar
charras committed
756

757
    GetScreen()->ClrSave();
758
    GetScreen()->ClrModify();
759
    return true;
760
}
761 762 763 764


bool PCB_EDIT_FRAME::doAutoSave()
{
765
    wxFileName tmpFileName = GetBoard()->GetFileName();
766 767
    wxFileName fn = tmpFileName;

768 769
    // Auto save file name is the normal file name prepended with
    // autosaveFilePrefix string.
770
    fn.SetName( wxString( autosavePrefix ) + fn.GetName() );
771

772 773 774
    wxLogTrace( traceAutoSave,
                wxT( "Creating auto save file <" + fn.GetFullPath() ) + wxT( ">" ) );

775 776
    if( SavePcbFile( fn.GetFullPath(), NO_BACKUP_FILE ) )
    {
777
        GetScreen()->SetModify();
778
        GetBoard()->SetFileName( tmpFileName.GetFullPath() );
779
        UpdateTitle();
780
        m_autoSaveState = false;
781 782 783
        return true;
    }

784
    GetBoard()->SetFileName( tmpFileName.GetFullPath() );
785 786 787

    return false;
}