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
46 GRID_TRICKS( aGrid ),
47 m_curRow( -1 )
48{
49}
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
96 m_files( aFiles ),
97 m_localFiles( new EMBEDDED_FILES() )
98{
99 for( auto& [name, file] : m_files->EmbeddedFileMap() )
100 {
102 m_localFiles->AddFile( newFile );
103 }
104
105 // Set up the standard buttons
106 m_delete_button->SetBitmap( KiBitmapBundle( BITMAPS::small_trash ) );
107 m_browse_button->SetBitmap( KiBitmapBundle( BITMAPS::small_folder ) );
108 m_files_grid->SetMargins( 0 - wxSYS_VSCROLL_X, 0 );
110
111 m_files_grid->PushEventHandler( new EMBEDDED_FILES_GRID_TRICKS( m_files_grid ) );
112
115 {
116 for( int ii = 0; ii < m_files_grid->GetNumberRows(); ii++ )
117 {
118 if( m_files_grid->GetCellValue( ii, 1 ) == file->GetLink() )
119 {
120 m_files_grid->DeleteRows( ii );
121 break;
122 }
123 }
124
125 m_files_grid->AppendRows( 1 );
126 int ii = m_files_grid->GetNumberRows() - 1;
127 m_files_grid->SetCellValue( ii, 0, file->name );
128 m_files_grid->SetCellValue( ii, 1, file->GetLink() );
129
130 } );
131}
132
133
135{
136 // Remove the GRID_TRICKS handler
137 m_files_grid->PopEventHandler( true );
138}
139
140
141void PANEL_EMBEDDED_FILES::onSize( wxSizeEvent& event )
142{
143 resizeGrid();
144}
145
146
148{
149 int panel_width = GetClientRect().GetWidth();
150 int first_width = m_files_grid->GetColSize( 0 );
151 int second_width = m_files_grid->GetColSize( 1 );
152
153 double ratio;
154
155 if( first_width + second_width > 0 )
156 ratio = (double)first_width / (double)( first_width + second_width );
157 else
158 ratio = 0.3;
159
160
161 m_files_grid->SetColSize( 0, panel_width * ratio );
162 m_files_grid->SetColSize( 1, panel_width * ( 1 - ratio ) );
163 Layout();
164}
165
166
168{
169 m_files_grid->ClearGrid();
170
171 if( m_files_grid->GetNumberRows() > 0 )
172 m_files_grid->DeleteRows( 0, m_files_grid->GetNumberRows() );
173
174 int ii = 0;
175
176 for( auto& [name, file] : m_localFiles->EmbeddedFileMap() )
177 {
178 while( m_files_grid->GetNumberRows() < ii + 1 )
179 m_files_grid->AppendRows( 1 );
180
181 m_files_grid->SetCellValue( ii, 0, name );
182 m_files_grid->SetCellValue( ii, 1, file->GetLink() );
183
184 ii++;
185 }
186
188
189 resizeGrid();
190
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 {
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
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 m_files->AddFile( file );
252 m_localFiles->RemoveFile( file->name, false );
253 }
254
256
257 return true;
258}
259
260
261void PANEL_EMBEDDED_FILES::onFontEmbedClick( wxCommandEvent& event )
262{
263 wxWindowUpdateLocker updateLock( this );
264
265 int row_pos = m_files_grid->GetGridCursorRow();
266 int col_pos = m_files_grid->GetGridCursorCol();
267 wxString row_name;
268
269 if( row_pos >= 0 )
270 row_name = m_files_grid->GetCellValue( row_pos, 0 );
271
272 for( int ii = 0; ii < m_files_grid->GetNumberRows(); ii++ )
273 {
274 wxString name = m_files_grid->GetCellValue( ii, 0 );
275
277
279 {
280 m_files_grid->DeleteRows( ii );
281 ii--;
283 }
284 }
285
286 if( m_cbEmbedFonts->IsChecked() )
287 {
288 std::set<KIFONT::OUTLINE_FONT*> fonts = m_files->GetFonts();
289
290 for( KIFONT::OUTLINE_FONT* font : fonts )
291 {
292 EMBEDDED_FILES::EMBEDDED_FILE* result = m_localFiles->AddFile( font->GetFileName(), true );
293
294 if( !result )
295 {
296 wxLogTrace( wxT( "KICAD_EMBED" ), wxString::Format( "Could not embed font %s",
297 font->GetFileName() ) );
298 continue;
299 }
300 }
301 }
302
303 if( row_pos >= 0 )
304 {
305 col_pos = std::max( std::min( col_pos, m_files_grid->GetNumberCols() - 1 ), 0 );
306 row_pos = std::max( std::min( row_pos, m_files_grid->GetNumberRows() - 1 ), 0 );
307 m_files_grid->SetGridCursor( row_pos, col_pos );
308
309 for( int ii = 0; ii < m_files_grid->GetNumberRows(); ++ii )
310 {
311 if( m_files_grid->GetCellValue( ii, 0 ) == row_name )
312 {
313 m_files_grid->SetGridCursor( ii, col_pos );
314 break;
315 }
316 }
317 }
318}
319
320
322{
323 wxFileName fileName( aFile );
324 wxString name = fileName.GetFullName();
325
326 if( m_localFiles->HasFile( name ) )
327 {
328 wxString msg = wxString::Format( _( "File '%s' already exists." ), name );
329
330 KIDIALOG errorDlg( m_parent, msg, _( "Confirmation" ), wxOK | wxCANCEL | wxICON_WARNING );
331 errorDlg.SetOKLabel( _( "Overwrite" ) );
332
333 if( errorDlg.ShowModal() != wxID_OK )
334 return nullptr;
335
336 for( int ii = 0; ii < m_files_grid->GetNumberRows(); ii++ )
337 {
338 if( m_files_grid->GetCellValue( ii, 0 ) == name )
339 {
340 m_files_grid->DeleteRows( ii );
341 break;
342 }
343 }
344 }
345
346 EMBEDDED_FILES::EMBEDDED_FILE* result = m_localFiles->AddFile( fileName, true );
347
348 if( !result )
349 {
350 wxString msg = wxString::Format( _( "Failed to add file '%s'." ), name );
351
352 KIDIALOG errorDlg( m_parent, msg, _( "Error" ), wxOK | wxICON_ERROR );
353 errorDlg.ShowModal();
354 return nullptr;
355 }
356
357 return result;
358}
359
360
361void PANEL_EMBEDDED_FILES::onAddEmbeddedFiles( wxCommandEvent& event )
362{
363 // TODO: Update strings to reflect that multiple files can be selected.
364 wxFileDialog fileDialog( this, _( "Select a file to embed" ), wxEmptyString, wxEmptyString,
365 _( "All Files" ) + wxT( " (*.*)|*.*" ),
366 wxFD_OPEN | wxFD_FILE_MUST_EXIST | wxFD_MULTIPLE );
367
368 if( fileDialog.ShowModal() == wxID_OK )
369 {
370 wxArrayString paths;
371 fileDialog.GetPaths( paths );
372
373 for( const wxString& path : paths )
375 }
376}
377
378
379bool PANEL_EMBEDDED_FILES::RemoveEmbeddedFile( const wxString& aFileName )
380{
381 wxString name = aFileName;
382
383 if( name.StartsWith( FILEEXT::KiCadUriPrefix ) )
384 name = name.Mid( FILEEXT::KiCadUriPrefix.size() + 3 );
385
386 int row = std::max( 0, m_files_grid->GetGridCursorRow() );
387
388 for( int ii = 0; ii < m_files_grid->GetNumberRows(); ii++ )
389 {
390 if( m_files_grid->GetCellValue( ii, 0 ) == name )
391 {
392 m_files_grid->DeleteRows( ii );
394
395 if( row < m_files_grid->GetNumberRows() )
396 m_files_grid->SetGridCursor( row, 0 );
397 else if( m_files_grid->GetNumberRows() > 0 )
398 m_files_grid->SetGridCursor( m_files_grid->GetNumberRows() - 1, 0 );
399
400 return true;
401 }
402 }
403
404 return false;
405}
406
407
409{
411 [&]( int row )
412 {
413 wxString name = m_files_grid->GetCellValue( row, 0 );
414
416 m_files_grid->DeleteRows( row );
417 } );
418}
419
420
421void PANEL_EMBEDDED_FILES::onExportFiles( wxCommandEvent& event )
422{
423 wxDirDialog dirDialog( this, _( "Select a directory to export files" ) );
424
425 if( dirDialog.ShowModal() != wxID_OK )
426 return;
427
428 wxString path = dirDialog.GetPath();
429
430 for( auto& [name, file] : m_localFiles->EmbeddedFileMap() )
431 {
432 wxFileName fileName( path, name );
433
434 if( fileName.FileExists() )
435 {
436 wxString msg = wxString::Format( _( "File '%s' already exists." ), fileName.GetFullName() );
437
438 KIDIALOG errorDlg( m_parent, msg, _( "Confirmation" ), wxOK | wxCANCEL | wxICON_WARNING );
439 errorDlg.SetOKCancelLabels( _( "Overwrite" ), _( "Skip" ) );
440 errorDlg.DoNotShowCheckbox( __FILE__, __LINE__ );
441
442 if( errorDlg.ShowModal() != wxID_OK )
443 continue;
444 }
445
446 bool skip_file = false;
447
448 while( 1 )
449 {
450 if( !fileName.IsDirWritable() )
451 {
452#ifndef __WXMAC__
453 wxString msg = wxString::Format( _( "Directory '%s' is not writable." ), fileName.GetFullName() );
454#else
455 wxString msg = wxString::Format( _( "Folder '%s' is not writable." ), fileName.GetPath() );
456#endif
457 // Don't set a 'do not show again' checkbox for this dialog
458 KIDIALOG errorDlg( m_parent, msg, _( "Error" ), wxYES_NO | wxCANCEL | wxICON_ERROR );
459 errorDlg.SetYesNoCancelLabels( _( "Retry" ), _( "Skip" ), _( "Cancel" ) );
460
461 int result = errorDlg.ShowModal();
462
463 if( result == wxID_CANCEL )
464 {
465 return;
466 }
467 else if( result == wxID_NO )
468 {
469 skip_file = true;
470 break;
471 }
472 }
473 else
474 {
475 break;
476 }
477 }
478
479 if( skip_file )
480 continue;
481
482
483 wxFFileOutputStream out( fileName.GetFullPath() );
484
485 if( !out.IsOk() )
486 {
487 wxString msg = wxString::Format( _( "Failed to open file '%s'." ), fileName.GetFullName() );
488
489 KIDIALOG errorDlg( m_parent, msg, _( "Error" ), wxOK | wxICON_ERROR );
490 errorDlg.ShowModal();
491 continue;
492 }
493
494 out.Write( file->decompressedData.data(), file->decompressedData.size() );
495
496 if( !out.IsOk() || ( out.LastWrite() != file->decompressedData.size() ) )
497 {
498 wxString msg = wxString::Format( _( "Failed to write file '%s'." ), fileName.GetFullName() );
499
500 KIDIALOG errorDlg( m_parent, msg, _( "Error" ), wxOK | wxICON_ERROR );
501
502 errorDlg.ShowModal();
503 }
504 }
505}
const char * name
Definition: DXF_plotter.cpp:62
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
EMBEDDED_FILES_GRID_TRICKS(WX_GRID *aGrid)
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.
void SetFileAddedCallback(FILE_ADDED_CALLBACK callback)
EMBEDDED_FILE * GetEmbeddedFile(const wxString &aName) const
Returns the embedded file with the given name or nullptr if it does not exist.
bool HasFile(const wxString &name) const
virtual void RunOnNestedEmbeddedFiles(const std::function< void(EMBEDDED_FILES *)> &aFunction)
Provide access to nested embedded files, such as symbols in schematics and footprints in boards.
void ClearEmbeddedFiles(bool aDeleteFiles=true)
virtual std::set< KIFONT::OUTLINE_FONT * > GetFonts() const
EMBEDDED_FILE * AddFile(const wxFileName &aName, bool aOverwrite)
Load a file from disk and adds it to the collection.
const std::map< wxString, EMBEDDED_FILE * > & EmbeddedFileMap() const
void SetAreFontsEmbedded(bool aEmbedFonts)
bool GetAreFontsEmbedded() const
Add mouse and command handling (such as cut, copy, and paste) to a WX_GRID instance.
Definition: grid_tricks.h:61
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.
Definition: grid_tricks.h:128
Helper class to create more flexible dialogs, including 'do not show again' checkbox handling.
Definition: kidialog.h:50
void DoNotShowCheckbox(wxString file, int line)
Shows the 'do not show again' checkbox.
Definition: kidialog.cpp:51
bool SetOKCancelLabels(const ButtonLabel &ok, const ButtonLabel &cancel) override
Definition: kidialog.h:60
int ShowModal() override
Definition: kidialog.cpp:95
Class OUTLINE_FONT implements outline font drawing.
Definition: outline_font.h:53
Class PANEL_EMBEDDED_FILES_BASE.
bool RemoveEmbeddedFile(const wxString &aFileName)
EMBEDDED_FILES * m_files
PANEL_EMBEDDED_FILES(wxWindow *parent, EMBEDDED_FILES *aFiles)
void onFontEmbedClick(wxCommandEvent &event) override
void onAddEmbeddedFiles(wxCommandEvent &event) override
void onDeleteEmbeddedFile(wxCommandEvent &event) override
bool TransferDataToWindow() override
void onSize(wxSizeEvent &event) override
EMBEDDED_FILES * m_localFiles
void onExportFiles(wxCommandEvent &event) override
EMBEDDED_FILES::EMBEDDED_FILE * AddEmbeddedFile(const wxString &aFileName)
bool TransferDataFromWindow() override
void SetBitmap(const wxBitmapBundle &aBmp)
void OnDeleteRows(const std::function< void(int row)> &aDeleter)
Handles a row deletion event.
Definition: wx_grid.cpp:704
void EnableAlternateRowColors(bool aEnable=true)
Enable alternate row highlighting, where every odd row has a different background color than the even...
Definition: wx_grid.cpp:308
bool IsOK(wxWindow *aParent, const wxString &aMessage)
Display a yes/no dialog with aMessage and returns the user response.
Definition: confirm.cpp:251
This file is part of the common library.
#define _(s)
static const std::string KiCadUriPrefix
This file is part of the common library.
@ PCB_T
Definition: typeinfo.h:82
@ SCHEMATIC_T
Definition: typeinfo.h:205