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, dick@softplc.com
6  * Copyright (C) 2008 Wayne Stambaugh <stambaughw@gmail.com>
7  * Copyright (C) 2004-2021 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 
29 #include <confirm.h>
31 #include <board_design_settings.h>
32 #include <bitmaps.h>
35 #include <widgets/wx_grid.h>
36 #include <footprint.h>
37 #include <footprint_edit_frame.h>
40 #include "filename_resolver.h"
41 #include <pgm_base.h>
45 #include <kiway_holder.h>
46 
48 {
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 );
67  trick->SetTooltipEnable( COL_PROBLEM );
68 
69  m_modelsGrid->PushEventHandler( trick );
70 
71  // Get the last 3D directory
72  PCBNEW_SETTINGS* cfg = Pgm().GetSettingsManager().GetAppSettings<PCBNEW_SETTINGS>();
73 
74  if( cfg->m_lastFootprint3dDir.IsEmpty() )
75  wxGetEnv( KICAD6_3DMODEL_DIR, &cfg->m_lastFootprint3dDir );
76 
77  // Icon showing warning/error information
78  wxGridCellAttr* attr = new wxGridCellAttr;
79  attr->SetReadOnly();
80  m_modelsGrid->SetColAttr( COL_PROBLEM, attr );
81 
82  // Filename
83  attr = new wxGridCellAttr;
85  "*.*", true, m_frame->Prj().GetProjectPath() ) );
86  m_modelsGrid->SetColAttr( COL_FILENAME, attr );
87 
88  // Show checkbox
89  attr = new wxGridCellAttr;
90  attr->SetRenderer( new wxGridCellBoolRenderer() );
91  attr->SetReadOnly(); // not really; we delegate interactivity to GRID_TRICKS
92  attr->SetAlignment( wxALIGN_CENTER, wxALIGN_CENTER );
93  m_modelsGrid->SetColAttr( COL_SHOWN, attr );
94  m_modelsGrid->SetWindowStyleFlag( m_modelsGrid->GetWindowStyle() & ~wxHSCROLL );
95 
96  m_frame->Prj().Get3DCacheManager()->GetResolver()->SetProgramBase( &Pgm() );
97 
99 
100  bLowerSizer3D->Add( m_previewPane, 1, wxEXPAND, 5 );
101 
102  // Configure button logos
106 }
107 
108 
110 {
111  // Delete the GRID_TRICKS.
112  m_modelsGrid->PopEventHandler( true );
113 
114  // free the memory used by all models, otherwise models which were
115  // browsed but not used would consume memory
116  m_frame->Prj().Get3DCacheManager()->FlushCache( false );
117 
118  delete m_previewPane;
119 }
120 
121 
123 {
125  return true;
126 }
127 
129 {
130  // Only commit changes in the editor, not the models
131  // The container dialog is responsible for moving the new models into
132  // the footprint inside a commit.
134  return false;
135 
136  return true;
137 }
138 
139 
141 {
142  wxString default_path;
143  wxGetEnv( KICAD6_3DMODEL_DIR, &default_path );
144 
145 #ifdef __WINDOWS__
146  default_path.Replace( wxT( "/" ), wxT( "\\" ) );
147 #endif
148 
149  m_shapes3D_list.clear();
151 
152  wxString origPath, alias, shortPath;
153  FILENAME_RESOLVER* res = m_frame->Prj().Get3DCacheManager()->GetResolver();
154 
155  for( const FP_3DMODEL& model : m_footprint->Models() )
156  {
157  m_shapes3D_list.push_back( model );
158  origPath = model.m_Filename;
159 
160  if( res && res->SplitAlias( origPath, alias, shortPath ) )
161  origPath = alias + wxT( ":" ) + shortPath;
162 
163  m_modelsGrid->AppendRows( 1 );
164  int row = m_modelsGrid->GetNumberRows() - 1;
165  m_modelsGrid->SetCellValue( row, COL_FILENAME, origPath );
166  m_modelsGrid->SetCellValue( row, COL_SHOWN, model.m_Show ? wxT( "1" ) : wxT( "0" ) );
167 
168  // Must be after the filename is set
169  updateValidateStatus( row );
170  }
171 
172  select3DModel( 0 );
173 
175  m_modelsGrid->SetColSize( COL_SHOWN, m_modelsGrid->GetVisibleWidth( COL_SHOWN, true, false, false ) );
176 
177  Layout();
178 }
179 
180 
182 {
183  m_inSelect = true;
184 
185  aModelIdx = std::max( 0, aModelIdx );
186  aModelIdx = std::min( aModelIdx, m_modelsGrid->GetNumberRows() - 1 );
187 
188  if( m_modelsGrid->GetNumberRows() )
189  {
190  m_modelsGrid->SelectRow( aModelIdx );
191  m_modelsGrid->SetGridCursor( aModelIdx, COL_FILENAME );
192  }
193 
194  m_previewPane->SetSelectedModel( aModelIdx );
195 
196  m_inSelect = false;
197 }
198 
199 
201 {
202  if( !m_inSelect )
203  select3DModel( aEvent.GetRow() );
204 }
205 
206 
208 {
209  if( aEvent.GetCol() == COL_FILENAME )
210  {
211  bool hasAlias = false;
212  FILENAME_RESOLVER* res = m_frame->Prj().Get3DCacheManager()->GetResolver();
213  wxString filename = m_modelsGrid->GetCellValue( aEvent.GetRow(), COL_FILENAME );
214 
215  filename.Replace( "\n", "" );
216  filename.Replace( "\r", "" );
217  filename.Replace( "\t", "" );
218 
219  // The user is warned about failed validation through the updateValidateStatus call below
220  if( filename.empty() || !res->ValidateFileName( filename, hasAlias ) )
221  {
222  wxMessageBox( _( "Error: illegal or empty filename." ) );
223  aEvent.Veto();
224  return;
225  }
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  m_shapes3D_list[ aEvent.GetRow() ].m_Filename = filename;
237  m_modelsGrid->SetCellValue( aEvent.GetRow(), COL_FILENAME, filename );
238 
239  updateValidateStatus( aEvent.GetRow() );
240  }
241  else if( aEvent.GetCol() == COL_SHOWN )
242  {
243  wxString showValue = m_modelsGrid->GetCellValue( aEvent.GetRow(), COL_SHOWN );
244 
245  m_shapes3D_list[ aEvent.GetRow() ].m_Show = ( showValue == wxT( "1" ) );
246  }
247 
249 }
250 
251 
253 {
255  return;
256 
257  int idx = m_modelsGrid->GetGridCursorRow();
258 
259  if( idx >= 0 && m_modelsGrid->GetNumberRows() && !m_shapes3D_list.empty() )
260  {
261  m_shapes3D_list.erase( m_shapes3D_list.begin() + idx );
262  m_modelsGrid->DeleteRows( idx );
263 
264  select3DModel( idx ); // will clamp idx within bounds
266  }
267 }
268 
269 
271 {
273  return;
274 
275  int selected = m_modelsGrid->GetGridCursorRow();
276 
277  PROJECT& prj = m_frame->Prj();
278  FP_3DMODEL model;
279 
280  wxString initialpath = prj.GetRString( PROJECT::VIEWER_3D_PATH );
281  wxString sidx = prj.GetRString( PROJECT::VIEWER_3D_FILTER_INDEX );
282  int filter = 0;
283 
284  // If the PROJECT::VIEWER_3D_PATH hasn't been set yet, use the KICAD6_3DMODEL_DIR environment
285  // variable and fall back to the project path if necessary.
286  if( initialpath.IsEmpty() )
287  {
288  if( !wxGetEnv( "KICAD6_3DMODEL_DIR", &initialpath ) || initialpath.IsEmpty() )
289  initialpath = prj.GetProjectPath();
290  }
291 
292  if( !sidx.empty() )
293  {
294  long tmp;
295  sidx.ToLong( &tmp );
296 
297  if( tmp > 0 && tmp <= INT_MAX )
298  filter = (int) tmp;
299  }
300 
301  if( !S3D::Select3DModel( this, m_frame->Prj().Get3DCacheManager(), initialpath, filter, &model )
302  || model.m_Filename.empty() )
303  {
304  select3DModel( selected );
305  return;
306  }
307 
308  prj.SetRString( PROJECT::VIEWER_3D_PATH, initialpath );
309  sidx = wxString::Format( wxT( "%i" ), filter );
311  FILENAME_RESOLVER* res = m_frame->Prj().Get3DCacheManager()->GetResolver();
312  wxString alias;
313  wxString shortPath;
314  wxString filename = model.m_Filename;
315 
316  if( res && res->SplitAlias( filename, alias, shortPath ) )
317  filename = alias + wxT( ":" ) + shortPath;
318 
319 #ifdef __WINDOWS__
320  // In KiCad files, filenames and paths are stored using Unix notation
321  model.m_Filename.Replace( "\\", "/" );
322 #endif
323 
324  model.m_Show = true;
325  m_shapes3D_list.push_back( model );
326 
327  int idx = m_modelsGrid->GetNumberRows();
328  m_modelsGrid->AppendRows( 1 );
329  m_modelsGrid->SetCellValue( idx, COL_FILENAME, filename );
330  m_modelsGrid->SetCellValue( idx, COL_SHOWN, wxT( "1" ) );
331 
332  updateValidateStatus( idx );
333 
334  select3DModel( idx );
336 }
337 
338 
340 {
342  return;
343 
344  FP_3DMODEL model;
345 
346  model.m_Show = true;
347  m_shapes3D_list.push_back( model );
348 
349  int row = m_modelsGrid->GetNumberRows();
350  m_modelsGrid->AppendRows( 1 );
351  m_modelsGrid->SetCellValue( row, COL_SHOWN, wxT( "1" ) );
352  m_modelsGrid->SetCellValue( row, COL_PROBLEM, "" );
353 
354  select3DModel( row );
355 
356  m_modelsGrid->SetFocus();
357  m_modelsGrid->MakeCellVisible( row, COL_FILENAME );
358  m_modelsGrid->SetGridCursor( row, COL_FILENAME );
359 
360  m_modelsGrid->EnableCellEditControl( true );
361  m_modelsGrid->ShowCellEditControl();
362 }
363 
364 
366 {
367  int icon = 0;
368  wxString errStr;
369 
370  switch( validateModelExists( m_modelsGrid->GetCellValue( aRow, COL_FILENAME) ) )
371  {
373  icon = 0;
374  errStr = "";
375  break;
376 
378  icon = wxICON_ERROR;
379  errStr = _( "File not found" );
380  break;
381 
383  icon = wxICON_ERROR;
384  errStr = _( "Unable to open file" );
385  break;
386 
387  default:
388  icon = wxICON_ERROR;
389  errStr = _( "Unknown error" );
390  break;
391  }
392 
393  m_modelsGrid->SetCellValue( aRow, COL_PROBLEM, errStr );
394  m_modelsGrid->SetCellRenderer( aRow, COL_PROBLEM,
395  new GRID_CELL_STATUS_ICON_RENDERER( icon ) );
396 }
397 
398 
400 {
401  FILENAME_RESOLVER* resolv = m_frame->Prj().Get3DFilenameResolver();
402 
403  if( !resolv )
405 
406  wxString fullPath = resolv->ResolvePath( aFilename );
407 
408  if( fullPath.IsEmpty() )
410 
411  if( wxFileName::IsFileReadable( fullPath ) )
413  else
415 }
416 
417 
418 void PANEL_FP_PROPERTIES_3D_MODEL::Cfg3DPath( wxCommandEvent& event )
419 {
420  if( S3D::Configure3DPaths( this, m_frame->Prj().Get3DCacheManager()->GetResolver() ) )
422 }
423 
424 
426 {
427  // Account for scroll bars
428  int modelsWidth = aWidth - ( m_modelsGrid->GetSize().x - m_modelsGrid->GetClientSize().x );
429 
430  int width = modelsWidth - m_modelsGrid->GetColSize( COL_SHOWN )
431  - m_modelsGrid->GetColSize( COL_PROBLEM ) - 5;
432 
433  m_modelsGrid->SetColSize( COL_FILENAME, width );
434 }
435 
436 
437 void PANEL_FP_PROPERTIES_3D_MODEL::OnUpdateUI( wxUpdateUIEvent& event )
438 {
439  m_button3DShapeRemove->Enable( m_modelsGrid->GetNumberRows() > 0 );
440 }
void SetSelectedModel(int idx)
Set the currently selected index in the model list so that the scale/rotation/offset controls can be ...
void On3DModelSelected(wxGridEvent &) override
Container for project specific data.
Definition: project.h:62
std::list< FP_3DMODEL > & Models()
Definition: footprint.h:182
void OnUpdateUI(wxUpdateUIEvent &event) 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...
This file is part of the common library.
void SetReadOnly(bool aReadOnly)
Definition: json_settings.h:83
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...
bool Select3DModel(wxWindow *aParent, S3D_CACHE *aCache, wxString &prevModelSelectDir, int &prevModelWildcard, FP_3DMODEL *aModel)
Add mouse and command handling (such as cut, copy, and paste) to a WX_GRID instance.
Definition: grid_tricks.h:55
MODEL_VALIDATE_ERRORS validateModelExists(const wxString &aFilename)
std::vector< FP_3DMODEL > m_shapes3D_list
Dialog helper object to sit in the inheritance tree between wxDialog and any class written by wxFormB...
Definition: dialog_shim.h:82
void OnAdd3DModel(wxCommandEvent &event) override
void On3DModelCellChanged(wxGridEvent &aEvent) override
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Definition: single_top.cpp:106
virtual const wxString GetProjectPath() const
Return the full path of the project.
Definition: project.cpp:122
int GetVisibleWidth(int aCol, bool aHeader=true, bool aContents=false, bool aKeep=true)
Calculates the specified column based on the actual size of the text on screen.
Definition: wx_grid.cpp:246
bool ValidateFileName(const wxString &aFileName, bool &hasAlias) const
Returns true if the given path is a valid aliased relative path.
void OnAdd3DRow(wxCommandEvent &event) override
wxString m_lastFootprint3dDir
bool m_Show
Include model in rendering.
Definition: footprint.h:98
void OnRemove3DModel(wxCommandEvent &event) override
PROJECT & Prj() const
Return a reference to the PROJECT associated with this KIWAY.
#define KICAD6_3DMODEL_DIR
A variable name whose value holds the path of 3D shape files.
#define _(s)
Editor for wxGrid cells that adds a file/folder browser to the grid input field.
bool CommitPendingChanges(bool aQuietMode=false)
Close any open cell edit controls.
Definition: wx_grid.cpp:190
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
void Cfg3DPath(wxCommandEvent &event) override
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:212
wxString ResolvePath(const wxString &aFileName)
Determines the full path of the given file name.
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
wxString m_Filename
The 3D shape filename in 3D library.
Definition: footprint.h:97
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:227
bool Configure3DPaths(wxWindow *aParent, FILENAME_RESOLVER *aResolver)
see class PGM_BASE
Provide an extensible class to resolve 3D model paths.
Common, abstract interface for edit frames.
void ClearRows()
wxWidgets recently added an ASSERT which fires if the position is greater than or equal to the number...
Definition: wx_grid.h:106
Class PANEL_FP_PROPERTIES_3D_MODEL_BASE.
void SetTooltipEnable(int aCol, bool aEnable=true)
Enable the tooltip for a column.
Definition: grid_tricks.h:68
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)