KiCad PCB EDA Suite
Loading...
Searching...
No Matches
design_block_pane.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#include <design_block.h>
22#include <design_block_io.h>
23#include <paths.h>
24#include <env_paths.h>
25#include <pgm_base.h>
26#include <common.h>
27#include <kidialog.h>
32#include <kiface_base.h>
33#include <core/kicad_algo.h>
34#include <template_fieldnames.h>
35#include <wx/sizer.h>
36#include <wx/textdlg.h>
37#include <confirm.h>
39#include <tool/tool_manager.h>
40
42 std::vector<LIB_ID>& aHistoryList ) :
43 WX_PANEL( aParent ),
44 m_frame( aParent )
45{
46 m_frame->Bind( wxEVT_AUI_PANE_CLOSE, &DESIGN_BLOCK_PANE::OnClosed, this );
47 m_frame->Bind( EDA_LANG_CHANGED, &DESIGN_BLOCK_PANE::OnLanguageChanged, this );
48}
49
50
52{
53 m_frame->Unbind( wxEVT_AUI_PANE_CLOSE, &DESIGN_BLOCK_PANE::OnClosed, this );
54 m_frame->Unbind( EDA_LANG_CHANGED, &DESIGN_BLOCK_PANE::OnLanguageChanged, this );
55}
56
57
58void DESIGN_BLOCK_PANE::OnLanguageChanged( wxCommandEvent& aEvent )
59{
60 if( m_chooserPanel )
61 m_chooserPanel->ShowChangedLanguage();
62
64
65 aEvent.Skip();
66}
67
68
69void DESIGN_BLOCK_PANE::OnClosed( wxAuiManagerEvent& aEvent )
70{
71 if( APP_SETTINGS_BASE* cfg = m_frame->config() )
72 {
73 if( IsShownOnScreen() ) // Ensure the panel is shown when trying to save its size
74 m_frame->SaveSettings( cfg );
75 }
76
77 aEvent.Skip();
78}
79
80
82{
83 m_chooserPanel->SaveSettings();
84}
85
86
88{
89 return m_chooserPanel->GetSelectedLibId( aUnit );
90}
91
92
94{
95 m_chooserPanel->SelectLibId( aLibId );
96}
97
98
100{
101 m_chooserPanel->RefreshLibs();
102}
103
104
105DESIGN_BLOCK* DESIGN_BLOCK_PANE::GetDesignBlock( const LIB_ID& aLibId, bool aUseCacheLib, bool aShowErrorMsg,
106 wxString* aErrorMsg )
107{
108 DESIGN_BLOCK_LIBRARY_ADAPTER* prjLibs = m_frame->Prj().DesignBlockLibs();
109
110 wxCHECK_MSG( prjLibs, nullptr, wxS( "Invalid design block library table." ) );
111
112 wxString error;
113 DESIGN_BLOCK* designBlock = prjLibs->DesignBlockLoadWithOptionalNickname( aLibId, true, &error );
114
115 if( !designBlock )
116 {
117 if( aErrorMsg )
118 *aErrorMsg = error;
119
120 if( aShowErrorMsg )
121 {
122 wxString msg = wxString::Format( _( "Error loading design block %s from library '%s'." ),
123 aLibId.GetLibItemName().wx_str(), aLibId.GetLibNickname().wx_str() );
124 DisplayErrorMessage( m_frame, msg, error );
125 }
126 }
127
128 return designBlock;
129}
130
131
132DESIGN_BLOCK* DESIGN_BLOCK_PANE::GetSelectedDesignBlock( bool aUseCacheLib, bool aShowErrorMsg )
133{
134 if( !GetSelectedLibId().IsValid() )
135 return nullptr;
136
137 return GetDesignBlock( GetSelectedLibId(), aUseCacheLib, aShowErrorMsg );
138}
139
140
141wxString DESIGN_BLOCK_PANE::CreateNewDesignBlockLibrary( const wxString& aDialogTitle )
142{
143 return createNewDesignBlockLibrary( aDialogTitle );
144}
145
146
147wxString DESIGN_BLOCK_PANE::createNewDesignBlockLibrary( const wxString& aDialogTitle )
148{
149 wxFileName fn;
150 static bool isGlobal = true;
151 FILEDLG_HOOK_NEW_LIBRARY tableChooser( isGlobal );
152
153 if( !m_frame->LibraryFileBrowser( aDialogTitle, false, fn, FILEEXT::KiCadDesignBlockLibPathWildcard(),
154 FILEEXT::KiCadDesignBlockLibPathExtension, false, &tableChooser ) )
155 {
156 return wxEmptyString;
157 }
158
159 isGlobal = tableChooser.GetUseGlobalTable();
160
161 wxString libPath = fn.GetFullPath();
162
164
165 // We can save libs only using DESIGN_BLOCK_IO_MGR::KICAD_SEXP format (.pretty libraries)
167
168 try
169 {
171
172 bool writable = false;
173 bool exists = false;
174
175 try
176 {
177 writable = pi->IsLibraryWritable( libPath );
178 exists = fn.Exists();
179 }
180 catch( const IO_ERROR& )
181 {
182 // best efforts....
183 }
184
185 if( exists )
186 {
187 if( !writable )
188 {
189 wxString msg = wxString::Format( _( "Library %s is read only." ), libPath );
190 m_frame->ShowInfoBarError( msg );
191 return wxEmptyString;
192 }
193 else
194 {
195 wxString msg = wxString::Format( _( "Library %s already exists." ), libPath );
196 KIDIALOG dlg( m_frame, msg, _( "Confirmation" ), wxOK | wxCANCEL | wxICON_WARNING );
197 dlg.SetOKLabel( _( "Overwrite" ) );
198 dlg.DoNotShowCheckbox( __FILE__, __LINE__ );
199
200 if( dlg.ShowModal() == wxID_CANCEL )
201 return wxEmptyString;
202
203 pi->DeleteLibrary( libPath );
204 }
205 }
206
207 pi->CreateLibrary( libPath );
208 }
209 catch( const IO_ERROR& ioe )
210 {
211 DisplayError( m_frame, ioe.What() );
212 return wxEmptyString;
213 }
214
215 AddDesignBlockLibrary( aDialogTitle, libPath, scope );
216
217 return libPath;
218}
219
220
221bool DESIGN_BLOCK_PANE::AddDesignBlockLibrary( const wxString& aDialogTitle, const wxString& aFilename,
222 LIBRARY_TABLE_SCOPE aScope )
223{
224 DESIGN_BLOCK_LIBRARY_ADAPTER* adapter = m_frame->Prj().DesignBlockLibs();
226
227 wxFileName fn( aFilename );
228 wxString libPath = fn.GetFullPath();
229 wxString libName = fn.GetName();
230
231 if( libName.IsEmpty() )
232 return false;
233
234 // Open a dialog to ask for a description
235 wxString description = wxGetTextFromUser( _( "Enter a description for the library:" ), aDialogTitle,
236 wxEmptyString, m_frame );
237
239
242
243 wxString type = DESIGN_BLOCK_IO_MGR::ShowType( lib_type );
244
245 // KiCad lib is our default guess. So it might not have the .kicad_blocks extension
246 // In this case, the extension is part of the library name
248 libName = fn.GetFullName();
249
250 // try to use path normalized to an environmental variable or project path
251 wxString normalizedPath = NormalizePath( libPath, &Pgm().GetLocalEnvVariables(), &m_frame->Prj() );
252
253 std::optional<LIBRARY_TABLE*> optTable = manager.Table( LIBRARY_TABLE_TYPE::DESIGN_BLOCK, aScope );
254 wxCHECK( optTable.has_value(), false );
255 LIBRARY_TABLE* table = optTable.value();
256
257 bool success = true;
258 LIBRARY_TABLE_ROW& newRow = table->InsertRow();
259
260 newRow.SetNickname( libName );
261 newRow.SetURI( normalizedPath );
262 newRow.SetType( type );
263 newRow.SetDescription( description );
264
265 table->Save().map_error(
266 [&]( const LIBRARY_ERROR& aError )
267 {
268 DisplayError( m_frame, _( "Error saving library table:\n\n" ) + aError.message );
269 success = false;
270 } );
271
272 if( success )
273 {
274 manager.ReloadTables( aScope, { LIBRARY_TABLE_TYPE::DESIGN_BLOCK } );
275 adapter->LoadOne( libName );
277
278 LIB_ID libID( libName, wxEmptyString );
279 RefreshLibs();
280 SelectLibId( libID );
281 }
282
283 return success;
284}
285
286
287bool DESIGN_BLOCK_PANE::DeleteDesignBlockLibrary( const wxString& aLibName, bool aConfirm )
288{
289 if( aLibName.IsEmpty() )
290 {
291 DisplayErrorMessage( m_frame, _( "Please select a library to delete." ) );
292 return false;
293 }
294
295 if( !m_frame->Prj().DesignBlockLibs()->IsDesignBlockLibWritable( aLibName ) )
296 {
297 wxString msg = wxString::Format( _( "Library '%s' is read only." ), aLibName );
298 m_frame->ShowInfoBarError( msg );
299 return false;
300 }
301
302 // Confirmation
303 wxString msg = wxString::Format( _( "Delete design block library '%s' from disk? This will "
304 "delete all design blocks within the library." ),
305 aLibName.GetData() );
306
307 if( aConfirm && !IsOK( m_frame, msg ) )
308 return false;
309
310 try
311 {
312 if( !m_frame->Prj().DesignBlockLibs()->DeleteLibrary( aLibName ) )
313 {
314 msg.Printf( _( "Design block library '%s' could not be deleted." ), aLibName.GetData() );
315 DisplayError( m_frame, msg );
316 return false;
317 }
318 }
319 catch( const IO_ERROR& ioe )
320 {
321 DisplayError( m_frame, ioe.What() );
322 return false;
323 }
324
325 msg.Printf( _( "Design block library '%s' deleted" ), aLibName.GetData() );
326 m_frame->SetStatusText( msg );
327
328 RefreshLibs();
329
330 return true;
331}
332
333
334bool DESIGN_BLOCK_PANE::DeleteDesignBlockFromLibrary( const LIB_ID& aLibId, bool aConfirm )
335{
336 if( !aLibId.IsValid() )
337 return false;
338
339 wxString libname = aLibId.GetLibNickname();
340 wxString dbname = aLibId.GetLibItemName();
341
342 if( !m_frame->Prj().DesignBlockLibs()->IsDesignBlockLibWritable( libname ) )
343 {
344 wxString msg = wxString::Format( _( "Library '%s' is read only." ), libname );
345 m_frame->ShowInfoBarError( msg );
346 return false;
347 }
348
349 // Confirmation
350 wxString msg = wxString::Format( _( "Delete design block '%s' in library '%s' from disk?" ),
351 dbname.GetData(), libname.GetData() );
352
353 if( aConfirm && !IsOK( m_frame, msg ) )
354 return false;
355
356 try
357 {
358 m_frame->Prj().DesignBlockLibs()->DeleteDesignBlock( libname, dbname );
359 }
360 catch( const IO_ERROR& ioe )
361 {
362 DisplayError( m_frame, ioe.What() );
363 return false;
364 }
365
366 msg.Printf( _( "Design block '%s' deleted from library '%s'" ), dbname.GetData(), libname.GetData() );
367
368 m_frame->SetStatusText( msg );
369
370 RefreshLibs();
371
372 return true;
373}
374
375
377{
378 if( !aLibId.IsValid() )
379 return false;
380
381 wxString libname = aLibId.GetLibNickname();
382
383 if( !m_frame->Prj().DesignBlockLibs()->IsDesignBlockLibWritable( libname ) )
384 {
385 wxString msg = wxString::Format( _( "Library '%s' is read only." ), libname );
386 m_frame->ShowInfoBarError( msg );
387 return false;
388 }
389
390 std::unique_ptr<DESIGN_BLOCK> designBlock( GetDesignBlock( aLibId, true, true ) );
391
392 if( !designBlock )
393 return false;
394
395 wxString originalName = designBlock->GetLibId().GetLibItemName();
396 DIALOG_DESIGN_BLOCK_PROPERTIES dlg( m_frame, designBlock.get() );
397
398 if( dlg.ShowModal() != wxID_OK )
399 return false;
400
401 wxString newName = designBlock->GetLibId().GetLibItemName();
402
403 try
404 {
405 if( originalName != newName )
406 {
407 if( m_frame->Prj().DesignBlockLibs()->DesignBlockExists( libname, newName ) )
408 {
409 if( !checkOverwrite( m_frame, libname, newName ) )
410 return false;
411 }
412
413 m_frame->Prj().DesignBlockLibs()->SaveDesignBlock( libname, designBlock.get() );
414 m_frame->Prj().DesignBlockLibs()->DeleteDesignBlock( libname, originalName );
415 }
416 else
417 {
418 m_frame->Prj().DesignBlockLibs()->SaveDesignBlock( libname, designBlock.get() );
419 }
420 }
421 catch( const IO_ERROR& ioe )
422 {
423 DisplayError( m_frame, ioe.What() );
424 return false;
425 }
426
427 RefreshLibs();
428 SelectLibId( designBlock->GetLibId() );
429
430 return true;
431}
432
433
434bool DESIGN_BLOCK_PANE::checkOverwrite( wxWindow* aFrame, wxString& libname, wxString& newName )
435{
436 wxString msg = wxString::Format( _( "Design block '%s' already exists in library '%s'." ),
437 newName.GetData(), libname.GetData() );
438
439 if( OKOrCancelDialog( aFrame, _( "Confirmation" ), msg, _( "Overwrite existing design block?" ), _( "Overwrite" ) )
440 != wxID_OK )
441 {
442 return false;
443 }
444
445 return true;
446}
APP_SETTINGS_BASE is a settings class that should be derived for each standalone KiCad application.
@ KICAD_SEXP
S-expression KiCad file format.
static const wxString ShowType(DESIGN_BLOCK_FILE_T aFileType)
static DESIGN_BLOCK_FILE_T GuessPluginTypeFromLibPath(const wxString &aLibPath, int aCtl=0)
static DESIGN_BLOCK_IO * FindPlugin(DESIGN_BLOCK_FILE_T aFileType)
DESIGN_BLOCK * DesignBlockLoadWithOptionalNickname(const LIB_ID &aDesignBlockId, bool aKeepUUID=false, wxString *aErrorMsg=nullptr)
Load a design block having aDesignBlockId with possibly an empty nickname.
std::optional< LIB_STATUS > LoadOne(LIB_DATA *aLib) override
bool DeleteDesignBlockLibrary(const wxString &aLibName, bool aConfirm)
bool checkOverwrite(wxWindow *aFrame, wxString &libname, wxString &newName)
bool DeleteDesignBlockFromLibrary(const LIB_ID &aLibId, bool aConfirm)
void SelectLibId(const LIB_ID &aLibId)
LIB_ID GetSelectedLibId(int *aUnit=nullptr) const
DESIGN_BLOCK * GetDesignBlock(const LIB_ID &aLibId, bool aUseCacheLib, bool aShowErrorMsg, wxString *aErrorMsg=nullptr)
Load design block from design block library table.
DESIGN_BLOCK_PANE(EDA_DRAW_FRAME *aParent, const LIB_ID *aPreselect, std::vector< LIB_ID > &aHistoryList)
wxString createNewDesignBlockLibrary(const wxString &aDialogTitle)
PANEL_DESIGN_BLOCK_CHOOSER * m_chooserPanel
bool EditDesignBlockProperties(const LIB_ID &aLibId)
virtual void setLabelsAndTooltips()=0
wxString CreateNewDesignBlockLibrary(const wxString &aDialogTitle)
Creates a new design block library.
virtual void OnLanguageChanged(wxCommandEvent &aEvent)
EDA_DRAW_FRAME * m_frame
DESIGN_BLOCK * GetSelectedDesignBlock(bool aUseCacheLib, bool aShowErrorMsg)
void OnClosed(wxAuiManagerEvent &aEvent)
bool AddDesignBlockLibrary(const wxString &aDialogTitle, const wxString &aFilename, LIBRARY_TABLE_SCOPE aScope)
Add an existing library to a library table (presumed to be either the global or project design block ...
int ShowModal() override
The base class for create windows for drawing purpose.
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
virtual const wxString What() const
A composite of Problem() and Where()
Helper class to create more flexible dialogs, including 'do not show again' checkbox handling.
Definition kidialog.h:38
void DoNotShowCheckbox(wxString file, int line)
Shows the 'do not show again' checkbox.
Definition kidialog.cpp:51
int ShowModal() override
Definition kidialog.cpp:89
void ReloadTables(LIBRARY_TABLE_SCOPE aScope, std::initializer_list< LIBRARY_TABLE_TYPE > aTablesToLoad={})
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 SetNickname(const wxString &aNickname)
void SetType(const wxString &aType)
void SetDescription(const wxString &aDescription)
void SetURI(const wxString &aUri)
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:45
bool IsValid() const
Check if this LID_ID is valid.
Definition lib_id.h:168
const UTF8 & GetLibItemName() const
Definition lib_id.h:98
const UTF8 & GetLibNickname() const
Return the logical library name portion of a LIB_ID.
Definition lib_id.h:83
void PreloadDesignBlockLibraries(KIWAY *aKiway)
Starts a background job to preload the global and project design block libraries.
Definition pgm_base.cpp:892
virtual LIBRARY_MANAGER & GetLibraryManager() const
Definition pgm_base.h:125
wxString wx_str() const
Definition utf8.cpp:41
WX_PANEL(wxWindow *parent, wxWindowID id=wxID_ANY, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(-1,-1), long style=wxTAB_TRAVERSAL, const wxString &name=wxEmptyString)
Definition wx_panel.cpp:24
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
void DisplayError(wxWindow *aParent, const wxString &aText)
Display an error or warning message box with aMessage.
Definition confirm.cpp:192
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.
static const std::string KiCadDesignBlockLibPathExtension
static wxString KiCadDesignBlockLibPathWildcard()
std::unique_ptr< T > IO_RELEASER
Helper to hold and release an IO_BASE object when exceptions are thrown.
Definition io_mgr.h:33
LIBRARY_TABLE_SCOPE
static std::string normalizedPath(std::string aPath)
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
wxString message
Definition of file extensions used in Kicad.