Better 3D filename resolution

This commit is contained in:
Cirilo Bernardo 2016-06-09 07:48:49 +02:00 committed by jean-pierre charras
parent 8c01318141
commit e0a7f0041a
8 changed files with 241 additions and 135 deletions

View File

@ -1,7 +1,7 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2015 Cirilo Bernardo <cirilo.bernardo@gmail.com>
* Copyright (C) 2015-2016 Cirilo Bernardo <cirilo.bernardo@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -32,8 +32,6 @@
#include <wx/datetime.h>
#include <wx/filename.h>
#include <wx/log.h>
#include <wx/thread.h>
#include <wx/utils.h>
#include <wx/stdpaths.h>
#include <boost/uuid/sha1.hpp>
@ -43,13 +41,13 @@
#include "3d_cache.h"
#include "3d_info.h"
#include "common.h"
#include "sg/scenegraph.h"
#include "3d_filename_resolver.h"
#include "3d_plugin_manager.h"
#include "plugins/3dapi/ifsg_api.h"
#define CACHE_CONFIG_NAME wxT( "cache.cfg" )
#define MASK_3D_CACHE "3D_CACHE"
static wxCriticalSection lock3D_cache;
@ -225,19 +223,23 @@ SCENEGRAPH* S3D_CACHE::load( const wxString& aModelFile, S3D_CACHE_ENTRY** aCach
if( mi != m_CacheMap.end() )
{
wxFileName fname( full3Dpath );
wxDateTime fmdate = fname.GetModificationTime();
bool reload = false;
if( fmdate != mi->second->modTime )
if( fname.FileExists() )
{
unsigned char hashSum[20];
getSHA1( full3Dpath, hashSum );
mi->second->modTime = fmdate;
wxDateTime fmdate = fname.GetModificationTime();
if( !isSHA1Same( hashSum, mi->second->sha1sum ) )
if( fmdate != mi->second->modTime )
{
mi->second->SetSHA1( hashSum );
reload = true;
unsigned char hashSum[20];
getSHA1( full3Dpath, hashSum );
mi->second->modTime = fmdate;
if( !isSHA1Same( hashSum, mi->second->sha1sum ) )
{
mi->second->SetSHA1( hashSum );
reload = true;
}
}
}
@ -538,7 +540,13 @@ bool S3D_CACHE::Set3DConfigDir( const wxString& aConfigDir )
if( !m_ConfigDir.empty() )
return false;
wxFileName cfgdir( aConfigDir, wxT( "" ) );
wxFileName cfgdir;
if( aConfigDir.StartsWith( "${" ) || aConfigDir.StartsWith( "$(" ) )
cfgdir.Assign( ExpandEnvVarSubstitutions( aConfigDir ), "" );
else
cfgdir.Assign( aConfigDir, "" );
cfgdir.Normalize();
if( !cfgdir.DirExists() )
@ -620,9 +628,9 @@ wxString S3D_CACHE::Get3DConfigDir( bool createDefault )
cfgpath.AssignDir( wxStandardPaths::Get().GetUserConfigDir() );
#if !defined( __WINDOWS__ ) && !defined( __WXMAC__ )
wxString envstr;
wxString envstr = ExpandEnvVarSubstitutions( "XDG_CONFIG_HOME" );
if( !wxGetEnv( wxT( "XDG_CONFIG_HOME" ), &envstr ) || envstr.IsEmpty() )
if( envstr.IsEmpty() )
{
// XDG_CONFIG_HOME is not set, so use the fallback
cfgpath.AppendDir( wxT( ".config" ) );

View File

@ -1,7 +1,7 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2015 Cirilo Bernardo <cirilo.bernardo@gmail.com>
* Copyright (C) 2015-2016 Cirilo Bernardo <cirilo.bernardo@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -23,7 +23,6 @@
#include <common.h>
#include <wx/thread.h>
#include <pgm_base.h>
#include "3d_cache_wrapper.h"

View File

@ -1,7 +1,7 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2015 Cirilo Bernardo <cirilo.bernardo@gmail.com>
* Copyright (C) 2015-2016 Cirilo Bernardo <cirilo.bernardo@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -21,19 +21,16 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <iostream>
#include <sstream>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <sstream>
#include <wx/filename.h>
#include <wx/log.h>
#include <wx/thread.h>
#include <wx/utils.h>
#include <wx/msgdlg.h>
#include <pgm_base.h>
#include "common.h"
#include "3d_filename_resolver.h"
// configuration file version
@ -64,7 +61,13 @@ bool S3D_FILENAME_RESOLVER::Set3DConfigDir( const wxString& aConfigDir )
if( aConfigDir.empty() )
return false;
wxFileName cfgdir( aConfigDir, wxT( "" ) );
wxFileName cfgdir;
if( aConfigDir.StartsWith( "${" ) || aConfigDir.StartsWith( "$(" ) )
cfgdir.Assign( ExpandEnvVarSubstitutions( aConfigDir ), "" );
else
cfgdir.Assign( aConfigDir, "" );
cfgdir.Normalize();
if( false == cfgdir.DirExists() )
@ -82,7 +85,13 @@ bool S3D_FILENAME_RESOLVER::SetProjectDir( const wxString& aProjDir, bool* flgCh
if( aProjDir.empty() )
return false;
wxFileName projdir( aProjDir, wxT( "" ) );
wxFileName projdir;
if( aProjDir.StartsWith( "${" ) || aProjDir.StartsWith( "$(" ) )
projdir.Assign( ExpandEnvVarSubstitutions( aProjDir ), "" );
else
projdir.Assign( aProjDir, "" );
projdir.Normalize();
if( false == projdir.DirExists() )
@ -96,10 +105,9 @@ bool S3D_FILENAME_RESOLVER::SetProjectDir( const wxString& aProjDir, bool* flgCh
if( m_Paths.empty() )
{
S3D_ALIAS al;
al.m_alias = _( "(DEFAULT)" );
al.m_pathvar = _( "${PROJDIR}" );
al.m_alias = "${KIPRJMOD}";
al.m_pathvar = "${KIPRJMOD}";
al.m_pathexp = m_curProjDir;
al.m_description = _( "Current project directory" );
m_Paths.push_back( al );
m_NameMap.clear();
@ -177,6 +185,7 @@ bool S3D_FILENAME_RESOLVER::createPathList( void )
m_Paths.push_back( lpath );
wxFileName fndummy;
wxUniChar psep = fndummy.GetPathSeparator();
bool hasKISYS3DMOD = false;
// iterate over the list of internally defined ENV VARs
// and add existing paths to the resolver
@ -202,21 +211,29 @@ bool S3D_FILENAME_RESOLVER::createPathList( void )
continue;
}
fndummy.Assign( mS->second.GetValue(), "" );
if( !fndummy.DirExists() )
{
++mS;
continue;
}
// ensure system ENV VARs supercede internally defined vars
wxString tmp( "${" );
tmp.Append( mS->first );
tmp.Append( "}" );
wxString pathVal = ExpandEnvVarSubstitutions( tmp );
if( pathVal.empty() )
{
pathVal = mS->second.GetValue();
if( pathVal.StartsWith( "${" ) || pathVal.StartsWith( "$(" ) )
pathVal = ExpandEnvVarSubstitutions( pathVal );
}
fndummy.Assign( pathVal, "" );
fndummy.Normalize();
if( tmp == "${KISYS3DMOD}" )
hasKISYS3DMOD = true;
lpath.m_alias = tmp;
lpath.m_pathvar = tmp;
lpath.m_pathexp = mS->second.GetValue();
lpath.m_pathexp = fndummy.GetFullPath();
if( !lpath.m_pathexp.empty() && psep == *lpath.m_pathexp.rbegin() )
lpath.m_pathexp.erase( --lpath.m_pathexp.end() );
@ -227,6 +244,26 @@ bool S3D_FILENAME_RESOLVER::createPathList( void )
}
}
// special case: if KISYSMOD is not internally defined but is defined by
// the system, then create an entry here
wxString envar = ExpandEnvVarSubstitutions( "${KISYS3DMOD}" );
if( !hasKISYS3DMOD && !envar.empty() )
{
lpath.m_alias = "${KISYS3DMOD}";
lpath.m_pathvar = "${KISYS3DMOD}";
fndummy.Assign( envar, "" );
fndummy.Normalize();
lpath.m_pathexp = fndummy.GetFullPath();
if( !lpath.m_pathexp.empty() && psep == *lpath.m_pathexp.rbegin() )
lpath.m_pathexp.erase( --lpath.m_pathexp.end() );
if( !lpath.m_pathexp.empty() )
m_Paths.push_back( lpath );
}
if( !m_ConfigDir.empty() )
readPathList();
@ -268,6 +305,7 @@ bool S3D_FILENAME_RESOLVER::UpdatePathList( std::vector< S3D_ALIAS >& aPathList
wxString S3D_FILENAME_RESOLVER::ResolvePath( const wxString& aFileName )
{
wxCriticalSectionLocker lock( lock3D_resolver );
if( aFileName.empty() )
return wxEmptyString;
@ -289,43 +327,49 @@ wxString S3D_FILENAME_RESOLVER::ResolvePath( const wxString& aFileName )
tname.Replace( wxT( "/" ), wxT( "\\" ) );
#endif
// this case covers full paths and paths relative to
// the current working directory (which is not necessarily
// the current project directory)
if( wxFileName::FileExists( tname ) )
// Note: variable expansion must be performed using a threadsafe
// wrapper for the getenv() system call. If we allow the
// wxFileName::Normalize() routine to perform expansion then
// we will have a race condition since wxWidgets does not assure
// a threadsafe wrapper for getenv().
if( tname.StartsWith( wxT( "${" ) ) || tname.StartsWith( wxT( "$(" ) ) )
tname = ExpandEnvVarSubstitutions( tname );
wxFileName tmpFN( tname );
// in the case of absolute filenames we don't store a map item
if( !aFileName.StartsWith( "${" ) && !aFileName.StartsWith( "$(" )
&& !aFileName.StartsWith( ":" ) && tmpFN.IsAbsolute() )
{
wxFileName tmp( tname );
tmpFN.Normalize();
if( tmp.Normalize() )
tname = tmp.GetFullPath();
if( tmpFN.FileExists() )
return tmpFN.GetFullPath();
return wxEmptyString;
}
// this case covers full paths, leading expanded vars, and paths
// relative to the current working directory (which is not necessarily
// the current project directory)
if( tmpFN.FileExists() )
{
tmpFN.Normalize();
tname = tmpFN.GetFullPath();
m_NameMap.insert( std::pair< wxString, wxString > ( aFileName, tname ) );
// special case: if a path begins with ${ENV_VAR} but is not in the
// resolver's path list then add it
if( aFileName.StartsWith( "${" ) || aFileName.StartsWith( "$(" ) )
checkEnvVarPath( aFileName );
return tname;
}
// at this point aFileName:
// a. is a legacy ${} shortened name
// b. an aliased shortened name
// c. cannot be determined
if( aFileName.StartsWith( wxT( "${" ) ) )
// if a path begins with ${ENV_VAR}/$(ENV_VAR) and is not resolved then the
// file either does not exist or the ENV_VAR is not defined
if( aFileName.StartsWith( "${" ) || aFileName.StartsWith( "$(" ) )
{
wxFileName tmp( aFileName );
if( tmp.Normalize() )
{
tname = tmp.GetFullPath();
m_NameMap.insert( std::pair< wxString, wxString > ( aFileName, tname ) );
return tname;
}
else if( resolveVirtualEnv( aFileName, tname ) )
{
m_NameMap.insert( std::pair< wxString, wxString > ( aFileName, tname ) );
return tname;
}
if( !( m_errflags & ERRFLG_ENVPATH ) )
{
m_errflags |= ERRFLG_ENVPATH;
@ -338,6 +382,10 @@ wxString S3D_FILENAME_RESOLVER::ResolvePath( const wxString& aFileName )
return wxEmptyString;
}
// at this point aFileName is:
// a. an aliased shortened name or
// b. cannot be determined
std::list< S3D_ALIAS >::const_iterator sPL = m_Paths.begin();
std::list< S3D_ALIAS >::const_iterator ePL = m_Paths.end();
@ -345,50 +393,53 @@ wxString S3D_FILENAME_RESOLVER::ResolvePath( const wxString& aFileName )
// note: this is not necessarily the same as the current working
// directory, which has already been checked. This case accounts
// for partial paths which do not contain ${KIPRJMOD}.
if( !sPL->m_pathexp.empty() )
// This check is performed before checking the path relative to
// ${KISYS3DMOD} so that users can potentially override a model
// within ${KISYS3DMOD}
if( !sPL->m_pathexp.empty() && !tname.StartsWith( ":" ) )
{
wxFileName fpath( wxFileName::DirName( sPL->m_pathexp ) );
wxString fullPath = fpath.GetPathWithSep() + tname;
tmpFN.Assign( sPL->m_pathexp, "" );
wxString fullPath = tmpFN.GetPathWithSep() + tname;
if( fullPath.StartsWith( "${" ) || fullPath.StartsWith( "$(" ) )
fullPath = ExpandEnvVarSubstitutions( fullPath );
if( wxFileName::FileExists( fullPath ) )
{
wxFileName tmp( fullPath );
if( tmp.Normalize() )
tname = tmp.GetFullPath();
tmpFN.Assign( fullPath );
tmpFN.Normalize();
tname = tmpFN.GetFullPath();
m_NameMap.insert( std::pair< wxString, wxString > ( aFileName, tname ) );
return tname;
}
}
// ${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( "${" ) )
// check the partial path relative to ${KISYS3DMOD} (legacy behavior)
if( !tname.StartsWith( ":" ) )
{
if( sPL->m_alias == "${KISYS3DMOD}" )
wxFileName fpath;
wxString fullPath( "${KISYS3DMOD}" );
fullPath.Append( fpath.GetPathSeparator() );
fullPath.Append( tname );
fullPath = ExpandEnvVarSubstitutions( fullPath );
fpath.Assign( fullPath );
if( fpath.Normalize() && fpath.FileExists() )
{
wxFileName fpath( wxFileName::DirName( sPL->m_pathexp ) );
wxString fullPath = fpath.GetPathWithSep() + tname;
if( wxFileName::FileExists( fullPath ) )
{
wxFileName tmp( fullPath );
if( tmp.Normalize() )
tname = tmp.GetFullPath();
m_NameMap.insert( std::pair< wxString, wxString > ( aFileName, tname ) );
return tname;
}
tname = fpath.GetFullPath();
m_NameMap.insert( std::pair< wxString, wxString > ( aFileName, tname ) );
return tname;
}
++sPL;
}
// ${ENV_VAR} paths have already been checked; skip them
while( sPL != ePL && ( sPL->m_alias.StartsWith( "${" )
|| sPL->m_alias.StartsWith( "$(" ) ) )
++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
@ -416,6 +467,9 @@ wxString S3D_FILENAME_RESOLVER::ResolvePath( const wxString& aFileName )
wxFileName fpath( wxFileName::DirName( sPL->m_pathexp ) );
wxString fullPath = fpath.GetPathWithSep() + relpath;
if( fullPath.StartsWith( "${") || fullPath.StartsWith( "$(" ) )
fullPath = ExpandEnvVarSubstitutions( fullPath );
if( wxFileName::FileExists( fullPath ) )
{
wxFileName tmp( fullPath );
@ -424,7 +478,6 @@ wxString S3D_FILENAME_RESOLVER::ResolvePath( const wxString& aFileName )
tname = tmp.GetFullPath();
m_NameMap.insert( std::pair< wxString, wxString > ( aFileName, tname ) );
return tname;
}
}
@ -462,7 +515,13 @@ bool S3D_FILENAME_RESOLVER::addPath( const S3D_ALIAS& aPath )
tpath.m_pathvar.erase( tpath.m_pathvar.length() - 1 );
#endif
wxFileName path( tpath.m_pathvar, wxT( "" ) );
wxFileName path;
if( tpath.m_pathvar.StartsWith( "${" ) || tpath.m_pathvar.StartsWith( "$(" ) )
path.Assign( ExpandEnvVarSubstitutions( tpath.m_pathvar ), "" );
else
path.Assign( tpath.m_pathvar, "" );
path.Normalize();
if( !path.DirExists() )
@ -645,7 +704,8 @@ bool S3D_FILENAME_RESOLVER::writePathList( void )
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( "${" ) )
while( sPL != ePL && ( sPL->m_alias.StartsWith( "${" )
|| sPL->m_alias.StartsWith( "$(" ) ) )
++sPL;
wxFileName cfgpath( m_ConfigDir, S3D_RESOLVER_CONFIG );
@ -721,47 +781,69 @@ bool S3D_FILENAME_RESOLVER::writePathList( void )
}
bool S3D_FILENAME_RESOLVER::resolveVirtualEnv( const wxString& aFileName, wxString& aFullPath )
void S3D_FILENAME_RESOLVER::checkEnvVarPath( const wxString& aPath )
{
aFullPath.clear();
bool useParen = false;
if( !aFileName.StartsWith( "${" ) )
return false;
if( aPath.StartsWith( "$(" ) )
useParen = true;
else if( !aPath.StartsWith( "${" ) )
return;
size_t eDelim = aFileName.find( '}' );
size_t pEnd;
if( eDelim == wxString::npos || eDelim + 2 >= aFileName.length() )
return false;
if( useParen )
pEnd = aPath.find( ")" );
else
pEnd = aPath.find( "}" );
wxString tPath = aFileName.substr( 0, eDelim + 1 );
if( pEnd == wxString::npos )
return;
wxString envar = aPath.substr( 0, pEnd + 1 );
// check if the alias exists; if not then add it to the end of the
// env var section of the path list
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 == envar )
return;
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;
}
break;
++sPL;
}
return false;
S3D_ALIAS lpath;
lpath.m_alias = envar;
lpath.m_pathvar = lpath.m_alias;
wxFileName tmpFN;
if( lpath.m_alias.StartsWith( "${" ) || lpath.m_alias.StartsWith( "$(" ) )
tmpFN.Assign( ExpandEnvVarSubstitutions( lpath.m_alias ), "" );
else
tmpFN.Assign( lpath.m_alias, "" );
wxUniChar psep = tmpFN.GetPathSeparator();
tmpFN.Normalize();
if( !tmpFN.DirExists() )
return;
lpath.m_pathexp = tmpFN.GetFullPath();
if( !lpath.m_pathexp.empty() && psep == *lpath.m_pathexp.rbegin() )
lpath.m_pathexp.erase( --lpath.m_pathexp.end() );
if( lpath.m_pathexp.empty() )
return;
m_Paths.insert( sPL, lpath );
return;
}
@ -802,7 +884,7 @@ wxString S3D_FILENAME_RESOLVER::ShortenPath( const wxString& aFullPathName )
fname.Replace( wxT( "\\" ), wxT( "/" ) );
#endif
if( sL->m_alias.StartsWith( "${" ) )
if( sL->m_alias.StartsWith( "${" ) || sL->m_alias.StartsWith( "$(" ) )
{
// old style ENV_VAR
tname = sL->m_alias;
@ -1009,6 +1091,7 @@ bool S3D_FILENAME_RESOLVER::ValidateFileName( const wxString& aFileName, bool& h
wxString lpath = filename.substr( 0, pos0 );
// check the alias for restricted characters
if( wxString::npos != lpath.find_first_of( wxT( "{}[]()%~<>\"='`;:.,&?/\\|$" ) ) )
return false;

View File

@ -1,7 +1,7 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2015 Cirilo Bernardo <cirilo.bernardo@gmail.com>
* Copyright (C) 2015-2016 Cirilo Bernardo <cirilo.bernardo@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -99,12 +99,12 @@ 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.
* Function checkEnvVarPath
* checks the ${ENV_VAR} component of a path and adds
* it to the resolver's path list if it is not yet in
* the list
*/
bool resolveVirtualEnv( const wxString& aFileName, wxString& aFullPath );
void checkEnvVarPath( const wxString& aPath );
public:
S3D_FILENAME_RESOLVER();

View File

@ -285,7 +285,7 @@ void S3D_PLUGIN_MANAGER::listPlugins( const wxString& aPath,
// Per definition a loadable "xxx.bundle" is similar to an "xxx.app" app
// bundle being a folder with some special content in it. We obviously don't
// want to have that here for our loadable module, so just use ".so".
nameFilter.Append( wxT( ".so" ) );
nameFilter.Append( ".so" );
#endif
wxString lp = wd.GetNameWithSep();
@ -316,7 +316,13 @@ void S3D_PLUGIN_MANAGER::checkPluginName( const wxString& aPath,
if( aPath.empty() || !wxFileName::FileExists( aPath ) )
return;
wxFileName path( aPath );
wxFileName path;
if( aPath.StartsWith( "${" ) || aPath.StartsWith( "$(" ) )
path.Assign( ExpandEnvVarSubstitutions( aPath ) );
else
path.Assign( aPath );
path.Normalize();
// determine if the path is already in the list
@ -355,7 +361,13 @@ void S3D_PLUGIN_MANAGER::checkPluginPath( const wxString& aPath,
aPath.ToUTF8() );
#endif
wxFileName path( wxFileName::DirName( aPath ) );
wxFileName path;
if( aPath.StartsWith( "${" ) || aPath.StartsWith( "$(" ) )
path.Assign( ExpandEnvVarSubstitutions( aPath ), "" );
else
path.Assign( aPath, "" );
path.Normalize();
if( !wxFileName::DirExists( path.GetFullPath() ) )

View File

@ -54,7 +54,8 @@ DLG_3D_PATH_CONFIG::DLG_3D_PATH_CONFIG( wxWindow* aParent, S3D_FILENAME_RESOLVER
size_t listsize = rpaths->size();
size_t listidx = 0;
while( rI != rE && (*rI).m_alias.StartsWith( "${" ) )
while( rI != rE && ( (*rI).m_alias.StartsWith( "${" )
|| (*rI).m_alias.StartsWith( "$(" ) ) )
{
++listidx;
++rI;

View File

@ -1,7 +1,7 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -29,6 +29,7 @@
#include "3d_viewer.h"
#include "3d_struct.h"
#include "common.h"
#include "modelparsers.h"
#include <kiway.h>
#include <3d_cache.h>
@ -136,8 +137,9 @@ void S3D_MASTER::SetShape3DName( const wxString& aShapeName )
// if the m_Shape3DName is not an absolute path the default path
// given by the environment variable KISYS3DMOD will be used
if( m_Shape3DName.StartsWith( wxT("${") ) )
m_Shape3DFullFilename = wxExpandEnvVars( m_Shape3DName );
if( m_Shape3DName.StartsWith( wxT("${") ) ||
m_Shape3DName.StartsWith( wxT("$(") ) )
m_Shape3DFullFilename = ExpandEnvVarSubstitutions( m_Shape3DName );
else
m_Shape3DFullFilename = m_Shape3DName;

View File

@ -494,13 +494,14 @@ bool PCB_EDIT_FRAME::OnHotKey( wxDC* aDC, int aHotkeyCode, const wxPoint& aPosit
case HK_CANVAS_LEGACY:
evt_type = ID_MENU_CANVAS_LEGACY;
break;
case HK_ZONE_FILL_OR_REFILL:
evt_type = ID_POPUP_PCB_FILL_ALL_ZONES;
break;
case HK_ZONE_REMOVE_FILLED:
evt_type = ID_POPUP_PCB_REMOVE_FILLED_AREAS_IN_ALL_ZONES;
break;
}
if( evt_type != 0 )