KiCad PCB EDA Suite
Loading...
Searching...
No Matches
panel_design_block_lib_table.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
24
25#include <set>
26#include <wx/dir.h>
27#include <wx/regex.h>
28#include <wx/dirdlg.h>
29#include <wx/filedlg.h>
30#include <wx/msgdlg.h>
31
32#include <common.h>
33#include <project.h>
34#include <env_vars.h>
35#include <lib_id.h>
36#include <bitmaps.h>
38#include <widgets/wx_grid.h>
41#include <confirm.h>
44#include <pgm_base.h>
45#include <env_paths.h>
50#include <kiway.h>
51#include <kiway_express.h>
54#include <paths.h>
55#include <macros.h>
59
72
73
78class LIBRARY_TRAVERSER : public wxDirTraverser
79{
80public:
81 LIBRARY_TRAVERSER( std::vector<std::string> aSearchExtensions, wxString aInitialDir ) :
82 m_searchExtensions( aSearchExtensions ), m_currentDir( aInitialDir )
83 {
84 }
85
86 virtual wxDirTraverseResult OnFile( const wxString& aFileName ) override
87 {
88 wxFileName file( aFileName );
89
90 for( const std::string& ext : m_searchExtensions )
91 {
92 if( file.GetExt().IsSameAs( ext, false ) )
93 m_foundDirs.insert( { m_currentDir, 1 } );
94 }
95
96 return wxDIR_CONTINUE;
97 }
98
99 virtual wxDirTraverseResult OnOpenError( const wxString& aOpenErrorName ) override
100 {
101 m_failedDirs.insert( { aOpenErrorName, 1 } );
102 return wxDIR_IGNORE;
103 }
104
105 bool HasDirectoryOpenFailures() { return m_failedDirs.size() > 0; }
106
107 virtual wxDirTraverseResult OnDir( const wxString& aDirName ) override
108 {
109 m_currentDir = aDirName;
110 return wxDIR_CONTINUE;
111 }
112
113 void GetPaths( wxArrayString& aPathArray )
114 {
115 for( std::pair<const wxString, int>& foundDirsPair : m_foundDirs )
116 aPathArray.Add( foundDirsPair.first );
117 }
118
119 void GetFailedPaths( wxArrayString& aPathArray )
120 {
121 for( std::pair<const wxString, int>& failedDirsPair : m_failedDirs )
122 aPathArray.Add( failedDirsPair.first );
123 }
124
125private:
126 std::vector<std::string> m_searchExtensions;
127 wxString m_currentDir;
128 std::unordered_map<wxString, int> m_foundDirs;
129 std::unordered_map<wxString, int> m_failedDirs;
130};
131
132
137{
138public:
140 LIBRARY_MANAGER_ADAPTER* aAdapter, const wxArrayString& aPluginChoices,
141 wxString* aMRUDirectory, const wxString& aProjectPath,
143 IO_BASE::IO_FILE_DESC>& aSupportedFiles ) :
144 LIB_TABLE_GRID_DATA_MODEL( aParent, aGrid, aTableToEdit, aAdapter, aPluginChoices, aMRUDirectory,
145 aProjectPath ),
146 m_supportedDesignBlockFiles( aSupportedFiles )
147 {
148 }
149
150 void SetValue( int aRow, int aCol, const wxString& aValue ) override
151 {
152 wxCHECK( aRow < (int) size(), /* void */ );
153
154 LIB_TABLE_GRID_DATA_MODEL::SetValue( aRow, aCol, aValue );
155
156 // If setting a filepath, attempt to auto-detect the format
157 if( aCol == COL_URI )
158 {
159 LIBRARY_TABLE_ROW& row = at( static_cast<size_t>( aRow ) );
160 wxString uri = LIBRARY_MANAGER::ExpandURI( row.URI(), Pgm().GetSettingsManager().Prj() );
161
164
165 if( pluginType != DESIGN_BLOCK_IO_MGR::FILE_TYPE_NONE )
166 SetValue( aRow, COL_TYPE, DESIGN_BLOCK_IO_MGR::ShowType( pluginType ) );
167 }
168 }
169
170protected:
171 wxString getFileTypes( WX_GRID* aGrid, int aRow ) override
172 {
173 auto* table = static_cast<DESIGN_BLOCK_LIB_TABLE_GRID_DATA_MODEL*>( aGrid->GetTable() );
174 LIBRARY_TABLE_ROW& tableRow = table->at( aRow );
175
176 if( tableRow.Type() == LIBRARY_TABLE_ROW::TABLE_TYPE_NAME )
177 return wxEmptyString;
178
179 if( tableRow.Type().IsEmpty() )
180 return wxEmptyString;
181
184 return wxEmptyString;
185
187
188 if( pluginDesc.m_IsFile )
189 return pluginDesc.FileFilter();
190
191 return wxEmptyString;
192 }
193
194private:
195 const std::map<DESIGN_BLOCK_IO_MGR::DESIGN_BLOCK_FILE_T, IO_BASE::IO_FILE_DESC>& m_supportedDesignBlockFiles;
196};
197
198
200{
201public:
208
209protected:
210 void optionsEditor( int aRow ) override
211 {
212 LIB_TABLE_GRID_DATA_MODEL* tbl = static_cast<LIB_TABLE_GRID_DATA_MODEL*>( m_grid->GetTable() );
213
214 if( tbl->GetNumberRows() > aRow )
215 {
216 LIBRARY_TABLE_ROW& row = tbl->At( static_cast<size_t>( aRow ) );
217 const wxString& options = row.Options();
218 wxString result = options;
219 std::map<std::string, UTF8> choices;
220
223 pi->GetLibraryOptions( &choices );
224
225 DIALOG_PLUGIN_OPTIONS dlg( wxGetTopLevelParent( m_grid ), row.Nickname(), choices, options, &result );
226 dlg.ShowModal();
227
228 if( options != result )
229 {
230 row.SetOptions( result );
231 m_grid->Refresh();
232 }
233 }
234 }
235
236 void openTable( const LIBRARY_TABLE_ROW& aRow ) override
237 {
238 wxString uri = LIBRARY_MANAGER::ExpandURI( aRow.URI(), Pgm().GetSettingsManager().Prj() );
239 auto nestedTable = std::make_unique<LIBRARY_TABLE>( uri, LIBRARY_TABLE_SCOPE::GLOBAL );
240
241 m_panel->AddTable( nestedTable.get(), aRow.Nickname(), true );
242 }
243
244 wxString getTablePreamble() override
245 {
246 return wxT( "(design_block_lib_table" );
247 }
248
249protected:
251};
252
253
254void PANEL_DESIGN_BLOCK_LIB_TABLE::AddTable( LIBRARY_TABLE* table, const wxString& aTitle, bool aClosable )
255{
256 DESIGN_BLOCK_LIBRARY_ADAPTER* adapter = m_project->DesignBlockLibs();
257 wxString projectPath = m_project->GetProjectPath();
258
260
261 WX_GRID* grid = get_grid( (int) m_notebook->GetPageCount() - 1 );
262
263 if( table->Path().StartsWith( projectPath ) )
264 {
266 &m_lastProjectLibDir, projectPath,
268 true /* take ownership */ );
269 }
270 else
271 {
272 wxString* lastGlobalLibDir = nullptr;
273
274 if( KICAD_SETTINGS* cfg = GetAppSettings<KICAD_SETTINGS>( "kicad" ) )
275 {
276 if( cfg->m_lastDesignBlockLibDir.IsEmpty() )
277 cfg->m_lastDesignBlockLibDir = PATHS::GetDefaultUserDesignBlocksPath();
278
279 lastGlobalLibDir = &cfg->m_lastDesignBlockLibDir;
280 }
281
283 lastGlobalLibDir, wxEmptyString,
285 true /* take ownership */ );
286 }
287
288 // add Cut, Copy, and Paste to wxGrids
289 grid->PushEventHandler( new DESIGN_BLOCK_GRID_TRICKS( this, grid ) );
290
291 auto autoSizeCol =
292 [&]( int aCol )
293 {
294 int prevWidth = grid->GetColSize( aCol );
295
296 grid->AutoSizeColumn( aCol, false );
297 grid->SetColSize( aCol, std::max( prevWidth, grid->GetColSize( aCol ) ) );
298 };
299
300 // all but COL_OPTIONS, which is edited with Option Editor anyways.
301 autoSizeCol( COL_NICKNAME );
302 autoSizeCol( COL_TYPE );
303 autoSizeCol( COL_URI );
304 autoSizeCol( COL_DESCR );
305
306 if( grid->GetNumberRows() > 0 )
307 {
308 grid->SetGridCursor( 0, COL_NICKNAME );
309 grid->SelectRow( 0 );
310 }
311}
312
313
315 PROJECT* aProject ) :
317 m_project( aProject ),
318 m_parent( aParent )
319{
320 m_lastProjectLibDir = m_project->GetProjectPath();
321
323
324 for( auto& [fileType, desc] : m_supportedDesignBlockFiles )
326
327 std::optional<LIBRARY_TABLE*> table = Pgm().GetLibraryManager().Table( LIBRARY_TABLE_TYPE::DESIGN_BLOCK,
329 wxASSERT( table.has_value() );
330
331 AddTable( table.value(), _( "Global Libraries" ), false /* closable */ );
332
333 std::optional<LIBRARY_TABLE*> projectTable = Pgm().GetLibraryManager().Table( LIBRARY_TABLE_TYPE::DESIGN_BLOCK,
335
336 if( projectTable.has_value() )
337 AddTable( projectTable.value(), _( "Project Specific Libraries" ), false /* closable */ );
338
339 m_notebook->SetArtProvider( new WX_AUI_TAB_ART() );
340
341 // There aren't (yet) any legacy DesignBlock libraries to migrate
342 m_migrate_libs_button->Hide();
343
344 // add Cut, Copy, and Paste to wxGrids
345 m_path_subs_grid->PushEventHandler( new GRID_TRICKS( m_path_subs_grid ) );
346
348
349 m_path_subs_grid->SetColLabelValue( 0, _( "Name" ) );
350 m_path_subs_grid->SetColLabelValue( 1, _( "Value" ) );
351
352 // Configure button logos
358
359 // For aesthetic reasons, we must set the size of m_browseButton to match the other bitmaps
360 // manually (for instance m_append_button)
361 Layout(); // Needed at least on MSW to compute the actual buttons sizes, after initializing
362 // their bitmaps
363 wxSize buttonSize = m_append_button->GetSize();
364
365 m_browseButton->SetWidthPadding( 4 );
366 m_browseButton->SetMinSize( buttonSize );
367
368 // Populate the browse library options
369 wxMenu* browseMenu = m_browseButton->GetSplitButtonMenu();
370
371 for( auto& [type, desc] : m_supportedDesignBlockFiles )
372 {
373 wxString entryStr = DESIGN_BLOCK_IO_MGR::ShowType( type );
374 wxString midPart;
375
376 if( desc.m_IsFile && !desc.m_FileExtensions.empty() )
377 {
378 entryStr << wxString::Format( wxS( " (%s)" ), JoinExtensions( desc.m_FileExtensions ) );
379 }
380 else if( !desc.m_IsFile && !desc.m_ExtensionsInDir.empty() )
381 {
382 midPart = wxString::Format( _( "folder with %s files" ), JoinExtensions( desc.m_ExtensionsInDir ) );
383 entryStr << wxString::Format( wxS( " (%s)" ), midPart );
384 }
385
386 browseMenu->Append( type, entryStr );
387 browseMenu->Bind( wxEVT_COMMAND_MENU_SELECTED, &PANEL_DESIGN_BLOCK_LIB_TABLE::browseLibrariesHandler,
388 this, type );
389 }
390
391 Layout();
392
393 m_notebook->Bind( wxEVT_AUINOTEBOOK_PAGE_CLOSE, &PANEL_DESIGN_BLOCK_LIB_TABLE::onNotebookPageCloseRequest, this );
394 // This is the button only press for the browse button instead of the menu
396}
397
398
400{
401 wxMenu* browseMenu = m_browseButton->GetSplitButtonMenu();
402
403 for( auto& [type, desc] : m_supportedDesignBlockFiles )
404 {
405 browseMenu->Unbind( wxEVT_COMMAND_MENU_SELECTED, &PANEL_DESIGN_BLOCK_LIB_TABLE::browseLibrariesHandler,
406 this, type );
407 }
408
410
411 // Delete the GRID_TRICKS.
412 // (Notebook page GRID_TRICKS are deleted by LIB_TABLE_NOTEBOOK_PANEL.)
413 m_path_subs_grid->PopEventHandler( true );
414}
415
416
418{
420 {
422
423 if( !pi )
424 continue;
425
426 if( const IO_BASE::IO_FILE_DESC& desc = pi->GetLibraryDesc() )
427 m_supportedDesignBlockFiles.emplace( type, desc );
428 }
429}
430
431
436
437
439{
440 return static_cast<LIB_TABLE_NOTEBOOK_PANEL*>( m_notebook->GetPage( aPage ) )->GetGrid();
441}
442
443
445{
446 wxString msg;
447
448 for( int page = 0 ; page < (int) m_notebook->GetPageCount(); ++page )
449 {
450 WX_GRID* grid = get_grid( page );
451
453 [&]( int aRow, int aCol )
454 {
455 // show the tabbed panel holding the grid we have flunked:
456 if( m_notebook->GetSelection() != page )
457 m_notebook->SetSelection( page );
458
459 grid->MakeCellVisible( aRow, 0 );
460 grid->SetGridCursor( aRow, aCol );
461 } ) )
462 {
463 return false;
464 }
465 }
466
467 return true;
468}
469
470
475
476
481
482
487
488
493
494
496{
497 wxAuiNotebook* notebook = (wxAuiNotebook*) aEvent.GetEventObject();
498 wxWindow* page = notebook->GetPage( aEvent.GetSelection() );
499
500 if( LIB_TABLE_NOTEBOOK_PANEL* panel = dynamic_cast<LIB_TABLE_NOTEBOOK_PANEL*>( page ) )
501 {
502 if( panel->GetClosable() )
503 {
504 if( !panel->GetCanClose() )
505 aEvent.Veto();
506 }
507 else
508 {
509 aEvent.Veto();
510 }
511 }
512}
513
514
515// @todo refactor this function into single location shared with PANEL_SYM_LIB_TABLE
517{
518 if( !cur_grid()->CommitPendingChanges() )
519 return;
520
521 wxArrayInt selectedRows = cur_grid()->GetSelectedRows();
522
523 if( selectedRows.empty() && cur_grid()->GetGridCursorRow() >= 0 )
524 selectedRows.push_back( cur_grid()->GetGridCursorRow() );
525
526 wxArrayInt rowsToMigrate;
528 wxString msg;
529
530 for( int row : selectedRows )
531 {
532 if( cur_grid()->GetCellValue( row, COL_TYPE ) != kicadType )
533 rowsToMigrate.push_back( row );
534 }
535
536 if( rowsToMigrate.size() <= 0 )
537 {
538 wxMessageBox( _( "Select one or more rows containing libraries to save as current KiCad format." ) );
539 return;
540 }
541 else
542 {
543 if( rowsToMigrate.size() == 1 )
544 {
545 msg.Printf( _( "Save '%s' as current KiCad format and replace entry in table?" ),
546 cur_grid()->GetCellValue( rowsToMigrate[0], COL_NICKNAME ) );
547 }
548 else
549 {
550 msg.Printf( _( "Save %d libraries as current KiCad format and replace entries in table?" ),
551 (int) rowsToMigrate.size() );
552 }
553
554 if( !IsOK( m_parent, msg ) )
555 return;
556 }
557
558 for( int row : rowsToMigrate )
559 {
560 wxString libName = cur_grid()->GetCellValue( row, COL_NICKNAME );
561 wxString relPath = cur_grid()->GetCellValue( row, COL_URI );
562 wxString resolvedPath = ExpandEnvVarSubstitutions( relPath, m_project );
563 wxFileName legacyLib( resolvedPath );
564
565 if( !legacyLib.Exists() )
566 {
567 msg.Printf( _( "Library '%s' not found." ), relPath );
568 DisplayErrorMessage( wxGetTopLevelParent( this ), msg );
569 continue;
570 }
571
572 wxFileName newLib( resolvedPath );
573 newLib.AppendDir( newLib.GetName() + "." + FILEEXT::KiCadDesignBlockLibPathExtension );
574 newLib.SetName( "" );
575 newLib.ClearExt();
576
577 if( newLib.DirExists() )
578 {
579 msg.Printf( _( "Folder '%s' already exists. Do you want overwrite any existing design blocks?" ),
580 newLib.GetFullPath() );
581
582 switch( wxMessageBox( msg, _( "Migrate Library" ), wxYES_NO | wxCANCEL | wxICON_QUESTION, m_parent ) )
583 {
584 case wxYES: break;
585 case wxNO: continue;
586 case wxCANCEL: return;
587 }
588 }
589
590 wxString options = cur_grid()->GetCellValue( row, COL_OPTIONS );
591 std::map<std::string, UTF8> props( LIBRARY_TABLE::ParseOptions( options.ToStdString() ) );
592
593 if( DESIGN_BLOCK_IO_MGR::ConvertLibrary( &props, legacyLib.GetFullPath(), newLib.GetFullPath() ) )
594 {
595 relPath = NormalizePath( newLib.GetFullPath(), &Pgm().GetLocalEnvVariables(), m_project );
596
597 cur_grid()->SetCellValue( row, COL_URI, relPath );
598 cur_grid()->SetCellValue( row, COL_TYPE, kicadType );
599 }
600 else
601 {
602 DisplayErrorMessage( m_parent, wxString::Format( _( "Failed to save design block library file '%s'." ),
603 newLib.GetFullPath() ) );
604 }
605 }
606}
607
608
610{
611 if( !cur_grid()->CommitPendingChanges() )
612 return;
613
615
616 // We are bound both to the menu and button with this one handler
617 // So we must set the file type based on it
618 if( event.GetEventType() == wxEVT_BUTTON )
619 {
620 // Let's default to adding a kicad design block file for just the design block
622 }
623 else
624 {
625 fileType = static_cast<DESIGN_BLOCK_IO_MGR::DESIGN_BLOCK_FILE_T>( event.GetId() );
626 }
627
629 return;
630
633
634 wxString title = wxString::Format( _( "Select %s Library" ), DESIGN_BLOCK_IO_MGR::ShowType( fileType ) );
635 wxString dummy;
636 wxString* lastDir;
637
638 if( m_notebook->GetSelection() == 0 )
639 lastDir = cfg ? &cfg->m_lastDesignBlockLibDir : &dummy;
640 else
641 lastDir = &m_lastProjectLibDir;
642
643 wxArrayString files;
644 wxWindow* topLevelParent = wxGetTopLevelParent( this );
645
646 if( fileDesc.m_IsFile )
647 {
648 wxFileDialog dlg( topLevelParent, title, *lastDir, wxEmptyString, fileDesc.FileFilter(),
649 wxFD_OPEN | wxFD_FILE_MUST_EXIST | wxFD_MULTIPLE );
650
651 if( dlg.ShowModal() == wxID_CANCEL )
652 return;
653
654 dlg.GetPaths( files );
655 *lastDir = dlg.GetDirectory();
656 }
657 else
658 {
659 wxDirDialog dlg( topLevelParent, title, *lastDir,
660 wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST | wxDD_MULTIPLE );
661
662 if( dlg.ShowModal() == wxID_CANCEL )
663 return;
664
665 dlg.GetPaths( files );
666
667 if( !files.IsEmpty() )
668 {
669 wxFileName first( files.front() );
670 *lastDir = first.GetPath();
671 }
672 }
673
674 // Drop the last directory if the path is a .pretty folder
676 cfg->m_lastDesignBlockLibDir = cfg->m_lastDesignBlockLibDir.BeforeLast( wxFileName::GetPathSeparator() );
677
678 const ENV_VAR_MAP& envVars = Pgm().GetLocalEnvVariables();
679 bool addDuplicates = false;
680 bool applyToAll = false;
681 wxString warning = _( "Warning: Duplicate Nicknames" );
682 wxString msg = _( "An item nicknamed '%s' already exists." );
683 wxString detailedMsg = _( "One of the nicknames will need to be changed." );
684
685 for( const wxString& filePath : files )
686 {
687 wxFileName fn( filePath );
688 wxString nickname = LIB_ID::FixIllegalChars( fn.GetName(), true );
689 bool doAdd = true;
690
692 nickname = LIB_ID::FixIllegalChars( fn.GetFullName(), true ).wx_str();
693
694 if( cur_model()->ContainsNickname( nickname ) )
695 {
696 if( !applyToAll )
697 {
698 // The cancel button adds the library to the table anyway
699 addDuplicates = OKOrCancelDialog( m_parent, warning, wxString::Format( msg, nickname ), detailedMsg,
700 _( "Skip" ), _( "Add Anyway" ), &applyToAll ) == wxID_CANCEL;
701 }
702
703 doAdd = addDuplicates;
704 }
705
706 if( doAdd && cur_grid()->AppendRows( 1 ) )
707 {
708 int last_row = cur_grid()->GetNumberRows() - 1;
709
710 cur_grid()->SetCellValue( last_row, COL_NICKNAME, nickname );
711 cur_grid()->SetCellValue( last_row, COL_TYPE, DESIGN_BLOCK_IO_MGR::ShowType( fileType ) );
712
713 // try to use path normalized to an environmental variable or project path
714 wxString path = NormalizePath( filePath, &envVars, m_project->GetProjectPath() );
715
716 // Do not use the project path in the global library table. This will almost
717 // assuredly be wrong for a different project.
718 if( m_notebook->GetSelection() == 0 && path.Contains( wxT( "${KIPRJMOD}" ) ) )
719 path = fn.GetFullPath();
720
721 cur_grid()->SetCellValue( last_row, COL_URI, path );
722 }
723 }
724
725 if( !files.IsEmpty() )
726 {
727 cur_grid()->MakeCellVisible( cur_grid()->GetNumberRows() - 1, COL_ENABLED );
728 cur_grid()->SetGridCursor( cur_grid()->GetNumberRows() - 1, COL_NICKNAME );
729 }
730}
731
732
734{
735 // Account for scroll bars
736 aWidth -= ( m_path_subs_grid->GetSize().x - m_path_subs_grid->GetClientSize().x );
737
738 m_path_subs_grid->AutoSizeColumn( 0 );
739 m_path_subs_grid->SetColSize( 0, std::max( 72, m_path_subs_grid->GetColSize( 0 ) ) );
740 m_path_subs_grid->SetColSize( 1, std::max( 120, aWidth - m_path_subs_grid->GetColSize( 0 ) ) );
741}
742
743
745{
746 adjustPathSubsGridColumns( event.GetSize().GetX() );
747
748 event.Skip();
749}
750
751
753{
754 if( !cur_grid()->CommitPendingChanges() )
755 return false;
756
757 if( !verifyTables() )
758 return false;
759
761
762 std::optional<LIBRARY_TABLE*> optTable = manager.Table( LIBRARY_TABLE_TYPE::DESIGN_BLOCK,
764 wxCHECK( optTable.has_value(), false );
765 LIBRARY_TABLE* globalTable = optTable.value();
766
767 if( get_model( 0 )->Table() != *globalTable )
768 {
769 m_parent->m_GlobalTableChanged = true;
770 *globalTable = get_model( 0 )->Table();
771
772 globalTable->Save().map_error(
773 []( const LIBRARY_ERROR& aError )
774 {
775 wxMessageBox( _( "Error saving global library table:\n\n" ) + aError.message,
776 _( "File Save Error" ), wxOK | wxICON_ERROR );
777 } );
778 }
779
781
782 if( optTable.has_value() && get_model( 1 )->Table().Path() == optTable.value()->Path() )
783 {
784 LIBRARY_TABLE* projectTable = optTable.value();
785
786 if( get_model( 1 )->Table() != *projectTable )
787 {
788 m_parent->m_ProjectTableChanged = true;
789 *projectTable = get_model( 1 )->Table();
790
791 projectTable->Save().map_error(
792 []( const LIBRARY_ERROR& aError )
793 {
794 wxMessageBox( _( "Error saving project library table:\n\n" ) + aError.message,
795 _( "File Save Error" ), wxOK | wxICON_ERROR );
796 } );
797 }
798 }
799
800 for( int ii = 0; ii < (int) m_notebook->GetPageCount(); ++ii )
801 {
802 LIB_TABLE_NOTEBOOK_PANEL* panel = static_cast<LIB_TABLE_NOTEBOOK_PANEL*>( m_notebook->GetPage( ii ) );
803
804 if( panel->GetClosable() && panel->TableModified() )
805 {
806 panel->SaveTable();
807 m_parent->m_GlobalTableChanged = true;
808 m_parent->m_ProjectTableChanged = true;
809 }
810 }
811
812 return true;
813}
814
815
819{
820 wxRegEx re( ".*?(\\$\\{(.+?)\\})|(\\$\\((.+?)\\)).*?", wxRE_ADVANCED );
821 wxASSERT( re.IsValid() ); // wxRE_ADVANCED is required.
822
823 std::set<wxString> unique;
824
825 // clear the table
826 m_path_subs_grid->ClearRows();
827
828 for( int page = 0 ; page < (int) m_notebook->GetPageCount(); ++page )
829 {
831
832 for( int row = 0; row < model->GetNumberRows(); ++row )
833 {
834 wxString uri = model->GetValue( row, COL_URI );
835
836 while( re.Matches( uri ) )
837 {
838 wxString envvar = re.GetMatch( uri, 2 );
839
840 // if not ${...} form then must be $(...)
841 if( envvar.IsEmpty() )
842 envvar = re.GetMatch( uri, 4 );
843
844 // ignore duplicates
845 unique.insert( envvar );
846
847 // delete the last match and search again
848 uri.Replace( re.GetMatch( uri, 0 ), wxEmptyString );
849 }
850 }
851 }
852
853 // Make sure this special environment variable shows up even if it was
854 // not used yet. It is automatically set by KiCad to the directory holding
855 // the current project.
856 unique.insert( PROJECT_VAR_NAME );
858
859 // This special environment variable is used to locate 3d shapes
860 unique.insert( ENV_VAR::GetVersionedEnvVarName( wxS( "3DMODEL_DIR" ) ) );
861
862 for( const wxString& evName : unique )
863 {
864 int row = m_path_subs_grid->GetNumberRows();
865 m_path_subs_grid->AppendRows( 1 );
866
867 m_path_subs_grid->SetCellValue( row, 0, wxT( "${" ) + evName + wxT( "}" ) );
868 m_path_subs_grid->SetCellEditor( row, 0, new GRID_CELL_READONLY_TEXT_EDITOR() );
869
870 wxString evValue;
871 wxGetEnv( evName, &evValue );
872 m_path_subs_grid->SetCellValue( row, 1, evValue );
873 m_path_subs_grid->SetCellEditor( row, 1, new GRID_CELL_READONLY_TEXT_EDITOR() );
874 }
875
876 adjustPathSubsGridColumns( m_path_subs_grid->GetRect().GetWidth() );
877}
878
879//-----</event handlers>---------------------------------
880
881void InvokeEditDesignBlockLibTable( KIWAY* aKiway, wxWindow *aParent )
882{
883 wxString projectTablePath = aKiway->Prj().DesignBlockLibTblName();
884 wxString msg;
885
886 DIALOG_EDIT_LIBRARY_TABLES dlg( aParent, _( "Design Block Libraries" ) );
887
888 dlg.InstallPanel( new PANEL_DESIGN_BLOCK_LIB_TABLE( &dlg, &aKiway->Prj() ) );
889
890 if( dlg.ShowModal() == wxID_CANCEL )
891 return;
892
893 if( dlg.m_GlobalTableChanged )
895
896 if( dlg.m_ProjectTableChanged )
897 {
898 // Trigger a reload of the table and cancel an in-progress background load
900 }
901
902 // Trigger a load of any new block libraries
904
905 std::string payload = "";
906 aKiway->ExpressMail( FRAME_SCH, MAIL_RELOAD_LIB, payload );
907 aKiway->ExpressMail( FRAME_PCB_EDITOR, MAIL_RELOAD_LIB, payload );
908
909 return;
910}
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
Definition bitmap.cpp:110
void openTable(const LIBRARY_TABLE_ROW &aRow) override
DESIGN_BLOCK_GRID_TRICKS(PANEL_DESIGN_BLOCK_LIB_TABLE *aPanel, WX_GRID *aGrid)
PANEL_DESIGN_BLOCK_LIB_TABLE * m_panel
@ KICAD_SEXP
S-expression KiCad file format.
@ DESIGN_BLOCK_FILE_UNKNOWN
0 is not a legal menu id on Mac
static const wxString ShowType(DESIGN_BLOCK_FILE_T aFileType)
static DESIGN_BLOCK_FILE_T GuessPluginTypeFromLibPath(const wxString &aLibPath, int aCtl=0)
static DESIGN_BLOCK_FILE_T EnumFromStr(const wxString &aFileType)
static bool ConvertLibrary(std::map< std::string, UTF8 > *aOldFileProps, const wxString &aOldFilePath, const wxString &aNewFilePath)
Convert a design block library to the latest KiCad format.
static DESIGN_BLOCK_IO * FindPlugin(DESIGN_BLOCK_FILE_T aFileType)
This class builds a wxGridTableBase by wrapping an #DESIGN_BLOCK_LIB_TABLE object.
void SetValue(int aRow, int aCol, const wxString &aValue) override
wxString getFileTypes(WX_GRID *aGrid, int aRow) override
const std::map< DESIGN_BLOCK_IO_MGR::DESIGN_BLOCK_FILE_T, IO_BASE::IO_FILE_DESC > & m_supportedDesignBlockFiles
DESIGN_BLOCK_LIB_TABLE_GRID_DATA_MODEL(DIALOG_SHIM *aParent, WX_GRID *aGrid, const LIBRARY_TABLE &aTableToEdit, LIBRARY_MANAGER_ADAPTER *aAdapter, const wxArrayString &aPluginChoices, wxString *aMRUDirectory, const wxString &aProjectPath, const std::map< DESIGN_BLOCK_IO_MGR::DESIGN_BLOCK_FILE_T, IO_BASE::IO_FILE_DESC > &aSupportedFiles)
An options editor in the form of a two column name/value spreadsheet like (table) UI.
Dialog helper object to sit in the inheritance tree between wxDialog and any class written by wxFormB...
Definition dialog_shim.h:68
int ShowModal() override
Add mouse and command handling (such as cut, copy, and paste) to a WX_GRID instance.
Definition grid_tricks.h:61
WX_GRID * m_grid
I don't own the grid, but he owns me.
void SetTooltipEnable(int aCol, bool aEnable=true)
Enable the tooltip for a column.
Definition grid_tricks.h:75
wxString m_lastDesignBlockLibDir
A minimalistic software bus for communications between various DLLs/DSOs (DSOs) within the same KiCad...
Definition kiway.h:292
virtual void ExpressMail(FRAME_T aDestination, MAIL_T aCommand, std::string &aPayload, wxWindow *aSource=nullptr, bool aFromOtherThread=false)
Send aPayload to aDestination from aSource.
Definition kiway.cpp:507
virtual PROJECT & Prj() const
Return the PROJECT associated with this KIWAY.
Definition kiway.cpp:200
The interface used by the classes that actually can load IO plugins for the different parts of KiCad ...
static wxString ExpandURI(const wxString &aShortURI, const PROJECT &aProject)
std::optional< LIBRARY_TABLE * > Table(LIBRARY_TABLE_TYPE aType, LIBRARY_TABLE_SCOPE aScope)
Retrieves a given table; creating a new empty project table if a valid project is loaded and the give...
void LoadGlobalTables(std::initializer_list< LIBRARY_TABLE_TYPE > aTablesToLoad={})
(Re)loads the global library tables in the given list, or all tables if no list is given
void ProjectChanged()
Notify all adapters that the project has changed.
void SetOptions(const wxString &aOptions)
const wxString & Type() const
static const wxString TABLE_TYPE_NAME
const wxString & URI() const
const wxString & Nickname() const
const wxString & Options() const
LIBRARY_RESULT< void > Save()
static std::map< std::string, UTF8 > ParseOptions(const std::string &aOptionsList)
void GetPaths(wxArrayString &aPathArray)
virtual wxDirTraverseResult OnOpenError(const wxString &aOpenErrorName) override
std::unordered_map< wxString, int > m_failedDirs
std::vector< std::string > m_searchExtensions
void GetFailedPaths(wxArrayString &aPathArray)
LIBRARY_TRAVERSER(std::vector< std::string > aSearchExtensions, wxString aInitialDir)
std::unordered_map< wxString, int > m_foundDirs
virtual wxDirTraverseResult OnDir(const wxString &aDirName) override
virtual wxDirTraverseResult OnFile(const wxString &aFileName) override
static UTF8 FixIllegalChars(const UTF8 &aLibItemName, bool aLib)
Replace illegal LIB_ID item name characters with underscores '_'.
Definition lib_id.cpp:192
This abstract base class mixes any object derived from #LIB_TABLE into wxGridTableBase so the result ...
virtual LIBRARY_TABLE_ROW & at(size_t aIndex)
LIBRARY_TABLE_ROW & At(size_t aIndex)
void SetValue(int aRow, int aCol, const wxString &aValue) override
LIB_TABLE_GRID_DATA_MODEL(DIALOG_SHIM *aParent, WX_GRID *aGrid, const LIBRARY_TABLE &aTableToEdit, LIBRARY_MANAGER_ADAPTER *aAdapter, const wxArrayString &aPluginChoices, wxString *aMRUDirectory, const wxString &aProjectPath)
static void MoveUpHandler(WX_GRID *aGrid)
LIB_TABLE_GRID_TRICKS(WX_GRID *aGrid)
static void DeleteRowHandler(WX_GRID *aGrid)
static bool VerifyTable(WX_GRID *aGrid, std::function< void(int aRow, int aCol)> aErrorHandler)
static void AppendRowHandler(WX_GRID *aGrid)
static void MoveDownHandler(WX_GRID *aGrid)
static void AddTable(wxAuiNotebook *aNotebook, const wxString &aTitle, bool aClosable)
PANEL_DESIGN_BLOCK_LIB_TABLE_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(-1,-1), long style=wxTAB_TRAVERSAL, const wxString &name=wxEmptyString)
Dialog to show and edit symbol library tables.
void moveUpHandler(wxCommandEvent &event) override
void onMigrateLibraries(wxCommandEvent &event) override
void onNotebookPageCloseRequest(wxAuiNotebookEvent &aEvent)
void browseLibrariesHandler(wxCommandEvent &event)
void populateEnvironReadOnlyTable()
Populate the readonly environment variable table with names and values by examining all the full_uri ...
std::map< DESIGN_BLOCK_IO_MGR::DESIGN_BLOCK_FILE_T, IO_BASE::IO_FILE_DESC > m_supportedDesignBlockFiles
void moveDownHandler(wxCommandEvent &event) override
DIALOG_EDIT_LIBRARY_TABLES * m_parent
DESIGN_BLOCK_LIB_TABLE_GRID_DATA_MODEL * get_model(int aPage) const
void appendRowHandler(wxCommandEvent &event) override
PANEL_DESIGN_BLOCK_LIB_TABLE(DIALOG_EDIT_LIBRARY_TABLES *aParent, PROJECT *aProject)
void onSizeGrid(wxSizeEvent &event) override
void deleteRowHandler(wxCommandEvent &event) override
void AddTable(LIBRARY_TABLE *table, const wxString &aTitle, bool aClosable)
bool verifyTables()
Trim important fields, removes blank row entries, and checks for duplicates.
DESIGN_BLOCK_LIB_TABLE_GRID_DATA_MODEL * cur_model() const
static wxString GetDefaultUserDesignBlocksPath()
Gets the default path we point users to create projects.
Definition paths.cpp:104
void PreloadDesignBlockLibraries(KIWAY *aKiway)
Starts a background job to preload the global and project design block libraries.
Definition pgm_base.cpp:871
virtual ENV_VAR_MAP & GetLocalEnvVariables() const
Definition pgm_base.cpp:783
virtual LIBRARY_MANAGER & GetLibraryManager() const
Definition pgm_base.h:131
Container for project specific data.
Definition project.h:65
virtual const wxString DesignBlockLibTblName() const
Return the path and file name of this projects design block library table.
Definition project.cpp:203
wxString wx_str() const
Definition utf8.cpp:45
const wxString ExpandEnvVarSubstitutions(const wxString &aString, const PROJECT *aProject)
Replace any environment variable & text variable references with their values.
Definition common.cpp:509
wxString JoinExtensions(const std::vector< std::string > &aExts)
Join a list of file extensions for use in a file dialog.
Definition common.cpp:599
The common library.
int OKOrCancelDialog(wxWindow *aParent, const wxString &aWarning, const wxString &aMessage, const wxString &aDetailedMessage, const wxString &aOKLabel, const wxString &aCancelLabel, bool *aApplyToAll)
Display a warning dialog with aMessage and returns the user response.
Definition confirm.cpp:150
bool IsOK(wxWindow *aParent, const wxString &aMessage)
Display a yes/no dialog with aMessage and returns the user response.
Definition confirm.cpp:259
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition confirm.cpp:202
This file is part of the common library.
#define _(s)
wxString NormalizePath(const wxFileName &aFilePath, const ENV_VAR_MAP *aEnvVars, const wxString &aProjectPath)
Normalize a file path to an environmental variable, if possible.
Definition env_paths.cpp:73
Helper functions to substitute paths with environmental variables.
Functions related to environment variables, including help functions.
@ FRAME_PCB_EDITOR
Definition frame_type.h:42
@ FRAME_SCH
Definition frame_type.h:34
static const std::string KiCadDesignBlockLibPathExtension
std::map< wxString, ENV_VAR_ITEM > ENV_VAR_MAP
std::unique_ptr< T > IO_RELEASER
Helper to hold and release an IO_BASE object when exceptions are thrown.
Definition io_mgr.h:33
PROJECT & Prj()
Definition kicad.cpp:629
This file contains miscellaneous commonly used macros and functions.
@ MAIL_RELOAD_LIB
Definition mail_type.h:57
KICOMMON_API wxString GetVersionedEnvVarName(const wxString &aBaseName)
Construct a versioned environment variable based on this KiCad major version.
Definition env_vars.cpp:77
void InvokeEditDesignBlockLibTable(KIWAY *aKiway, wxWindow *aParent)
SETTINGS_MANAGER * GetSettingsManager()
PGM_BASE & Pgm()
The global program "get" accessor.
Definition pgm_base.cpp:946
see class PGM_BASE
#define PROJECT_VAR_NAME
A variable name whose value holds the current project directory.
Definition project.h:41
T * GetAppSettings(const char *aFilename)
std::vector< FAB_LAYER_COLOR > dummy
MODEL3D_FORMAT_TYPE fileType(const char *aFileName)
Container that describes file type info.
Definition io_base.h:43
bool m_IsFile
Whether the library is a folder or a file.
Definition io_base.h:51
wxString FileFilter() const
Definition io_base.cpp:40
wxString message
Container that describes file type info for the add a library options.
bool m_IsFile
Whether the library is a folder or a file.
wxString m_Description
Description shown in the file picker dialog.
wxString m_FileFilter
Filter used for file pickers if m_IsFile is true.
DESIGN_BLOCK_IO_MGR::DESIGN_BLOCK_FILE_T m_Plugin
wxString m_FolderSearchExtension
In case of folders it stands for extensions of files stored inside.
std::string path
KIBIS_MODEL * model
wxString result
Test unit parsing edge cases and error handling.
Definition of file extensions used in Kicad.