KiCad PCB EDA Suite
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
30#include <bitmaps.h>
33#include <widgets/wx_grid.h>
34#include <footprint.h>
35#include <fp_lib_table.h>
39#include "filename_resolver.h"
40#include <pgm_base.h>
41#include <kiplatform/ui.h>
45#include <wx/defs.h>
46
48{
51 COL_SHOWN = 2
52};
53
55 PCB_BASE_EDIT_FRAME* aFrame, FOOTPRINT* aFootprint, DIALOG_SHIM* aDialogParent,
56 wxWindow* aParent, wxWindowID aId, const wxPoint& aPos, const wxSize& aSize, long aStyle,
57 const wxString& aName ) :
58 PANEL_FP_PROPERTIES_3D_MODEL_BASE( aParent, aId, aPos, aSize, aStyle, aName ),
59 m_parentDialog( aDialogParent ),
60 m_frame( aFrame ),
61 m_footprint( aFootprint ),
62 m_inSelect( false )
63{
64 m_modelsGrid->SetDefaultRowSize( m_modelsGrid->GetDefaultRowSize() + 4 );
65
66 GRID_TRICKS* trick = new GRID_TRICKS( m_modelsGrid, [this]( wxCommandEvent& aEvent )
67 {
68 OnAdd3DRow( aEvent );
69 } );
71
72 m_modelsGrid->PushEventHandler( trick );
73
74 // Get the last 3D directory
75 PCBNEW_SETTINGS* cfg = Pgm().GetSettingsManager().GetAppSettings<PCBNEW_SETTINGS>();
76
77 if( cfg->m_lastFootprint3dDir.IsEmpty() )
79
80 // Icon showing warning/error information
81 wxGridCellAttr* attr = new wxGridCellAttr;
82 attr->SetReadOnly();
83 m_modelsGrid->SetColAttr( COL_PROBLEM, attr );
84
85 // Filename
86 attr = new wxGridCellAttr;
88 wxT( "*.*" ), true, m_frame->Prj().GetProjectPath() ) );
89 m_modelsGrid->SetColAttr( COL_FILENAME, attr );
90
91 // Show checkbox
92 attr = new wxGridCellAttr;
93 attr->SetRenderer( new wxGridCellBoolRenderer() );
94 attr->SetReadOnly(); // not really; we delegate interactivity to GRID_TRICKS
95 attr->SetAlignment( wxALIGN_CENTER, wxALIGN_CENTER );
96 m_modelsGrid->SetColAttr( COL_SHOWN, attr );
97 m_modelsGrid->SetWindowStyleFlag( m_modelsGrid->GetWindowStyle() & ~wxHSCROLL );
98
99 m_frame->Prj().Get3DCacheManager()->GetResolver()->SetProgramBase( &Pgm() );
100
102
103 m_LowerSizer3D->Add( m_previewPane, 1, wxEXPAND, 5 );
104
105 // Configure button logos
109}
110
111
113{
114 // Delete the GRID_TRICKS.
115 m_modelsGrid->PopEventHandler( true );
116
117 // free the memory used by all models, otherwise models which were
118 // browsed but not used would consume memory
119 m_frame->Prj().Get3DCacheManager()->FlushCache( false );
120
121 delete m_previewPane;
122}
123
124
126{
128 return true;
129}
130
132{
133 // Only commit changes in the editor, not the models
134 // The container dialog is responsible for moving the new models into
135 // the footprint inside a commit.
137 return false;
138
139 return true;
140}
141
142
144{
145 wxString default_path;
146 wxGetEnv( KICAD6_3DMODEL_DIR, &default_path );
147
148#ifdef __WINDOWS__
149 default_path.Replace( wxT( "/" ), wxT( "\\" ) );
150#endif
151
152 m_shapes3D_list.clear();
154
155 wxString origPath, alias, shortPath;
156 FILENAME_RESOLVER* res = m_frame->Prj().Get3DCacheManager()->GetResolver();
157
158 for( const FP_3DMODEL& model : m_footprint->Models() )
159 {
160 m_shapes3D_list.push_back( model );
161 origPath = model.m_Filename;
162
163 if( res && res->SplitAlias( origPath, alias, shortPath ) )
164 origPath = alias + wxT( ":" ) + shortPath;
165
166 m_modelsGrid->AppendRows( 1 );
167 int row = m_modelsGrid->GetNumberRows() - 1;
168 m_modelsGrid->SetCellValue( row, COL_FILENAME, origPath );
169 m_modelsGrid->SetCellValue( row, COL_SHOWN, model.m_Show ? wxT( "1" ) : wxT( "0" ) );
170
171 // Must be after the filename is set
173 }
174
175 select3DModel( 0 );
176
178 m_modelsGrid->SetColSize( COL_SHOWN, m_modelsGrid->GetVisibleWidth( COL_SHOWN, true, false ) );
179
180 Layout();
181}
182
183
185{
186 m_inSelect = true;
187
188 aModelIdx = std::max( 0, aModelIdx );
189 aModelIdx = std::min( aModelIdx, m_modelsGrid->GetNumberRows() - 1 );
190
191 if( m_modelsGrid->GetNumberRows() )
192 {
193 m_modelsGrid->SelectRow( aModelIdx );
194 m_modelsGrid->SetGridCursor( aModelIdx, COL_FILENAME );
195 }
196
197 m_previewPane->SetSelectedModel( aModelIdx );
198
199 m_inSelect = false;
200}
201
202
204{
205 if( !m_inSelect )
206 select3DModel( aEvent.GetRow() );
207}
208
209
211{
212 if( aEvent.GetCol() == COL_FILENAME )
213 {
214 bool hasAlias = false;
215 FILENAME_RESOLVER* res = m_frame->Prj().Get3DCacheManager()->GetResolver();
216 wxString filename = m_modelsGrid->GetCellValue( aEvent.GetRow(), COL_FILENAME );
217
218 // Perform cleanup and validation on the filename if it isn't empty
219 if( !filename.empty() )
220 {
221 filename.Replace( wxT( "\n" ), wxT( "" ) );
222 filename.Replace( wxT( "\r" ), wxT( "" ) );
223 filename.Replace( wxT( "\t" ), wxT( "" ) );
224
225 res->ValidateFileName( filename, hasAlias );
226
227 // If the user has specified an alias in the name then prepend ':'
228 if( hasAlias )
229 filename.insert( 0, wxT( ":" ) );
230
231#ifdef __WINDOWS__
232 // In KiCad files, filenames and paths are stored using Unix notation
233 filename.Replace( wxT( "\\" ), wxT( "/" ) );
234#endif
235
236 // Update the grid with the modified filename
237 m_modelsGrid->SetCellValue( aEvent.GetRow(), COL_FILENAME, filename );
238 }
239
240 // Save the filename in the 3D shapes table
241 m_shapes3D_list[ aEvent.GetRow() ].m_Filename = filename;
242
243 // Update the validation status
244 updateValidateStatus( aEvent.GetRow() );
245 }
246 else if( aEvent.GetCol() == COL_SHOWN )
247 {
248 wxString showValue = m_modelsGrid->GetCellValue( aEvent.GetRow(), COL_SHOWN );
249
250 m_shapes3D_list[ aEvent.GetRow() ].m_Show = ( showValue == wxT( "1" ) );
251 }
252
254}
255
256
258{
260 return;
261
262 int idx = m_modelsGrid->GetGridCursorRow();
263
264 if( idx >= 0 && m_modelsGrid->GetNumberRows() && !m_shapes3D_list.empty() )
265 {
266 m_shapes3D_list.erase( m_shapes3D_list.begin() + idx );
267 m_modelsGrid->DeleteRows( idx );
268
269 select3DModel( idx ); // will clamp idx within bounds
271 }
272}
273
274
276{
278 return;
279
280 int selected = m_modelsGrid->GetGridCursorRow();
281
282 PROJECT& prj = m_frame->Prj();
283 FP_3DMODEL model;
284
285 wxString initialpath = prj.GetRString( PROJECT::VIEWER_3D_PATH );
286 wxString sidx = prj.GetRString( PROJECT::VIEWER_3D_FILTER_INDEX );
287 int filter = 0;
288
289 // If the PROJECT::VIEWER_3D_PATH hasn't been set yet, use the KICAD6_3DMODEL_DIR environment
290 // variable and fall back to the project path if necessary.
291 if( initialpath.IsEmpty() )
292 {
293 if( !wxGetEnv( wxT( "KICAD6_3DMODEL_DIR" ), &initialpath ) || initialpath.IsEmpty() )
294 initialpath = prj.GetProjectPath();
295 }
296
297 if( !sidx.empty() )
298 {
299 long tmp;
300 sidx.ToLong( &tmp );
301
302 if( tmp > 0 && tmp <= INT_MAX )
303 filter = (int) tmp;
304 }
305
306 if( !S3D::Select3DModel( this, m_frame->Prj().Get3DCacheManager(), initialpath, filter, &model )
307 || model.m_Filename.empty() )
308 {
309 select3DModel( selected );
310 updateValidateStatus( selected );
311 return;
312 }
313
314 prj.SetRString( PROJECT::VIEWER_3D_PATH, initialpath );
315 sidx = wxString::Format( wxT( "%i" ), filter );
317 FILENAME_RESOLVER* res = m_frame->Prj().Get3DCacheManager()->GetResolver();
318 wxString alias;
319 wxString shortPath;
320 wxString filename = model.m_Filename;
321
322 if( res && res->SplitAlias( filename, alias, shortPath ) )
323 filename = alias + wxT( ":" ) + shortPath;
324
325#ifdef __WINDOWS__
326 // In KiCad files, filenames and paths are stored using Unix notation
327 model.m_Filename.Replace( wxT( "\\" ), wxT( "/" ) );
328#endif
329
330 model.m_Show = true;
331 m_shapes3D_list.push_back( model );
332
333 int idx = m_modelsGrid->GetNumberRows();
334 m_modelsGrid->AppendRows( 1 );
335 m_modelsGrid->SetCellValue( idx, COL_FILENAME, filename );
336 m_modelsGrid->SetCellValue( idx, COL_SHOWN, wxT( "1" ) );
337
338 select3DModel( idx );
340
342}
343
344
346{
348 return;
349
350 FP_3DMODEL model;
351
352 model.m_Show = true;
353 m_shapes3D_list.push_back( model );
354
355 int row = m_modelsGrid->GetNumberRows();
356 m_modelsGrid->AppendRows( 1 );
357 m_modelsGrid->SetCellValue( row, COL_SHOWN, wxT( "1" ) );
358 m_modelsGrid->SetCellValue( row, COL_PROBLEM, "" );
359
360 select3DModel( row );
361
362 m_modelsGrid->SetFocus();
363 m_modelsGrid->MakeCellVisible( row, COL_FILENAME );
364 m_modelsGrid->SetGridCursor( row, COL_FILENAME );
365
366 m_modelsGrid->EnableCellEditControl( true );
367 m_modelsGrid->ShowCellEditControl();
368
370}
371
372
374{
375 int icon = 0;
376 wxString errStr;
377
378 switch( validateModelExists( m_modelsGrid->GetCellValue( aRow, COL_FILENAME) ) )
379 {
381 icon = 0;
382 errStr = "";
383 break;
384
386 icon = wxICON_WARNING;
387 errStr = _( "No filename entered" );
388 break;
389
391 icon = wxICON_ERROR;
392 errStr = _( "Illegal filename" );
393 break;
394
396 icon = wxICON_ERROR;
397 errStr = _( "File not found" );
398 break;
399
401 icon = wxICON_ERROR;
402 errStr = _( "Unable to open file" );
403 break;
404
405 default:
406 icon = wxICON_ERROR;
407 errStr = _( "Unknown error" );
408 break;
409 }
410
411 m_modelsGrid->SetCellValue( aRow, COL_PROBLEM, errStr );
412 m_modelsGrid->SetCellRenderer( aRow, COL_PROBLEM,
413 new GRID_CELL_STATUS_ICON_RENDERER( icon ) );
414}
415
416
418{
419 if( aFilename.empty() )
421
422 bool hasAlias = false;
423 FILENAME_RESOLVER* resolv = m_frame->Prj().Get3DFilenameResolver();
424
425 if( !resolv )
427
428 if( !resolv->ValidateFileName( aFilename, hasAlias ) )
430
431 wxString libraryName = m_footprint->GetFPID().GetLibNickname();
432 const FP_LIB_TABLE_ROW* fpRow = nullptr;
433 try
434 {
435 fpRow = m_frame->Prj().PcbFootprintLibs()->FindRow( libraryName, false );
436 }
437 catch( ... )
438 {
439 // if libraryName is not found in table, do nothing
440 }
441
442 wxString footprintBasePath = wxEmptyString;
443
444 if( fpRow )
445 footprintBasePath = fpRow->GetFullURI( true );
446
447 wxString fullPath = resolv->ResolvePath( aFilename, footprintBasePath );
448
449 if( fullPath.IsEmpty() )
451
452 if( !wxFileName::IsFileReadable( fullPath ) )
454
456}
457
458
459void PANEL_FP_PROPERTIES_3D_MODEL::Cfg3DPath( wxCommandEvent& event )
460{
461 if( S3D::Configure3DPaths( this, m_frame->Prj().Get3DCacheManager()->GetResolver() ) )
463}
464
465
467{
468 // Account for scroll bars
469 int modelsWidth = KIPLATFORM::UI::GetUnobscuredSize( m_modelsGrid ).x;
470
471 int width = modelsWidth - m_modelsGrid->GetColSize( COL_SHOWN )
472 - m_modelsGrid->GetColSize( COL_PROBLEM );
473
474 if( width > 0 )
475 m_modelsGrid->SetColSize( COL_FILENAME, width );
476}
477
478
480{
482
483 event.Skip();
484}
485
486
487void PANEL_FP_PROPERTIES_3D_MODEL::OnUpdateUI( wxUpdateUIEvent& event )
488{
489 m_button3DShapeRemove->Enable( m_modelsGrid->GetNumberRows() > 0 );
490}
wxBitmap KiBitmap(BITMAPS aBitmap, int aHeightTag)
Construct a wxBitmap from an image identifier Returns the image from the active theme if the image ha...
Definition: bitmap.cpp:105
@ small_folder
Dialog helper object to sit in the inheritance tree between wxDialog and any class written by wxFormB...
Definition: dialog_shim.h:83
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.
bool SplitAlias(const wxString &aFileName, wxString &anAlias, wxString &aRelPath) const
Return true if the given name contains an alias and populates the string anAlias with the alias and a...
wxString ResolvePath(const wxString &aFileName, const wxString &aWorkingPath)
Determines the full path of the given file name.
const LIB_ID & GetFPID() const
Definition: footprint.h:207
std::vector< FP_3DMODEL > & Models()
Definition: footprint.h:188
wxString m_Filename
The 3D shape filename in 3D library.
Definition: footprint.h:99
bool m_Show
Include model in rendering.
Definition: footprint.h:100
Hold a record identifying a library accessed by the appropriate footprint library PLUGIN object in th...
Definition: fp_lib_table.h:41
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
void SetReadOnly(bool aReadOnly)
Definition: json_settings.h:85
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...
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.
Container for project specific data.
Definition: project.h:63
@ VIEWER_3D_FILTER_INDEX
Definition: project.h:170
@ VIEWER_3D_PATH
Definition: project.h:169
virtual const wxString GetProjectPath() const
Return the full path of the project.
Definition: project.cpp:125
virtual FP_LIB_TABLE * PcbFootprintLibs(KIWAY &aKiway)
Return the table of footprint libraries.
Definition: project.cpp:318
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:253
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:264
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:282
void ClearRows()
wxWidgets recently added an ASSERT which fires if the position is greater than or equal to the number...
Definition: wx_grid.h:109
bool CommitPendingChanges(bool aQuietMode=false)
Close any open cell edit controls.
Definition: wx_grid.cpp:226
#define _(s)
#define KICAD6_3DMODEL_DIR
A variable name whose value holds the path of 3D shape files.
wxSize GetUnobscuredSize(const wxWindow *aWindow)
Tries to determine the size of the viewport of a scrollable widget (wxDataViewCtrl,...
Definition: gtk/ui.cpp:126
bool Configure3DPaths(wxWindow *aParent, FILENAME_RESOLVER *aResolver)
bool Select3DModel(wxWindow *aParent, S3D_CACHE *aCache, wxString &prevModelSelectDir, int &prevModelWildcard, FP_3DMODEL *aModel)
see class PGM_BASE
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Definition: single_top.cpp:111