KiCad PCB EDA Suite
Loading...
Searching...
No Matches
panel_fp_properties_3d_model.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 (C) 2018 Jean-Pierre Charras, jp.charras at wanadoo.fr
5 * Copyright (C) 2015 Dick Hollenbeck, [email protected]
6 * Copyright (C) 2008 Wayne Stambaugh <[email protected]>
7 * Copyright (C) 2004-2022 KiCad Developers, see AUTHORS.txt for contributors.
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, you may find one here:
21 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
22 * or you may search the http://www.gnu.org website for the version 2 license,
23 * or you may write to the Free Software Foundation, Inc.,
24 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
25 */
26
28
31#include <env_vars.h>
32#include <bitmaps.h>
35#include <widgets/wx_grid.h>
37#include <footprint.h>
38#include <fp_lib_table.h>
39#include <footprint.h>
43#include "filename_resolver.h"
44#include <pgm_base.h>
45#include <kiplatform/ui.h>
49#include <project_pcb.h>
50
51#include <wx/defs.h>
52#include <wx/msgdlg.h>
53
55{
58 COL_SHOWN = 2
59};
60
62 FOOTPRINT* aFootprint,
63 DIALOG_SHIM* aDialogParent,
64 wxWindow* aParent, wxWindowID aId,
65 const wxPoint& aPos,
66 const wxSize& aSize, long aStyle,
67 const wxString& aName ) :
68 PANEL_FP_PROPERTIES_3D_MODEL_BASE( aParent, aId, aPos, aSize, aStyle, aName ),
69 m_parentDialog( aDialogParent ),
70 m_frame( aFrame ),
71 m_footprint( aFootprint ),
72 m_inSelect( false )
73{
74 m_modelsGrid->SetDefaultRowSize( m_modelsGrid->GetDefaultRowSize() + 4 );
75
76 GRID_TRICKS* trick = new GRID_TRICKS( m_modelsGrid, [this]( wxCommandEvent& aEvent )
77 {
78 OnAdd3DRow( aEvent );
79 } );
81
82 m_modelsGrid->PushEventHandler( trick );
83
84 // Get the last 3D directory
86
87 if( cfg->m_lastFootprint3dDir.IsEmpty() )
88 {
89 wxGetEnv( ENV_VAR::GetVersionedEnvVarName( wxS( "3DMODEL_DIR" ) ),
91 }
92
93 // Icon showing warning/error information
94 wxGridCellAttr* attr = new wxGridCellAttr;
95 attr->SetReadOnly();
96 m_modelsGrid->SetColAttr( COL_PROBLEM, attr );
97
98 // Filename
99 attr = new wxGridCellAttr;
101 &cfg->m_lastFootprint3dDir, wxT( "*.*" ), true,
102 m_frame->Prj().GetProjectPath() ) );
103 m_modelsGrid->SetColAttr( COL_FILENAME, attr );
104
105 // Show checkbox
106 attr = new wxGridCellAttr;
107 attr->SetRenderer( new wxGridCellBoolRenderer() );
108 attr->SetReadOnly(); // not really; we delegate interactivity to GRID_TRICKS
109 attr->SetAlignment( wxALIGN_CENTER, wxALIGN_CENTER );
110 m_modelsGrid->SetColAttr( COL_SHOWN, attr );
111 m_modelsGrid->SetWindowStyleFlag( m_modelsGrid->GetWindowStyle() & ~wxHSCROLL );
112
114
117
118 m_LowerSizer3D->Add( m_previewPane, 1, wxEXPAND, 5 );
119
120 // Configure button logos
121 m_button3DShapeAdd->SetBitmap( KiBitmapBundle( BITMAPS::small_plus ) );
122 m_button3DShapeBrowse->SetBitmap( KiBitmapBundle( BITMAPS::small_folder ) );
123 m_button3DShapeRemove->SetBitmap( KiBitmapBundle( BITMAPS::small_trash ) );
124}
125
126
128{
129 // Delete the GRID_TRICKS.
130 m_modelsGrid->PopEventHandler( true );
131
132 // free the memory used by all models, otherwise models which were
133 // browsed but not used would consume memory
135
136 delete m_previewPane;
137}
138
139
141{
143 return true;
144}
145
147{
148 // Only commit changes in the editor, not the models
149 // The container dialog is responsible for moving the new models into
150 // the footprint inside a commit.
152 return false;
153
155
156 for( const auto& [name, file] : fp->EmbeddedFileMap() )
157 {
158 if( !m_footprint->HasFile( name ) )
160 }
161 return true;
162}
163
164
166{
167 wxString default_path;
168 wxGetEnv( ENV_VAR::GetVersionedEnvVarName( wxS( "3DMODEL_DIR" ) ), &default_path );
169
170#ifdef __WINDOWS__
171 default_path.Replace( wxT( "/" ), wxT( "\\" ) );
172#endif
173
174 m_shapes3D_list.clear();
176
177 wxString origPath, alias, shortPath;
179
180 for( const FP_3DMODEL& model : m_footprint->Models() )
181 {
182 m_shapes3D_list.push_back( model );
183 origPath = model.m_Filename;
184
185 if( res && res->SplitAlias( origPath, alias, shortPath ) )
186 origPath = alias + wxT( ":" ) + shortPath;
187
188 m_modelsGrid->AppendRows( 1 );
189 int row = m_modelsGrid->GetNumberRows() - 1;
190 m_modelsGrid->SetCellValue( row, COL_FILENAME, origPath );
191 m_modelsGrid->SetCellValue( row, COL_SHOWN, model.m_Show ? wxT( "1" ) : wxT( "0" ) );
192
193 // Must be after the filename is set
195 }
196
197 select3DModel( 0 );
198
200 m_modelsGrid->SetColSize( COL_SHOWN, m_modelsGrid->GetVisibleWidth( COL_SHOWN, true, false ) );
201
202 Layout();
203}
204
205
207{
208 m_inSelect = true;
209
210 aModelIdx = std::max( 0, aModelIdx );
211 aModelIdx = std::min( aModelIdx, m_modelsGrid->GetNumberRows() - 1 );
212
213 if( m_modelsGrid->GetNumberRows() )
214 {
215 m_modelsGrid->SelectRow( aModelIdx );
216 m_modelsGrid->SetGridCursor( aModelIdx, COL_FILENAME );
217 }
218
219 m_previewPane->SetSelectedModel( aModelIdx );
220
221 m_inSelect = false;
222}
223
224
226{
227 if( !m_inSelect )
228 select3DModel( aEvent.GetRow() );
229}
230
231
233{
234 if( aEvent.GetCol() == COL_FILENAME )
235 {
236 bool hasAlias = false;
238 wxString filename = m_modelsGrid->GetCellValue( aEvent.GetRow(), COL_FILENAME );
239
240 // Perform cleanup and validation on the filename if it isn't empty
241 if( !filename.empty() )
242 {
243 filename.Replace( wxT( "\n" ), wxT( "" ) );
244 filename.Replace( wxT( "\r" ), wxT( "" ) );
245 filename.Replace( wxT( "\t" ), wxT( "" ) );
246
247 res->ValidateFileName( filename, hasAlias );
248
249 // If the user has specified an alias in the name then prepend ':'
250 if( hasAlias )
251 filename.insert( 0, wxT( ":" ) );
252
253#ifdef __WINDOWS__
254 // In KiCad files, filenames and paths are stored using Unix notation
255 filename.Replace( wxT( "\\" ), wxT( "/" ) );
256#endif
257
258 // Update the grid with the modified filename
259 m_modelsGrid->SetCellValue( aEvent.GetRow(), COL_FILENAME, filename );
260 }
261
262 // Save the filename in the 3D shapes table
263 m_shapes3D_list[ aEvent.GetRow() ].m_Filename = filename;
264
265 // Update the validation status
266 updateValidateStatus( aEvent.GetRow() );
267 }
268 else if( aEvent.GetCol() == COL_SHOWN )
269 {
270 wxString showValue = m_modelsGrid->GetCellValue( aEvent.GetRow(), COL_SHOWN );
271
272 m_shapes3D_list[ aEvent.GetRow() ].m_Show = ( showValue == wxT( "1" ) );
273 }
274
276}
277
278
280{
282 return;
283
284 int idx = m_modelsGrid->GetGridCursorRow();
285
286 if( idx >= 0 && m_modelsGrid->GetNumberRows() && !m_shapes3D_list.empty() )
287 {
288 // Don't allow selection until we call select3DModel(), below. Otherwise wxWidgets
289 // has a tendency to get its knickers in a knot....
290 m_inSelect = true;
291
292 m_shapes3D_list.erase( m_shapes3D_list.begin() + idx );
293 m_modelsGrid->DeleteRows( idx );
294
295 select3DModel( idx ); // will clamp idx within bounds
297 }
298}
299
300
302{
304 return;
305
306 int selected = m_modelsGrid->GetGridCursorRow();
307
308 PROJECT& prj = m_frame->Prj();
309 FP_3DMODEL model;
312
313 wxString initialpath = prj.GetRString( PROJECT::VIEWER_3D_PATH );
314 wxString sidx = prj.GetRString( PROJECT::VIEWER_3D_FILTER_INDEX );
315 int filter = 0;
316
317 // If the PROJECT::VIEWER_3D_PATH hasn't been set yet, use the 3DMODEL_DIR environment
318 // variable and fall back to the project path if necessary.
319 if( initialpath.IsEmpty() )
320 {
321 if( !wxGetEnv( ENV_VAR::GetVersionedEnvVarName( wxS( "3DMODEL_DIR" ) ), &initialpath )
322 || initialpath.IsEmpty() )
323 {
324 initialpath = prj.GetProjectPath();
325 }
326 }
327
328 if( !sidx.empty() )
329 {
330 long tmp;
331 sidx.ToLong( &tmp );
332
333 if( tmp > 0 && tmp <= INT_MAX )
334 filter = (int) tmp;
335 }
336
337
338 DIALOG_SELECT_3DMODEL dm( m_parentDialog, cache, &model, initialpath, filter );
339
340 // Use QuasiModal so that Configure3DPaths (and its help window) will work
341 int retval = dm.ShowQuasiModal();
342
343 if( retval != wxID_OK || model.m_Filename.empty() )
344 {
345 if( selected >= 0 )
346 {
347 select3DModel( selected );
348 updateValidateStatus( selected );
349 }
350
351 return;
352 }
353
354 if( dm.IsEmbedded3DModel() )
355 {
356 wxString libraryName = m_footprint->GetFPID().GetLibNickname();
357 const FP_LIB_TABLE_ROW* fpRow = nullptr;
358
359 wxString footprintBasePath = wxEmptyString;
360
361 try
362 {
363 fpRow = PROJECT_PCB::PcbFootprintLibs( &m_frame->Prj() )->FindRow( libraryName, false );
364
365 if( fpRow )
366 footprintBasePath = fpRow->GetFullURI( true );
367 }
368 catch( ... )
369 {
370 // if libraryName is not found in table, do nothing
371 }
372
373
374 wxString fullPath = res->ResolvePath( model.m_Filename, footprintBasePath, nullptr );
375 wxFileName fname( fullPath );
376
378
379 if( !result )
380 {
381
382 wxString msg = wxString::Format( _( "Error adding 3D model" ) );
383 wxMessageBox( msg, _( "Error" ), wxICON_ERROR | wxOK, this );
384 return;
385 }
386
387 model.m_Filename = result->GetLink();
388 }
389
390
391 prj.SetRString( PROJECT::VIEWER_3D_PATH, initialpath );
392 sidx = wxString::Format( wxT( "%i" ), filter );
394
395 wxString alias;
396 wxString shortPath;
397 wxString filename = model.m_Filename;
398
399 if( res && res->SplitAlias( filename, alias, shortPath ) )
400 filename = alias + wxT( ":" ) + shortPath;
401
402#ifdef __WINDOWS__
403 // In KiCad files, filenames and paths are stored using Unix notation
404 model.m_Filename.Replace( wxT( "\\" ), wxT( "/" ) );
405#endif
406
407 model.m_Show = true;
408 m_shapes3D_list.push_back( model );
409
410 int idx = m_modelsGrid->GetNumberRows();
411 m_modelsGrid->AppendRows( 1 );
412 m_modelsGrid->SetCellValue( idx, COL_FILENAME, filename );
413 m_modelsGrid->SetCellValue( idx, COL_SHOWN, wxT( "1" ) );
414
415 select3DModel( idx );
417
419}
420
421
423{
425 return;
426
427 FP_3DMODEL model;
428
429 model.m_Show = true;
430 m_shapes3D_list.push_back( model );
431
432 int row = m_modelsGrid->GetNumberRows();
433 m_modelsGrid->AppendRows( 1 );
434 m_modelsGrid->SetCellValue( row, COL_SHOWN, wxT( "1" ) );
435 m_modelsGrid->SetCellValue( row, COL_PROBLEM, "" );
436
437 select3DModel( row );
438
439 m_modelsGrid->SetFocus();
440 m_modelsGrid->MakeCellVisible( row, COL_FILENAME );
441 m_modelsGrid->SetGridCursor( row, COL_FILENAME );
442
443 m_modelsGrid->EnableCellEditControl( true );
444 m_modelsGrid->ShowCellEditControl();
445
447}
448
449
451{
452 int icon = 0;
453 wxString errStr;
454
455 switch( validateModelExists( m_modelsGrid->GetCellValue( aRow, COL_FILENAME) ) )
456 {
457 case MODEL_VALIDATE_ERRORS::MODEL_NO_ERROR:
458 icon = 0;
459 errStr = "";
460 break;
461
462 case MODEL_VALIDATE_ERRORS::NO_FILENAME:
463 icon = wxICON_WARNING;
464 errStr = _( "No filename entered" );
465 break;
466
467 case MODEL_VALIDATE_ERRORS::ILLEGAL_FILENAME:
468 icon = wxICON_ERROR;
469 errStr = _( "Illegal filename" );
470 break;
471
472 case MODEL_VALIDATE_ERRORS::RESOLVE_FAIL:
473 icon = wxICON_ERROR;
474 errStr = _( "File not found" );
475 break;
476
477 case MODEL_VALIDATE_ERRORS::OPEN_FAIL:
478 icon = wxICON_ERROR;
479 errStr = _( "Unable to open file" );
480 break;
481
482 default:
483 icon = wxICON_ERROR;
484 errStr = _( "Unknown error" );
485 break;
486 }
487
488 m_modelsGrid->SetCellValue( aRow, COL_PROBLEM, errStr );
489 m_modelsGrid->SetCellRenderer( aRow, COL_PROBLEM,
490 new GRID_CELL_STATUS_ICON_RENDERER( icon ) );
491}
492
493
495{
496 if( aFilename.empty() )
497 return MODEL_VALIDATE_ERRORS::NO_FILENAME;
498
499 bool hasAlias = false;
501
502 if( !resolv )
503 return MODEL_VALIDATE_ERRORS::RESOLVE_FAIL;
504
505 if( !resolv->ValidateFileName( aFilename, hasAlias ) )
506 return MODEL_VALIDATE_ERRORS::ILLEGAL_FILENAME;
507
508 wxString libraryName = m_footprint->GetFPID().GetLibNickname();
509 const FP_LIB_TABLE_ROW* fpRow = nullptr;
510 try
511 {
512 fpRow = PROJECT_PCB::PcbFootprintLibs( &m_frame->Prj() )->FindRow( libraryName, false );
513 }
514 catch( ... )
515 {
516 // if libraryName is not found in table, do nothing
517 }
518
519 wxString footprintBasePath = wxEmptyString;
520
521 if( fpRow )
522 footprintBasePath = fpRow->GetFullURI( true );
523
524 wxString fullPath = resolv->ResolvePath( aFilename, footprintBasePath, m_footprint );
525
526 if( fullPath.IsEmpty() )
527 return MODEL_VALIDATE_ERRORS::RESOLVE_FAIL;
528
529 if( !wxFileName::IsFileReadable( fullPath ) )
530 return MODEL_VALIDATE_ERRORS::OPEN_FAIL;
531
532 return MODEL_VALIDATE_ERRORS::MODEL_NO_ERROR;
533}
534
535
536void PANEL_FP_PROPERTIES_3D_MODEL::Cfg3DPath( wxCommandEvent& event )
537{
538 DIALOG_CONFIGURE_PATHS dlg( this );
539
540 if( dlg.ShowQuasiModal() == wxID_OK )
542}
543
544
546{
547 // Account for scroll bars
548 int modelsWidth = KIPLATFORM::UI::GetUnobscuredSize( m_modelsGrid ).x;
549
550 int width = modelsWidth - m_modelsGrid->GetColSize( COL_SHOWN )
551 - m_modelsGrid->GetColSize( COL_PROBLEM );
552
553 if( width > 0 )
554 m_modelsGrid->SetColSize( COL_FILENAME, width );
555}
556
557
559{
561
562 event.Skip();
563}
564
565
566void PANEL_FP_PROPERTIES_3D_MODEL::OnUpdateUI( wxUpdateUIEvent& event )
567{
568 m_button3DShapeRemove->Enable( m_modelsGrid->GetNumberRows() > 0 );
569}
const char * name
Definition: DXF_plotter.cpp:57
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap)
Definition: bitmap.cpp:110
Dialog helper object to sit in the inheritance tree between wxDialog and any class written by wxFormB...
Definition: dialog_shim.h:88
int ShowQuasiModal()
bool HasFile(const wxString &name) const
EMBEDDED_FILE * AddFile(const wxFileName &aName, bool aOverwrite)
Loads a file from disk and adds it to the collection.
const std::map< wxString, EMBEDDED_FILE * > & EmbeddedFileMap() const
Provide an extensible class to resolve 3D model paths.
bool ValidateFileName(const wxString &aFileName, bool &hasAlias) const
Returns true if the given path is a valid aliased relative path.
void SetProgramBase(PGM_BASE *aBase)
Set a pointer to the application's PGM_BASE instance used to extract the local env vars.
wxString ResolvePath(const wxString &aFileName, const wxString &aWorkingPath, const EMBEDDED_FILES *aFiles)
Determines the full path of the given file name.
const LIB_ID & GetFPID() const
Definition: footprint.h:237
std::vector< FP_3DMODEL > & Models()
Definition: footprint.h:209
wxString m_Filename
The 3D shape filename in 3D library.
Definition: footprint.h:102
bool m_Show
Include model in rendering.
Definition: footprint.h:103
Hold a record identifying a library accessed by the appropriate footprint library #PLUGIN object in t...
Definition: fp_lib_table.h:42
const FP_LIB_TABLE_ROW * FindRow(const wxString &aNickName, bool aCheckIfEnabled=false)
Return an FP_LIB_TABLE_ROW if aNickName is found in this table or in any chained fall back table frag...
Editor for wxGrid cells that adds a file/folder browser to the grid input field.
Add mouse and command handling (such as cut, copy, and paste) to a WX_GRID instance.
Definition: grid_tricks.h:61
void SetTooltipEnable(int aCol, bool aEnable=true)
Enable the tooltip for a column.
Definition: grid_tricks.h:75
PROJECT & Prj() const
Return a reference to the PROJECT associated with this KIWAY.
const UTF8 & GetLibNickname() const
Return the logical library name portion of a LIB_ID.
Definition: lib_id.h:87
const wxString GetFullURI(bool aSubstituted=false) const
Return the full location specifying URI for the LIB, either in original UI form or in environment var...
Class PANEL_FP_PROPERTIES_3D_MODEL_BASE.
void OnUpdateUI(wxUpdateUIEvent &event) override
void Cfg3DPath(wxCommandEvent &event) override
void OnRemove3DModel(wxCommandEvent &event) override
void On3DModelCellChanged(wxGridEvent &aEvent) override
PANEL_FP_PROPERTIES_3D_MODEL(PCB_BASE_EDIT_FRAME *aFrame, FOOTPRINT *aFootprint, DIALOG_SHIM *aDialogParent, wxWindow *aParent, wxWindowID aId=wxID_ANY, const wxPoint &aPos=wxDefaultPosition, const wxSize &aSize=wxDefaultSize, long aStyle=wxTAB_TRAVERSAL, const wxString &aName=wxEmptyString)
void OnGridSize(wxSizeEvent &event) override
MODEL_VALIDATE_ERRORS validateModelExists(const wxString &aFilename)
void OnAdd3DModel(wxCommandEvent &event) override
void OnAdd3DRow(wxCommandEvent &event) override
std::vector< FP_3DMODEL > m_shapes3D_list
void On3DModelSelected(wxGridEvent &) override
void UpdateDummyFootprint(bool aRelaodRequired=true)
Copy shapes from the current shape list which are flagged for preview to the copy of footprint that i...
FOOTPRINT * GetDummyFootprint() const
Get the dummy footprint that is used for previewing the 3D model.
void SetSelectedModel(int idx)
Set the currently selected index in the model list so that the scale/rotation/offset controls can be ...
wxString m_lastFootprint3dDir
Common, abstract interface for edit frames.
virtual SETTINGS_MANAGER & GetSettingsManager() const
Definition: pgm_base.h:142
static FP_LIB_TABLE * PcbFootprintLibs(PROJECT *aProject)
Return the table of footprint libraries without Kiway.
Definition: project_pcb.cpp:37
static S3D_CACHE * Get3DCacheManager(PROJECT *aProject, bool updateProjDir=false)
Return a pointer to an instance of the 3D cache manager.
Definition: project_pcb.cpp:77
static FILENAME_RESOLVER * Get3DFilenameResolver(PROJECT *aProject)
Accessor for 3D path resolver.
Container for project specific data.
Definition: project.h:62
@ VIEWER_3D_FILTER_INDEX
Definition: project.h:188
@ VIEWER_3D_PATH
Definition: project.h:187
virtual const wxString GetProjectPath() const
Return the full path of the project.
Definition: project.cpp:135
virtual void SetRString(RSTRING_T aStringId, const wxString &aString)
Store a "retained string", which is any session and project specific string identified in enum RSTRIN...
Definition: project.cpp:269
virtual const wxString & GetRString(RSTRING_T aStringId)
Return a "retained string", which is any session and project specific string identified in enum RSTRI...
Definition: project.cpp:280
Cache for storing the 3D shapes.
Definition: 3d_cache.h:54
void FlushCache(bool closePlugins=true)
Free all data in the cache and by default closes all plugins.
Definition: 3d_cache.cpp:608
FILENAME_RESOLVER * GetResolver() noexcept
Definition: 3d_cache.cpp:596
T * GetAppSettings()
Returns a handle to the a given settings by type If the settings have already been loaded,...
bool Enable(bool aEnable=true) override
void SetBitmap(const wxBitmapBundle &aBmp)
int GetVisibleWidth(int aCol, bool aHeader=true, bool aContents=true, bool aKeep=false)
Calculates the specified column based on the actual size of the text on screen.
Definition: wx_grid.cpp:691
void ClearRows()
wxWidgets recently added an ASSERT which fires if the position is greater than or equal to the number...
Definition: wx_grid.h:165
bool CommitPendingChanges(bool aQuietMode=false)
Close any open cell edit controls.
Definition: wx_grid.cpp:594
#define _(s)
Declaration of the eda_3d_viewer class.
Functions related to environment variables, including help functions.
KICOMMON_API wxString GetVersionedEnvVarName(const wxString &aBaseName)
Constructs a versioned environment variable based on this KiCad major version.
Definition: env_vars.cpp:74
wxSize GetUnobscuredSize(const wxWindow *aWindow)
Tries to determine the size of the viewport of a scrollable widget (wxDataViewCtrl,...
Definition: wxgtk/ui.cpp:195
PGM_BASE & Pgm()
The global Program "get" accessor.
Definition: pgm_base.cpp:1059
see class PGM_BASE
VECTOR3I res