dialog_fp_lib_table.cpp 23.4 KB
Newer Older
1 2 3 4
/*
 * This program source code file is part of KiCad, a free EDA CAD application.
 *
 * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
5
 * Copyright (C) 2013 CERN
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
 * Copyright (C) 2012 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
 */


Dick Hollenbeck's avatar
Dick Hollenbeck committed
27 28
/*  TODO:

29
*)  After any change to uri, reparse the environment variables.
30

Dick Hollenbeck's avatar
Dick Hollenbeck committed
31 32 33 34
*/


#include <set>
35
#include <wx/regex.h>
Dick Hollenbeck's avatar
Dick Hollenbeck committed
36

37
#include <fctsys.h>
38
#include <project.h>
39 40
#include <dialog_fp_lib_table_base.h>
#include <fp_lib_table.h>
41 42
#include <fp_lib_table_lexer.h>
#include <invoke_pcb_dialog.h>
43
#include <grid_tricks.h>
44
#include <confirm.h>
45 46 47 48 49 50 51 52 53 54 55 56

/// grid column order is established by this sequence
enum COL_ORDER
{
    COL_NICKNAME,
    COL_URI,
    COL_TYPE,
    COL_OPTIONS,
    COL_DESCR,
    COL_COUNT       // keep as last
};

57

Dick Hollenbeck's avatar
Dick Hollenbeck committed
58 59
/**
 * Class FP_TBL_MODEL
60
 * mixes in FP_LIB_TABLE into wxGridTableBase so the result can be used
Dick Hollenbeck's avatar
Dick Hollenbeck committed
61
 * as a table within wxGrid.
Dick Hollenbeck's avatar
Dick Hollenbeck committed
62
 */
63 64
class FP_TBL_MODEL : public wxGridTableBase, public FP_LIB_TABLE
{
65
    friend class FP_GRID_TRICKS;
66 67

public:
68

69 70
    /**
     * Constructor FP_TBL_MODEL
Dick Hollenbeck's avatar
Dick Hollenbeck committed
71 72
     * is a copy constructor that builds a wxGridTableBase (table model) by wrapping
     * an FP_LIB_TABLE.
73 74 75 76 77 78 79 80
     */
    FP_TBL_MODEL( const FP_LIB_TABLE& aTableToEdit ) :
        FP_LIB_TABLE( aTableToEdit )    // copy constructor
    {
    }

    //-----<wxGridTableBase overloads>-------------------------------------------

81 82
    int         GetNumberRows()     { return rows.size(); }
    int         GetNumberCols()     { return COL_COUNT; }
83 84 85 86 87 88 89 90 91

    wxString    GetValue( int aRow, int aCol )
    {
        if( unsigned( aRow ) < rows.size() )
        {
            const ROW&  r  = rows[aRow];

            switch( aCol )
            {
92 93 94 95 96
            case COL_NICKNAME:  return r.GetNickName();
            case COL_URI:       return r.GetFullURI();
            case COL_TYPE:      return r.GetType();
            case COL_OPTIONS:   return r.GetOptions();
            case COL_DESCR:     return r.GetDescr();
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
            default:
                ;       // fall thru to wxEmptyString
            }
        }

        return wxEmptyString;
    }

    void    SetValue( int aRow, int aCol, const wxString &aValue )
    {
        if( unsigned( aRow ) < rows.size() )
        {
            ROW&  r  = rows[aRow];

            switch( aCol )
            {
113 114 115 116 117
            case COL_NICKNAME:  r.SetNickName( aValue );    break;
            case COL_URI:       r.SetFullURI( aValue );     break;
            case COL_TYPE:      r.SetType( aValue  );       break;
            case COL_OPTIONS:   r.SetOptions( aValue );     break;
            case COL_DESCR:     r.SetDescr( aValue );       break;
118 119 120 121 122 123
            }
        }
    }

    bool IsEmptyCell( int aRow, int aCol )
    {
124
        return !GetValue( aRow, aCol );
125 126 127 128 129 130 131
    }

    bool InsertRows( size_t aPos = 0, size_t aNumRows = 1 )
    {
        if( aPos < rows.size() )
        {
            rows.insert( rows.begin() + aPos, aNumRows, ROW() );
Dick Hollenbeck's avatar
Dick Hollenbeck committed
132 133 134 135 136 137 138 139 140 141 142 143

            // use the (wxGridStringTable) source Luke.
            if( GetView() )
            {
                wxGridTableMessage msg( this,
                                        wxGRIDTABLE_NOTIFY_ROWS_INSERTED,
                                        aPos,
                                        aNumRows );

                GetView()->ProcessTableMessage( msg );
            }

144 145 146 147 148 149 150
            return true;
        }
        return false;
    }

    bool AppendRows( size_t aNumRows = 1 )
    {
Dick Hollenbeck's avatar
Dick Hollenbeck committed
151 152
        // do not modify aNumRows, original value needed for wxGridTableMessage below
        for( int i = aNumRows; i; --i )
153
            rows.push_back( ROW() );
Dick Hollenbeck's avatar
Dick Hollenbeck committed
154 155 156 157 158 159 160 161 162 163

        if( GetView() )
        {
            wxGridTableMessage msg( this,
                                    wxGRIDTABLE_NOTIFY_ROWS_APPENDED,
                                    aNumRows );

            GetView()->ProcessTableMessage( msg );
        }

164 165 166 167 168
        return true;
    }

    bool DeleteRows( size_t aPos, size_t aNumRows )
    {
Dick Hollenbeck's avatar
Dick Hollenbeck committed
169 170 171
        // aPos may be a large positive, e.g. size_t(-1), and the sum of
        // aPos+aNumRows may wrap here, so both ends of the range are tested.
        if( aPos < rows.size() && aPos + aNumRows <= rows.size() )
172 173 174
        {
            ROWS_ITER start = rows.begin() + aPos;
            rows.erase( start, start + aNumRows );
Dick Hollenbeck's avatar
Dick Hollenbeck committed
175 176 177 178 179 180 181 182 183 184 185

            if( GetView() )
            {
                wxGridTableMessage msg( this,
                                        wxGRIDTABLE_NOTIFY_ROWS_DELETED,
                                        aPos,
                                        aNumRows );

                GetView()->ProcessTableMessage( msg );
            }

186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
            return true;
        }
        return false;
    }

    void Clear()
    {
        rows.clear();
        nickIndex.clear();
    }

    wxString GetColLabelValue( int aCol )
    {
        switch( aCol )
        {
201 202
        case COL_NICKNAME:  return _( "Nickname" );
        case COL_URI:       return _( "Library Path" );
Dick Hollenbeck's avatar
Dick Hollenbeck committed
203

204
        // keep this "Plugin Type" text fairly long so column is sized wide enough
Dick Hollenbeck's avatar
Dick Hollenbeck committed
205
        case COL_TYPE:      return _( "Plugin Type" );
206 207 208
        case COL_OPTIONS:   return _( "Options" );
        case COL_DESCR:     return _( "Description" );
        default:            return wxEmptyString;
209 210 211 212 213 214 215
        }
    }

    //-----</wxGridTableBase overloads>------------------------------------------
};


216
class FP_GRID_TRICKS : public GRID_TRICKS
217
{
218 219 220
public:
    FP_GRID_TRICKS( wxGrid* aGrid ) :
        GRID_TRICKS( aGrid )
Dick Hollenbeck's avatar
Dick Hollenbeck committed
221
    {
222
    }
Dick Hollenbeck's avatar
Dick Hollenbeck committed
223

224
protected:
225

226 227 228 229 230
    /// handle specialized clipboard text, with leading "(fp_lib_table", OR
    /// spreadsheet formatted text.
    virtual void paste_text( const wxString& cb_text )
    {
        FP_TBL_MODEL*       tbl = (FP_TBL_MODEL*) m_grid->GetTable();
231

232
        size_t  ndx = cb_text.find( wxT( "(fp_lib_table" ) );
Dick Hollenbeck's avatar
Dick Hollenbeck committed
233

234
        if( ndx != std::string::npos )
235
        {
236 237
            // paste the ROWs of s-expression (fp_lib_table), starting
            // at column 0 regardless of current cursor column.
Dick Hollenbeck's avatar
Dick Hollenbeck committed
238

239 240 241 242
            STRING_LINE_READER  slr( TO_UTF8( cb_text ), wxT( "Clipboard" ) );
            FP_LIB_TABLE_LEXER  lexer( &slr );
            FP_LIB_TABLE        tmp_tbl;
            bool                parsed = true;
243

244 245 246 247 248 249
            try
            {
                tmp_tbl.Parse( &lexer );
            }
            catch( PARSE_ERROR& pe )
            {
250
                DisplayError( NULL, pe.errorText );
251 252
                parsed = false;
            }
253

254 255 256
            if( parsed )
            {
                const int cur_row = std::max( getCursorRow(), 0 );
257

258 259 260 261 262 263
                // if clipboard rows would extend past end of current table size...
                if( tmp_tbl.GetCount() > tbl->GetNumberRows() - cur_row )
                {
                    int newRowsNeeded = tmp_tbl.GetCount() - ( tbl->GetNumberRows() - cur_row );
                    tbl->AppendRows( newRowsNeeded );
                }
264

265 266 267 268 269 270 271 272
                for( int i = 0;  i < tmp_tbl.GetCount();  ++i )
                {
                    tbl->At( cur_row+i ) = tmp_tbl.At( i );
                }
            }
            m_grid->AutoSizeColumns( false );
        }
        else
273
        {
274 275
            // paste spreadsheet formatted text.
            GRID_TRICKS::paste_text( cb_text );
276
        }
277 278
    }
};
279 280


281 282 283 284 285 286 287
/**
 * Class DIALOG_FP_LIB_TABLE
 * shows and edits the PCB library tables.  Two tables are expected, one global
 * and one project specific.
 */
class DIALOG_FP_LIB_TABLE : public DIALOG_FP_LIB_TABLE_BASE
{
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

public:
    DIALOG_FP_LIB_TABLE( wxTopLevelWindow* aParent, FP_LIB_TABLE* aGlobal, FP_LIB_TABLE* aProject ) :
        DIALOG_FP_LIB_TABLE_BASE( aParent ),
        m_global( aGlobal ),
        m_project( aProject )
    {
        // wxGrid only supports user owned tables if they exist past end of ~wxGrid(),
        // so make it a grid owned table.
        m_global_grid->SetTable(  new FP_TBL_MODEL( *aGlobal ),  true );
        m_project_grid->SetTable( new FP_TBL_MODEL( *aProject ), true );

        // add Cut, Copy, and Paste to wxGrids
        m_global_grid->PushEventHandler( new FP_GRID_TRICKS( m_global_grid ) );
        m_project_grid->PushEventHandler( new FP_GRID_TRICKS( m_project_grid ) );

        m_global_grid->AutoSizeColumns( false );
        m_project_grid->AutoSizeColumns( false );

        wxArrayString choices;

        choices.Add( IO_MGR::ShowType( IO_MGR::KICAD ) );
        choices.Add( IO_MGR::ShowType( IO_MGR::GITHUB ) );
        choices.Add( IO_MGR::ShowType( IO_MGR::LEGACY ) );
        choices.Add( IO_MGR::ShowType( IO_MGR::EAGLE ) );
        choices.Add( IO_MGR::ShowType( IO_MGR::GEDA_PCB ) );

        /* PCAD_PLUGIN does not support Footprint*() functions
        choices.Add( IO_MGR::ShowType( IO_MGR::GITHUB ) );
        */

        wxGridCellAttr* attr;

        attr = new wxGridCellAttr;
        attr->SetEditor( new wxGridCellChoiceEditor( choices ) );
        m_project_grid->SetColAttr( COL_TYPE, attr );

        attr = new wxGridCellAttr;
        attr->SetEditor( new wxGridCellChoiceEditor( choices ) );
        m_global_grid->SetColAttr( COL_TYPE, attr );

329
        populateEnvironReadOnlyTable();
330

331 332 333
        for( int i=0; i<2; ++i )
        {
            wxGrid* g = i==0 ? m_global_grid : m_project_grid;
334

335
            // all but COL_OPTIONS, which is edited with Option Editor anyways.
336
            g->AutoSizeColumn( COL_NICKNAME, false );
337 338 339
            g->AutoSizeColumn( COL_TYPE, false );
            g->AutoSizeColumn( COL_URI, false );
            g->AutoSizeColumn( COL_DESCR, false );
340

341
            // would set this to width of title, if it was easily known.
342 343 344 345 346 347
            g->SetColSize( COL_OPTIONS, 80 );
        }

        // This scrunches the dialog hideously, probably due to wxAUI container.
        // Fit();
        // We derive from DIALOG_SHIM so prior size will be used anyways.
348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366

        // fire pageChangedHandler() so m_cur_grid gets set
        wxAuiNotebookEvent uneventful;
        pageChangedHandler( uneventful );

        // for ALT+A handling, we want the initial focus to be on the first selected grid.
        m_cur_grid->SetFocus();
    }

    ~DIALOG_FP_LIB_TABLE()
    {
        // Delete the GRID_TRICKS.
        // Any additional event handlers should be popped before the window is deleted.
        m_global_grid->PopEventHandler( true );
        m_project_grid->PopEventHandler( true );
    }


private:
Dick Hollenbeck's avatar
Dick Hollenbeck committed
367
    typedef FP_LIB_TABLE::ROW   ROW;
368

369 370
    /// If the cursor is not on a valid cell, because there are no rows at all, return -1,
    /// else return a 0 based column index.
371
    int getCursorCol() const
372
    {
373
        return m_cur_grid->GetGridCursorCol();
374 375
    }

376 377
    /// If the cursor is not on a valid cell, because there are no rows at all, return -1,
    /// else return a 0 based row index.
378
    int getCursorRow() const
379
    {
380
        return m_cur_grid->GetGridCursorRow();
381
    }
382

383 384 385 386 387 388 389 390
    /**
     * Function verifyTables
     * trims important fields, removes blank row entries, and checks for duplicates.
     * @return bool - true if tables are OK, else false.
     */
    bool verifyTables()
    {
        for( int t=0; t<2; ++t )
391
        {
392
            FP_TBL_MODEL& model = t==0 ? *global_model() : *project_model();
393 394

            for( int r = 0; r < model.GetNumberRows(); )
395
            {
396 397
                wxString nick = model.GetValue( r, COL_NICKNAME ).Trim( false ).Trim();
                wxString uri  = model.GetValue( r, COL_URI ).Trim( false ).Trim();
398

399
                if( !nick || !uri )
400
                {
401
                    // Delete the "empty" row, where empty means missing nick or uri.
402 403 404 405 406 407 408 409 410 411
                    // This also updates the UI which could be slow, but there should only be a few
                    // rows to delete, unless the user fell asleep on the Add Row
                    // button.
                    model.DeleteRows( r, 1 );
                }
                else if( nick.find(':') != size_t(-1) )
                {
                    wxString msg = wxString::Format(
                        _( "Illegal character '%s' found in Nickname: '%s' in row %d" ),
                        wxT( ":" ), GetChars( nick ), r );
412

413
                    // show the tabbed panel holding the grid we have flunked:
414
                    if( &model != cur_model() )
415
                    {
416
                        m_auinotebook->SetSelection( &model == global_model() ? 0 : 1 );
417 418
                    }

419
                    // go to the problematic row
420
                    m_cur_grid->SetGridCursor( r, 0 );
421 422
                    m_cur_grid->SelectBlock( r, 0, r, 0 );
                    m_cur_grid->MakeCellVisible( r, 0 );
423

424 425 426 427 428
                    wxMessageDialog errdlg( this, msg, _( "No Colon in Nicknames" ) );
                    errdlg.ShowModal();
                    return false;
                }
                else
429
                {
430 431 432 433 434 435 436
                    // set the trimmed values back into the table so they get saved to disk.
                    model.SetValue( r, COL_NICKNAME, nick );
                    model.SetValue( r, COL_URI, uri );
                    ++r;        // this row was OK.
                }
            }
        }
437

438 439 440
        // check for duplicate nickNames, separately in each table.
        for( int t=0; t<2; ++t )
        {
441
            FP_TBL_MODEL& model = t==0 ? *global_model() : *project_model();
442

443 444
            for( int r1 = 0; r1 < model.GetNumberRows() - 1;  ++r1 )
            {
445
                wxString    nick1 = model.GetValue( r1, COL_NICKNAME );
446

447 448 449
                for( int r2=r1+1; r2 < model.GetNumberRows();  ++r2 )
                {
                    wxString    nick2 = model.GetValue( r2, COL_NICKNAME );
450

451
                    if( nick1 == nick2 )
452
                    {
453 454 455 456
                        wxString msg = wxString::Format(
                            _( "Duplicate Nickname: '%s' in rows %d and %d" ),
                            GetChars( nick1 ), r1+1, r2+1
                            );
457

458
                        // show the tabbed panel holding the grid we have flunked:
459
                        if( &model != cur_model() )
460
                        {
461
                            m_auinotebook->SetSelection( &model == global_model() ? 0 : 1 );
462
                        }
463

464
                        // go to the lower of the two rows, it is technically the duplicate:
465
                        m_cur_grid->SetGridCursor( r2, 0 );
466 467 468 469 470 471
                        m_cur_grid->SelectBlock( r2, 0, r2, 0 );
                        m_cur_grid->MakeCellVisible( r2, 0 );

                        wxMessageDialog errdlg( this, msg, _( "Please Delete or Modify One" ) );
                        errdlg.ShowModal();
                        return false;
472 473 474 475
                    }
                }
            }
        }
476 477

        return true;
Dick Hollenbeck's avatar
Dick Hollenbeck committed
478
    }
Dick Hollenbeck's avatar
Dick Hollenbeck committed
479

480 481
    //-----<event handlers>----------------------------------

482 483
    void onKeyDown( wxKeyEvent& ev )
    {
484 485 486 487 488 489 490 491
#if 0
        // send the key to the current grid
        ((wxEvtHandler*)m_cur_grid)->ProcessEvent( ev );
#else
        // or no:
        // m_cur_grid has the focus most of the time anyways, so above not needed.
        ev.Skip();
#endif
492 493
    }

494 495 496
    void pageChangedHandler( wxAuiNotebookEvent& event )
    {
        int pageNdx = m_auinotebook->GetSelection();
497
        m_cur_grid = ( pageNdx == 0 ) ? m_global_grid : m_project_grid;
498 499 500 501
    }

    void appendRowHandler( wxMouseEvent& event )
    {
502 503 504 505
        if( m_cur_grid->AppendRows( 1 ) )
        {
            int last_row = m_cur_grid->GetNumberRows() - 1;

506
            // wx documentation is wrong, SetGridCursor does not make visible.
507
            m_cur_grid->MakeCellVisible( last_row, 0 );
508
            m_cur_grid->SetGridCursor( last_row, 0 );
509
        }
510 511 512 513
    }

    void deleteRowHandler( wxMouseEvent& event )
    {
514 515 516
        int rowCount = m_cur_grid->GetNumberRows();
        int curRow   = getCursorRow();

Dick Hollenbeck's avatar
Dick Hollenbeck committed
517 518 519
        if( curRow >= 0 )
        {
            m_cur_grid->DeleteRows( curRow );
520

Dick Hollenbeck's avatar
Dick Hollenbeck committed
521 522 523
            if( curRow && curRow == rowCount - 1 )
                m_cur_grid->SetGridCursor( curRow-1, getCursorCol() );
        }
524 525 526 527
    }

    void moveUpHandler( wxMouseEvent& event )
    {
528
        int curRow = getCursorRow();
Dick Hollenbeck's avatar
Dick Hollenbeck committed
529 530
        if( curRow >= 1 )
        {
531
            int curCol = getCursorCol();
Dick Hollenbeck's avatar
Dick Hollenbeck committed
532

533
            FP_TBL_MODEL* tbl = cur_model();
Dick Hollenbeck's avatar
Dick Hollenbeck committed
534

535
            ROW move_me = tbl->rows[curRow];
Dick Hollenbeck's avatar
Dick Hollenbeck committed
536

537 538 539
            tbl->rows.erase( tbl->rows.begin() + curRow );
            --curRow;
            tbl->rows.insert( tbl->rows.begin() + curRow, move_me );
Dick Hollenbeck's avatar
Dick Hollenbeck committed
540 541 542

            if( tbl->GetView() )
            {
543
                // fire a msg to cause redrawing
Dick Hollenbeck's avatar
Dick Hollenbeck committed
544 545 546 547 548 549 550
                wxGridTableMessage msg( tbl,
                                        wxGRIDTABLE_NOTIFY_ROWS_INSERTED,
                                        curRow,
                                        0 );

                tbl->GetView()->ProcessTableMessage( msg );
            }
551

552
            m_cur_grid->MakeCellVisible( curRow, curCol );
553
            m_cur_grid->SetGridCursor( curRow, curCol );
Dick Hollenbeck's avatar
Dick Hollenbeck committed
554
        }
555 556 557 558
    }

    void moveDownHandler( wxMouseEvent& event )
    {
559
        FP_TBL_MODEL* tbl = cur_model();
560

561
        int curRow = getCursorRow();
562 563
        if( unsigned( curRow + 1 ) < tbl->rows.size() )
        {
564
            int curCol  = getCursorCol();
565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582

            ROW move_me = tbl->rows[curRow];

            tbl->rows.erase( tbl->rows.begin() + curRow );
             ++curRow;
            tbl->rows.insert( tbl->rows.begin() + curRow, move_me );

            if( tbl->GetView() )
            {
                // fire a msg to cause redrawing
                wxGridTableMessage msg( tbl,
                                        wxGRIDTABLE_NOTIFY_ROWS_INSERTED,
                                        curRow - 1,
                                        0 );

                tbl->GetView()->ProcessTableMessage( msg );
            }

583
            m_cur_grid->MakeCellVisible( curRow, curCol );
584 585
            m_cur_grid->SetGridCursor( curRow, curCol );
        }
586 587
    }

588
    void optionsEditor( wxMouseEvent& event )
589
    {
590
        FP_TBL_MODEL*   tbl = cur_model();
591

592 593 594 595
        if( tbl->GetNumberRows() )
        {
            int     curRow = getCursorRow();
            ROW&    row    = tbl->rows[curRow];
596

597 598
            wxString        result;
            const wxString& options = row.GetOptions();
599

600
            InvokePluginOptionsEditor( this, row.GetNickName(), row.GetType(), options, &result );
601

602 603 604
            if( options != result )
            {
                row.SetOptions( result );
605

606 607 608 609
                // all but options:
                m_cur_grid->AutoSizeColumn( COL_NICKNAME, false );
                m_cur_grid->AutoSizeColumn( COL_URI, false );
                m_cur_grid->AutoSizeColumn( COL_TYPE, false );
610

611 612
                // On Windows, the grid is not refresh,
                // so force resfresh after a change
613
#ifdef __WINDOWS__
614
                Refresh();
615
#endif
616
            }
Dick Hollenbeck's avatar
Dick Hollenbeck committed
617 618
        }
    }
619 620

    void onCancelButtonClick( wxCommandEvent& event )
621
    {
Dick Hollenbeck's avatar
Dick Hollenbeck committed
622
        EndModal( 0 );
623 624
    }

625
    void onCancelCaptionButtonClick( wxCloseEvent& event )
626
    {
627
        EndModal( 0 );
628 629
    }

630
    void onOKButtonClick( wxCommandEvent& event )
631
    {
Dick Hollenbeck's avatar
Dick Hollenbeck committed
632
        int dialogRet = 0;
633

634 635 636
        // stuff any pending cell editor text into the table.
        m_cur_grid->SaveEditControlValue();

637
        if( verifyTables() )
Dick Hollenbeck's avatar
Dick Hollenbeck committed
638
        {
639
            if( *global_model() != *m_global )
640 641
            {
                dialogRet |= 1;
642

643
                *m_global  = *global_model();
644 645
                m_global->reindex();
            }
646

647
            if( *project_model() != *m_project )
648 649
            {
                dialogRet |= 2;
Dick Hollenbeck's avatar
Dick Hollenbeck committed
650

651
                *m_project = *project_model();
652 653
                m_project->reindex();
            }
654

655 656
            EndModal( dialogRet );
        }
657 658
    }

Dick Hollenbeck's avatar
Dick Hollenbeck committed
659 660 661 662 663 664 665 666 667 668
    /// Populate the readonly environment variable table with names and values
    /// by examining all the full_uri columns.
    void populateEnvironReadOnlyTable()
    {
        wxRegEx re( wxT( ".*?\\$\\{(.+?)\\}.*?" ), wxRE_ADVANCED );
        wxASSERT( re.IsValid() );   // wxRE_ADVANCED is required.

        std::set< wxString >        unique;
        typedef std::set<wxString>::const_iterator      SET_CITER;

669
        // clear the table
Dick Hollenbeck's avatar
Dick Hollenbeck committed
670 671
        m_path_subs_grid->DeleteRows( 0, m_path_subs_grid->GetNumberRows() );

672 673 674 675 676
        FP_TBL_MODEL*   gbl = global_model();
        FP_TBL_MODEL*   prj = project_model();

        int gblRowCount = gbl->GetNumberRows();
        int prjRowCount = prj->GetNumberRows();
Dick Hollenbeck's avatar
Dick Hollenbeck committed
677 678 679 680
        int row;

        for( row = 0;  row < gblRowCount;  ++row )
        {
681
            wxString uri = gbl->GetValue( row, COL_URI );
Dick Hollenbeck's avatar
Dick Hollenbeck committed
682 683 684 685 686 687 688 689 690 691 692 693

            while( re.Matches( uri ) )
            {
                wxString envvar = re.GetMatch( uri, 1 );

                // ignore duplicates
                unique.insert( envvar );

                // delete the last match and search again
                uri.Replace( re.GetMatch( uri, 0 ), wxEmptyString );
            }
        }
694

Dick Hollenbeck's avatar
Dick Hollenbeck committed
695 696
        for( row = 0;  row < prjRowCount;  ++row )
        {
697
            wxString uri = prj->GetValue( row, COL_URI );
Dick Hollenbeck's avatar
Dick Hollenbeck committed
698 699 700 701 702 703 704 705 706 707 708 709 710

            while( re.Matches( uri ) )
            {
                wxString envvar = re.GetMatch( uri, 1 );

                // ignore duplicates
                unique.insert( envvar );

                // delete the last match and search again
                uri.Replace( re.GetMatch( uri, 0 ), wxEmptyString );
            }
        }

711 712 713
        // Make sure this special environment variable shows up even if it was
        // not used yet.  It is automatically set by KiCad to the directory holding
        // the current project.
714
        unique.insert( PROJECT_VAR_NAME );
715
        unique.insert( FP_LIB_TABLE::GlobalPathEnvVariableName() );
716

Dick Hollenbeck's avatar
Dick Hollenbeck committed
717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733
        m_path_subs_grid->AppendRows( unique.size() );

        row = 0;
        for( SET_CITER it = unique.begin();  it != unique.end();  ++it, ++row )
        {
            wxString    evName = *it;
            wxString    evValue;

            m_path_subs_grid->SetCellValue( row, 0, evName );

            if( wxGetEnv( evName, &evValue ) )
                m_path_subs_grid->SetCellValue( row, 1, evValue );
        }

        m_path_subs_grid->AutoSizeColumns();
    }

734
    //-----</event handlers>---------------------------------
735

736
    // caller's tables are modified only on OK button and successful verification.
737 738 739
    FP_LIB_TABLE*       m_global;
    FP_LIB_TABLE*       m_project;

740 741 742
    FP_TBL_MODEL*       global_model()  const   { return (FP_TBL_MODEL*) m_global_grid->GetTable(); }
    FP_TBL_MODEL*       project_model() const   { return (FP_TBL_MODEL*) m_project_grid->GetTable(); }
    FP_TBL_MODEL*       cur_model() const       { return (FP_TBL_MODEL*) m_cur_grid->GetTable(); }
743 744

    wxGrid*             m_cur_grid;     ///< changed based on tab choice
745 746 747
};


748
int InvokePcbLibTableEditor( wxTopLevelWindow* aParent, FP_LIB_TABLE* aGlobal, FP_LIB_TABLE* aProject )
749
{
Dick Hollenbeck's avatar
Dick Hollenbeck committed
750
    DIALOG_FP_LIB_TABLE dlg( aParent, aGlobal, aProject );
751

Dick Hollenbeck's avatar
Dick Hollenbeck committed
752
    int dialogRet = dlg.ShowModal();    // returns value passed to EndModal() above
753

Dick Hollenbeck's avatar
Dick Hollenbeck committed
754
    return dialogRet;
755
}