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, see <https://www.gnu.org/licenses/>.
18 */
19
20
21#include <set>
22#include <wx/dir.h>
23#include <wx/regex.h>
24#include <wx/dirdlg.h>
25#include <wx/filedlg.h>
26#include <wx/msgdlg.h>
27
28#include <common.h>
29#include <project.h>
30#include <env_vars.h>
31#include <lib_id.h>
32#include <bitmaps.h>
34#include <widgets/wx_grid.h>
37#include <confirm.h>
40#include <pgm_base.h>
41#include <env_paths.h>
46#include <kiway.h>
47#include <kiplatform/ui.h>
48#include <kiway_mail.h>
51#include <paths.h>
52#include <macros.h>
56
69
70
75class LIBRARY_TRAVERSER : public wxDirTraverser
76{
77public:
78 LIBRARY_TRAVERSER( std::vector<std::string> aSearchExtensions, wxString aInitialDir ) :
79 m_searchExtensions( aSearchExtensions ), m_currentDir( aInitialDir )
80 {
81 }
82
83 virtual wxDirTraverseResult OnFile( const wxString& aFileName ) override
84 {
85 wxFileName file( aFileName );
86
87 for( const std::string& ext : m_searchExtensions )
88 {
89 if( file.GetExt().IsSameAs( ext, false ) )
90 m_foundDirs.insert( { m_currentDir, 1 } );
91 }
92
93 return wxDIR_CONTINUE;
94 }
95
96 virtual wxDirTraverseResult OnOpenError( const wxString& aOpenErrorName ) override
97 {
98 m_failedDirs.insert( { aOpenErrorName, 1 } );
99 return wxDIR_IGNORE;
100 }
101
102 bool HasDirectoryOpenFailures() { return m_failedDirs.size() > 0; }
103
104 virtual wxDirTraverseResult OnDir( const wxString& aDirName ) override
105 {
106 m_currentDir = aDirName;
107 return wxDIR_CONTINUE;
108 }
109
110 void GetPaths( wxArrayString& aPathArray )
111 {
112 for( std::pair<const wxString, int>& foundDirsPair : m_foundDirs )
113 aPathArray.Add( foundDirsPair.first );
114 }
115
116 void GetFailedPaths( wxArrayString& aPathArray )
117 {
118 for( std::pair<const wxString, int>& failedDirsPair : m_failedDirs )
119 aPathArray.Add( failedDirsPair.first );
120 }
121
122private:
123 std::vector<std::string> m_searchExtensions;
124 wxString m_currentDir;
125 std::unordered_map<wxString, int> m_foundDirs;
126 std::unordered_map<wxString, int> m_failedDirs;
127};
128
129
134{
135public:
137 LIBRARY_MANAGER_ADAPTER* aAdapter, const wxArrayString& aPluginChoices,
138 wxString* aMRUDirectory, const wxString& aProjectPath,
140 IO_BASE::IO_FILE_DESC>& aSupportedFiles ) :
141 LIB_TABLE_GRID_DATA_MODEL( aParent, aGrid, aTableToEdit, aAdapter, aPluginChoices, aMRUDirectory,
142 aProjectPath ),
143 m_supportedDesignBlockFiles( aSupportedFiles )
144 {
145 }
146
147 void SetValue( int aRow, int aCol, const wxString& aValue ) override
148 {
149 wxCHECK( aRow < (int) size(), /* void */ );
150
151 LIB_TABLE_GRID_DATA_MODEL::SetValue( aRow, aCol, aValue );
152
153 // If setting a filepath, attempt to auto-detect the format
154 if( aCol == COL_URI )
155 {
156 LIBRARY_TABLE_ROW& row = at( static_cast<size_t>( aRow ) );
157 wxString uri = LIBRARY_MANAGER::ExpandURI( row.URI(), Pgm().GetSettingsManager().Prj() );
158
161
162 if( pluginType != DESIGN_BLOCK_IO_MGR::FILE_TYPE_NONE )
163 SetValue( aRow, COL_TYPE, DESIGN_BLOCK_IO_MGR::ShowType( pluginType ) );
164 }
165 }
166
167protected:
168 wxString getFileTypes( WX_GRID* aGrid, int aRow ) override
169 {
170 auto* table = static_cast<DESIGN_BLOCK_LIB_TABLE_GRID_DATA_MODEL*>( aGrid->GetTable() );
171 LIBRARY_TABLE_ROW& tableRow = table->at( aRow );
172
173 if( tableRow.Type() == LIBRARY_TABLE_ROW::TABLE_TYPE_NAME )
174 {
175 wxString filter = _( "Design Block Library Tables" );
176#ifndef __WXOSX__
177 filter << wxString::Format( _( " (%s)|%s" ), FILEEXT::DesignBlockLibraryTableFileName,
179#else
180 filter << wxString::Format( _( " (%s)|%s" ), wxFileSelectorDefaultWildcardStr,
181 wxFileSelectorDefaultWildcardStr );
182#endif
183 return filter;
184 }
185
186 if( tableRow.Type().IsEmpty() )
187 return wxEmptyString;
188
191 return wxEmptyString;
192
194
195 if( pluginDesc.m_IsFile )
196 return pluginDesc.FileFilter();
197
198 return wxEmptyString;
199 }
200
201private:
202 const std::map<DESIGN_BLOCK_IO_MGR::DESIGN_BLOCK_FILE_T, IO_BASE::IO_FILE_DESC>& m_supportedDesignBlockFiles;
203};
204
205
207{
208public:
215
216 static bool SupportsVisibilityColumn() { return true; }
217
218protected:
219 void optionsEditor( int aRow ) override
220 {
221 LIB_TABLE_GRID_DATA_MODEL* tbl = static_cast<LIB_TABLE_GRID_DATA_MODEL*>( m_grid->GetTable() );
222
223 if( aRow < tbl->GetNumberRows() )
224 {
225 LIBRARY_TABLE_ROW& row = tbl->At( static_cast<size_t>( aRow ) );
226 const wxString& options = row.Options();
227 wxString result = options;
228 std::map<std::string, UTF8> choices;
229
232
233 if( pi )
234 pi->GetLibraryOptions( &choices );
235
236 DIALOG_PLUGIN_OPTIONS dlg( wxGetTopLevelParent( m_grid ), row.Nickname(), choices, options, &result );
237 dlg.ShowModal();
238
239 if( options != result )
240 {
241 row.SetOptions( result );
242 m_grid->Refresh();
243 }
244 }
245 }
246
247 void openTable( const LIBRARY_TABLE_ROW& aRow ) override
248 {
249 wxFileName fn( LIBRARY_MANAGER::ExpandURI( aRow.URI(), Pgm().GetSettingsManager().Prj() ) );
250 std::shared_ptr<LIBRARY_TABLE> child = std::make_shared<LIBRARY_TABLE>( fn, LIBRARY_TABLE_SCOPE::GLOBAL,
252
253 if( !child->IsOk() )
254 {
255 wxMessageBox( _( "Unable to load library table." ) );
256 }
257 else
258 {
260
261 m_panel->OpenTable( child, aRow.Nickname() );
262 }
263 }
264
265 wxString getTablePreamble() override
266 {
267 return wxT( "(design_block_lib_table" );
268 }
269
274
275protected:
277};
278
279
280void PANEL_DESIGN_BLOCK_LIB_TABLE::OpenTable( const std::shared_ptr<LIBRARY_TABLE>& aTable, const wxString& aTitle )
281{
282 wxString tabTitle = aTitle;
283
284 if( aTable->IsReadOnly() )
285 tabTitle += wxS( " " ) + _( "(read-only)" );
286
287 for( int ii = 2; ii < (int) m_notebook->GetPageCount(); ++ii )
288 {
289 wxString candidate = m_notebook->GetPageText( ii );
290
291 if( candidate.EndsWith( " *" ) )
292 candidate = candidate.Left( candidate.Length() - 2 );
293
294 if( candidate == tabTitle )
295 {
296 // Something is pretty fishy with wxAuiNotebook::ChangeSelection(); on Mac at least it
297 // results in a re-entrant call where the second call is one page behind.
298 for( int attempts = 0; attempts < 3; ++attempts )
299 m_notebook->ChangeSelection( ii );
300
301 return;
302 }
303 }
304
305 m_nestedTables.push_back( aTable );
306 AddTable( aTable.get(), tabTitle, true );
307
308 // Something is pretty fishy with wxAuiNotebook::ChangeSelection(); on Mac at least it
309 // results in a re-entrant call where the second call is one page behind.
310 for( int attempts = 0; attempts < 3; ++attempts )
311 m_notebook->ChangeSelection( m_notebook->GetPageCount() - 1 );
312}
313
314
315void PANEL_DESIGN_BLOCK_LIB_TABLE::AddTable( LIBRARY_TABLE* table, const wxString& aTitle, bool aClosable )
316{
317 DESIGN_BLOCK_LIBRARY_ADAPTER* adapter = m_project->DesignBlockLibs();
318 wxString projectPath = m_project->GetProjectPath();
319
321
322 WX_GRID* grid = get_grid( (int) m_notebook->GetPageCount() - 1 );
323
324 if( table->Path().StartsWith( projectPath ) )
325 {
327 &m_lastProjectLibDir, projectPath,
329 true /* take ownership */ );
330 }
331 else
332 {
333 wxString* lastGlobalLibDir = nullptr;
334
335 if( KICAD_SETTINGS* cfg = GetAppSettings<KICAD_SETTINGS>( "kicad" ) )
336 {
337 if( cfg->m_lastDesignBlockLibDir.IsEmpty() )
338 cfg->m_lastDesignBlockLibDir = PATHS::GetDefaultUserDesignBlocksPath();
339
340 lastGlobalLibDir = &cfg->m_lastDesignBlockLibDir;
341 }
342
344 lastGlobalLibDir, wxEmptyString,
346 true /* take ownership */ );
347 }
348
349 static_cast<LIB_TABLE_GRID_DATA_MODEL*>( grid->GetTable() )->RecheckRows();
350
351 LIB_TABLE_NOTEBOOK_PANEL* notebookPanel =
352 static_cast<LIB_TABLE_NOTEBOOK_PANEL*>( m_notebook->GetPage( m_notebook->GetPageCount() - 1 ) );
353
354 static_cast<LIB_TABLE_GRID_DATA_MODEL*>( grid->GetTable() )
356 [notebookPanel]()
357 {
358 notebookPanel->MarkDirty();
359 } );
360
361 // add Cut, Copy, and Paste to wxGrids
362 grid->PushEventHandler( new DESIGN_BLOCK_GRID_TRICKS( this, grid ) );
363
364 auto autoSizeCol =
365 [&]( int aCol )
366 {
367 int prevWidth = grid->GetColSize( aCol );
368
369 grid->AutoSizeColumn( aCol, false );
370 grid->SetColSize( aCol, std::max( prevWidth, grid->GetColSize( aCol ) ) );
371 };
372
373 // all but COL_OPTIONS, which is edited with Option Editor anyways.
374 autoSizeCol( COL_NICKNAME );
375 autoSizeCol( COL_TYPE );
376 autoSizeCol( COL_URI );
377 autoSizeCol( COL_DESCR );
378
379 if( grid->GetNumberRows() > 0 )
380 {
381 grid->SetGridCursor( 0, COL_NICKNAME );
382 grid->SelectRow( 0 );
383 }
384}
385
386
388 PROJECT* aProject ) :
390 m_project( aProject ),
391 m_parent( aParent ),
393{
394 m_notebook->SetArtProvider( new WX_AUI_TAB_ART() );
395
396 m_lastProjectLibDir = m_project->GetProjectPath();
397
399
400 for( auto& [fileType, desc] : m_supportedDesignBlockFiles )
402
403 std::optional<LIBRARY_TABLE*> table = Pgm().GetLibraryManager().Table( LIBRARY_TABLE_TYPE::DESIGN_BLOCK,
405 wxASSERT( table.has_value() );
406
407 AddTable( table.value(), _( "Global Libraries" ), false /* closable */ );
408
409 std::optional<LIBRARY_TABLE*> projectTable = Pgm().GetLibraryManager().Table( LIBRARY_TABLE_TYPE::DESIGN_BLOCK,
411
412 if( projectTable.has_value() )
413 AddTable( projectTable.value(), _( "Project Specific Libraries" ), false /* closable */ );
414
415 // There aren't (yet) any legacy DesignBlock libraries to migrate
416 m_migrate_libs_button->Hide();
417
418 // add Cut, Copy, and Paste to wxGrids
419 m_path_subs_grid->PushEventHandler( new GRID_TRICKS( m_path_subs_grid ) );
420
422
423 m_path_subs_grid->SetColLabelValue( 0, _( "Name" ) );
424 m_path_subs_grid->SetColLabelValue( 1, _( "Value" ) );
425
426 // Configure button logos
432
433 // For aesthetic reasons, we must set the size of m_browseButton to match the other bitmaps
434 // manually (for instance m_append_button)
435 Layout(); // Needed at least on MSW to compute the actual buttons sizes, after initializing
436 // their bitmaps
437 wxSize buttonSize = m_append_button->GetSize();
438
439 m_browseButton->SetWidthPadding( 4 );
440 m_browseButton->SetMinSize( buttonSize );
441
442 // Populate the browse library options
443 wxMenu* browseMenu = m_browseButton->GetSplitButtonMenu();
444
445 for( auto& [type, desc] : m_supportedDesignBlockFiles )
446 {
447 wxString entryStr = DESIGN_BLOCK_IO_MGR::ShowType( type );
448 wxString midPart;
449
450 if( desc.m_IsFile && !desc.m_FileExtensions.empty() )
451 {
452 entryStr << wxString::Format( wxS( " (%s)" ), JoinExtensions( desc.m_FileExtensions ) );
453 }
454 else if( !desc.m_IsFile && !desc.m_ExtensionsInDir.empty() )
455 {
456 midPart = wxString::Format( _( "folder with %s files" ), JoinExtensions( desc.m_ExtensionsInDir ) );
457 entryStr << wxString::Format( wxS( " (%s)" ), midPart );
458 }
459
460 browseMenu->Append( type, entryStr );
461 browseMenu->Bind( wxEVT_COMMAND_MENU_SELECTED, &PANEL_DESIGN_BLOCK_LIB_TABLE::browseLibrariesHandler,
462 this, type );
463 }
464
465 Layout();
466
467 m_notebook->Bind( wxEVT_AUINOTEBOOK_PAGE_CLOSE, &PANEL_DESIGN_BLOCK_LIB_TABLE::onNotebookPageCloseRequest, this );
468 m_notebook->Bind( wxEVT_AUINOTEBOOK_PAGE_CHANGING, &PANEL_DESIGN_BLOCK_LIB_TABLE::onNotebookPageChangeRequest, this );
469 // This is the button only press for the browse button instead of the menu
471}
472
473
475{
476 wxMenu* browseMenu = m_browseButton->GetSplitButtonMenu();
477
478 for( auto& [type, desc] : m_supportedDesignBlockFiles )
479 {
480 browseMenu->Unbind( wxEVT_COMMAND_MENU_SELECTED, &PANEL_DESIGN_BLOCK_LIB_TABLE::browseLibrariesHandler,
481 this, type );
482 }
483
485
486 // Delete the GRID_TRICKS.
487 // (Notebook page GRID_TRICKS are deleted by LIB_TABLE_NOTEBOOK_PANEL.)
488 m_path_subs_grid->PopEventHandler( true );
489}
490
491
493{
495 {
497 {
499 continue;
500 }
501
503
504 if( !pi )
505 continue;
506
507 if( const IO_BASE::IO_FILE_DESC& desc = pi->GetLibraryDesc() )
508 m_supportedDesignBlockFiles.emplace( type, desc );
509 }
510}
511
512
517
518
520{
521 return static_cast<LIB_TABLE_NOTEBOOK_PANEL*>( m_notebook->GetPage( aPage ) )->GetGrid();
522}
523
524
526{
527 for( int page = 0 ; page < (int) m_notebook->GetPageCount(); ++page )
528 {
529 WX_GRID* grid = get_grid( page );
530
532 [&]( int aRow, int aCol )
533 {
534 // show the tabbed panel holding the grid we have flunked:
535 if( m_notebook->GetSelection() != page )
536 m_notebook->SetSelection( page );
537
538 grid->MakeCellVisible( aRow, 0 );
539 grid->SetGridCursor( aRow, aCol );
540 } ) )
541 {
542 return false;
543 }
544 }
545
546 return true;
547}
548
549
555
556
561
562
567
568
573
574
576{
578 aEvent.Veto();
579 else
580 aEvent.Skip();
581}
582
583
585{
586 wxAuiNotebook* notebook = (wxAuiNotebook*) aEvent.GetEventObject();
587 wxWindow* page = notebook->GetPage( aEvent.GetSelection() );
588
589 if( LIB_TABLE_NOTEBOOK_PANEL* panel = dynamic_cast<LIB_TABLE_NOTEBOOK_PANEL*>( page ) )
590 {
591 if( panel->GetClosable() )
592 {
593 if( !panel->GetCanClose() )
594 aEvent.Veto();
595 }
596 else
597 {
598 aEvent.Veto();
599 }
600 }
601}
602
603
604// @todo refactor this function into single location shared with PANEL_SYM_LIB_TABLE
606{
607 if( !cur_grid()->CommitPendingChanges() )
608 return;
609
610 wxArrayInt selectedRows = cur_grid()->GetSelectedRows();
611
612 if( selectedRows.empty() && cur_grid()->GetGridCursorRow() >= 0 )
613 selectedRows.push_back( cur_grid()->GetGridCursorRow() );
614
615 wxArrayInt rowsToMigrate;
617 wxString nestedTableType = LIBRARY_TABLE_ROW::TABLE_TYPE_NAME;
618 wxString msg;
619
620 for( int row : selectedRows )
621 {
622 const wxString& type = cur_grid()->GetCellValue( row, COL_TYPE );
623
624 // Nested library tables are not design block libraries and cannot be migrated.
625 if( type != kicadType && type != nestedTableType )
626 rowsToMigrate.push_back( row );
627 }
628
629 if( rowsToMigrate.size() <= 0 )
630 {
631 wxMessageBox( _( "Select one or more rows containing libraries to save as current KiCad format." ) );
632 return;
633 }
634 else
635 {
636 if( rowsToMigrate.size() == 1 )
637 {
638 msg.Printf( _( "Save '%s' as current KiCad format and replace entry in table?" ),
639 cur_grid()->GetCellValue( rowsToMigrate[0], COL_NICKNAME ) );
640 }
641 else
642 {
643 msg.Printf( _( "Save %d libraries as current KiCad format and replace entries in table?" ),
644 (int) rowsToMigrate.size() );
645 }
646
647 if( !IsOK( m_parent, msg ) )
648 return;
649 }
650
651 for( int row : rowsToMigrate )
652 {
653 wxString relPath = cur_grid()->GetCellValue( row, COL_URI );
654 wxString resolvedPath = ExpandEnvVarSubstitutions( relPath, m_project );
655 wxFileName legacyLib( resolvedPath );
656
657 if( !legacyLib.Exists() )
658 {
659 msg.Printf( _( "Library '%s' not found." ), relPath );
660 DisplayErrorMessage( wxGetTopLevelParent( this ), msg );
661 continue;
662 }
663
664 wxFileName newLib( resolvedPath );
665 newLib.AppendDir( newLib.GetName() + "." + FILEEXT::KiCadDesignBlockLibPathExtension );
666 newLib.SetName( "" );
667 newLib.ClearExt();
668
669 if( newLib.DirExists() )
670 {
671 msg.Printf( _( "Folder '%s' already exists. Do you want overwrite any existing design blocks?" ),
672 newLib.GetFullPath() );
673
674 switch( wxMessageBox( msg, _( "Migrate Library" ), wxYES_NO | wxCANCEL | wxICON_QUESTION, m_parent ) )
675 {
676 case wxYES: break;
677 case wxNO: continue;
678 case wxCANCEL: return;
679 }
680 }
681
682 wxString options = cur_grid()->GetCellValue( row, COL_OPTIONS );
683 std::map<std::string, UTF8> props( LIBRARY_TABLE::ParseOptions( options.ToStdString() ) );
684
685 if( DESIGN_BLOCK_IO_MGR::ConvertLibrary( &props, legacyLib.GetFullPath(), newLib.GetFullPath() ) )
686 {
687 relPath = NormalizePath( newLib.GetFullPath(), &Pgm().GetLocalEnvVariables(), m_project );
688
689 cur_grid()->SetCellValue( row, COL_URI, relPath );
690 cur_grid()->SetCellValue( row, COL_TYPE, kicadType );
691 }
692 else
693 {
694 DisplayErrorMessage( m_parent, wxString::Format( _( "Failed to save design block library file '%s'." ),
695 newLib.GetFullPath() ) );
696 }
697 }
698}
699
700
702{
703 if( !cur_grid()->CommitPendingChanges() )
704 return;
705
707
708 // We are bound both to the menu and button with this one handler
709 // So we must set the file type based on it
710 if( event.GetEventType() == wxEVT_BUTTON )
711 {
712 // Let's default to adding a kicad design block file for just the design block
714 }
715 else
716 {
717 fileType = static_cast<DESIGN_BLOCK_IO_MGR::DESIGN_BLOCK_FILE_T>( event.GetId() );
718 }
719
721 return;
722
725
726 wxString title = wxString::Format( _( "Select %s Library" ), DESIGN_BLOCK_IO_MGR::ShowType( fileType ) );
727 wxString dummy;
728 wxString* lastDir;
729
730 if( m_notebook->GetSelection() == 0 )
731 lastDir = cfg ? &cfg->m_lastDesignBlockLibDir : &dummy;
732 else
733 lastDir = &m_lastProjectLibDir;
734
735 wxArrayString files;
736 wxWindow* topLevelParent = wxGetTopLevelParent( this );
737
738 if( fileDesc.m_IsFile )
739 {
740 wxFileDialog dlg( topLevelParent, title, *lastDir, wxEmptyString, fileDesc.FileFilter(),
741 wxFD_OPEN | wxFD_FILE_MUST_EXIST | wxFD_MULTIPLE );
742
744
745 if( dlg.ShowModal() == wxID_CANCEL )
746 return;
747
748 dlg.GetPaths( files );
749 *lastDir = dlg.GetDirectory();
750 }
751 else
752 {
753 wxDirDialog dlg( topLevelParent, title, *lastDir,
754 wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST | wxDD_MULTIPLE );
755
756 if( dlg.ShowModal() == wxID_CANCEL )
757 return;
758
759 dlg.GetPaths( files );
760
761 if( !files.IsEmpty() )
762 {
763 wxFileName first( files.front() );
764 *lastDir = first.GetPath();
765 }
766 }
767
768 // Drop the last directory if the path is a .pretty folder
770 cfg->m_lastDesignBlockLibDir = cfg->m_lastDesignBlockLibDir.BeforeLast( wxFileName::GetPathSeparator() );
771
772 const ENV_VAR_MAP& envVars = Pgm().GetLocalEnvVariables();
773 bool addDuplicates = false;
774 bool applyToAll = false;
775 wxString warning = _( "Warning: Duplicate Nicknames" );
776 wxString msg = _( "An item nicknamed '%s' already exists." );
777 wxString detailedMsg = _( "One of the nicknames will need to be changed." );
778
779 for( const wxString& filePath : files )
780 {
781 wxFileName fn( filePath );
782 wxString nickname = LIB_ID::FixIllegalChars( fn.GetName(), true );
783 bool doAdd = true;
784
786 nickname = LIB_ID::FixIllegalChars( fn.GetFullName(), true ).wx_str();
787
788 if( cur_model()->ContainsNickname( nickname ) )
789 {
790 if( !applyToAll )
791 {
792 // The cancel button adds the library to the table anyway
793 addDuplicates = OKOrCancelDialog( m_parent, warning, wxString::Format( msg, nickname ), detailedMsg,
794 _( "Skip" ), _( "Add Anyway" ), &applyToAll ) == wxID_CANCEL;
795 }
796
797 doAdd = addDuplicates;
798 }
799
800 if( doAdd && cur_grid()->AppendRows( 1 ) )
801 {
802 int last_row = cur_grid()->GetNumberRows() - 1;
803
804 cur_grid()->SetCellValue( last_row, COL_NICKNAME, nickname );
805 cur_grid()->SetCellValue( last_row, COL_TYPE, DESIGN_BLOCK_IO_MGR::ShowType( fileType ) );
806
807 // try to use path normalized to an environmental variable or project path
808 wxString path = NormalizePath( filePath, &envVars, m_project->GetProjectPath() );
809
810 // Do not use the project path in the global library table. This will almost
811 // assuredly be wrong for a different project.
812 if( m_notebook->GetSelection() == 0 && path.Contains( wxT( "${KIPRJMOD}" ) ) )
813 path = fn.GetFullPath();
814
815 cur_grid()->SetCellValue( last_row, COL_URI, path );
816 }
817 }
818
819 if( !files.IsEmpty() )
820 {
821 cur_grid()->MakeCellVisible( cur_grid()->GetNumberRows() - 1, COL_ENABLED );
822 cur_grid()->SetGridCursor( cur_grid()->GetNumberRows() - 1, COL_NICKNAME );
823 }
824}
825
826
828{
829 // Account for scroll bars
830 aWidth -= ( m_path_subs_grid->GetSize().x - m_path_subs_grid->GetClientSize().x );
831
832 m_path_subs_grid->AutoSizeColumn( 0 );
833 m_path_subs_grid->SetColSize( 0, std::max( 72, m_path_subs_grid->GetColSize( 0 ) ) );
834 m_path_subs_grid->SetColSize( 1, std::max( 120, aWidth - m_path_subs_grid->GetColSize( 0 ) ) );
835}
836
837
839{
840 adjustPathSubsGridColumns( event.GetSize().GetX() );
841
842 event.Skip();
843}
844
845
847{
848 if( !cur_grid()->CommitPendingChanges() )
849 return false;
850
851 if( !verifyTables() )
852 return false;
853
854 bool success = true;
856 int firstNestedTable = 1;
857 std::optional<LIBRARY_TABLE*> globalTable = manager.Table( LIBRARY_TABLE_TYPE::DESIGN_BLOCK,
859
860 if( globalTable.has_value() && get_model( 0 )->Table() != *globalTable.value() )
861 {
862 m_parent->m_GlobalTableChanged = true;
863 *globalTable.value() = get_model( 0 )->Table();
864
865 globalTable.value()->Save().map_error(
866 [&success]( const LIBRARY_ERROR& aError )
867 {
868 wxMessageBox( _( "Error saving global library table:\n\n" ) + aError.message,
869 _( "File Save Error" ), wxOK | wxICON_ERROR );
870 success = false;
871 } );
872 }
873
874 std::optional<LIBRARY_TABLE*> projectTable = manager.Table( LIBRARY_TABLE_TYPE::DESIGN_BLOCK,
876
877 if( projectTable.has_value() && get_model( 1 )->Table().Path() == projectTable.value()->Path() )
878 {
879 firstNestedTable = 2;
880
881 if( get_model( 1 )->Table() != *projectTable.value() )
882 {
883 m_parent->m_ProjectTableChanged = true;
884 *projectTable.value() = get_model( 1 )->Table();
885
886 projectTable.value()->Save().map_error(
887 [&success]( const LIBRARY_ERROR& aError )
888 {
889 wxMessageBox( _( "Error saving project library table:\n\n" ) + aError.message,
890 _( "File Save Error" ), wxOK | wxICON_ERROR );
891 success = false;
892 } );
893 }
894 }
895
896 for( int ii = firstNestedTable; ii < (int) m_notebook->GetPageCount(); ++ii )
897 {
898 LIB_TABLE_NOTEBOOK_PANEL* panel = static_cast<LIB_TABLE_NOTEBOOK_PANEL*>( m_notebook->GetPage( ii ) );
899
900 if( panel->TableModified() )
901 success &= panel->SaveTable();
902 }
903
905 return success;
906}
907
908
912{
913 wxRegEx re( ".*?(\\$\\{(.+?)\\})|(\\$\\((.+?)\\)).*?", wxRE_ADVANCED );
914 wxASSERT( re.IsValid() ); // wxRE_ADVANCED is required.
915
916 std::set<wxString> unique;
917
918 // clear the table
919 m_path_subs_grid->ClearRows();
920
921 for( int page = 0 ; page < (int) m_notebook->GetPageCount(); ++page )
922 {
924
925 for( int row = 0; row < model->GetNumberRows(); ++row )
926 {
927 wxString uri = model->GetValue( row, COL_URI );
928
929 while( re.Matches( uri ) )
930 {
931 wxString envvar = re.GetMatch( uri, 2 );
932
933 // if not ${...} form then must be $(...)
934 if( envvar.IsEmpty() )
935 envvar = re.GetMatch( uri, 4 );
936
937 // ignore duplicates
938 unique.insert( envvar );
939
940 // delete the last match and search again
941 uri.Replace( re.GetMatch( uri, 0 ), wxEmptyString );
942 }
943 }
944 }
945
946 // Make sure this special environment variable shows up even if it was
947 // not used yet. It is automatically set by KiCad to the directory holding
948 // the current project.
949 unique.insert( PROJECT_VAR_NAME );
951
952 // This special environment variable is used to locate 3d shapes
953 unique.insert( ENV_VAR::GetVersionedEnvVarName( wxS( "3DMODEL_DIR" ) ) );
954
955 for( const wxString& evName : unique )
956 {
957 int row = m_path_subs_grid->GetNumberRows();
958 m_path_subs_grid->AppendRows( 1 );
959
960 m_path_subs_grid->SetCellValue( row, 0, wxT( "${" ) + evName + wxT( "}" ) );
961 m_path_subs_grid->SetCellEditor( row, 0, new GRID_CELL_READONLY_TEXT_EDITOR() );
962
963 wxString evValue;
964 wxGetEnv( evName, &evValue );
965 m_path_subs_grid->SetCellValue( row, 1, evValue );
966 m_path_subs_grid->SetCellEditor( row, 1, new GRID_CELL_READONLY_TEXT_EDITOR() );
967 }
968
969 adjustPathSubsGridColumns( m_path_subs_grid->GetRect().GetWidth() );
970}
971
972//-----</event handlers>---------------------------------
973
974void InvokeEditDesignBlockLibTable( KIWAY* aKiway, wxWindow *aParent )
975{
976 DIALOG_EDIT_LIBRARY_TABLES dlg( aParent, _( "Design Block Libraries" ) );
977 PANEL_DESIGN_BLOCK_LIB_TABLE* panel = new PANEL_DESIGN_BLOCK_LIB_TABLE( &dlg, &aKiway->Prj() );
978
979 dlg.InstallPanel( panel, panel->GetNotebook() );
980
981 // User can choose to save changes on a Cancel, so don't exit on wxID_CANCEL.
982 dlg.ShowModal();
983
984 if( dlg.m_GlobalTableChanged )
986
987 if( dlg.m_ProjectTableChanged )
988 {
989 // Trigger a reload of the table and cancel an in-progress background load
991 }
992
993 // Trigger a load of any new block libraries
995
996 std::string payload = "";
997 aKiway->ExpressMail( FRAME_SCH, MAIL_RELOAD_LIB, payload );
998 aKiway->ExpressMail( FRAME_PCB_EDITOR, MAIL_RELOAD_LIB, payload );
999}
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
Definition bitmap.cpp:106
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)
void InstallPanel(wxPanel *aPanel, wxAuiNotebook *aNotebook)
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:80
int ShowModal() override
Add mouse and command handling (such as cut, copy, and paste) to a WX_GRID instance.
Definition grid_tricks.h:57
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:71
wxString m_lastDesignBlockLibDir
A minimalistic software bus for communications between various DLLs/DSOs (DSOs) within the same KiCad...
Definition kiway.h:348
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:486
virtual PROJECT & Prj() const
Return the PROJECT associated with this KIWAY.
Definition kiway.cpp:201
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 ApplyLibOverrides(LIBRARY_TABLE &aTable)
Applies stored user overrides (disabled/hidden) to rows of a read-only table.
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:205
This abstract base class mixes any object derived from LIBRARY_TABLE into wxGridTableBase so the resu...
virtual LIBRARY_TABLE_ROW & at(size_t aIndex)
void SetChangeCallback(std::function< void()> aCallback)
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 void AppendRowHandler(WX_GRID *aGrid, const wxString &aType)
static bool VerifyTable(WX_GRID *aGrid, bool aSupportsVisibilityColumn, std::function< void(int aRow, int aCol)> aErrorHandler)
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)
std::vector< std::shared_ptr< LIBRARY_TABLE > > m_nestedTables
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
void onNotebookPageChangeRequest(wxAuiNotebookEvent &aEvent)
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)
void OpenTable(const std::shared_ptr< LIBRARY_TABLE > &table, const wxString &aTitle)
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:892
virtual ENV_VAR_MAP & GetLocalEnvVariables() const
Definition pgm_base.cpp:792
virtual LIBRARY_MANAGER & GetLibraryManager() const
Definition pgm_base.h:125
Container for project specific data.
Definition project.h:63
wxString wx_str() const
Definition utf8.cpp:41
const wxString ExpandEnvVarSubstitutions(const wxString &aString, const PROJECT *aProject)
Replace any environment variable & text variable references with their values.
Definition common.cpp:776
wxString JoinExtensions(const std::vector< std::string > &aExts)
Join a list of file extensions for use in a file dialog.
Definition common.cpp:867
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:165
bool IsOK(wxWindow *aParent, const wxString &aMessage)
Display a yes/no dialog with aMessage and returns the user response.
Definition confirm.cpp:274
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition confirm.cpp:217
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:38
@ FRAME_SCH
Definition frame_type.h:30
static const std::string KiCadDesignBlockLibPathExtension
static const std::string DesignBlockLibraryTableFileName
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:733
This file contains miscellaneous commonly used macros and functions.
@ MAIL_RELOAD_LIB
Definition mail_type.h:54
KICOMMON_API wxString GetVersionedEnvVarName(const wxString &aBaseName)
Construct a versioned environment variable based on this KiCad major version.
Definition env_vars.cpp:78
void AllowNetworkFileSystems(wxDialog *aDialog)
Configure a file dialog to show network and virtual file systems.
Definition wxgtk/ui.cpp:533
void InvokeEditDesignBlockLibTable(KIWAY *aKiway, wxWindow *aParent)
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
#define PROJECT_VAR_NAME
A variable name whose value holds the current project directory.
Definition project.h:38
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.