void PGM_BASE::SetLanguagePath()
{
    SEARCH_STACK    guesses;

    SystemDirsAppend( &guesses );

    // Add our internat dir to the wxLocale catalog of paths
    for( unsigned i = 0; i < guesses.GetCount(); i++ )
    {
        wxFileName fn( guesses[i], wxEmptyString );

        // Append path for Windows and unix KiCad package install
        fn.AppendDir( wxT( "share" ) );
        fn.AppendDir( wxT( "internat" ) );

        if( fn.IsDirReadable() )
        {
            wxLogDebug( wxT( "Adding locale lookup path: " ) + fn.GetPath() );
            wxLocale::AddCatalogLookupPathPrefix( fn.GetPath() );
        }

        // Append path for unix standard install
        fn.RemoveLastDir();
        fn.AppendDir( wxT( "kicad" ) );
        fn.AppendDir( wxT( "internat" ) );

        if( fn.IsDirReadable() )
        {
            wxLogDebug( wxT( "Adding locale lookup path: " ) + fn.GetPath() );
            wxLocale::AddCatalogLookupPathPrefix( fn.GetPath() );
        }
    }
}
DIALOG_EESCHEMA_CONFIG::DIALOG_EESCHEMA_CONFIG( wxWindow* aParent,
            wxString* aCallersProjectSpecificLibPaths, wxArrayString* aCallersLibNames ) :
    DIALOG_EESCHEMA_CONFIG_FBP( aParent ),
    m_callers_project_specific_lib_paths( aCallersProjectSpecificLibPaths ),
    m_callers_lib_names( aCallersLibNames ),
    m_lib_list_changed( false ),
    m_lib_path_changed( false )
{
    m_ListLibr->InsertItems( *aCallersLibNames, 0 );

    // Load user libs paths:
    wxArrayString paths;

    SEARCH_STACK::Split( &paths, *aCallersProjectSpecificLibPaths );

    for( unsigned i=0; i<paths.GetCount();  ++i )
    {
        wxString path = paths[i];

        if( wxFileName::DirExists( Prj().AbsolutePath( path ) ) )
            m_listUserPaths->Append( path );
    }

    // Display actual library paths which come in part from KIFACE::KifaceSearch()
    // along with aCallersProjectSpecificLibPaths at the front.
    SEARCH_STACK* libpaths = Prj().SchSearchS();

    DBG( libpaths->Show( __func__ ); )

    for( unsigned ii = 0; ii < libpaths->GetCount(); ii++ )
SEARCH_STACK* PROJECT::SchSearchS()
{
    SEARCH_STACK* ss = (SEARCH_STACK*) GetElem( PROJECT::ELEM_SCH_SEARCH_STACK );

    wxASSERT( !ss || dynamic_cast<SEARCH_STACK*>( GetElem( PROJECT::ELEM_SCH_SEARCH_STACK ) ) );

    if( !ss )
    {
        ss = new SEARCH_STACK();

        // Make PROJECT the new SEARCH_STACK owner.
        SetElem( PROJECT::ELEM_SCH_SEARCH_STACK, ss );

        // to the empty SEARCH_STACK for SchSearchS(), add project dir as first
        ss->AddPaths( m_project_name.GetPath() );

        // next add the paths found in *.pro, variable "LibDir"
        wxString        libDir;

        try
        {
            PART_LIBS::LibNamesAndPaths( this, false, &libDir );
        }
        catch( const IO_ERROR& DBG( ioe ) )
        {
            DBG(printf( "%s: %s\n", __func__, TO_UTF8( ioe.What() ) );)
        }

        if( !!libDir )
        {
            wxArrayString   paths;

            SEARCH_STACK::Split( &paths, libDir );

            for( unsigned i =0; i<paths.GetCount();  ++i )
            {
                wxString path = AbsolutePath( paths[i] );

                ss->AddPaths( path );     // at the end
            }
        }

        // append all paths from aSList
        add_search_paths( ss, Kiface().KifaceSearch(), -1 );

        // addLibrarySearchPaths( SEARCH_STACK* aSP, wxConfigBase* aCfg )
        // This is undocumented, but somebody wanted to store !schematic!
        // library search paths in the .kicad_common file?
        add_search_paths( ss, Pgm().CommonSettings(), -1 );
    }
/**
 * Function FindFileInSearchPaths
 * looks in "this" for \a aFilename, but first modifies every search
 * path by appending a list of path fragments from aSubdirs.  That modification
 * is not relative.
 */
wxString FindFileInSearchPaths( const SEARCH_STACK& aStack,
        const wxString& aFilename, const wxArrayString* aSubdirs )
{
    wxPathList paths;

    for( unsigned i = 0; i < aStack.GetCount(); ++i )
    {
        wxFileName fn( aStack[i], wxEmptyString );

        if( aSubdirs )
        {
            for( unsigned j = 0; j < aSubdirs->GetCount(); j++ )
                fn.AppendDir( (*aSubdirs)[j] );
        }

        wxLogDebug( wxT( "    %s" ), GetChars( fn.GetFullPath() ) );

        if( fn.DirExists() )
        {
            paths.Add( fn.GetPath() );
        }
    }

    return paths.FindValidPath( aFilename );
}
/**
 * Function missingLegacyLibs
 * tests the list of \a aLibNames by URI to determine if any of them are missing from
 * the #FP_LIB_TABLE.
 *
 * @note The missing legacy footprint library test is performed by using old library
 *       file path lookup method.  If the library is found, it is compared against all
 *       of the URIs in the table rather than the nickname.  This was done because the
 *       user could change the nicknames from the default table.  Using the full path
 *       is more reliable.
 *
 * @param aLibNames is the list of legacy library names.
 * @param aErrorMsg is a pointer to a wxString object to store the URIs of any missing
 *                  legacy library paths.  Can be NULL.
 * @return true if there are missing legacy libraries.  Otherwise false.
 */
static bool missingLegacyLibs( FP_LIB_TABLE* aTbl, SEARCH_STACK& aSStack,
        const wxArrayString& aLibNames, wxString* aErrorMsg )
{
    bool missing = false;

    for( unsigned i = 0;  i < aLibNames.GetCount();  i++ )
    {
        wxFileName  fn( wxEmptyString, aLibNames[i], LegacyFootprintLibPathExtension );

        wxString    legacyLibPath = aSStack.FindValidPath( fn.GetFullPath() );

        /*
        if( legacyLibPath.IsEmpty() )
            continue;
        */

        if( !aTbl->FindRowByURI( legacyLibPath ) )
        {
            missing = true;

            if( aErrorMsg )
            {
                *aErrorMsg += wxChar( '"' );

                if( !legacyLibPath )
                    *aErrorMsg += !legacyLibPath ? aLibNames[i] : legacyLibPath;

                *aErrorMsg += wxT( "\"\n" );
            }
        }
    }

    return missing;
}
void DIALOG_EDIT_COMPONENT_IN_LIBRARY::BrowseAndSelectDocFile( wxCommandEvent& event )
{
    PROJECT&        prj = Prj();
    SEARCH_STACK*   search = prj.SchSearchS();

    wxString    mask = wxT( "*" );
    wxString    docpath = prj.GetRString( PROJECT::DOC_PATH );

    if( !docpath )
        docpath = search->LastVisitedPath( wxT( "doc" ) );

    wxString    fullFileName = EDA_FILE_SELECTOR( _( "Doc Files" ),
                                                  docpath,
                                                  wxEmptyString,
                                                  wxEmptyString,
                                                  mask,
                                                  this,
                                                  wxFD_OPEN,
                                                  true );
    if( fullFileName.IsEmpty() )
        return;

    /* If the path is already in the library search paths
     * list, just add the library name to the list.  Otherwise, add
     * the library name with the full or relative path.
     * the relative path, when possible is preferable,
     * because it preserve use of default libraries paths, when the path is a sub path of
     * these default paths
     */
    wxFileName fn = fullFileName;

    prj.SetRString( PROJECT::DOC_PATH, fn.GetPath() );

    wxString filename = search->FilenameWithRelativePathInSearchList(
            fullFileName, wxPathOnly( Prj().GetProjectFullName() ) );

    // Filenames are always stored in unix like mode, ie separator "\" is stored as "/"
    // to ensure files are identical under unices and windows
#ifdef __WINDOWS__
    filename.Replace( wxT( "\\" ), wxT( "/" ) );
#endif
    m_DocfileCtrl->SetValue( filename );
}
static bool copy_pro_file_template( const SEARCH_STACK& aSearchS, const wxString& aDestination )
{
    if( aDestination.IsEmpty() )
    {
        wxLogTrace( tracePathsAndFiles, "%s: destination is empty.", __func__ );
        return false;
    }

    wxString templateFile = wxT( "kicad." ) + ProjectFileExtension;

    wxString kicad_pro_template = aSearchS.FindValidPath( templateFile );

    if( !kicad_pro_template )
    {
        wxLogTrace( tracePathsAndFiles, "%s: template file '%s' not found using search paths.",
                    __func__, TO_UTF8( templateFile ) );

        wxFileName  templ( wxStandardPaths::Get().GetDocumentsDir(),
                            wxT( "kicad" ), ProjectFileExtension );

        if( !templ.IsFileReadable() )
        {
            wxString msg = wxString::Format( _(
                    "Unable to find \"%s\" template config file." ),
                    GetChars( templateFile ) );

            DisplayErrorMessage( nullptr, _( "Error copying project file template" ), msg );

            return false;
        }

        kicad_pro_template = templ.GetFullPath();
    }

    wxLogTrace( tracePathsAndFiles, "%s: using template file '%s' as project file.",
                __func__, TO_UTF8( kicad_pro_template ) );

    // Verify aDestination can be created. if this is not the case, wxCopyFile
    // will generate a crappy log error message, and we *do not want* this kind
    // of stupid message
    wxFileName fn( aDestination );
    bool success = true;

    if( fn.IsOk() && fn.IsDirWritable() )
        success = wxCopyFile( kicad_pro_template, aDestination );
    else
    {
        wxLogMessage( _( "Cannot create prj file \"%s\" (Directory not writable)" ),
                      GetChars( aDestination) );
        success = false;
    }

    return success;
}
wxString RETAINED_PATH::LastVisitedPath( const SEARCH_STACK& aSStack, const wxString& aSubPathToSearch )
{
    if( !!m_retained_path )
        return m_retained_path;

    wxString path;

    // Initialize default path to the main default lib path
    // this is the second path in list (the first is the project path)
    unsigned pcount = aSStack.GetCount();

    if( pcount )
    {
        unsigned ipath = 0;

        if( aSStack[0] == wxGetCwd() )
            ipath = 1;

        // First choice of path:
        if( ipath < pcount )
            path = aSStack[ipath];

        // Search a sub path matching aSubPathToSearch
        if( !aSubPathToSearch.IsEmpty() )
        {
            for( ; ipath < pcount; ipath++ )
            {
                if( aSStack[ipath].Contains( aSubPathToSearch ) )
                {
                    path = aSStack[ipath];
                    break;
                }
            }
        }
    }

    if( path.IsEmpty() )
        path = wxGetCwd();

    return path;
}
Beispiel #9
0
bool PGM_KICAD::OnPgmInit( wxApp* aWxApp )
{
    m_wx_app = aWxApp;      // first thing.

    wxString absoluteArgv0 = wxStandardPaths::Get().GetExecutablePath();

    if( !wxIsAbsolutePath( absoluteArgv0 ) )
    {
        wxLogError( wxT( "No meaningful argv[0]" ) );
        return false;
    }

    // Set LIB_ENV_VAR *before* loading the KIFACE DSOs, in case they have hard
    // dependencies on subsidiary DSOs below it.
    set_lib_env_var( absoluteArgv0 );

    if( !initPgm() )
        return false;

    m_bm.Init();

    // Add search paths to feed the PGM_KICAD::SysSearch() function,
    // currenly limited in support to only look for project templates
    {
        SEARCH_STACK bases;

        SystemDirsAppend( &bases );

        // DBG( bases.Show( (std::string(__func__) + " bases").c_str() );)

        for( unsigned i = 0; i < bases.GetCount(); ++i )
        {
            wxFileName fn( bases[i], wxEmptyString );

            // Add KiCad template file path to search path list.
            fn.AppendDir( wxT( "template" ) );
            m_bm.m_search.AddPaths( fn.GetPath() );
        }

        //DBG( m_bm.m_search.Show( (std::string( __func__ ) + " SysSearch()").c_str() );)
    }

    // Must be called before creating the main frame in order to
    // display the real hotkeys in menus or tool tips
    extern struct EDA_HOTKEY_CONFIG kicad_Manager_Hokeys_Descr[];
    ReadHotkeyConfig( KICAD_MANAGER_FRAME_NAME, kicad_Manager_Hokeys_Descr );

    KICAD_MANAGER_FRAME* frame = new KICAD_MANAGER_FRAME( NULL, wxT( "KiCad" ),
                                     wxDefaultPosition, wxDefaultSize );
    App().SetTopWindow( frame );

    Kiway.SetTop( frame );

    bool prjloaded = false;    // true when the project is loaded

    if( App().argc > 1 )
        frame->SetProjectFileName( App().argv[1] );

    else if( GetFileHistory().GetCount() )
    {
        wxString last_pro = GetFileHistory().GetHistoryFile( 0 );

        if( !wxFileExists( last_pro ) )
        {
            GetFileHistory().RemoveFileFromHistory( 0 );

            wxFileName namelessProject( wxStandardPaths::Get().GetDocumentsDir(), NAMELESS_PROJECT,
                                        ProjectFileExtension );

            frame->SetProjectFileName( namelessProject.GetFullPath() );
        }
        else
        {
            // Try to open the last opened project,
            // if a project name is not given when starting Kicad
            frame->SetProjectFileName( last_pro );

            wxCommandEvent cmd( 0, wxID_FILE1 );

            frame->OnFileHistory( cmd );
            prjloaded = true;    // OnFileHistory() loads the project
        }
    }
    else	// there is no history
    {
            wxFileName namelessProject( wxStandardPaths::Get().GetDocumentsDir(), NAMELESS_PROJECT,
                                        ProjectFileExtension );

            frame->SetProjectFileName( namelessProject.GetFullPath() );
    }

    if( !prjloaded )
    {
        wxCommandEvent cmd( 0, wxID_ANY );

        frame->OnLoadProject( cmd );
    }

    frame->Show( true );
    frame->Raise();

    return true;
}
// non-member so it can be moved easily, and kept REALLY private.
// Do NOT Clear() in here.
static void add_search_paths( SEARCH_STACK* aDst, const SEARCH_STACK& aSrc, int aIndex )
{
    for( unsigned i=0; i<aSrc.GetCount();  ++i )
        aDst->AddPaths( aSrc[i], aIndex );
}
bool LIB_EDIT_FRAME::SaveActiveLibrary( bool newFile )
{
    wxFileName fn;
    wxString   msg;

    m_canvas->EndMouseCapture( ID_NO_TOOL_SELECTED, m_canvas->GetDefaultCursor() );

    PART_LIB* lib = GetCurLib();

    // Just in case the library hasn't been cached yet.
    lib->GetCount();

    if( !lib )
    {
        DisplayError( this, _( "No library specified." ) );
        return false;
    }

    wxString oldFileName = lib->GetFullFileName();

    if( GetScreen()->IsModify() )
    {
        if( IsOK( this, _( "Include last component changes?" ) ) )
        {
            lib->EnableBuffering();

            try
            {
                SaveOnePart( lib, false );
            }
            catch( ... )
            {
                lib->EnableBuffering( false );
                msg.Printf( _( "Unexpected error occured saving part to '%s' symbol library." ),
                            lib->GetName() );
                DisplayError( this, msg );
                return false;
            }

            lib->EnableBuffering( false );
        }
    }

    if( newFile )
    {
        PROJECT&        prj = Prj();
        SEARCH_STACK*   search = prj.SchSearchS();

        // Get a new name for the library
        wxString default_path = prj.GetRString( PROJECT::SCH_LIB_PATH );

        if( !default_path )
            default_path = search->LastVisitedPath();

        wxFileDialog dlg( this, _( "Part Library Name:" ), default_path,
                          wxEmptyString, SchematicLibraryFileWildcard,
                          wxFD_SAVE | wxFD_OVERWRITE_PROMPT );

        if( dlg.ShowModal() == wxID_CANCEL )
            return false;

        fn = dlg.GetPath();

        // The GTK file chooser doesn't return the file extension added to
        // file name so add it here.
        if( fn.GetExt().IsEmpty() )
            fn.SetExt( SchematicLibraryFileExtension );

        prj.SetRString( PROJECT::SCH_LIB_PATH, fn.GetPath() );
    }
    else
    {
        fn = wxFileName( lib->GetFullFileName() );

        msg.Printf( _( "Modify library file '%s' ?" ), GetChars( fn.GetFullPath() ) );

        if( !IsOK( this, msg ) )
            return false;
    }

    // Verify the user has write privileges before attempting to save the library file.
    if( !IsWritable( fn ) )
        return false;

    ClearMsgPanel();

    wxFileName libFileName = fn;
    wxFileName backupFileName = fn;

    // Rename the old .lib file to .bak.
    if( libFileName.FileExists() )
    {
        backupFileName.SetExt( "bak" );

        if( backupFileName.FileExists() )
            wxRemoveFile( backupFileName.GetFullPath() );

        if( !wxRenameFile( libFileName.GetFullPath(), backupFileName.GetFullPath() ) )
        {
            libFileName.MakeAbsolute();
            msg = _( "Failed to rename old component library file " ) +
                  backupFileName.GetFullPath();
            DisplayError( this, msg );
        }
    }

    wxFileName docFileName = libFileName;

    docFileName.SetExt( DOC_EXT );

    // Rename .doc file to .bck.
    if( docFileName.FileExists() )
    {
        backupFileName.SetExt( "bck" );

        if( backupFileName.FileExists() )
            wxRemoveFile( backupFileName.GetFullPath() );

        if( !wxRenameFile( docFileName.GetFullPath(), backupFileName.GetFullPath() ) )
        {
            msg = _( "Failed to save old library document file " ) + backupFileName.GetFullPath();
            DisplayError( this, msg );
        }
    }

    try
    {
        lib->SetFileName( fn.GetFullPath() );
        lib->Save();
    }
    catch( ... /* IO_ERROR ioe */ )
    {
        lib->SetFileName( oldFileName );
        msg.Printf( _( "Failed to create symbol library file '%s'" ),
                    GetChars( docFileName.GetFullPath() ) );
        DisplayError( this, msg );
        return false;
    }

    lib->SetFileName( oldFileName );
    msg.Printf( _( "Library file '%s' saved" ), GetChars( fn.GetFullPath() ) );
    fn.SetExt( DOC_EXT );
    wxString msg1;
    msg1.Printf( _( "Documentation file '%s' saved" ), GetChars( fn.GetFullPath() ) );
    AppendMsgPanel( msg, msg1, BLUE );
    UpdateAliasSelectList();
    UpdatePartSelectList();
    refreshSchematic();

    return true;
}
void LIB_EDIT_FRAME::LoadOneSymbol()
{
    LIB_PART*       part = GetCurPart();

    // Exit if no library entry is selected or a command is in progress.
    if( !part || ( m_drawItem && m_drawItem->GetFlags() ) )
        return;

    PROJECT&        prj = Prj();
    SEARCH_STACK*   search = prj.SchSearchS();

    m_canvas->SetIgnoreMouseEvents( true );

    wxString default_path = prj.GetRString( PROJECT::SCH_LIB_PATH );
    if( !default_path )
        default_path = search->LastVisitedPath();

    wxFileDialog dlg( this, _( "Import Symbol Drawings" ), default_path,
                      wxEmptyString, SchematicSymbolFileWildcard,
                      wxFD_OPEN | wxFD_FILE_MUST_EXIST );

    if( dlg.ShowModal() == wxID_CANCEL )
        return;

    SetCrossHairPosition( wxPoint( 0, 0 ) );
    m_canvas->MoveCursorToCrossHair();
    m_canvas->SetIgnoreMouseEvents( false );

    wxString filename = dlg.GetPath();

    prj.SetRString( PROJECT::SCH_LIB_PATH, filename );

    std::unique_ptr<PART_LIB> lib( new PART_LIB( LIBRARY_TYPE_SYMBOL, filename ) );

    wxString err;

    if( !lib->Load( err ) )
    {
        wxString msg = wxString::Format( _(
            "Error '%s' occurred loading part file '%s'." ),
            GetChars( err ),
            GetChars( filename )
            );
        DisplayError( this, msg );
        return;
    }

    if( lib->IsEmpty() )
    {
        wxString msg = wxString::Format( _(
            "No parts found in part file '%s'." ),
            GetChars( filename )
            );
        DisplayError( this, msg );
        return;
    }

    if( lib->GetCount() > 1 )
    {
        wxString msg = wxString::Format( _(
            "More than one part in part file '%s'." ),
            GetChars( filename )
            );
        wxMessageBox( msg, _( "Warning" ), wxOK | wxICON_EXCLAMATION, this );
    }

    LIB_PART*   first = lib->GetFirstEntry()->GetPart();
    LIB_ITEMS&  drawList = first->GetDrawItemList();

    for( LIB_ITEM& item : drawList )
    {
        if( item.Type() == LIB_FIELD_T )
            continue;

        if( item.GetUnit() )
            item.SetUnit( m_unit );

        if( item.GetConvert() )
            item.SetConvert( m_convert );

        item.SetFlags( IS_NEW | SELECTED );

        LIB_ITEM* newItem = (LIB_ITEM*) item.Clone();

        newItem->SetParent( part );
        part->AddDrawItem( newItem );
    }

    part->RemoveDuplicateDrawItems();
    part->ClearSelectedItems();

    OnModify();
    m_canvas->Refresh();
}
void LIB_EDIT_FRAME::SaveOneSymbol()
{
    wxString        msg;
    PROJECT&        prj = Prj();
    SEARCH_STACK*   search = prj.SchSearchS();
    LIB_PART*       part = GetCurPart();

    if( !part || part->GetDrawItemList().empty() )
        return;

    wxString default_path = prj.GetRString( PROJECT::SCH_LIB_PATH );
    if( !default_path )
        default_path = search->LastVisitedPath();

    wxFileDialog dlg( this, _( "Export Symbol Drawings" ), default_path,
                      part->GetName(), SchematicSymbolFileWildcard,
                      wxFD_SAVE | wxFD_OVERWRITE_PROMPT );

    if( dlg.ShowModal() == wxID_CANCEL )
        return;

    wxFileName fn = dlg.GetPath();

    /* The GTK file chooser doesn't return the file extension added to
     * file name so add it here. */
    if( fn.GetExt().IsEmpty() )
        fn.SetExt( SchematicSymbolFileExtension );

    prj.SetRString( PROJECT::SCH_LIB_PATH, fn.GetPath() );

    msg.Printf( _( "Saving symbol in '%s'" ), GetChars( fn.GetPath() ) );
    SetStatusText( msg );

    wxString line;

    // File header
    line << wxT( LIBFILE_IDENT ) << wxT( " " ) << LIB_VERSION_MAJOR
         << wxT( "." ) << LIB_VERSION_MINOR << wxT( "  SYMBOL  " )
         << wxT( "Date: " ) << DateAndTime() << wxT( "\n" );

    // Component name comment and definition.
    line << wxT( "# SYMBOL " ) << part->GetName() << wxT( "\n#\nDEF " )
         << part->GetName() << wxT( " " );

    if( !part->GetReferenceField().GetText().IsEmpty() )
        line << part->GetReferenceField().GetText() << wxT( " " );
    else
        line << wxT( "~ " );

    line << 0 << wxT( " " ) << part->GetPinNameOffset() << wxT( " " );

    if( part->ShowPinNumbers() )
        line << wxT( "Y " );
    else
        line << wxT( "N " );

    if( part->ShowPinNames() )
        line << wxT( "Y " );
    else
        line << wxT( "N " );

    line << wxT( "1 0 N\n" );

    try
    {
        FILE_OUTPUTFORMATTER    formatter( fn.GetFullPath() );

        try
        {
            formatter.Print( 0, "%s", TO_UTF8( line ) );
            part->GetReferenceField().Save( formatter );
            part->GetValueField().Save( formatter );
            formatter.Print( 0, "DRAW\n" );

            LIB_ITEMS& drawList = part->GetDrawItemList();

            for( LIB_ITEM& item : drawList )
            {
                if( item.Type() == LIB_FIELD_T )
                    continue;

                // Don't save unused parts or alternate body styles.
                if( m_unit && item.GetUnit() && ( item.GetUnit() != m_unit ) )
                    continue;

                if( m_convert && item.GetConvert() && ( item.GetConvert() != m_convert ) )
                    continue;

                item.Save( formatter );
            }

            formatter.Print( 0, "ENDDRAW\n" );
            formatter.Print( 0, "ENDDEF\n" );
        }
        catch( const IO_ERROR& ioe )
        {
            msg.Printf( _( "An error occurred attempting to save symbol file '%s'" ),
                        GetChars( fn.GetFullPath() ) );
            DisplayError( this, msg );
        }
    }
    catch( const IO_ERROR& ioe )
    {
        DisplayError( this, ioe.errorText );
        return;
    }
}
// See also FindKicadHelpPath.cpp.notused.
wxString SearchHelpFileFullPath( const SEARCH_STACK& aSStack, const wxString& aBaseName )
{
    wxArrayString   subdirs;
    wxArrayString   altsubdirs;
    SEARCH_STACK    ss = aSStack;

    // It might already be in aSStack, but why depend on other code
    // far away when it's so easy to add it again (to our copy) as the first place to look.

    // This is CMAKE_INSTALL_PREFIX unless DEFAULT_INSTALL_PATH was defined during
    // build configuration:
    ss.AddPaths( wxT( DEFAULT_INSTALL_PATH ), 0 );

#if defined(__WXMAC__)
    ss.AddPaths( GetOSXKicadMachineDataDir() );
    ss.AddPaths( Pgm().GetExecutablePath(), 0 );

    // OS X packages can have the help files in
    // /Library/Application\ Support/kicad/help,
    // and in Contents/SharedSupport/help inside the
    // bundle.
    // Below we account for an international subdirectory.
    subdirs.Add( wxT( "help" ) );
    altsubdirs.Add( wxT( "Contents" ) );
    altsubdirs.Add( wxT( "SharedSupport" ) );
    altsubdirs.Add( wxT( "help" ) );
#endif

#if ! defined(__WXMAC__) // && defined(__linux__)
    // This is the executable path minus the trailing bin directory used on Windows and Linux.
    wxFileName tmp( Pgm().GetExecutablePath(), wxEmptyString );
    wxArrayString binDirs = tmp.GetDirs();

    if( !binDirs.IsEmpty() && binDirs[ binDirs.GetCount() - 1 ].CmpNoCase( wxT( "bin" ) ) == 0 )
        tmp.RemoveLastDir();

    ss.AddPaths( tmp.GetPath(), 0 );

    // Based on kicad-doc.bzr/CMakeLists.txt, line 20, the help files are
    // installed into "<CMAKE_INSTALL_PREFIX>/share/doc/kicad/help" for linux.
    // This is ${KICAD_HELP} var in that CMakeLists.txt file.
    // Below we account for an international subdirectory.
    subdirs.Add( wxT( "share" ) );
    subdirs.Add( wxT( "doc" ) );
    subdirs.Add( wxT( "kicad" ) );
    subdirs.Add( wxT( "help" ) );

    // Based on kicad-doc.bzr/CMakeLists.txt, line 35, the help files are
    // installed into "<CMAKE_INSTALL_PREFIX>/doc/help" for Windows.
    // This is ${KICAD_HELP} var in that CMakeLists.txt file.
    // Below we account for an international subdirectory.
    altsubdirs.Add( wxT( "doc" ) );
    altsubdirs.Add( wxT( "help" ) );
#endif

    // If there's a KICAD environment variable set, always use that guy's path first.
    if( !Pgm().GetKicadEnvVariable().IsEmpty() )
        ss.AddPaths( Pgm().GetKicadEnvVariable(), 0 );

    /* Search for a help file.
     *  we *must* find a help file.
     *  so help is searched in directories in this order:
     *  help/<canonical name> like help/en_GB
     *  help/<short name> like help/en
     *  help/en
     */

    wxLocale* i18n = Pgm().GetLocale();

    // We try to find help file in help/<canonical name>
    // If fails, try to find help file in help/<short canonical name>
    // If fails, try to find help file in help/en
    wxArrayString locale_name_dirs;
    locale_name_dirs.Add( i18n->GetCanonicalName() );           // canonical name like fr_FR

    // wxLocale::GetName() does not return always the short name
    locale_name_dirs.Add( i18n->GetName().BeforeLast( '_' ) );  // short canonical name like fr
    locale_name_dirs.Add( wxT( "en" ) );                        // default (en)

#if defined(DEBUG) && 1
    ss.Show( wxString( __func__ ) );
    wxLogDebug( wxT( "%s: m_help_file:'%s'" ), __func__, GetChars( aBaseName ) );
#endif

    wxLogDebug( wxT( "Checking SEARCH_STACK for file %s" ), GetChars( aBaseName ) );

    // Help files can be html (.html ext) or pdf (.pdf ext) files.
    // Therefore, <BaseName>.html file is searched and if not found,
    // <BaseName>.pdf file is searched in the same paths
    wxString fn;

    for( unsigned ii = 0; ii < locale_name_dirs.GetCount(); ii++ )
    {
        subdirs.Add( locale_name_dirs[ii] );
        altsubdirs.Add( locale_name_dirs[ii] );

        fn = FindFileInSearchPaths( ss, aBaseName + wxT( ".html" ), &altsubdirs );

        if( !fn.IsEmpty() )
            break;

        fn = FindFileInSearchPaths( ss, aBaseName + wxT( ".pdf" ), &altsubdirs );

        if( !fn.IsEmpty() )
            break;

        fn = FindFileInSearchPaths( ss, aBaseName + wxT( ".html" ), &subdirs );

        if( !fn.IsEmpty() )
            break;

        fn = FindFileInSearchPaths( ss, aBaseName + wxT( ".pdf" ), &subdirs );

        if( !fn.IsEmpty() )
            break;

        subdirs.RemoveAt( subdirs.GetCount() - 1 );
        altsubdirs.RemoveAt( altsubdirs.GetCount() - 1 );
    }

    return fn;
}
void SystemDirsAppend( SEARCH_STACK* aSearchStack )
{
    // No clearing is done here, the most general approach is NOT to assume that
    // our appends will be the only thing in the stack.  This function has no
    // knowledge of caller's intentions.

    // wxPathList::AddEnvList() is broken, use SEARCH_STACK::AddPaths().
    // SEARCH_STACK::AddPaths() will verify readability and existence of
    // each directory before adding.
    SEARCH_STACK maybe;

    // User environment variable path is the first search path.  Chances are
    // if the user is savvy enough to set an environment variable they know
    // what they are doing.  It should take precedence over anything else.
    // Otherwise don't set it.
    maybe.AddPaths( wxGetenv( wxT( "KICAD" ) ) );

#ifdef __WXMAC__
    // Add the directory for the user-dependent, program specific data files.
    maybe.AddPaths( GetOSXKicadUserDataDir() );

    // Global machine specific application data
    maybe.AddPaths( GetOSXKicadMachineDataDir() );

    // Global application specific data files inside bundle
    maybe.AddPaths( GetOSXKicadDataDir() );
#else
    // This is from CMAKE_INSTALL_PREFIX.
    // Useful when KiCad is installed by `make install`.
    // Use as second ranked place.
    maybe.AddPaths( wxT( DEFAULT_INSTALL_PATH ) );

    // Add the directory for the user-dependent, program specific data files.
    // According to wxWidgets documentation:
    // Unix: ~/.appname
    // Windows: C:\Documents and Settings\username\Application Data\appname
    maybe.AddPaths( wxStandardPaths::Get().GetUserDataDir() );

    {
        // Should be full path to this program executable.
        wxString   bin_dir = Pgm().GetExecutablePath();

#if defined(__MINGW32__)
        // bin_dir uses unix path separator.  So to parse with wxFileName
        // use windows separator, especially important for server inclusion:
        // like: \\myserver\local_path .
        bin_dir.Replace( wxFileName::GetPathSeparator( wxPATH_UNIX ),
                         wxFileName::GetPathSeparator( wxPATH_WIN ) );
#endif

        wxFileName bin_fn( bin_dir, wxEmptyString );

        // Dir of the global (not user-specific), application specific, data files.
        // From wx docs:
        // Unix: prefix/share/appname
        // Windows: the directory where the executable file is located
        // Mac: appname.app/Contents/SharedSupport bundle subdirectory
        wxString data_dir = wxStandardPaths::Get().GetDataDir();

        if( bin_fn.GetPath() != data_dir )
        {
            // add data_dir if it is different from the bin_dir
            maybe.AddPaths( data_dir );
        }

        // Up one level relative to binary path with "share" appended below.
        bin_fn.RemoveLastDir();
        maybe.AddPaths( bin_fn.GetPath() );
    }

    /* The normal OS program file install paths allow for a binary to be
     * installed in a different path from the library files.  This is
     * useful for development purposes so the library and documentation
     * files do not need to be installed separately.  If someone can
     * figure out a way to implement this without #ifdef, please do.
     */
#if defined(__MINGW32__)
    maybe.AddPaths( wxGetenv( wxT( "PROGRAMFILES" ) ) );
#else
    maybe.AddPaths( wxGetenv( wxT( "PATH" ) ) );
#endif
#endif

#if defined(DEBUG) && 0
    maybe.Show( "maybe wish list" );
#endif

    // Append 1) kicad, 2) kicad/share, 3) share, and 4) share/kicad to each
    // possible base path in 'maybe'. Since SEARCH_STACK::AddPaths() will verify
    // readability and existence of each directory, not all of these will be
    // actually appended.
    for( unsigned i = 0; i < maybe.GetCount();  ++i )
    {
        wxFileName fn( maybe[i], wxEmptyString );

#ifndef __WXMAC__
        if( fn.GetPath().AfterLast( fn.GetPathSeparator() ) == wxT( "bin" ) )
        {
            fn.RemoveLastDir();

            if( !fn.GetDirCount() )
                continue;               // at least on linux
        }
#endif

        aSearchStack->AddPaths( fn.GetPath() );

#ifndef __WXMAC__
        fn.AppendDir( wxT( "kicad" ) );
        aSearchStack->AddPaths( fn.GetPath() );     // add maybe[i]/kicad

        fn.AppendDir( wxT( "share" ) );
        aSearchStack->AddPaths( fn.GetPath() );     // add maybe[i]/kicad/share

        fn.RemoveLastDir();                         // ../  clear share
        fn.RemoveLastDir();                         // ../  clear kicad

        fn.AppendDir( wxT( "share" ) );
        aSearchStack->AddPaths( fn.GetPath() );     // add maybe[i]/share

        fn.AppendDir( wxT( "kicad" ) );
        aSearchStack->AddPaths( fn.GetPath() );     // add maybe[i]/share/kicad
#endif
    }

#if defined(DEBUG) && 0
    // final results:
    aSearchStack->Show( __func__ );
#endif
}
/**
 * Function convertFromLegacy
 * converts the footprint names in \a aNetList from the legacy format to the #FPID format.
 *
 * @param aNetList is the #NETLIST object to convert.
 * @param aLibNames is the list of legacy footprint library names from the currently loaded
 *                  project.
 * @param aReporter is the #REPORTER object to dump messages into.
 * @return true if all footprint names were successfully converted to a valid FPID.
 */
static bool convertFromLegacy( FP_LIB_TABLE* aTbl, SEARCH_STACK& aSStack, NETLIST& aNetList,
        const wxArrayString& aLibNames, REPORTER* aReporter = NULL ) throw( IO_ERROR )
{
    wxString   msg;
    FPID       lastFPID;
    COMPONENT* component;
    MODULE*    module = 0;
    bool       retv = true;

    if( aNetList.IsEmpty() )
        return true;

    aNetList.SortByFPID();

    wxString   libPath;

    PLUGIN::RELEASER pi( IO_MGR::PluginFind( IO_MGR::LEGACY ) );

    for( unsigned ii = 0; ii < aNetList.GetCount(); ii++ )
    {
        component = aNetList.GetComponent( ii );

        // The footprint hasn't been assigned yet so ignore it.
        if( component->GetFPID().empty() )
            continue;

        if( component->GetFPID() != lastFPID )
        {
            module = NULL;

            for( unsigned ii = 0; ii < aLibNames.GetCount(); ii++ )
            {
                wxFileName fn( wxEmptyString, aLibNames[ii], LegacyFootprintLibPathExtension );

                libPath = aSStack.FindValidPath( fn.GetFullPath() );

                if( !libPath )
                {
                    if( aReporter )
                    {
                        msg.Printf( _( "Cannot find footprint library file '%s' in any of the "
                                       "KiCad legacy library search paths.\n" ),
                                    GetChars( fn.GetFullPath() ) );
                        aReporter->Report( msg );
                    }

                    retv = false;
                    continue;
                }

                module = pi->FootprintLoad( libPath, component->GetFPID().GetFootprintName() );

                if( module )
                {
                    lastFPID = component->GetFPID();
                    break;
                }
            }
        }

        if( !module )
        {
            if( aReporter )
            {
                msg.Printf( _( "Component '%s' footprint '%s' was not found in any legacy "
                               "library.\n" ),
                            GetChars( component->GetReference() ),
                            GetChars( component->GetFPID().Format() ) );
                aReporter->Report( msg );
            }

            // Clear the footprint assignment since the old library lookup method is no
            // longer valid.
            FPID emptyFPID;

            component->SetFPID( emptyFPID );
            retv = false;
            continue;
        }
        else
        {
            wxString    libNickname;

            const FP_LIB_TABLE::ROW* row;

            if( ( row = aTbl->FindRowByURI( libPath ) ) != NULL )
                libNickname = row->GetNickName();

            if( libNickname.IsEmpty() )
            {
                if( aReporter )
                {
                    msg.Printf( _( "Component '%s' with footprint '%s' and legacy library path '%s' "
                                   "was not found in the footprint library table.\n" ),
                                GetChars( component->GetReference() ),
                                GetChars( component->GetFPID().Format() ),
                                GetChars( libPath )
                                );
                    aReporter->Report( msg );
                }

                retv = false;
            }
            else
            {
                FPID newFPID = lastFPID;
                newFPID.SetLibNickname( libNickname );

                if( !newFPID.IsValid() )
                {
                    if( aReporter )
                    {
                        msg.Printf( _( "Component '%s' FPID '%s' is not valid.\n" ),
                                    GetChars( component->GetReference() ),
                                    GetChars( newFPID.Format() ) );
                        aReporter->Report( msg );
                    }

                    retv = false;
                }
                else
                {
                    // The footprint name should already be set.
                    component->SetFPID( newFPID );
                }
            }
        }
    }

    return retv;
}