3D file name resolver improvements.

* Paths defined via pcbnew->Preferences->Configure Paths are now included in
  the 3D file browser's drop-down list except for KICAD_PTEMPLATES, KIGITHUB,
  KISYSMOD, any paths resembling a URL, and any non-existent paths. When an
  absolute path is shortened using one of these path aliases, let's say
  'KISYS3DMOD' then the name appears in the pcb file as "${KISYS3DMOD}/blah.wrl".
  If a user defines and actual environment variable with the same name, then
  that user defined variable will have precedence over the internally defined
  variable.

* Paths relative to the current project directory are no longer represented
  as "some/path/to/file.wrl".  For compatibility with previous KiCad versions
  it shall always be stored as ${KIPRJMOD}/some/path/to/file.wrl.

* Various restrictions on alias path values have been removed since the
  imposed restrictions could result in corrupted user data as aliases are
  silently dropped.

* Overall the patch should improve back-compatibility by providing a ${ENV_VAR}
  mechanism for supporting different model root directories and ensuring that
  paths relative to KIPRJMOD are expressed in a way that is compatible with
  earlier KiCad versions. This allows users more flexibility and the ability
  to work in a way that ensures 3D model files can be correctly resolved in
  earlier versions of KiCad. Users who do not care about back-compatibility
  may prefer to use the newer alias system.
This commit is contained in:
Cirilo Bernardo 2016-04-17 18:35:32 -04:00 committed by Wayne Stambaugh
parent 32fc9198c3
commit 6120472d60
9 changed files with 258 additions and 174 deletions

View File

@ -686,6 +686,13 @@ bool S3D_CACHE::SetProjectDir( const wxString& aProjDir )
}
void S3D_CACHE::SetProgramBase( PGM_BASE* aBase )
{
m_FNResolver->SetProgramBase( aBase );
return;
}
wxString S3D_CACHE::GetProjectDir( void )
{
return m_FNResolver->GetProjectDir();

View File

@ -38,6 +38,7 @@
#include "plugins/3dapi/c3dmodel.h"
class PGM_BASE;
class S3D_CACHE;
class S3D_CACHE_ENTRY;
class SCENEGRAPH;
@ -138,6 +139,14 @@ public:
*/
bool SetProjectDir( const wxString& aProjDir );
/**
* Function SetProgramBase
* sets the filename resolver's pointer to the application's
* PGM_BASE instance; the pointer is used to extract the
* local env vars.
*/
void SetProgramBase( PGM_BASE* aBase );
/**
* Function GetProjectDir
* returns the current project's working directory

View File

@ -24,6 +24,7 @@
#include <common.h>
#include <wx/thread.h>
#include <pgm_base.h>
#include "3d_cache_wrapper.h"
static wxCriticalSection lock3D_wrapper;
@ -57,6 +58,7 @@ S3D_CACHE* PROJECT::Get3DCacheManager( bool updateProjDir )
wxFileName cfgpath;
cfgpath.AssignDir( GetKicadConfigPath() );
cfgpath.AppendDir( wxT( "3d" ) );
cache->SetProgramBase( &Pgm() );
cache->Set3DConfigDir( cfgpath.GetFullPath() );
SetElem( ELEM_3DCACHE, cw );
updateProjDir = true;

View File

@ -32,6 +32,7 @@
#include <wx/thread.h>
#include <wx/utils.h>
#include <wx/msgdlg.h>
#include <pgm_base.h>
#include "3d_filename_resolver.h"
@ -54,6 +55,7 @@ static bool getHollerith( const std::string& aString, size_t& aIndex, wxString&
S3D_FILENAME_RESOLVER::S3D_FILENAME_RESOLVER()
{
m_errflags = 0;
m_pgm = NULL;
}
@ -86,7 +88,7 @@ bool S3D_FILENAME_RESOLVER::SetProjectDir( const wxString& aProjDir, bool* flgCh
if( false == projdir.DirExists() )
return false;
wxString path = projdir.GetPath();
m_curProjDir = projdir.GetPath();
if( flgChanged )
*flgChanged = false;
@ -96,7 +98,7 @@ bool S3D_FILENAME_RESOLVER::SetProjectDir( const wxString& aProjDir, bool* flgCh
S3D_ALIAS al;
al.m_alias = _( "(DEFAULT)" );
al.m_pathvar = _( "${PROJDIR}" );
al.m_pathexp = path;
al.m_pathexp = m_curProjDir;
al.m_description = _( "Current project directory" );
m_Paths.push_back( al );
m_NameMap.clear();
@ -107,9 +109,9 @@ bool S3D_FILENAME_RESOLVER::SetProjectDir( const wxString& aProjDir, bool* flgCh
}
else
{
if( m_Paths.front().m_pathexp.Cmp( path ) )
if( m_Paths.front().m_pathexp.Cmp( m_curProjDir ) )
{
m_Paths.front().m_pathexp = path;
m_Paths.front().m_pathexp = m_curProjDir;
m_NameMap.clear();
if( flgChanged )
@ -138,10 +140,22 @@ bool S3D_FILENAME_RESOLVER::SetProjectDir( const wxString& aProjDir, bool* flgCh
wxString S3D_FILENAME_RESOLVER::GetProjectDir( void )
{
if( m_Paths.empty() )
return wxEmptyString;
return m_curProjDir;
}
return m_Paths.front().m_pathexp;
void S3D_FILENAME_RESOLVER::SetProgramBase( PGM_BASE* aBase )
{
m_pgm = aBase;
if( NULL == m_pgm || m_Paths.empty() )
return;
// recreate the path list
m_Paths.clear();
createPathList();
return;
}
@ -157,15 +171,61 @@ bool S3D_FILENAME_RESOLVER::createPathList( void )
// the user may change this later with a call to SetProjectDir()
S3D_ALIAS lpath;
lpath.m_alias = _( "(DEFAULT)" );
lpath.m_pathvar = _( "${PROJDIR}" );
lpath.m_description = _( "Current project directory" );
lpath.m_alias = _( "${KIPRJMOD}" );
lpath.m_pathvar = _( "${KIPRJMOD}" );
lpath.m_pathexp = m_curProjDir;
m_Paths.push_back( lpath );
wxFileName fndummy;
wxUniChar psep = fndummy.GetPathSeparator();
// iterate over the list of internally defined ENV VARs
// and add existing paths to the resolver
if( m_pgm )
{
ENV_VAR_MAP_CITER mS = m_pgm->GetLocalEnvVariables().begin();
ENV_VAR_MAP_CITER mE = m_pgm->GetLocalEnvVariables().end();
while( mS != mE )
{
// filter out URLs, template directories, and known system paths
if( mS->first == wxString( "KICAD_PTEMPLATES" )
|| mS->first == wxString( "KIGITHUB" )
|| mS->first == wxString( "KISYSMOD" ) )
{
++mS;
continue;
}
if( wxString::npos != mS->second.GetValue().find( wxString( "://" ) ) )
{
++mS;
continue;
}
fndummy.Assign( mS->second.GetValue(), "" );
if( !fndummy.DirExists() )
{
++mS;
continue;
}
wxString tmp( "${" );
tmp.Append( mS->first );
tmp.Append( "}" );
lpath.m_alias = tmp;
lpath.m_pathvar = tmp;
lpath.m_pathexp = mS->second.GetValue();
if( !lpath.m_pathexp.empty() && psep == *lpath.m_pathexp.rbegin() )
lpath.m_pathexp.erase( --lpath.m_pathexp.end() );
m_Paths.push_back( lpath );
lpath.m_alias = wxT( "KISYS3DMOD" );
lpath.m_pathvar = wxT( "${KISYS3DMOD}" );
lpath.m_description = _( "Legacy 3D environment path" );
addPath( lpath );
++mS;
}
}
if( !m_ConfigDir.empty() )
readPathList();
@ -191,7 +251,9 @@ bool S3D_FILENAME_RESOLVER::createPathList( void )
bool S3D_FILENAME_RESOLVER::UpdatePathList( std::vector< S3D_ALIAS >& aPathList )
{
while( m_Paths.size() > 2 )
wxUniChar envMarker( '$' );
while( !m_Paths.empty() && envMarker != *m_Paths.back().m_alias.rbegin() )
m_Paths.pop_back();
size_t nI = aPathList.size();
@ -258,6 +320,11 @@ wxString S3D_FILENAME_RESOLVER::ResolvePath( const wxString& aFileName )
return tname;
}
else if( resolveVirtualEnv( aFileName, tname ) )
{
m_NameMap.insert( std::pair< wxString, wxString > ( aFileName, tname ) );
return tname;
}
if( !( m_errflags & ERRFLG_ENVPATH ) )
{
@ -276,7 +343,8 @@ wxString S3D_FILENAME_RESOLVER::ResolvePath( const wxString& aFileName )
// check the path relative to the current project directory;
// note: this is not necessarily the same as the current working
// directory, which has already been checked
// directory, which has already been checked. This case accounts
// for partial paths which do not contain ${KIPRJMOD}.
if( !sPL->m_pathexp.empty() )
{
wxFileName fpath( wxFileName::DirName( sPL->m_pathexp ) );
@ -295,11 +363,12 @@ wxString S3D_FILENAME_RESOLVER::ResolvePath( const wxString& aFileName )
}
}
++sPL; // skip to item 2: KISYS3DMOD
// check if the path is relative to KISYS3DMOD but lacking
// the "KISYS3DMOD:" alias tag
if( !sPL->m_pathexp.empty() )
// ${ENV_VAR} paths have already been checked; skip all but
// ${KISYS3DMOD}, since legacy behavior was to check if paths
// were relative to ${KISYS3DMOD}
while( sPL != ePL && sPL->m_alias.StartsWith( "${" ) )
{
if( sPL->m_alias == "${KISYS3DMOD}" )
{
wxFileName fpath( wxFileName::DirName( sPL->m_pathexp ) );
wxString fullPath = fpath.GetPathWithSep() + tname;
@ -317,6 +386,10 @@ wxString S3D_FILENAME_RESOLVER::ResolvePath( const wxString& aFileName )
}
}
++sPL;
}
// at this point the filename must contain an alias or else it is invalid
wxString alias; // the alias portion of the short filename
wxString relpath; // the path relative to the alias
@ -324,8 +397,10 @@ wxString S3D_FILENAME_RESOLVER::ResolvePath( const wxString& aFileName )
{
if( !( m_errflags & ERRFLG_RELPATH ) )
{
// this can happen if the file was intended to be relative to
// ${KISYS3DMOD} but ${KISYS3DMOD} not set or incorrect.
m_errflags |= ERRFLG_RELPATH;
wxString errmsg = _( "[3D File Resolver] No such path; ensure KISYS3DMOD is correctly defined" );
wxString errmsg = _( "[3D File Resolver] No such path" );
errmsg.append( "\n" );
errmsg.append( tname );
wxLogTrace( MASK_3D_RESOLVER, "%s\n", errmsg.ToUTF8() );
@ -378,7 +453,6 @@ bool S3D_FILENAME_RESOLVER::addPath( const S3D_ALIAS& aPath )
wxCriticalSectionLocker lock( lock3D_resolver );
S3D_ALIAS tpath = aPath;
tpath.m_duplicate = false;
#ifdef _WIN32
while( tpath.m_pathvar.EndsWith( wxT( "\\" ) ) )
@ -424,83 +498,6 @@ bool S3D_FILENAME_RESOLVER::addPath( const S3D_ALIAS& aPath )
while( sPL != ePL )
{
// aliases with the same m_pathvar are forbidden and the
// user must be forced to fix the problem in order to
// obtain good filename resolution
if( !sPL->m_pathvar.empty() && !tpath.m_pathvar.empty()
&& !tpath.m_pathvar.Cmp( sPL->m_pathvar ) )
{
wxString msg = _( "This alias: " );
msg.append( tpath.m_alias );
msg.append( wxT( "\n" ) );
msg.append( _( "This path: " ) );
msg.append( tpath.m_pathvar );
msg.append( wxT( "\n" ) );
msg.append( _( "Existing alias: " ) );
msg.append( sPL->m_alias );
msg.append( wxT( "\n" ) );
msg.append( _( "Existing path: " ) );
msg.append( sPL->m_pathvar );
wxMessageBox( msg, _( "Bad alias (duplicate path)" ) );
return false;
}
// aliases with the same m_pathexp are acceptable (one or both
// aliases being tested may be expanded variables) but when shortening
// names the preference is for (a) a fully specified path in m_pathvar
// then (b) the more senior alias in the list
if( !sPL->m_pathexp.empty() && !tpath.m_pathexp.empty() )
{
if( !tpath.m_pathexp.Cmp( sPL->m_pathexp ) )
{
wxString msg = _( "This alias: " );
msg.append( tpath.m_alias );
msg.append( wxT( "\n" ) );
msg.append( _( "Existing alias: " ) );
msg.append( sPL->m_alias );
msg.append( wxT( "\n" ) );
msg.append( _( "This path: " ) );
msg.append( tpath.m_pathexp );
msg.append( wxT( "\n" ) );
msg.append( _( "Existing path: " ) );
msg.append( sPL->m_pathexp );
msg.append( wxT( "\n" ) );
msg.append( _( "This full path: " ) );
msg.append( tpath.m_pathexp );
msg.append( wxT( "\n" ) );
msg.append( _( "Existing full path: " ) );
msg.append( sPL->m_pathexp );
wxMessageBox( msg, _( "Bad alias (duplicate path)" ) );
if( tpath.m_pathvar.StartsWith( wxT( "${" ) ) )
tpath.m_duplicate = true;
else if( sPL->m_pathvar.StartsWith( wxT( "${" ) ) )
sPL->m_duplicate = true;
}
if( ( tpath.m_pathexp.find( sPL->m_pathexp ) != wxString::npos
|| sPL->m_pathexp.find( tpath.m_pathexp ) != wxString::npos )
&& tpath.m_pathexp.Cmp( sPL->m_pathexp ) )
{
wxString msg = _( "This alias: " );
msg.append( tpath.m_alias );
msg.append( wxT( "\n" ) );
msg.append( _( "This path: " ) );
msg.append( tpath.m_pathexp );
msg.append( wxT( "\n" ) );
msg.append( _( "Existing alias: " ) );
msg.append( sPL->m_alias );
msg.append( wxT( "\n" ) );
msg.append( _( "Existing path: " ) );
msg.append( sPL->m_pathexp );
wxMessageBox( msg, _( "Bad alias (common path)" ) );
return false;
}
}
if( !tpath.m_alias.Cmp( sPL->m_alias ) )
{
wxString msg = _( "Alias: " );
@ -519,8 +516,6 @@ bool S3D_FILENAME_RESOLVER::addPath( const S3D_ALIAS& aPath )
++sPL;
}
// Note: at this point we may still have duplicated paths
m_Paths.push_back( tpath );
return true;
}
@ -572,7 +567,6 @@ bool S3D_FILENAME_RESOLVER::readPathList( void )
int lineno = 0;
S3D_ALIAS al;
al.m_duplicate = false;
size_t idx;
int vnum = 0; // version number
@ -647,11 +641,18 @@ bool S3D_FILENAME_RESOLVER::writePathList( void )
return false;
}
// skip all ${ENV_VAR} alias names
std::list< S3D_ALIAS >::const_iterator sPL = m_Paths.begin();
std::list< S3D_ALIAS >::const_iterator ePL = m_Paths.end();
while( sPL != ePL && sPL->m_alias.StartsWith( "${" ) )
++sPL;
wxFileName cfgpath( m_ConfigDir, S3D_RESOLVER_CONFIG );
wxString cfgname = cfgpath.GetFullPath();
std::ofstream cfgFile;
if( m_Paths.empty() || 1 == m_Paths.size() )
if( sPL == ePL )
{
wxMessageDialog md( NULL,
_( "3D search path list is empty;\ncontinue to write empty file?" ),
@ -692,23 +693,10 @@ bool S3D_FILENAME_RESOLVER::writePathList( void )
}
cfgFile << "#V" << CFGFILE_VERSION << "\n";
std::list< S3D_ALIAS >::const_iterator sPL = m_Paths.begin();
std::list< S3D_ALIAS >::const_iterator ePL = m_Paths.end();
// the first entry is the current project dir; we never add the implicit
// project dir to the path list in the configuration file
++sPL;
std::string tstr;
while( sPL != ePL )
{
// never write the KISYS3DMOD entry
if( !sPL->m_alias.Cmp( wxT( "KISYS3DMOD") ) )
{
++sPL;
continue;
}
tstr = sPL->m_alias.ToUTF8();
cfgFile << "\"" << tstr.size() << ":" << tstr << "\",";
tstr = sPL->m_pathvar.ToUTF8();
@ -733,6 +721,50 @@ bool S3D_FILENAME_RESOLVER::writePathList( void )
}
bool S3D_FILENAME_RESOLVER::resolveVirtualEnv( const wxString& aFileName, wxString& aFullPath )
{
aFullPath.clear();
if( !aFileName.StartsWith( "${" ) )
return false;
size_t eDelim = aFileName.find( '}' );
if( eDelim == wxString::npos || eDelim + 2 >= aFileName.length() )
return false;
wxString tPath = aFileName.substr( 0, eDelim + 1 );
std::list< S3D_ALIAS >::const_iterator sPL = m_Paths.begin();
std::list< S3D_ALIAS >::const_iterator ePL = m_Paths.end();
while( sPL != ePL )
{
if( !sPL->m_alias.StartsWith( "${" ) )
return false;
if( sPL->m_alias == tPath )
{
tPath.Append( aFileName.substr( eDelim + 2 ) );
wxFileName tFile( tPath );
tFile.Normalize();
if( tFile.FileExists() )
{
aFullPath = tFile.GetFullPath();
return true;
}
return false;
}
++sPL;
}
return false;
}
wxString S3D_FILENAME_RESOLVER::ShortenPath( const wxString& aFullPathName )
{
wxString fname = aFullPathName;
@ -745,32 +777,11 @@ wxString S3D_FILENAME_RESOLVER::ShortenPath( const wxString& aFullPathName )
std::list< S3D_ALIAS >::const_iterator eL = m_Paths.end();
size_t idx;
// test for files within the current project directory
// and KISYS3DMOD directory
for( int i = 0; i < 2 && sL != eL; ++i )
{
if( !sL->m_pathexp.empty() )
{
wxFileName fpath( sL->m_pathexp, wxT( "" ) );
wxString fps = fpath.GetPathWithSep();
idx = fname.find( fps );
if( std::string::npos != idx && 0 == idx )
{
fname = fname.substr( fps.size() );
return fname;
}
}
++sL;
}
while( sL != eL )
{
// undefined paths and duplicates do not participate
// in the file name shortening procedure
if( sL->m_pathexp.empty() || sL->m_duplicate )
// undefined paths do not participate in the
// file name shortening procedure
if( sL->m_pathexp.empty() )
{
++sL;
continue;
@ -791,10 +802,21 @@ wxString S3D_FILENAME_RESOLVER::ShortenPath( const wxString& aFullPathName )
fname.Replace( wxT( "\\" ), wxT( "/" ) );
#endif
if( sL->m_alias.StartsWith( "${" ) )
{
// old style ENV_VAR
tname = sL->m_alias;
tname.Append( "/" );
tname.append( fname );
}
else
{
// new style alias
tname = ":";
tname.append( sL->m_alias );
tname.append( ":" );
tname.append( fname );
}
return tname;
}

View File

@ -39,9 +39,10 @@
#include <wx/string.h>
#include "str_rsort.h"
class PGM_BASE;
struct S3D_ALIAS
{
bool m_duplicate;
wxString m_alias; // alias to the base path
wxString m_pathvar; // base path as stored in the config file
wxString m_pathexp; // expanded base path
@ -56,6 +57,8 @@ private:
// mapping of (short) file names to resolved names
std::map< wxString, wxString, S3D::rsort_wxString > m_NameMap;
int m_errflags;
PGM_BASE* m_pgm;
wxString m_curProjDir;
/**
* Function createPathList
@ -95,6 +98,14 @@ private:
*/
bool writePathList( void );
/**
* Function resolveVirtualEnv
* extracts the ${ENV_VAR} component of aFileName and puts a
* resolved path in aFullPath if the ${ENV_VAR} exists in the
* alias list and the referenced file exists.
*/
bool resolveVirtualEnv( const wxString& aFileName, wxString& aFullPath );
public:
S3D_FILENAME_RESOLVER();
@ -120,6 +131,13 @@ public:
bool SetProjectDir( const wxString& aProjDir, bool* flgChanged = NULL );
wxString GetProjectDir( void );
/**
* Function SetProgramBase
* sets a pointer to the application's PGM_BASE instance;
* the pointer is used to extract the local env vars.
*/
void SetProgramBase( PGM_BASE* aBase );
/**
* Function UpdatePathList

View File

@ -42,33 +42,35 @@ DLG_3D_PATH_CONFIG::DLG_3D_PATH_CONFIG( wxWindow* aParent, S3D_FILENAME_RESOLVER
m_aliasValidator.SetCharExcludes( wxT( "{}[]()%~<>\"='`;:.,&?/\\|$" ) );
const std::list< S3D_ALIAS >* rpaths = m_resolver->GetPaths();
std::list< S3D_ALIAS >::const_iterator rI = rpaths->begin();
std::list< S3D_ALIAS >::const_iterator rE = rpaths->end();
size_t listsize = rpaths->size();
size_t listidx = 0;
if( listsize > 0 )
m_curdir = rpaths->front().m_pathexp;
while( rI != rE && (*rI).m_alias.StartsWith( "${" ) )
{
++listidx;
++rI;
}
if( listsize < 3 )
if( listidx < listsize )
m_curdir = (*rI).m_pathexp;
else
return;
listsize = listsize - 2 - m_Aliases->GetNumberRows();
listsize = listsize - listidx - m_Aliases->GetNumberRows();
// note: if the list allocation fails we have bigger problems
// and there is no point in trying to notify the user here
if( listsize > 0 && !m_Aliases->InsertRows( 0, listsize ) )
return;
std::list< S3D_ALIAS >::const_iterator sL = rpaths->begin();
std::list< S3D_ALIAS >::const_iterator eL = rpaths->end();
int nitems = 0;
// skip the current project dir and KISYS3DMOD
++sL;
++sL;
wxGridCellTextEditor* pEdAlias;
while( sL != eL )
while( rI != rE )
{
m_Aliases->SetCellValue( nitems, 0, sL->m_alias );
m_Aliases->SetCellValue( nitems, 0, rI->m_alias );
if( 0 == nitems )
{
@ -83,12 +85,12 @@ DLG_3D_PATH_CONFIG::DLG_3D_PATH_CONFIG( wxWindow* aParent, S3D_FILENAME_RESOLVER
m_Aliases->SetCellEditor( nitems, 0, pEdAlias );
}
m_Aliases->SetCellValue( nitems, 1, sL->m_pathvar );
m_Aliases->SetCellValue( nitems++, 2, sL->m_description );
m_Aliases->SetCellValue( nitems, 1, rI->m_pathvar );
m_Aliases->SetCellValue( nitems++, 2, rI->m_description );
// TODO: implement a wxGridCellEditor which invokes a wxDirDialog
++sL;
++rI;
}
m_Aliases->AutoSize();

View File

@ -22,6 +22,7 @@
*/
#include <set>
#include "dlg_select_3dmodel.h"
#include "project.h"
#include "3d_cache/3d_info.h"
@ -240,12 +241,20 @@ void DLG_SELECT_3DMODEL::updateDirChoiceList( void )
std::list< S3D_ALIAS > const* md = m_resolver->GetPaths();
std::list< S3D_ALIAS >::const_iterator sL = md->begin();
std::list< S3D_ALIAS >::const_iterator eL = md->end();
std::vector< wxString > cl;
std::set< wxString > cl;
wxString prjDir;
// extract the current project dir
if( sL != eL )
{
prjDir = sL->m_pathexp;
++sL;
}
while( sL != eL )
{
if( !sL->m_pathexp.empty() && !sL->m_duplicate )
cl.push_back( sL->m_pathexp );
if( !sL->m_pathexp.empty() && sL->m_pathexp.compare( prjDir ) )
cl.insert( sL->m_pathexp );
++sL;
}
@ -253,7 +262,19 @@ void DLG_SELECT_3DMODEL::updateDirChoiceList( void )
if( !cl.empty() )
{
dirChoices->Clear();
dirChoices->Append( (int)cl.size(), &cl[0] );
if( !prjDir.empty() )
dirChoices->Append( prjDir );
std::set< wxString >::const_iterator sI = cl.begin();
std::set< wxString >::const_iterator eI = cl.end();
while( sI != eI )
{
dirChoices->Append( *sI );
++sI;
}
dirChoices->Select( 0 );
}

View File

@ -75,6 +75,7 @@ DIALOG_MODULE_BOARD_EDITOR::DIALOG_MODULE_BOARD_EDITOR( PCB_EDIT_FRAME* aParent
m_OrientValueCtrl->SetValidator( m_OrientValidator );
m_OrientValidator.SetWindow( m_OrientValueCtrl );
aParent->Prj().Get3DCacheManager()->GetResolver()->SetProgramBase( &Pgm() );
m_PreviewPane = new PANEL_PREV_3D( m_Panel3D, aParent->Prj().Get3DCacheManager() );
bLowerSizer3D->Add( m_PreviewPane, 1, wxEXPAND, 5 );

View File

@ -50,6 +50,7 @@
#include <module_editor_frame.h>
#include <dialog_edit_module_for_Modedit.h>
#include <wildcards_and_files_ext.h>
#include <pgm_base.h>
#include "3d_cache/dialogs/panel_prev_model.h"
#include "3d_cache/dialogs/3d_cache_dialogs.h"
#include "3d_cache/3d_cache.h"
@ -70,6 +71,7 @@ DIALOG_MODULE_MODULE_EDITOR::DIALOG_MODULE_MODULE_EDITOR( FOOTPRINT_EDIT_FRAME*
icon.CopyFromBitmap( KiBitmap( icon_modedit_xpm ) );
SetIcon( icon );
aParent->Prj().Get3DCacheManager()->GetResolver()->SetProgramBase( &Pgm() );
m_PreviewPane = new PANEL_PREV_3D( m_Panel3D, aParent->Prj().Get3DCacheManager() );
bLowerSizer3D->Add( m_PreviewPane, 1, wxEXPAND, 5 );