KiCad PCB EDA Suite
Loading...
Searching...
No Matches
panel_embedded_files.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 3
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/gpl-3.0.html
19 * or you may search the http://www.gnu.org website for the version 3 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#include <eda_item.h>
25#include <confirm.h>
26#include <bitmaps.h>
28#include <embedded_files.h>
29#include <font/outline_font.h>
30#include <kidialog.h>
32#include <widgets/wx_grid.h>
33
34#include <wx/clipbrd.h>
35#include <wx/dirdlg.h>
36#include <wx/filedlg.h>
37#include <wx/filename.h>
38#include <wx/log.h>
39#include <wx/menu.h>
40#include <wx/wfstream.h>
41#include <wx/wupdlock.h>
42
43/* ---------- GRID_TRICKS for embedded files grid ---------- */
44
50
51
52void EMBEDDED_FILES_GRID_TRICKS::showPopupMenu( wxMenu& menu, wxGridEvent& aEvent )
53{
54 if( const int row = aEvent.GetRow(); row >= 0 && row < m_grid->GetNumberRows() )
55 {
56 m_curRow = row;
57 menu.Append( EMBEDDED_FILES_GRID_TRICKS_COPY_FILENAME, _( "Copy Embedded Reference" ),
58 _( "Copy the reference for this embedded file" ) );
59 menu.AppendSeparator();
60 GRID_TRICKS::showPopupMenu( menu, aEvent );
61 }
62 else
63 {
64 m_curRow = -1;
65 }
66}
67
68
70{
71 if( event.GetId() == EMBEDDED_FILES_GRID_TRICKS_COPY_FILENAME )
72 {
73 if( m_curRow >= 0 )
74 {
75 const wxString cellValue = m_grid->GetCellValue( m_curRow, 1 );
76
77 if( wxTheClipboard->Open() )
78 {
79 wxTheClipboard->SetData( new wxTextDataObject( cellValue ) );
80 wxTheClipboard->Close();
81 }
82 }
83 }
84 else
85 {
87 }
88}
89
90
91/* ---------- End of GRID_TRICKS for embedded files grid ---------- */
92
93
94PANEL_EMBEDDED_FILES::PANEL_EMBEDDED_FILES( wxWindow* aParent, EMBEDDED_FILES* aFiles, int aFlags,
95 std::vector<const EMBEDDED_FILES*> aInheritedFiles ) :
97 m_files( aFiles ),
99 m_inheritedFiles( std::move( aInheritedFiles ) )
100{
101 m_files_grid->SetUseNativeColLabels();
102
103 for( auto& [name, file] : m_files->EmbeddedFileMap() )
104 {
105 EMBEDDED_FILES::EMBEDDED_FILE* newFile = new EMBEDDED_FILES::EMBEDDED_FILE( *file );
106 m_localFiles->AddFile( newFile );
107 }
108
109 for( const EMBEDDED_FILES* inheritedFiles : m_inheritedFiles )
110 {
111 for( auto& [name, file] : inheritedFiles->EmbeddedFileMap() )
112 {
113 if( m_localFiles->HasFile( name ) )
114 continue;
115
116 EMBEDDED_FILES::EMBEDDED_FILE* newFile = new EMBEDDED_FILES::EMBEDDED_FILE( *file );
117 m_localFiles->AddFile( newFile );
118 m_inheritedFileNames.insert( name );
119 }
120 }
121
122 if( aFlags & NO_MARGINS )
123 {
124 m_filesGridSizer->Detach( m_files_grid );
125 m_filesGridSizer->Add( m_files_grid, 5, wxEXPAND, 5 );
126
127 m_buttonsSizer->Detach( m_browse_button );
128 m_buttonsSizer->Prepend( m_browse_button, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 );
129
130 m_buttonsSizer->Detach( m_export );
131 m_buttonsSizer->Add( m_export, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 );
132 }
133
134 // Set up the standard buttons
135 m_delete_button->SetBitmap( KiBitmapBundle( BITMAPS::small_trash ) );
136 m_browse_button->SetBitmap( KiBitmapBundle( BITMAPS::small_folder ) );
137 m_files_grid->SetMargins( 0 - wxSYS_VSCROLL_X, 0 );
138 m_files_grid->EnableAlternateRowColors();
139
140 m_files_grid->PushEventHandler( new EMBEDDED_FILES_GRID_TRICKS( m_files_grid ) );
141 m_files_grid->SetupColumnAutosizer( 1 );
142
143 m_localFiles->SetFileAddedCallback(
145 {
146 for( int ii = 0; ii < m_files_grid->GetNumberRows(); ii++ )
147 {
148 if( m_files_grid->GetCellValue( ii, 1 ) == file->GetLink() )
149 {
150 m_files_grid->DeleteRows( ii );
151 break;
152 }
153 }
154
155 m_files_grid->AppendRows( 1 );
156 int ii = m_files_grid->GetNumberRows() - 1;
157 m_files_grid->SetCellValue( ii, 0, file->name );
158 m_files_grid->SetCellValue( ii, 1, file->GetLink() );
159
160 } );
161}
162
163
165{
166 // Remove the GRID_TRICKS handler
167 m_files_grid->PopEventHandler( true );
168 delete m_localFiles;
169}
170
171
173{
174 m_files_grid->ClearGrid();
175 m_files_grid->ClearRows();
176
177 int ii = 0;
178
179 for( auto& [name, file] : m_localFiles->EmbeddedFileMap() )
180 {
181 while( m_files_grid->GetNumberRows() < ii + 1 )
182 m_files_grid->AppendRows( 1 );
183
184 m_files_grid->SetCellValue( ii, 0, name );
185 m_files_grid->SetCellValue( ii, 1, file->GetLink() );
186
187 ii++;
188 }
189
190 m_cbEmbedFonts->SetValue( m_files->GetAreFontsEmbedded() );
191 return true;
192}
193
194
196{
197 std::optional<bool> deleteReferences;
198
199 auto confirmDelete =
200 [&]() -> bool
201 {
202 if( EDA_ITEM* parent = dynamic_cast<EDA_ITEM*>( m_files ) )
203 {
204 if( parent->Type() == PCB_T )
205 {
206 return IsOK( m_parent, _( "Deleted embedded files are also referenced in some footprints.\n"
207 "Delete from footprints as well?" ) );
208 }
209 else if( parent->Type() == SCHEMATIC_T )
210 {
211 return IsOK( m_parent, _( "Deleted embedded files are also referenced in some symbols.\n"
212 "Delete from symbols as well?" ) );
213 }
214 }
215
216 wxFAIL_MSG( wxT( "Unexpected embedded files owner" ) );
217 return false;
218 };
219
220 for( const auto& [name, file] : m_files->EmbeddedFileMap() )
221 {
222 if( !m_localFiles->HasFile( name ) )
223 {
224 m_files->RunOnNestedEmbeddedFiles(
225 [&]( EMBEDDED_FILES* nested_files )
226 {
227 if( nested_files->HasFile( name ) )
228 {
229 if( !deleteReferences.has_value() )
230 deleteReferences = confirmDelete();
231
232 if( deleteReferences.value() )
233 nested_files->RemoveFile( name, true );
234 }
235 } );
236 }
237
238 if( deleteReferences.has_value() && deleteReferences.value() == false )
239 break;
240 }
241
242 m_files->ClearEmbeddedFiles();
243
244 std::vector<EMBEDDED_FILES::EMBEDDED_FILE*> files;
245
246 for( const auto& [name, file] : m_localFiles->EmbeddedFileMap() )
247 files.push_back( file );
248
249 for( EMBEDDED_FILES::EMBEDDED_FILE* file : files )
250 {
251 if( m_inheritedFileNames.count( file->name ) )
252 continue;
253
254 m_files->AddFile( file );
255 m_localFiles->RemoveFile( file->name, false );
256 }
257
258 m_files->SetAreFontsEmbedded( m_cbEmbedFonts->IsChecked() );
259
260 return true;
261}
262
263
264void PANEL_EMBEDDED_FILES::onFontEmbedClick( wxCommandEvent& event )
265{
266 wxWindowUpdateLocker updateLock( this );
267
268 int row_pos = m_files_grid->GetGridCursorRow();
269 int col_pos = m_files_grid->GetGridCursorCol();
270 wxString row_name;
271
272 if( row_pos >= 0 )
273 row_name = m_files_grid->GetCellValue( row_pos, 0 );
274
275 for( int ii = 0; ii < m_files_grid->GetNumberRows(); ii++ )
276 {
277 wxString name = m_files_grid->GetCellValue( ii, 0 );
278
279 EMBEDDED_FILES::EMBEDDED_FILE* file = m_localFiles->GetEmbeddedFile( name );
280
282 {
283 m_files_grid->DeleteRows( ii );
284 ii--;
285 m_localFiles->RemoveFile( name );
286 }
287 }
288
289 if( m_cbEmbedFonts->IsChecked() )
290 {
291 std::set<KIFONT::OUTLINE_FONT*> fonts = m_files->GetFonts();
292
293 for( KIFONT::OUTLINE_FONT* font : fonts )
294 {
295 EMBEDDED_FILES::EMBEDDED_FILE* result = m_localFiles->AddFile( font->GetFileName(), true );
296
297 if( !result )
298 {
299 wxLogTrace( wxT( "KICAD_EMBED" ), wxString::Format( "Could not embed font %s",
300 font->GetFileName() ) );
301 continue;
302 }
303 }
304 }
305
306 if( row_pos >= 0 )
307 {
308 col_pos = std::max( std::min( col_pos, m_files_grid->GetNumberCols() - 1 ), 0 );
309 row_pos = std::max( std::min( row_pos, m_files_grid->GetNumberRows() - 1 ), 0 );
310 m_files_grid->SetGridCursor( row_pos, col_pos );
311
312 for( int ii = 0; ii < m_files_grid->GetNumberRows(); ++ii )
313 {
314 if( m_files_grid->GetCellValue( ii, 0 ) == row_name )
315 {
316 m_files_grid->SetGridCursor( ii, col_pos );
317 break;
318 }
319 }
320 }
321}
322
323
325{
326 wxFileName fileName( aFile );
327 wxString name = fileName.GetFullName();
328
329 if( m_localFiles->HasFile( name ) )
330 {
331 wxString msg = wxString::Format( _( "File '%s' already exists." ), name );
332
333 KIDIALOG errorDlg( m_parent, msg, _( "Confirmation" ), wxOK | wxCANCEL | wxICON_WARNING );
334 errorDlg.SetOKLabel( _( "Overwrite" ) );
335
336 if( errorDlg.ShowModal() != wxID_OK )
337 return nullptr;
338
339 for( int ii = 0; ii < m_files_grid->GetNumberRows(); ii++ )
340 {
341 if( m_files_grid->GetCellValue( ii, 0 ) == name )
342 {
343 m_files_grid->DeleteRows( ii );
344 break;
345 }
346 }
347 }
348
349 EMBEDDED_FILES::EMBEDDED_FILE* result = m_localFiles->AddFile( fileName, true );
350
351 if( !result )
352 {
353 wxString msg = wxString::Format( _( "Failed to add file '%s'." ), name );
354
355 KIDIALOG errorDlg( m_parent, msg, _( "Error" ), wxOK | wxICON_ERROR );
356 errorDlg.ShowModal();
357 return nullptr;
358 }
359
360 return result;
361}
362
363
364void PANEL_EMBEDDED_FILES::onAddEmbeddedFiles( wxCommandEvent& event )
365{
366 // TODO: Update strings to reflect that multiple files can be selected.
367 wxFileDialog fileDialog( this, _( "Select a file to embed" ), wxEmptyString, wxEmptyString,
368 _( "All Files" ) + wxT( " (*.*)|*.*" ),
369 wxFD_OPEN | wxFD_FILE_MUST_EXIST | wxFD_MULTIPLE );
370
371 if( fileDialog.ShowModal() == wxID_OK )
372 {
373 wxArrayString paths;
374 fileDialog.GetPaths( paths );
375
376 for( const wxString& path : paths )
378 }
379}
380
381
382bool PANEL_EMBEDDED_FILES::RemoveEmbeddedFile( const wxString& aFileName )
383{
384 wxString name = aFileName;
385
386 if( name.StartsWith( FILEEXT::KiCadUriPrefix ) )
387 name = name.Mid( FILEEXT::KiCadUriPrefix.size() + 3 );
388
389 if( m_inheritedFileNames.count( name ) )
390 {
391 wxString msg = _( "Embedded files inherited from a parent symbol cannot be removed." );
392
393 DisplayErrorMessage( this, msg );
394 return false;
395 }
396
397 int row = std::max( 0, m_files_grid->GetGridCursorRow() );
398
399 for( int ii = 0; ii < m_files_grid->GetNumberRows(); ii++ )
400 {
401 if( m_files_grid->GetCellValue( ii, 0 ) == name )
402 {
403 m_files_grid->DeleteRows( ii );
404 m_localFiles->RemoveFile( name );
405
406 if( row < m_files_grid->GetNumberRows() )
407 m_files_grid->SetGridCursor( row, 0 );
408 else if( m_files_grid->GetNumberRows() > 0 )
409 m_files_grid->SetGridCursor( m_files_grid->GetNumberRows() - 1, 0 );
410
411 return true;
412 }
413 }
414
415 return false;
416}
417
418
420{
421 m_files_grid->OnDeleteRows(
422 [&]( int row )
423 {
424 wxString name = m_files_grid->GetCellValue( row, 0 );
425
427 } );
428}
429
430
431void PANEL_EMBEDDED_FILES::onExportFiles( wxCommandEvent& event )
432{
433 wxDirDialog dirDialog( this, _( "Select a directory to export files" ) );
434
435 if( dirDialog.ShowModal() != wxID_OK )
436 return;
437
438 wxString path = dirDialog.GetPath();
439
440 for( auto& [name, file] : m_localFiles->EmbeddedFileMap() )
441 {
442 wxFileName fileName( path, name );
443
444 if( fileName.FileExists() )
445 {
446 wxString msg = wxString::Format( _( "File '%s' already exists." ), fileName.GetFullName() );
447
448 KIDIALOG errorDlg( m_parent, msg, _( "Confirmation" ), wxOK | wxCANCEL | wxICON_WARNING );
449 errorDlg.SetOKCancelLabels( _( "Overwrite" ), _( "Skip" ) );
450 errorDlg.DoNotShowCheckbox( __FILE__, __LINE__ );
451
452 if( errorDlg.ShowModal() != wxID_OK )
453 continue;
454 }
455
456 bool skip_file = false;
457
458 while( 1 )
459 {
460 if( !fileName.IsDirWritable() )
461 {
462#ifndef __WXMAC__
463 wxString msg = wxString::Format( _( "Directory '%s' is not writable." ), fileName.GetFullName() );
464#else
465 wxString msg = wxString::Format( _( "Folder '%s' is not writable." ), fileName.GetPath() );
466#endif
467 // Don't set a 'do not show again' checkbox for this dialog
468 KIDIALOG errorDlg( m_parent, msg, _( "Error" ), wxYES_NO | wxCANCEL | wxICON_ERROR );
469 errorDlg.SetYesNoCancelLabels( _( "Retry" ), _( "Skip" ), _( "Cancel" ) );
470
471 int result = errorDlg.ShowModal();
472
473 if( result == wxID_CANCEL )
474 {
475 return;
476 }
477 else if( result == wxID_NO )
478 {
479 skip_file = true;
480 break;
481 }
482 }
483 else
484 {
485 break;
486 }
487 }
488
489 if( skip_file )
490 continue;
491
492
493 wxFFileOutputStream out( fileName.GetFullPath() );
494
495 if( !out.IsOk() )
496 {
497 wxString msg = wxString::Format( _( "Failed to open file '%s'." ), fileName.GetFullName() );
498
499 KIDIALOG errorDlg( m_parent, msg, _( "Error" ), wxOK | wxICON_ERROR );
500 errorDlg.ShowModal();
501 continue;
502 }
503
504 out.Write( file->decompressedData.data(), file->decompressedData.size() );
505
506 if( !out.IsOk() || ( out.LastWrite() != file->decompressedData.size() ) )
507 {
508 wxString msg = wxString::Format( _( "Failed to write file '%s'." ), fileName.GetFullName() );
509
510 KIDIALOG errorDlg( m_parent, msg, _( "Error" ), wxOK | wxICON_ERROR );
511
512 errorDlg.ShowModal();
513 }
514 }
515}
const char * name
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
Definition bitmap.cpp:110
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:98
void doPopupSelection(wxCommandEvent &event) override
void showPopupMenu(wxMenu &menu, wxGridEvent &aEvent) override
void RemoveFile(const wxString &name, bool aErase=true)
Remove a file from the collection and frees the memory.
bool HasFile(const wxString &name) const
GRID_TRICKS(WX_GRID *aGrid)
virtual void doPopupSelection(wxCommandEvent &event)
virtual void showPopupMenu(wxMenu &menu, wxGridEvent &aEvent)
WX_GRID * m_grid
I don't own the grid, but he owns me.
Helper class to create more flexible dialogs, including 'do not show again' checkbox handling.
Definition kidialog.h:42
void DoNotShowCheckbox(wxString file, int line)
Shows the 'do not show again' checkbox.
Definition kidialog.cpp:55
bool SetOKCancelLabels(const ButtonLabel &ok, const ButtonLabel &cancel) override
Definition kidialog.h:52
int ShowModal() override
Definition kidialog.cpp:93
Class OUTLINE_FONT implements outline font drawing.
PANEL_EMBEDDED_FILES_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)
PANEL_EMBEDDED_FILES(wxWindow *aParent, EMBEDDED_FILES *aFiles, int aFlags=0, std::vector< const EMBEDDED_FILES * > aInheritedFiles={})
bool RemoveEmbeddedFile(const wxString &aFileName)
std::set< wxString > m_inheritedFileNames
void onFontEmbedClick(wxCommandEvent &event) override
void onAddEmbeddedFiles(wxCommandEvent &event) override
std::vector< const EMBEDDED_FILES * > m_inheritedFiles
void onDeleteEmbeddedFile(wxCommandEvent &event) override
bool TransferDataToWindow() override
EMBEDDED_FILES * m_localFiles
void onExportFiles(wxCommandEvent &event) override
EMBEDDED_FILES::EMBEDDED_FILE * AddEmbeddedFile(const wxString &aFileName)
bool TransferDataFromWindow() override
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)
static const std::string KiCadUriPrefix
STL namespace.
#define NO_MARGINS
std::vector< char > decompressedData
std::string path
wxString result
Test unit parsing edge cases and error handling.
@ PCB_T
Definition typeinfo.h:82
@ SCHEMATIC_T
Definition typeinfo.h:208