/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2019 CERN * Copyright (C) 2019-2023 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 as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dialog_pcm.h" #include "widgets/filedlg_new_project.h" KICAD_MANAGER_CONTROL::KICAD_MANAGER_CONTROL() : TOOL_INTERACTIVE( "kicad.Control" ), m_frame( nullptr ) { } void KICAD_MANAGER_CONTROL::Reset( RESET_REASON aReason ) { m_frame = getEditFrame(); } wxFileName KICAD_MANAGER_CONTROL::newProjectDirectory( wxString* aFileName, bool isRepo ) { wxString default_filename = aFileName ? *aFileName : wxString(); wxString default_dir = m_frame->GetMruPath(); wxFileDialog dlg( m_frame, _( "Create New Project" ), default_dir, default_filename, ( isRepo ? wxString( "" ) : FILEEXT::ProjectFileWildcard() ), wxFD_SAVE | wxFD_OVERWRITE_PROMPT ); // Add a "Create a new directory" checkbox FILEDLG_NEW_PROJECT newProjectHook; dlg.SetCustomizeHook( newProjectHook ); if( dlg.ShowModal() == wxID_CANCEL ) return wxFileName(); wxFileName pro( dlg.GetPath() ); // wxFileName automatically extracts an extension. But if it isn't // a .pro extension, we should keep it as part of the filename if( !pro.GetExt().IsEmpty() && pro.GetExt().ToStdString() != FILEEXT::ProjectFileExtension ) pro.SetName( pro.GetName() + wxT( "." ) + pro.GetExt() ); pro.SetExt( FILEEXT::ProjectFileExtension ); // enforce extension if( !pro.IsAbsolute() ) pro.MakeAbsolute(); // Append a new directory with the same name of the project file. bool createNewDir = false; createNewDir = newProjectHook.GetCreateNewDir(); if( createNewDir ) pro.AppendDir( pro.GetName() ); // Check if the project directory is empty if it already exists. wxDir directory( pro.GetPath() ); if( !pro.DirExists() ) { if( !pro.Mkdir() ) { wxString msg; msg.Printf( _( "Folder '%s' could not be created.\n\n" "Make sure you have write permissions and try again." ), pro.GetPath() ); DisplayErrorMessage( m_frame, msg ); return wxFileName(); } } else if( directory.HasFiles() ) { wxString msg = _( "The selected folder is not empty. It is recommended that you " "create projects in their own empty folder.\n\n" "Do you want to continue?" ); if( !IsOK( m_frame, msg ) ) return wxFileName(); } return pro; } int KICAD_MANAGER_CONTROL::NewProject( const TOOL_EVENT& aEvent ) { wxFileName pro = newProjectDirectory(); if( !pro.IsOk() ) return -1; m_frame->CreateNewProject( pro ); m_frame->LoadProject( pro ); return 0; } int KICAD_MANAGER_CONTROL::NewFromRepository( const TOOL_EVENT& aEvent ) { DIALOG_GIT_REPOSITORY dlg( m_frame, nullptr ); dlg.SetTitle( _( "Clone Project from Git Repository" ) ); int ret = dlg.ShowModal(); if( ret != wxID_OK ) return -1; wxString project_name = dlg.GetRepoName(); wxFileName pro = newProjectDirectory( &project_name, true ); if( !pro.IsOk() ) return -1; GIT_CLONE_HANDLER cloneHandler; cloneHandler.SetURL( dlg.GetRepoURL() ); cloneHandler.SetClonePath( pro.GetPath() ); cloneHandler.SetConnType( dlg.GetRepoType() ); cloneHandler.SetUsername( dlg.GetUsername() ); cloneHandler.SetPassword( dlg.GetPassword() ); cloneHandler.SetSSHKey( dlg.GetRepoSSHPath() ); cloneHandler.SetProgressReporter( std::make_unique( m_frame, _( "Cloning Repository" ), 1 ) ); if( !cloneHandler.PerformClone() ) { DisplayErrorMessage( m_frame, cloneHandler.GetErrorString() ); return -1; } std::vector projects = cloneHandler.GetProjectDirs(); if( projects.empty() ) { DisplayErrorMessage( m_frame, _( "No project files were found in the repository." ) ); return -1; } // Currently, we pick the first project file we find in the repository. // TODO: Look into spare checkout to allow the user to pick a partial repository wxString dest = pro.GetPath() + wxFileName::GetPathSeparator() + projects.front(); m_frame->LoadProject( dest ); Prj().GetLocalSettings().m_GitRepoPassword = dlg.GetPassword(); Prj().GetLocalSettings().m_GitRepoUsername = dlg.GetUsername(); Prj().GetLocalSettings().m_GitSSHKey = dlg.GetRepoSSHPath(); if( dlg.GetRepoType() == KIGIT_COMMON::GIT_CONN_TYPE::GIT_CONN_SSH ) Prj().GetLocalSettings().m_GitRepoType = "ssh"; else if( dlg.GetRepoType() == KIGIT_COMMON::GIT_CONN_TYPE::GIT_CONN_HTTPS ) Prj().GetLocalSettings().m_GitRepoType = "https"; else Prj().GetLocalSettings().m_GitRepoType = "local"; return 0; } int KICAD_MANAGER_CONTROL::NewJobsetFile( const TOOL_EVENT& aEvent ) { wxString default_dir = wxFileName( Prj().GetProjectFullName() ).GetPathWithSep(); wxFileDialog dlg( m_frame, _( "Create New Jobset" ), default_dir, wxEmptyString, FILEEXT::JobsetFileWildcard(), wxFD_SAVE | wxFD_OVERWRITE_PROMPT ); if( dlg.ShowModal() == wxID_CANCEL ) return -1; wxFileName jobsetFn( dlg.GetPath() ); m_frame->OpenJobsFile( jobsetFn.GetFullPath(), true ); return 0; } int KICAD_MANAGER_CONTROL::NewFromTemplate( const TOOL_EVENT& aEvent ) { SETTINGS_MANAGER& mgr = Pgm().GetSettingsManager(); KICAD_SETTINGS* settings = mgr.GetAppSettings( "kicad" ); DIALOG_TEMPLATE_SELECTOR* ps = new DIALOG_TEMPLATE_SELECTOR( m_frame, settings->m_TemplateWindowPos, settings->m_TemplateWindowSize ); wxFileName templatePath; // KiCad system template path. std::optional v = ENV_VAR::GetVersionedEnvVarValue( Pgm().GetLocalEnvVariables(), wxT( "TEMPLATE_DIR" ) ); if( v && !v->IsEmpty() ) { templatePath.AssignDir( *v ); ps->AddTemplatesPage( _( "System Templates" ), templatePath ); } // User template path. ENV_VAR_MAP_CITER it = Pgm().GetLocalEnvVariables().find( "KICAD_USER_TEMPLATE_DIR" ); if( it != Pgm().GetLocalEnvVariables().end() && it->second.GetValue() != wxEmptyString ) { templatePath.AssignDir( it->second.GetValue() ); ps->AddTemplatesPage( _( "User Templates" ), templatePath ); } // Show the project template selector dialog int result = ps->ShowModal(); settings->m_TemplateWindowPos = ps->GetPosition(); settings->m_TemplateWindowSize = ps->GetSize(); if( result != wxID_OK ) return -1; if( !ps->GetSelectedTemplate() ) { wxMessageBox( _( "No project template was selected. Cannot generate new project." ), _( "Error" ), wxOK | wxICON_ERROR, m_frame ); return -1; } // Get project destination folder and project file name. wxString default_dir = wxFileName( Prj().GetProjectFullName() ).GetPathWithSep(); wxString title = _( "New Project Folder" ); wxFileDialog dlg( m_frame, title, default_dir, wxEmptyString, FILEEXT::ProjectFileWildcard(), wxFD_SAVE | wxFD_OVERWRITE_PROMPT ); // Add a "Create a new directory" checkbox FILEDLG_NEW_PROJECT newProjectHook; dlg.SetCustomizeHook( newProjectHook ); if( dlg.ShowModal() == wxID_CANCEL ) return -1; wxFileName fn( dlg.GetPath() ); // wxFileName automatically extracts an extension. But if it isn't a .kicad_pro extension, // we should keep it as part of the filename if( !fn.GetExt().IsEmpty() && fn.GetExt().ToStdString() != FILEEXT::ProjectFileExtension ) fn.SetName( fn.GetName() + wxT( "." ) + fn.GetExt() ); fn.SetExt( FILEEXT::ProjectFileExtension ); if( !fn.IsAbsolute() ) fn.MakeAbsolute(); bool createNewDir = false; createNewDir = newProjectHook.GetCreateNewDir(); // Append a new directory with the same name of the project file. if( createNewDir ) fn.AppendDir( fn.GetName() ); // Check if the project directory is empty if it already exists. if( !fn.DirExists() ) { if( !fn.Mkdir() ) { wxString msg; msg.Printf( _( "Folder '%s' could not be created.\n\n" "Make sure you have write permissions and try again." ), fn.GetPath() ); DisplayErrorMessage( m_frame, msg ); return -1; } } if( !fn.IsDirWritable() ) { wxString msg; msg.Printf( _( "Insufficient permissions to write to folder '%s'." ), fn.GetPath() ); wxMessageDialog msgDlg( m_frame, msg, _( "Error" ), wxICON_ERROR | wxOK | wxCENTER ); msgDlg.ShowModal(); return -1; } // Make sure we are not overwriting anything in the destination folder. std::vector< wxFileName > destFiles; if( ps->GetSelectedTemplate()->GetDestinationFiles( fn, destFiles ) ) { std::vector overwrittenFiles; for( const wxFileName& file : destFiles ) { if( file.FileExists() ) overwrittenFiles.push_back( file ); } if( !overwrittenFiles.empty() ) { wxString extendedMsg = _( "Overwriting files:" ) + "\n"; for( const wxFileName& file : overwrittenFiles ) extendedMsg += "\n" + file.GetFullName(); KIDIALOG msgDlg( m_frame, _( "Similar files already exist in the destination folder." ), _( "Confirmation" ), wxOK | wxCANCEL | wxICON_WARNING ); msgDlg.SetExtendedMessage( extendedMsg ); msgDlg.SetOKLabel( _( "Overwrite" ) ); msgDlg.DoNotShowCheckbox( __FILE__, __LINE__ ); if( msgDlg.ShowModal() == wxID_CANCEL ) return -1; } } wxString errorMsg; // The selected template widget contains the template we're attempting to use to // create a project if( !ps->GetSelectedTemplate()->CreateProject( fn, &errorMsg ) ) { wxMessageDialog createDlg( m_frame, _( "A problem occurred creating new project from template." ), _( "Error" ), wxOK | wxICON_ERROR ); if( !errorMsg.empty() ) createDlg.SetExtendedMessage( errorMsg ); createDlg.ShowModal(); return -1; } m_frame->CreateNewProject( fn.GetFullPath() ); m_frame->LoadProject( fn ); return 0; } int KICAD_MANAGER_CONTROL::openProject( const wxString& aDefaultDir ) { wxString wildcard = FILEEXT::AllProjectFilesWildcard() + "|" + FILEEXT::ProjectFileWildcard() + "|" + FILEEXT::LegacyProjectFileWildcard(); wxFileDialog dlg( m_frame, _( "Open Existing Project" ), aDefaultDir, wxEmptyString, wildcard, wxFD_OPEN | wxFD_FILE_MUST_EXIST ); if( dlg.ShowModal() == wxID_CANCEL ) return -1; wxFileName pro( dlg.GetPath() ); if( !pro.IsAbsolute() ) pro.MakeAbsolute(); if( !pro.FileExists() ) return -1; m_frame->LoadProject( pro ); return 0; } int KICAD_MANAGER_CONTROL::OpenDemoProject( const TOOL_EVENT& aEvent ) { return openProject( PATHS::GetStockDemosPath() ); } int KICAD_MANAGER_CONTROL::OpenProject( const TOOL_EVENT& aEvent ) { return openProject( m_frame->GetMruPath() ); } int KICAD_MANAGER_CONTROL::CloseProject( const TOOL_EVENT& aEvent ) { m_frame->CloseProject( true ); return 0; } int KICAD_MANAGER_CONTROL::LoadProject( const TOOL_EVENT& aEvent ) { if( aEvent.Parameter() ) m_frame->LoadProject( wxFileName( *aEvent.Parameter() ) ); return 0; } int KICAD_MANAGER_CONTROL::ViewDroppedViewers( const TOOL_EVENT& aEvent ) { if( aEvent.Parameter() ) wxExecute( *aEvent.Parameter(), wxEXEC_ASYNC ); return 0; } class SAVE_AS_TRAVERSER : public wxDirTraverser { public: SAVE_AS_TRAVERSER( KICAD_MANAGER_FRAME* aFrame, const wxString& aSrcProjectDirPath, const wxString& aSrcProjectName, const wxString& aNewProjectDirPath, const wxString& aNewProjectName ) : m_frame( aFrame ), m_projectDirPath( aSrcProjectDirPath ), m_projectName( aSrcProjectName ), m_newProjectDirPath( aNewProjectDirPath ), m_newProjectName( aNewProjectName ) { } virtual wxDirTraverseResult OnFile( const wxString& aSrcFilePath ) override { // Recursion guard for a Save As to a location inside the source project. if( aSrcFilePath.StartsWith( m_newProjectDirPath + wxFileName::GetPathSeparator() ) ) return wxDIR_CONTINUE; wxFileName destFile( aSrcFilePath ); wxString ext = destFile.GetExt(); bool atRoot = destFile.GetPath() == m_projectDirPath; if( ext == FILEEXT::LegacyProjectFileExtension || ext == FILEEXT::ProjectFileExtension || ext == FILEEXT::ProjectLocalSettingsFileExtension ) { wxString destPath = destFile.GetPath(); if( destPath.StartsWith( m_projectDirPath ) ) { destPath.Replace( m_projectDirPath, m_newProjectDirPath, false ); destFile.SetPath( destPath ); } if( destFile.GetName() == m_projectName ) { destFile.SetName( m_newProjectName ); if( atRoot && ext != FILEEXT::ProjectLocalSettingsFileExtension ) m_newProjectFile = destFile; } if( ext == FILEEXT::LegacyProjectFileExtension ) { // All paths in the settings file are relative so we can just do a straight copy KiCopyFile( aSrcFilePath, destFile.GetFullPath(), m_errors ); } else if( ext == FILEEXT::ProjectFileExtension ) { PROJECT_FILE projectFile( aSrcFilePath ); projectFile.LoadFromFile(); projectFile.SaveAs( destFile.GetPath(), destFile.GetName() ); } else if( ext == FILEEXT::ProjectLocalSettingsFileExtension ) { PROJECT_LOCAL_SETTINGS projectLocalSettings( nullptr, aSrcFilePath ); projectLocalSettings.LoadFromFile(); projectLocalSettings.SaveAs( destFile.GetPath(), destFile.GetName() ); } } else if( ext == FILEEXT::KiCadSchematicFileExtension || ext == FILEEXT::KiCadSchematicFileExtension + FILEEXT::BackupFileSuffix || ext == FILEEXT::LegacySchematicFileExtension || ext == FILEEXT::LegacySchematicFileExtension + FILEEXT::BackupFileSuffix || ext == FILEEXT::SchematicSymbolFileExtension || ext == FILEEXT::LegacySymbolLibFileExtension || ext == FILEEXT::LegacySymbolDocumentFileExtension || ext == FILEEXT::KiCadSymbolLibFileExtension || ext == FILEEXT::NetlistFileExtension || destFile.GetName() == "sym-lib-table" ) { KIFACE* eeschema = m_frame->Kiway().KiFACE( KIWAY::FACE_SCH ); eeschema->SaveFileAs( m_projectDirPath, m_projectName, m_newProjectDirPath, m_newProjectName, aSrcFilePath, m_errors ); } else if( ext == FILEEXT::KiCadPcbFileExtension || ext == FILEEXT::KiCadPcbFileExtension + FILEEXT::BackupFileSuffix || ext == FILEEXT::LegacyPcbFileExtension || ext == FILEEXT::KiCadFootprintFileExtension || ext == FILEEXT::LegacyFootprintLibPathExtension || ext == FILEEXT::FootprintAssignmentFileExtension || destFile.GetName() == "fp-lib-table" ) { KIFACE* pcbnew = m_frame->Kiway().KiFACE( KIWAY::FACE_PCB ); pcbnew->SaveFileAs( m_projectDirPath, m_projectName, m_newProjectDirPath, m_newProjectName, aSrcFilePath, m_errors ); } else if( ext == FILEEXT::DrawingSheetFileExtension ) { KIFACE* pleditor = m_frame->Kiway().KiFACE( KIWAY::FACE_PL_EDITOR ); pleditor->SaveFileAs( m_projectDirPath, m_projectName, m_newProjectDirPath, m_newProjectName, aSrcFilePath, m_errors ); } else if( ext == FILEEXT::GerberJobFileExtension || ext == FILEEXT::DrillFileExtension || FILEEXT::IsGerberFileExtension( ext ) ) { KIFACE* gerbview = m_frame->Kiway().KiFACE( KIWAY::FACE_GERBVIEW ); gerbview->SaveFileAs( m_projectDirPath, m_projectName, m_newProjectDirPath, m_newProjectName, aSrcFilePath, m_errors ); } else if( destFile.GetName().StartsWith( FILEEXT::LockFilePrefix ) && ext == FILEEXT::LockFileExtension ) { // Ignore lock files } else { // Everything we don't recognize just gets a straight copy. wxString destPath = destFile.GetPathWithSep(); wxString destName = destFile.GetName(); wxUniChar pathSep = wxFileName::GetPathSeparator(); wxString srcProjectFootprintLib = pathSep + m_projectName + ".pretty" + pathSep; wxString newProjectFootprintLib = pathSep + m_newProjectName + ".pretty" + pathSep; if( destPath.StartsWith( m_projectDirPath ) ) destPath.Replace( m_projectDirPath, m_newProjectDirPath, false ); destPath.Replace( srcProjectFootprintLib, newProjectFootprintLib, true ); if( destName == m_projectName && ext != wxT( "zip" ) /* don't rename archives */ ) destFile.SetName( m_newProjectName ); destFile.SetPath( destPath ); KiCopyFile( aSrcFilePath, destFile.GetFullPath(), m_errors ); } return wxDIR_CONTINUE; } virtual wxDirTraverseResult OnDir( const wxString& aSrcDirPath ) override { // Recursion guard for a Save As to a location inside the source project. if( aSrcDirPath.StartsWith( m_newProjectDirPath ) ) return wxDIR_CONTINUE; wxFileName destDir( aSrcDirPath ); wxString destDirPath = destDir.GetPathWithSep(); wxUniChar pathSep = wxFileName::GetPathSeparator(); if( destDirPath.StartsWith( m_projectDirPath + pathSep ) || destDirPath.StartsWith( m_projectDirPath + PROJECT_BACKUPS_DIR_SUFFIX ) ) { destDirPath.Replace( m_projectDirPath, m_newProjectDirPath, false ); destDir.SetPath( destDirPath ); } if( destDir.GetName() == m_projectName ) { if( destDir.GetExt() == "pretty" ) destDir.SetName( m_newProjectName ); #if 0 // WAYNE STAMBAUGH TODO: // If we end up with a symbol equivalent to ".pretty" we'll want to handle it here.... else if( destDir.GetExt() == "sym_lib_dir_extension" ) destDir.SetName( m_newProjectName ); #endif } if( !wxMkdir( destDir.GetFullPath() ) ) { wxString msg; if( !m_errors.empty() ) m_errors += "\n"; msg.Printf( _( "Cannot copy folder '%s'." ), destDir.GetFullPath() ); m_errors += msg; } return wxDIR_CONTINUE; } wxString GetErrors() { return m_errors; } wxFileName GetNewProjectFile() { return m_newProjectFile; } private: KICAD_MANAGER_FRAME* m_frame; wxString m_projectDirPath; wxString m_projectName; wxString m_newProjectDirPath; wxString m_newProjectName; wxFileName m_newProjectFile; wxString m_errors; }; int KICAD_MANAGER_CONTROL::SaveProjectAs( const TOOL_EVENT& aEvent ) { wxString msg; wxFileName currentProjectFile( Prj().GetProjectFullName() ); wxString currentProjectDirPath = currentProjectFile.GetPath(); wxString currentProjectName = Prj().GetProjectName(); wxString default_dir = m_frame->GetMruPath(); Prj().GetProjectFile().SaveToFile( currentProjectDirPath ); Prj().GetLocalSettings().SaveToFile( currentProjectDirPath ); if( default_dir == currentProjectDirPath || default_dir == currentProjectDirPath + wxFileName::GetPathSeparator() ) { // Don't start within the current project wxFileName default_dir_fn( default_dir ); default_dir_fn.RemoveLastDir(); default_dir = default_dir_fn.GetPath(); } wxFileDialog dlg( m_frame, _( "Save Project To" ), default_dir, wxEmptyString, wxEmptyString, wxFD_SAVE ); if( dlg.ShowModal() == wxID_CANCEL ) return -1; wxFileName newProjectDir( dlg.GetPath(), wxEmptyString ); if( !newProjectDir.IsAbsolute() ) newProjectDir.MakeAbsolute(); if( wxDirExists( newProjectDir.GetFullPath() ) ) { msg.Printf( _( "'%s' already exists." ), newProjectDir.GetFullPath() ); DisplayErrorMessage( m_frame, msg ); return -1; } if( !wxMkdir( newProjectDir.GetFullPath() ) ) { msg.Printf( _( "Folder '%s' could not be created.\n\n" "Please make sure you have write permissions and try again." ), newProjectDir.GetPath() ); DisplayErrorMessage( m_frame, msg ); return -1; } if( !newProjectDir.IsDirWritable() ) { msg.Printf( _( "Insufficient permissions to write to folder '%s'." ), newProjectDir.GetFullPath() ); wxMessageDialog msgDlg( m_frame, msg, _( "Error!" ), wxICON_ERROR | wxOK | wxCENTER ); msgDlg.ShowModal(); return -1; } const wxString& newProjectDirPath = newProjectDir.GetFullPath(); const wxString& newProjectName = newProjectDir.GetDirs().Last(); wxDir currentProjectDir( currentProjectDirPath ); SAVE_AS_TRAVERSER traverser( m_frame, currentProjectDirPath, currentProjectName, newProjectDirPath, newProjectName ); currentProjectDir.Traverse( traverser ); if( !traverser.GetErrors().empty() ) DisplayErrorMessage( m_frame, traverser.GetErrors() ); if( !traverser.GetNewProjectFile().FileExists() ) m_frame->CreateNewProject( traverser.GetNewProjectFile() ); m_frame->LoadProject( traverser.GetNewProjectFile() ); return 0; } int KICAD_MANAGER_CONTROL::Refresh( const TOOL_EVENT& aEvent ) { m_frame->RefreshProjectTree(); return 0; } int KICAD_MANAGER_CONTROL::UpdateMenu( const TOOL_EVENT& aEvent ) { ACTION_MENU* actionMenu = aEvent.Parameter(); CONDITIONAL_MENU* conditionalMenu = dynamic_cast( actionMenu ); SELECTION dummySel; if( conditionalMenu ) conditionalMenu->Evaluate( dummySel ); if( actionMenu ) actionMenu->UpdateAll(); return 0; } int KICAD_MANAGER_CONTROL::ShowPlayer( const TOOL_EVENT& aEvent ) { FRAME_T playerType = aEvent.Parameter(); KIWAY_PLAYER* player; if( playerType == FRAME_SCH && !m_frame->IsProjectActive() ) { DisplayInfoMessage( m_frame, _( "Create (or open) a project to edit a schematic." ), wxEmptyString ); return -1; } else if( playerType == FRAME_PCB_EDITOR && !m_frame->IsProjectActive() ) { DisplayInfoMessage( m_frame, _( "Create (or open) a project to edit a pcb." ), wxEmptyString ); return -1; } // Prevent multiple KIWAY_PLAYER loading at one time if( !m_loading.try_lock() ) return -1; const std::lock_guard lock( m_loading, std::adopt_lock ); try { player = m_frame->Kiway().Player( playerType, true ); } catch( const IO_ERROR& err ) { wxLogError( _( "Application failed to load:\n" ) + err.What() ); return -1; } if ( !player ) { wxLogError( _( "Application cannot start." ) ); return -1; } if( !player->IsVisible() ) // A hidden frame might not have the document loaded. { wxString filepath; if( playerType == FRAME_SCH ) { wxFileName kicad_schematic( m_frame->SchFileName() ); wxFileName legacy_schematic( m_frame->SchLegacyFileName() ); if( !legacy_schematic.FileExists() || kicad_schematic.FileExists() ) filepath = kicad_schematic.GetFullPath(); else filepath = legacy_schematic.GetFullPath(); } else if( playerType == FRAME_PCB_EDITOR ) { wxFileName kicad_board( m_frame->PcbFileName() ); wxFileName legacy_board( m_frame->PcbLegacyFileName() ); if( !legacy_board.FileExists() || kicad_board.FileExists() ) filepath = kicad_board.GetFullPath(); else filepath = legacy_board.GetFullPath(); } if( !filepath.IsEmpty() ) { std::vector file_list{ filepath }; if( !player->OpenProjectFiles( file_list ) ) { player->Destroy(); return -1; } } wxBusyCursor busy; player->Show( true ); } // Needed on Windows, other platforms do not use it, but it creates no issue if( player->IsIconized() ) player->Iconize( false ); player->Raise(); // Raising the window does not set the focus on Linux. This should work on // any platform. if( wxWindow::FindFocus() != player ) player->SetFocus(); // Save window state to disk now. Don't wait around for a crash. if( Pgm().GetCommonSettings()->m_Session.remember_open_files && !player->GetCurrentFileName().IsEmpty() ) { wxFileName rfn( player->GetCurrentFileName() ); rfn.MakeRelativeTo( Prj().GetProjectPath() ); WINDOW_SETTINGS windowSettings; player->SaveWindowSettings( &windowSettings ); Prj().GetLocalSettings().SaveFileState( rfn.GetFullPath(), &windowSettings, true ); Prj().GetLocalSettings().SaveToFile( Prj().GetProjectPath() ); } return 0; } class TERMINATE_HANDLER : public wxProcess { public: TERMINATE_HANDLER( const wxString& appName ) { } void OnTerminate( int pid, int status ) override { delete this; } }; int KICAD_MANAGER_CONTROL::Execute( const TOOL_EVENT& aEvent ) { wxString execFile; wxString param; if( aEvent.IsAction( &KICAD_MANAGER_ACTIONS::viewGerbers ) ) execFile = GERBVIEW_EXE; else if( aEvent.IsAction( &KICAD_MANAGER_ACTIONS::convertImage ) ) execFile = BITMAPCONVERTER_EXE; else if( aEvent.IsAction( &KICAD_MANAGER_ACTIONS::showCalculator ) ) execFile = PCB_CALCULATOR_EXE; else if( aEvent.IsAction( &KICAD_MANAGER_ACTIONS::editDrawingSheet ) ) execFile = PL_EDITOR_EXE; else if( aEvent.IsAction( &KICAD_MANAGER_ACTIONS::openTextEditor ) ) execFile = Pgm().GetTextEditor(); else if( aEvent.IsAction( &KICAD_MANAGER_ACTIONS::editOtherSch ) ) execFile = EESCHEMA_EXE; else if( aEvent.IsAction( &KICAD_MANAGER_ACTIONS::editOtherPCB ) ) execFile = PCBNEW_EXE; else wxFAIL_MSG( "Execute(): unexpected request" ); if( execFile.IsEmpty() ) return 0; if( aEvent.Parameter() ) param = *aEvent.Parameter(); else if( aEvent.IsAction( &KICAD_MANAGER_ACTIONS::viewGerbers ) && m_frame->IsProjectActive() ) param = m_frame->Prj().GetProjectPath(); TERMINATE_HANDLER* callback = new TERMINATE_HANDLER( execFile ); long pid = ExecuteFile( execFile, param, callback ); if( pid > 0 ) { #ifdef __WXMAC__ wxString script = wxString::Format( wxS( "tell application \"System Events\"\n" " set frontmost of the first process whose unix id is %l to true\n" "end tell" ), pid ); // This non-parameterized use of wxExecute is fine because script is not derived // from user input. wxExecute( wxString::Format( "osascript -e '%s'", script ) ); #endif } else { delete callback; } return 0; } int KICAD_MANAGER_CONTROL::ShowPluginManager( const TOOL_EVENT& aEvent ) { if( KIPLATFORM::POLICY::GetPolicyBool( POLICY_KEY_PCM ) == KIPLATFORM::POLICY::PBOOL::DISABLED ) { // policy disables the plugin manager return 0; } // For some reason, after a click or a double click the bitmap button calling // PCM keeps the focus althougt the focus was not set to this button. // This hack force removing the focus from this button m_frame->SetFocus(); wxSafeYield(); if( !m_frame->GetPcm() ) m_frame->CreatePCM(); DIALOG_PCM pcm( m_frame, m_frame->GetPcm() ); pcm.ShowModal(); const std::unordered_set& changed = pcm.GetChangedPackageTypes(); if( changed.count( PCM_PACKAGE_TYPE::PT_PLUGIN ) || changed.count( PCM_PACKAGE_TYPE::PT_FAB ) ) { std::string payload = ""; m_frame->Kiway().ExpressMail( FRAME_PCB_EDITOR, MAIL_RELOAD_PLUGINS, payload ); } SETTINGS_MANAGER& mgr = Pgm().GetSettingsManager(); KICAD_SETTINGS* settings = mgr.GetAppSettings( "kicad" ); if( changed.count( PCM_PACKAGE_TYPE::PT_LIBRARY ) && ( settings->m_PcmLibAutoAdd || settings->m_PcmLibAutoRemove ) ) { // Reset project tables Prj().SetElem( PROJECT::ELEM::SYMBOL_LIB_TABLE, nullptr ); Prj().SetElem( PROJECT::ELEM::FPTBL, nullptr ); Prj().SetElem( PROJECT::ELEM::DESIGN_BLOCK_LIB_TABLE, nullptr ); KIWAY& kiway = m_frame->Kiway(); // Reset state containing global lib tables if( KIFACE* kiface = kiway.KiFACE( KIWAY::FACE_SCH, false ) ) kiface->Reset(); if( KIFACE* kiface = kiway.KiFACE( KIWAY::FACE_PCB, false ) ) kiface->Reset(); // Reload lib tables std::string payload = ""; kiway.ExpressMail( FRAME_FOOTPRINT_EDITOR, MAIL_RELOAD_LIB, payload ); kiway.ExpressMail( FRAME_FOOTPRINT_VIEWER, MAIL_RELOAD_LIB, payload ); kiway.ExpressMail( FRAME_CVPCB, MAIL_RELOAD_LIB, payload ); kiway.ExpressMail( FRAME_SCH_SYMBOL_EDITOR, MAIL_RELOAD_LIB, payload ); kiway.ExpressMail( FRAME_SCH_VIEWER, MAIL_RELOAD_LIB, payload ); } if( changed.count( PCM_PACKAGE_TYPE::PT_COLORTHEME ) ) Pgm().GetSettingsManager().ReloadColorSettings(); return 0; } void KICAD_MANAGER_CONTROL::setTransitions() { Go( &KICAD_MANAGER_CONTROL::NewProject, KICAD_MANAGER_ACTIONS::newProject.MakeEvent() ); Go( &KICAD_MANAGER_CONTROL::NewFromTemplate, KICAD_MANAGER_ACTIONS::newFromTemplate.MakeEvent() ); Go( &KICAD_MANAGER_CONTROL::NewFromRepository, KICAD_MANAGER_ACTIONS::newFromRepository.MakeEvent() ); Go( &KICAD_MANAGER_CONTROL::NewJobsetFile, KICAD_MANAGER_ACTIONS::newJobsetFile.MakeEvent() ); Go( &KICAD_MANAGER_CONTROL::OpenDemoProject, KICAD_MANAGER_ACTIONS::openDemoProject.MakeEvent() ); Go( &KICAD_MANAGER_CONTROL::OpenProject, KICAD_MANAGER_ACTIONS::openProject.MakeEvent() ); Go( &KICAD_MANAGER_CONTROL::CloseProject, KICAD_MANAGER_ACTIONS::closeProject.MakeEvent() ); Go( &KICAD_MANAGER_CONTROL::SaveProjectAs, ACTIONS::saveAs.MakeEvent() ); Go( &KICAD_MANAGER_CONTROL::LoadProject, KICAD_MANAGER_ACTIONS::loadProject.MakeEvent() ); Go( &KICAD_MANAGER_CONTROL::ViewDroppedViewers, KICAD_MANAGER_ACTIONS::viewDroppedGerbers.MakeEvent() ); Go( &KICAD_MANAGER_CONTROL::Refresh, ACTIONS::zoomRedraw.MakeEvent() ); Go( &KICAD_MANAGER_CONTROL::UpdateMenu, ACTIONS::updateMenu.MakeEvent() ); Go( &KICAD_MANAGER_CONTROL::ShowPlayer, KICAD_MANAGER_ACTIONS::editSchematic.MakeEvent() ); Go( &KICAD_MANAGER_CONTROL::ShowPlayer, KICAD_MANAGER_ACTIONS::editSymbols.MakeEvent() ); Go( &KICAD_MANAGER_CONTROL::ShowPlayer, KICAD_MANAGER_ACTIONS::editPCB.MakeEvent() ); Go( &KICAD_MANAGER_CONTROL::ShowPlayer, KICAD_MANAGER_ACTIONS::editFootprints.MakeEvent() ); Go( &KICAD_MANAGER_CONTROL::Execute, KICAD_MANAGER_ACTIONS::viewGerbers.MakeEvent() ); Go( &KICAD_MANAGER_CONTROL::Execute, KICAD_MANAGER_ACTIONS::convertImage.MakeEvent() ); Go( &KICAD_MANAGER_CONTROL::Execute, KICAD_MANAGER_ACTIONS::showCalculator.MakeEvent() ); Go( &KICAD_MANAGER_CONTROL::Execute, KICAD_MANAGER_ACTIONS::editDrawingSheet.MakeEvent() ); Go( &KICAD_MANAGER_CONTROL::Execute, KICAD_MANAGER_ACTIONS::openTextEditor.MakeEvent() ); Go( &KICAD_MANAGER_CONTROL::Execute, KICAD_MANAGER_ACTIONS::editOtherSch.MakeEvent() ); Go( &KICAD_MANAGER_CONTROL::Execute, KICAD_MANAGER_ACTIONS::editOtherPCB.MakeEvent() ); Go( &KICAD_MANAGER_CONTROL::ShowPluginManager, KICAD_MANAGER_ACTIONS::showPluginManager.MakeEvent() ); }