Refactor Eagle project import to use Kiway::ExpressMail()
This commit is contained in:
parent
9016344bb3
commit
1f078f533b
|
@ -253,6 +253,30 @@ void SCH_EDIT_FRAME::KiwayMailIn( KIWAY_EXPRESS& mail )
|
||||||
GetCanvas()->Refresh();
|
GetCanvas()->Refresh();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case MAIL_IMPORT_FILE:
|
||||||
|
{
|
||||||
|
// Extract file format type and path (plugin type and path separated with \n)
|
||||||
|
size_t split = payload.find( '\n' );
|
||||||
|
wxCHECK( split != std::string::npos, /*void*/ );
|
||||||
|
int importFormat;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
importFormat = std::stoi( payload.substr( 0, split ) );
|
||||||
|
}
|
||||||
|
catch( std::invalid_argument& e )
|
||||||
|
{
|
||||||
|
wxFAIL;
|
||||||
|
importFormat = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string path = payload.substr( split + 1 );
|
||||||
|
wxASSERT( !path.empty() );
|
||||||
|
|
||||||
|
if( importFormat >= 0 )
|
||||||
|
importFile( path, importFormat );
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
|
@ -676,7 +676,7 @@ void SCH_EDIT_FRAME::OnImportProject( wxCommandEvent& aEvent )
|
||||||
}
|
}
|
||||||
|
|
||||||
// For now there is only one import plugin
|
// For now there is only one import plugin
|
||||||
ImportFile( dlg.GetPath(), SCH_IO_MGR::SCH_EAGLE );
|
importFile( dlg.GetPath(), SCH_IO_MGR::SCH_EAGLE );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -752,7 +752,7 @@ bool SCH_EDIT_FRAME::doAutoSave()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool SCH_EDIT_FRAME::ImportFile( const wxString& aFileName, int aFileType )
|
bool SCH_EDIT_FRAME::importFile( const wxString& aFileName, int aFileType )
|
||||||
{
|
{
|
||||||
wxString fullFileName( aFileName );
|
wxString fullFileName( aFileName );
|
||||||
|
|
||||||
|
|
|
@ -812,14 +812,6 @@ public:
|
||||||
|
|
||||||
bool IsSearchCacheObsolete( const SCH_FIND_REPLACE_DATA& aSearchCriteria );
|
bool IsSearchCacheObsolete( const SCH_FIND_REPLACE_DATA& aSearchCriteria );
|
||||||
|
|
||||||
/**
|
|
||||||
* Load the given filename but sets the path to the current project path.
|
|
||||||
*
|
|
||||||
* @param full filepath of file to be imported.
|
|
||||||
* @param aFileType SCH_FILE_T value for file type
|
|
||||||
*/
|
|
||||||
bool ImportFile( const wxString& aFileName, int aFileType ) override;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if any of the screens has unsaved changes and asks the user whether to save or
|
* Checks if any of the screens has unsaved changes and asks the user whether to save or
|
||||||
* drop them.
|
* drop them.
|
||||||
|
@ -1126,6 +1118,14 @@ public:
|
||||||
wxPoint GetLastSheetPinPosition() const { return m_lastSheetPinPosition; }
|
wxPoint GetLastSheetPinPosition() const { return m_lastSheetPinPosition; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
/**
|
||||||
|
* Load the given filename but sets the path to the current project path.
|
||||||
|
*
|
||||||
|
* @param full filepath of file to be imported.
|
||||||
|
* @param aFileType SCH_FILE_T value for file type
|
||||||
|
*/
|
||||||
|
bool importFile( const wxString& aFileName, int aFileType );
|
||||||
|
|
||||||
bool validateSheet( SCH_SHEET* aSheet, SCH_SHEET_PATH* aHierarchy );
|
bool validateSheet( SCH_SHEET* aSheet, SCH_SHEET_PATH* aHierarchy );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -179,27 +179,6 @@ public:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Function ImportFile
|
|
||||||
* load the given filename but sets the path to the current project path.
|
|
||||||
* @param full filepath of file to be imported.
|
|
||||||
* @param aFileType enum value for filetype
|
|
||||||
*/
|
|
||||||
VTBL_ENTRY bool ImportFile( const wxString& aFileName, int aFileType )
|
|
||||||
{
|
|
||||||
// overload me for your wxFrame type.
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Rematch ophaned zones and vias to schematic nets.
|
|
||||||
*/
|
|
||||||
VTBL_ENTRY bool FixEagleNets()
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a list of net names currently in use by the player .
|
* Create a list of net names currently in use by the player .
|
||||||
|
|
|
@ -40,6 +40,7 @@ enum MAIL_T
|
||||||
MAIL_BACKANNOTATE_FOOTPRINTS, ///< CVPCB->SCH footprint stuffing at cvpcb termination
|
MAIL_BACKANNOTATE_FOOTPRINTS, ///< CVPCB->SCH footprint stuffing at cvpcb termination
|
||||||
MAIL_EESCHEMA_NETLIST, ///< EESCHEMA->CVPCB netlist immediately after launching CVPCB
|
MAIL_EESCHEMA_NETLIST, ///< EESCHEMA->CVPCB netlist immediately after launching CVPCB
|
||||||
MAIL_SCH_PCB_UPDATE, ///< Sch->PCB forward update
|
MAIL_SCH_PCB_UPDATE, ///< Sch->PCB forward update
|
||||||
|
MAIL_IMPORT_FILE, ///< Import a different format file
|
||||||
|
|
||||||
///< Sch->PCB forward update, requests SCH to re-generate netlist and send it to PCB
|
///< Sch->PCB forward update, requests SCH to re-generate netlist and send it to PCB
|
||||||
///< via another mail (kind of bootstrap)
|
///< via another mail (kind of bootstrap)
|
||||||
|
|
|
@ -47,11 +47,6 @@
|
||||||
|
|
||||||
#include <io_mgr.h>
|
#include <io_mgr.h>
|
||||||
#include <sch_io_mgr.h>
|
#include <sch_io_mgr.h>
|
||||||
#include <pcb_edit_frame.h>
|
|
||||||
#include <sch_edit_frame.h>
|
|
||||||
#include <netlist.h>
|
|
||||||
|
|
||||||
class PCB_EDIT_FRAME;
|
|
||||||
|
|
||||||
#include "kicad.h"
|
#include "kicad.h"
|
||||||
|
|
||||||
|
@ -124,17 +119,16 @@ void KICAD_MANAGER_FRAME::OnImportEagleFiles( wxCommandEvent& event )
|
||||||
|
|
||||||
SetProjectFileName( pro.GetFullPath() );
|
SetProjectFileName( pro.GetFullPath() );
|
||||||
wxString prj_filename = GetProjectFileName();
|
wxString prj_filename = GetProjectFileName();
|
||||||
wxString sch_filename = sch.GetFullPath();
|
|
||||||
|
|
||||||
if( sch.FileExists() )
|
if( sch.FileExists() )
|
||||||
{
|
{
|
||||||
SCH_EDIT_FRAME* schframe = (SCH_EDIT_FRAME*) Kiway.Player( FRAME_SCH, false );
|
KIWAY_PLAYER* schframe = Kiway.Player( FRAME_SCH, false );
|
||||||
|
|
||||||
if( !schframe )
|
if( !schframe )
|
||||||
{
|
{
|
||||||
try
|
try // SCH frame was not available, try to start it
|
||||||
{
|
{
|
||||||
schframe = (SCH_EDIT_FRAME*) Kiway.Player( FRAME_SCH, true );
|
schframe = Kiway.Player( FRAME_SCH, true );
|
||||||
}
|
}
|
||||||
catch( IO_ERROR err )
|
catch( IO_ERROR err )
|
||||||
{
|
{
|
||||||
|
@ -144,7 +138,8 @@ void KICAD_MANAGER_FRAME::OnImportEagleFiles( wxCommandEvent& event )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
schframe->ImportFile( sch_filename, SCH_IO_MGR::SCH_EAGLE );
|
schframe->Kiway().ExpressMail( FRAME_SCH, MAIL_IMPORT_FILE,
|
||||||
|
wxString::Format( "%d\n%s", SCH_IO_MGR::SCH_EAGLE, sch.GetFullPath() ).ToStdString(), this );
|
||||||
|
|
||||||
if( !schframe->IsShown() ) // the frame exists, (created by the dialog field editor)
|
if( !schframe->IsShown() ) // the frame exists, (created by the dialog field editor)
|
||||||
// but no project loaded.
|
// but no project loaded.
|
||||||
|
@ -161,13 +156,13 @@ void KICAD_MANAGER_FRAME::OnImportEagleFiles( wxCommandEvent& event )
|
||||||
|
|
||||||
if( pcb.FileExists() )
|
if( pcb.FileExists() )
|
||||||
{
|
{
|
||||||
PCB_EDIT_FRAME* pcbframe = (PCB_EDIT_FRAME*) Kiway.Player( FRAME_PCB, false );
|
KIWAY_PLAYER* pcbframe = Kiway.Player( FRAME_PCB, false );
|
||||||
|
|
||||||
if( !pcbframe )
|
if( !pcbframe )
|
||||||
{
|
{
|
||||||
try
|
try // PCB frame was not available, try to start it
|
||||||
{
|
{
|
||||||
pcbframe = (PCB_EDIT_FRAME*) Kiway.Player( FRAME_PCB, true );
|
pcbframe = Kiway.Player( FRAME_PCB, true );
|
||||||
}
|
}
|
||||||
catch( IO_ERROR err )
|
catch( IO_ERROR err )
|
||||||
{
|
{
|
||||||
|
@ -182,22 +177,17 @@ void KICAD_MANAGER_FRAME::OnImportEagleFiles( wxCommandEvent& event )
|
||||||
// if the frame is not visible, the board is not yet loaded
|
// if the frame is not visible, the board is not yet loaded
|
||||||
if( !pcbframe->IsVisible() )
|
if( !pcbframe->IsVisible() )
|
||||||
{
|
{
|
||||||
pcbframe->ImportFile( pcb.GetFullPath(), IO_MGR::EAGLE );
|
|
||||||
pcbframe->Show( true );
|
pcbframe->Show( true );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pcbframe->Kiway().ExpressMail( FRAME_PCB, MAIL_IMPORT_FILE,
|
||||||
|
wxString::Format( "%d\n%s", IO_MGR::EAGLE, pcb.GetFullPath() ).ToStdString(), this );
|
||||||
|
|
||||||
// On Windows, Raise() does not bring the window on screen, when iconized
|
// On Windows, Raise() does not bring the window on screen, when iconized
|
||||||
if( pcbframe->IsIconized() )
|
if( pcbframe->IsIconized() )
|
||||||
pcbframe->Iconize( false );
|
pcbframe->Iconize( false );
|
||||||
|
|
||||||
pcbframe->Raise();
|
pcbframe->Raise();
|
||||||
|
|
||||||
// Two stage project update:
|
|
||||||
// - first, assign valid timestamps to footprints
|
|
||||||
// - second, perform schematic annotation and update footprint references
|
|
||||||
pcbframe->Kiway().ExpressMail( FRAME_SCH, MAIL_SCH_PCB_UPDATE_REQUEST, "no-annotate;by-reference", this );
|
|
||||||
pcbframe->Kiway().ExpressMail( FRAME_SCH, MAIL_SCH_PCB_UPDATE_REQUEST, "quiet-annotate;by-timestamp", this );
|
|
||||||
pcbframe->FixEagleNets();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ReCreateTreePrj();
|
ReCreateTreePrj();
|
||||||
|
|
|
@ -411,6 +411,30 @@ void PCB_EDIT_FRAME::KiwayMailIn( KIWAY_EXPRESS& mail )
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case MAIL_IMPORT_FILE:
|
||||||
|
{
|
||||||
|
// Extract file format type and path (plugin type and path separated with \n)
|
||||||
|
size_t split = payload.find( '\n' );
|
||||||
|
wxCHECK( split != std::string::npos, /*void*/ );
|
||||||
|
int importFormat;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
importFormat = std::stoi( payload.substr( 0, split ) );
|
||||||
|
}
|
||||||
|
catch( std::invalid_argument& e )
|
||||||
|
{
|
||||||
|
wxFAIL;
|
||||||
|
importFormat = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string path = payload.substr( split + 1 );
|
||||||
|
wxASSERT( !path.empty() );
|
||||||
|
|
||||||
|
if( importFormat >= 0 )
|
||||||
|
importFile( path, importFormat );
|
||||||
|
}
|
||||||
|
|
||||||
// many many others.
|
// many many others.
|
||||||
default:
|
default:
|
||||||
;
|
;
|
||||||
|
|
|
@ -836,7 +836,7 @@ bool PCB_EDIT_FRAME::doAutoSave()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool PCB_EDIT_FRAME::ImportFile( const wxString& aFileName, int aFileType )
|
bool PCB_EDIT_FRAME::importFile( const wxString& aFileName, int aFileType )
|
||||||
{
|
{
|
||||||
switch( (IO_MGR::PCB_FILE_T) aFileType )
|
switch( (IO_MGR::PCB_FILE_T) aFileType )
|
||||||
{
|
{
|
||||||
|
@ -854,15 +854,15 @@ bool PCB_EDIT_FRAME::ImportFile( const wxString& aFileName, int aFileType )
|
||||||
UpdateTitle();
|
UpdateTitle();
|
||||||
OnModify();
|
OnModify();
|
||||||
|
|
||||||
|
// Extract a footprint library from the design and add it to the fp-lib-table
|
||||||
wxString newLibPath;
|
wxString newLibPath;
|
||||||
ArchiveModulesOnBoard( true, newfilename.GetName(), &newLibPath );
|
ArchiveModulesOnBoard( true, newfilename.GetName(), &newLibPath );
|
||||||
|
|
||||||
if( newLibPath.Length()>0 )
|
if( newLibPath.Length() > 0 )
|
||||||
{
|
{
|
||||||
FP_LIB_TABLE* prjlibtable = Prj().PcbFootprintLibs();
|
FP_LIB_TABLE* prjlibtable = Prj().PcbFootprintLibs();
|
||||||
const wxString& project_env = PROJECT_VAR_NAME;
|
const wxString& project_env = PROJECT_VAR_NAME;
|
||||||
wxString rel_path;
|
wxString rel_path, env_path;
|
||||||
wxString env_path;
|
|
||||||
|
|
||||||
wxGetEnv( project_env, &env_path );
|
wxGetEnv( project_env, &env_path );
|
||||||
|
|
||||||
|
@ -874,9 +874,7 @@ bool PCB_EDIT_FRAME::ImportFile( const wxString& aFileName, int aFileType )
|
||||||
newLibPath = rel_path;
|
newLibPath = rel_path;
|
||||||
|
|
||||||
FP_LIB_TABLE_ROW* row = new FP_LIB_TABLE_ROW( newfilename.GetName(),
|
FP_LIB_TABLE_ROW* row = new FP_LIB_TABLE_ROW( newfilename.GetName(),
|
||||||
newLibPath,
|
newLibPath, wxT( "KiCad" ), wxEmptyString );
|
||||||
wxT( "KiCad" ),
|
|
||||||
wxEmptyString ); // options
|
|
||||||
prjlibtable->InsertRow( row );
|
prjlibtable->InsertRow( row );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -893,12 +891,23 @@ bool PCB_EDIT_FRAME::ImportFile( const wxString& aFileName, int aFileType )
|
||||||
wxString msg = wxString::Format( _(
|
wxString msg = wxString::Format( _(
|
||||||
"Error occurred saving project specific footprint library "
|
"Error occurred saving project specific footprint library "
|
||||||
"table:\n\n%s" ),
|
"table:\n\n%s" ),
|
||||||
GetChars( ioe.What() )
|
GetChars( ioe.What() ) );
|
||||||
);
|
|
||||||
wxMessageBox( msg, _( "File Save Error" ), wxOK | wxICON_ERROR );
|
wxMessageBox( msg, _( "File Save Error" ), wxOK | wxICON_ERROR );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Two stage netlist update:
|
||||||
|
// - first, assign valid timestamps to footprints (no reannotation)
|
||||||
|
// - second, perform schematic annotation and update footprint references
|
||||||
|
// based on timestamps
|
||||||
|
Kiway().ExpressMail( FRAME_SCH, MAIL_SCH_PCB_UPDATE_REQUEST,
|
||||||
|
"no-annotate;by-reference", this );
|
||||||
|
Kiway().ExpressMail( FRAME_SCH, MAIL_SCH_PCB_UPDATE_REQUEST,
|
||||||
|
"quiet-annotate;by-timestamp", this );
|
||||||
|
|
||||||
|
fixEagleNets();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -912,53 +921,49 @@ bool PCB_EDIT_FRAME::ImportFile( const wxString& aFileName, int aFileType )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool PCB_EDIT_FRAME::FixEagleNets()
|
bool PCB_EDIT_FRAME::fixEagleNets()
|
||||||
{
|
{
|
||||||
KIWAY_PLAYER* schematicFrame = Kiway().Player( FRAME_SCH, false );
|
KIWAY_PLAYER* schematicFrame = Kiway().Player( FRAME_SCH, false );
|
||||||
|
|
||||||
// if the schematic file was also loaded. Fix any instances of ophaned zones and vias.
|
// if the schematic file was also loaded. Fix any instances of ophaned zones and vias.
|
||||||
if( schematicFrame )
|
if( !schematicFrame )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Get list of nets from schematic.
|
||||||
|
wxArrayString nets = schematicFrame->ListNets();
|
||||||
|
|
||||||
|
// perform netlist matching to prevent ophaned zones.
|
||||||
|
for( auto zone : GetBoard()->Zones() )
|
||||||
{
|
{
|
||||||
// Get list of nets from schematic.
|
wxString zoneNet = zone->GetNet()->GetNetname();
|
||||||
wxArrayString nets = schematicFrame->ListNets();
|
wxString localNet = "/" + zoneNet;
|
||||||
|
|
||||||
// perform netlist matching to prevent ophaned zones.
|
for( unsigned int i = 0; i < nets.GetCount(); i++ )
|
||||||
for( auto zone : GetBoard()->Zones() )
|
|
||||||
{
|
{
|
||||||
wxString zoneNet = zone->GetNet()->GetNetname();
|
if( nets[i].EndsWith( localNet ) )
|
||||||
wxString localNet = "/" + zoneNet;
|
|
||||||
|
|
||||||
for( int i = 0; i < nets.GetCount(); i++ )
|
|
||||||
{
|
{
|
||||||
if( nets[i].EndsWith( localNet ) )
|
NETINFO_ITEM* net = GetBoard()->FindNet( nets[i] );
|
||||||
{
|
|
||||||
NETINFO_ITEM* net = GetBoard()->FindNet( nets[i] );
|
|
||||||
|
|
||||||
if( net )
|
if( net )
|
||||||
{
|
zone->SetNetCode( net->GetNet() );
|
||||||
zone->SetNetCode( net->GetNet() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// perform netlist matching to prevent ophaned tracks/vias.
|
// perform netlist matching to prevent ophaned tracks/vias.
|
||||||
for( auto track : GetBoard()->Tracks() )
|
for( auto track : GetBoard()->Tracks() )
|
||||||
|
{
|
||||||
|
wxString trackNet = track->GetNet()->GetNetname();
|
||||||
|
wxString localNet = "/" + trackNet;
|
||||||
|
|
||||||
|
for( unsigned int i = 0; i < nets.GetCount(); i++ )
|
||||||
{
|
{
|
||||||
wxString trackNet = track->GetNet()->GetNetname();
|
if( nets[i].EndsWith( localNet ) )
|
||||||
wxString localNet = "/" + trackNet;
|
|
||||||
|
|
||||||
for( int i = 0; i < nets.GetCount(); i++ )
|
|
||||||
{
|
{
|
||||||
if( nets[i].EndsWith( localNet ) )
|
NETINFO_ITEM* net = GetBoard()->FindNet( nets[i] );
|
||||||
{
|
|
||||||
NETINFO_ITEM* net = GetBoard()->FindNet( nets[i] );
|
|
||||||
|
|
||||||
if( net )
|
if( net )
|
||||||
{
|
track->SetNetCode( net->GetNet() );
|
||||||
track->SetNetCode( net->GetNet() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -233,6 +233,18 @@ protected:
|
||||||
*/
|
*/
|
||||||
void duplicateItems( bool aIncrement ) override;
|
void duplicateItems( bool aIncrement ) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load the given filename but sets the path to the current project path.
|
||||||
|
* @param full filepath of file to be imported.
|
||||||
|
* @param aFileType PCB_FILE_T value for filetype
|
||||||
|
*/
|
||||||
|
bool importFile( const wxString& aFileName, int aFileType );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rematch orphaned zones and vias to schematic nets.
|
||||||
|
*/
|
||||||
|
bool fixEagleNets();
|
||||||
|
|
||||||
// protected so that PCB::IFACE::CreateWindow() is the only factory.
|
// protected so that PCB::IFACE::CreateWindow() is the only factory.
|
||||||
PCB_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent );
|
PCB_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent );
|
||||||
|
|
||||||
|
@ -869,19 +881,6 @@ public:
|
||||||
*/
|
*/
|
||||||
bool AppendBoardFile( const wxString& aFullFileName, int aCtl );
|
bool AppendBoardFile( const wxString& aFullFileName, int aCtl );
|
||||||
|
|
||||||
/**
|
|
||||||
* Function ImportFile
|
|
||||||
* load the given filename but sets the path to the current project path.
|
|
||||||
* @param full filepath of file to be imported.
|
|
||||||
* @param aFileType PCB_FILE_T value for filetype
|
|
||||||
*/
|
|
||||||
bool ImportFile( const wxString& aFileName, int aFileType ) override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Rematch ophaned zones and vias to schematic nets.
|
|
||||||
*/
|
|
||||||
bool FixEagleNets() override;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function SavePcbFile
|
* Function SavePcbFile
|
||||||
* writes the board data structures to \a a aFileName
|
* writes the board data structures to \a a aFileName
|
||||||
|
|
Loading…
Reference in New Issue