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