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
30#include <bitmaps.h>
33#include <widgets/wx_grid.h>
35#include <footprint.h>
36#include <fp_lib_table.h>
40#include "filename_resolver.h"
41#include <pgm_base.h>
42#include <kiplatform/ui.h>
46#include <wx/defs.h>
47
49{
52 COL_SHOWN = 2
53};
54
56 PCB_BASE_EDIT_FRAME* aFrame, FOOTPRINT* aFootprint, DIALOG_SHIM* aDialogParent,
57 wxWindow* aParent, wxWindowID aId, const wxPoint& aPos, const wxSize& aSize, long aStyle,
58 const wxString& aName ) :
59 PANEL_FP_PROPERTIES_3D_MODEL_BASE( aParent, aId, aPos, aSize, aStyle, aName ),
60 m_parentDialog( aDialogParent ),
61 m_frame( aFrame ),
62 m_footprint( aFootprint ),
63 m_inSelect( false )
64{
65 m_modelsGrid->SetDefaultRowSize( m_modelsGrid->GetDefaultRowSize() + 4 );
66
67 GRID_TRICKS* trick = new GRID_TRICKS( m_modelsGrid, [this]( wxCommandEvent& aEvent )
68 {
69 OnAdd3DRow( aEvent );
70 } );
72
73 m_modelsGrid->PushEventHandler( trick );
74
75 // Get the last 3D directory
76 PCBNEW_SETTINGS* cfg = Pgm().GetSettingsManager().GetAppSettings<PCBNEW_SETTINGS>();
77
78 if( cfg->m_lastFootprint3dDir.IsEmpty() )
80
81 // Icon showing warning/error information
82 wxGridCellAttr* attr = new wxGridCellAttr;
83 attr->SetReadOnly();
84 m_modelsGrid->SetColAttr( COL_PROBLEM, attr );
85
86 // Filename
87 attr = new wxGridCellAttr;
89 wxT( "*.*" ), true, m_frame->Prj().GetProjectPath() ) );
90 m_modelsGrid->SetColAttr( COL_FILENAME, attr );
91
92 // Show checkbox
93 attr = new wxGridCellAttr;
94 attr->SetRenderer( new wxGridCellBoolRenderer() );
95 attr->SetReadOnly(); // not really; we delegate interactivity to GRID_TRICKS
96 attr->SetAlignment( wxALIGN_CENTER, wxALIGN_CENTER );
97 m_modelsGrid->SetColAttr( COL_SHOWN, attr );
98 m_modelsGrid->SetWindowStyleFlag( m_modelsGrid->GetWindowStyle() & ~wxHSCROLL );
99
100 m_frame->Prj().Get3DCacheManager()->GetResolver()->SetProgramBase( &Pgm() );
101
103
104 m_LowerSizer3D->Add( m_previewPane, 1, wxEXPAND, 5 );
105
106 // Configure button logos
107 m_button3DShapeAdd->SetBitmap( KiBitmap( BITMAPS::small_plus ) );
108 m_button3DShapeBrowse->SetBitmap( KiBitmap( BITMAPS::small_folder ) );
109 m_button3DShapeRemove->SetBitmap( KiBitmap( BITMAPS::small_trash ) );
110}
111
112
114{
115 // Delete the GRID_TRICKS.
116 m_modelsGrid->PopEventHandler( true );
117
118 // free the memory used by all models, otherwise models which were
119 // browsed but not used would consume memory
120 m_frame->Prj().Get3DCacheManager()->FlushCache( false );
121
122 delete m_previewPane;
123}
124
125
127{
129 return true;
130}
131
133{
134 // Only commit changes in the editor, not the models
135 // The container dialog is responsible for moving the new models into
136 // the footprint inside a commit.
138 return false;
139
140 return true;
141}
142
143
145{
146 wxString default_path;
147 wxGetEnv( KICAD7_3DMODEL_DIR, &default_path );
148
149#ifdef __WINDOWS__
150 default_path.Replace( wxT( "/" ), wxT( "\\" ) );
151#endif
152
153 m_shapes3D_list.clear();
155
156 wxString origPath, alias, shortPath;
157 FILENAME_RESOLVER* res = m_frame->Prj().Get3DCacheManager()->GetResolver();
158
159 for( const FP_3DMODEL& model : m_footprint->Models() )
160 {
161 m_shapes3D_list.push_back( model );
162 origPath = model.m_Filename;
163
164 if( res && res->SplitAlias( origPath, alias, shortPath ) )
165 origPath = alias + wxT( ":" ) + shortPath;
166
167 m_modelsGrid->AppendRows( 1 );
168 int row = m_modelsGrid->GetNumberRows() - 1;
169 m_modelsGrid->SetCellValue( row, COL_FILENAME, origPath );
170 m_modelsGrid->SetCellValue( row, COL_SHOWN, model.m_Show ? wxT( "1" ) : wxT( "0" ) );
171
172 // Must be after the filename is set
174 }
175
176 select3DModel( 0 );
177
179 m_modelsGrid->SetColSize( COL_SHOWN, m_modelsGrid->GetVisibleWidth( COL_SHOWN, true, false ) );
180
181 Layout();
182}
183
184
186{
187 m_inSelect = true;
188
189 aModelIdx = std::max( 0, aModelIdx );
190 aModelIdx = std::min( aModelIdx, m_modelsGrid->GetNumberRows() - 1 );
191
192 if( m_modelsGrid->GetNumberRows() )
193 {
194 m_modelsGrid->SelectRow( aModelIdx );
195 m_modelsGrid->SetGridCursor( aModelIdx, COL_FILENAME );
196 }
197
198 m_previewPane->SetSelectedModel( aModelIdx );
199
200 m_inSelect = false;
201}
202
203
205{
206 if( !m_inSelect )
207 select3DModel( aEvent.GetRow() );
208}
209
210
212{
213 if( aEvent.GetCol() == COL_FILENAME )
214 {
215 bool hasAlias = false;
216 FILENAME_RESOLVER* res = m_frame->Prj().Get3DCacheManager()->GetResolver();
217 wxString filename = m_modelsGrid->GetCellValue( aEvent.GetRow(), COL_FILENAME );
218
219 // Perform cleanup and validation on the filename if it isn't empty
220 if( !filename.empty() )
221 {
222 filename.Replace( wxT( "\n" ), wxT( "" ) );
223 filename.Replace( wxT( "\r" ), wxT( "" ) );
224 filename.Replace( wxT( "\t" ), wxT( "" ) );
225
226 res->ValidateFileName( filename, hasAlias );
227
228 // If the user has specified an alias in the name then prepend ':'
229 if( hasAlias )
230 filename.insert( 0, wxT( ":" ) );
231
232#ifdef __WINDOWS__
233 // In KiCad files, filenames and paths are stored using Unix notation
234 filename.Replace( wxT( "\\" ), wxT( "/" ) );
235#endif
236
237 // Update the grid with the modified filename
238 m_modelsGrid->SetCellValue( aEvent.GetRow(), COL_FILENAME, filename );
239 }
240
241 // Save the filename in the 3D shapes table
242 m_shapes3D_list[ aEvent.GetRow() ].m_Filename = filename;
243
244 // Update the validation status
245 updateValidateStatus( aEvent.GetRow() );
246 }
247 else if( aEvent.GetCol() == COL_SHOWN )
248 {
249 wxString showValue = m_modelsGrid->GetCellValue( aEvent.GetRow(), COL_SHOWN );
250
251 m_shapes3D_list[ aEvent.GetRow() ].m_Show = ( showValue == wxT( "1" ) );
252 }
253
255}
256
257
259{
261 return;
262
263 int idx = m_modelsGrid->GetGridCursorRow();
264
265 if( idx >= 0 && m_modelsGrid->GetNumberRows() && !m_shapes3D_list.empty() )
266 {
267 // Don't allow selection until we call select3DModel(), below. Otherwise wxWidgets
268 // has a tendency to get its knickers in a knot....
269 m_inSelect = true;
270
271 m_shapes3D_list.erase( m_shapes3D_list.begin() + idx );
272 m_modelsGrid->DeleteRows( idx );
273
274 select3DModel( idx ); // will clamp idx within bounds
276 }
277}
278
279
281{
283 return;
284
285 int selected = m_modelsGrid->GetGridCursorRow();
286
287 PROJECT& prj = m_frame->Prj();
288 FP_3DMODEL model;
289
290 wxString initialpath = prj.GetRString( PROJECT::VIEWER_3D_PATH );
291 wxString sidx = prj.GetRString( PROJECT::VIEWER_3D_FILTER_INDEX );
292 int filter = 0;
293
294 // If the PROJECT::VIEWER_3D_PATH hasn't been set yet, use the KICAD7_3DMODEL_DIR environment
295 // variable and fall back to the project path if necessary.
296 if( initialpath.IsEmpty() )
297 {
298 if( !wxGetEnv( wxT( "KICAD7_3DMODEL_DIR" ), &initialpath ) || initialpath.IsEmpty() )
299 initialpath = prj.GetProjectPath();
300 }
301
302 if( !sidx.empty() )
303 {
304 long tmp;
305 sidx.ToLong( &tmp );
306
307 if( tmp > 0 && tmp <= INT_MAX )
308 filter = (int) tmp;
309 }
310
311 if( !S3D::Select3DModel( m_parentDialog, m_frame->Prj().Get3DCacheManager(), initialpath, filter, &model )
312 || model.m_Filename.empty() )
313 {
314 select3DModel( selected );
315 updateValidateStatus( selected );
316 return;
317 }
318
319 prj.SetRString( PROJECT::VIEWER_3D_PATH, initialpath );
320 sidx = wxString::Format( wxT( "%i" ), filter );
322 FILENAME_RESOLVER* res = m_frame->Prj().Get3DCacheManager()->GetResolver();
323 wxString alias;
324 wxString shortPath;
325 wxString filename = model.m_Filename;
326
327 if( res && res->SplitAlias( filename, alias, shortPath ) )
328 filename = alias + wxT( ":" ) + shortPath;
329
330#ifdef __WINDOWS__
331 // In KiCad files, filenames and paths are stored using Unix notation
332 model.m_Filename.Replace( wxT( "\\" ), wxT( "/" ) );
333#endif
334
335 model.m_Show = true;
336 m_shapes3D_list.push_back( model );
337
338 int idx = m_modelsGrid->GetNumberRows();
339 m_modelsGrid->AppendRows( 1 );
340 m_modelsGrid->SetCellValue( idx, COL_FILENAME, filename );
341 m_modelsGrid->SetCellValue( idx, COL_SHOWN, wxT( "1" ) );
342
343 select3DModel( idx );
345
347}
348
349
351{
353 return;
354
355 FP_3DMODEL model;
356
357 model.m_Show = true;
358 m_shapes3D_list.push_back( model );
359
360 int row = m_modelsGrid->GetNumberRows();
361 m_modelsGrid->AppendRows( 1 );
362 m_modelsGrid->SetCellValue( row, COL_SHOWN, wxT( "1" ) );
363 m_modelsGrid->SetCellValue( row, COL_PROBLEM, "" );
364
365 select3DModel( row );
366
367 m_modelsGrid->SetFocus();
368 m_modelsGrid->MakeCellVisible( row, COL_FILENAME );
369 m_modelsGrid->SetGridCursor( row, COL_FILENAME );
370
371 m_modelsGrid->EnableCellEditControl( true );
372 m_modelsGrid->ShowCellEditControl();
373
375}
376
377
379{
380 int icon = 0;
381 wxString errStr;
382
383 switch( validateModelExists( m_modelsGrid->GetCellValue( aRow, COL_FILENAME) ) )
384 {
385 case MODEL_VALIDATE_ERRORS::MODEL_NO_ERROR:
386 icon = 0;
387 errStr = "";
388 break;
389
390 case MODEL_VALIDATE_ERRORS::NO_FILENAME:
391 icon = wxICON_WARNING;
392 errStr = _( "No filename entered" );
393 break;
394
395 case MODEL_VALIDATE_ERRORS::ILLEGAL_FILENAME:
396 icon = wxICON_ERROR;
397 errStr = _( "Illegal filename" );
398 break;
399
400 case MODEL_VALIDATE_ERRORS::RESOLVE_FAIL:
401 icon = wxICON_ERROR;
402 errStr = _( "File not found" );
403 break;
404
405 case MODEL_VALIDATE_ERRORS::OPEN_FAIL:
406 icon = wxICON_ERROR;
407 errStr = _( "Unable to open file" );
408 break;
409
410 default:
411 icon = wxICON_ERROR;
412 errStr = _( "Unknown error" );
413 break;
414 }
415
416 m_modelsGrid->SetCellValue( aRow, COL_PROBLEM, errStr );
417 m_modelsGrid->SetCellRenderer( aRow, COL_PROBLEM,
418 new GRID_CELL_STATUS_ICON_RENDERER( icon ) );
419}
420
421
423{
424 if( aFilename.empty() )
425 return MODEL_VALIDATE_ERRORS::NO_FILENAME;
426
427 bool hasAlias = false;
428 FILENAME_RESOLVER* resolv = m_frame->Prj().Get3DFilenameResolver();
429
430 if( !resolv )
431 return MODEL_VALIDATE_ERRORS::RESOLVE_FAIL;
432
433 if( !resolv->ValidateFileName( aFilename, hasAlias ) )
434 return MODEL_VALIDATE_ERRORS::ILLEGAL_FILENAME;
435
436 wxString libraryName = m_footprint->GetFPID().GetLibNickname();
437 const FP_LIB_TABLE_ROW* fpRow = nullptr;
438 try
439 {
440 fpRow = m_frame->Prj().PcbFootprintLibs()->FindRow( libraryName, false );
441 }
442 catch( ... )
443 {
444 // if libraryName is not found in table, do nothing
445 }
446
447 wxString footprintBasePath = wxEmptyString;
448
449 if( fpRow )
450 footprintBasePath = fpRow->GetFullURI( true );
451
452 wxString fullPath = resolv->ResolvePath( aFilename, footprintBasePath );
453
454 if( fullPath.IsEmpty() )
455 return MODEL_VALIDATE_ERRORS::RESOLVE_FAIL;
456
457 if( !wxFileName::IsFileReadable( fullPath ) )
458 return MODEL_VALIDATE_ERRORS::OPEN_FAIL;
459
460 return MODEL_VALIDATE_ERRORS::MODEL_NO_ERROR;
461}
462
463
464void PANEL_FP_PROPERTIES_3D_MODEL::Cfg3DPath( wxCommandEvent& event )
465{
466 if( S3D::Configure3DPaths( this, m_frame->Prj().Get3DCacheManager()->GetResolver() ) )
468}
469
470
472{
473 // Account for scroll bars
474 int modelsWidth = KIPLATFORM::UI::GetUnobscuredSize( m_modelsGrid ).x;
475
476 int width = modelsWidth - m_modelsGrid->GetColSize( COL_SHOWN )
477 - m_modelsGrid->GetColSize( COL_PROBLEM );
478
479 if( width > 0 )
480 m_modelsGrid->SetColSize( COL_FILENAME, width );
481}
482
483
485{
487
488 event.Skip();
489}
490
491
492void PANEL_FP_PROPERTIES_3D_MODEL::OnUpdateUI( wxUpdateUIEvent& event )
493{
494 m_button3DShapeRemove->Enable( m_modelsGrid->GetNumberRows() > 0 );
495}
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:106
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.
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:214
std::vector< FP_3DMODEL > & Models()
Definition: footprint.h:186
wxString m_Filename
The 3D shape filename in 3D library.
Definition: footprint.h:97
bool m_Show
Include model in rendering.
Definition: footprint.h:98
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:64
@ VIEWER_3D_FILTER_INDEX
Definition: project.h:171
@ VIEWER_3D_PATH
Definition: project.h:170
virtual const wxString GetProjectPath() const
Return the full path of the project.
Definition: project.cpp:126
virtual FP_LIB_TABLE * PcbFootprintLibs(KIWAY &aKiway)
Return the table of footprint libraries.
Definition: project.cpp:324
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:254
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:265
void SetBitmap(const wxBitmap &aBmp)
bool Enable(bool aEnable=true) override
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:571
void ClearRows()
wxWidgets recently added an ASSERT which fires if the position is greater than or equal to the number...
Definition: wx_grid.h:147
bool CommitPendingChanges(bool aQuietMode=false)
Close any open cell edit controls.
Definition: wx_grid.cpp:474
#define _(s)
Declaration of the eda_3d_viewer class.
#define KICAD7_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:172
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
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Definition: single_top.cpp:115
VECTOR3I res