kicad-source/kicad/pcm/dialogs/dialog_pcm.cpp

537 lines
17 KiB
C++
Raw Normal View History

2020-10-26 03:54:36 -07:00
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2021 Andrew Lutsenko, anlutsenko at gmail dot com
2022-10-23 12:03:55 +01:00
* Copyright (C) 1992-2022 KiCad Developers, see AUTHORS.txt for contributors.
2020-10-26 03:54:36 -07:00
*
* 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 <http://www.gnu.org/licenses/>.
*/
// kicad_curl_easy.h **must be** included before any wxWidgets header to avoid conflicts
// at least on Windows/msys2
#include "kicad_curl/kicad_curl_easy.h"
2020-10-26 03:54:36 -07:00
#include "bitmaps.h"
#include "dialog_manage_repositories.h"
#include "dialog_pcm.h"
2020-10-26 03:54:36 -07:00
#include "grid_tricks.h"
#include "ki_exception.h"
#include "pcm_task_manager.h"
#include "pgm_base.h"
#include "settings/kicad_settings.h"
2020-10-26 03:54:36 -07:00
#include "settings/settings_manager.h"
#include "thread"
#include "widgets/wx_grid.h"
#include <fstream>
#include <launch_ext.h>
2020-10-26 03:54:36 -07:00
#include <sstream>
#include <vector>
#include <wx/filedlg.h>
#include <wx/msgdlg.h>
#define GRID_CELL_MARGIN 4
// Notes: These strings are static, so wxGetTranslation must be called to display the
// transalted text
2020-10-26 03:54:36 -07:00
static std::vector<std::pair<PCM_PACKAGE_TYPE, wxString>> PACKAGE_TYPE_LIST = {
{ PT_PLUGIN, _( "Plugins (%d)" ) },
2021-07-10 23:24:30 -07:00
{ PT_LIBRARY, _( "Libraries (%d)" ) },
{ PT_COLORTHEME, _( "Color themes (%d)" ) },
2020-10-26 03:54:36 -07:00
};
DIALOG_PCM::DIALOG_PCM( wxWindow* parent, std::shared_ptr<PLUGIN_CONTENT_MANAGER> pcm ) :
2022-10-23 12:03:55 +01:00
DIALOG_PCM_BASE( parent ),
m_pcm( pcm )
2020-10-26 03:54:36 -07:00
{
m_defaultBitmap = KiBitmap( BITMAPS::icon_pcm );
m_pcm->SetDialogWindow( this );
m_pcm->StopBackgroundUpdate();
2020-10-26 03:54:36 -07:00
m_gridPendingActions->PushEventHandler( new GRID_TRICKS( m_gridPendingActions ) );
m_discardActionButton->SetBitmap( KiBitmap( BITMAPS::small_trash ) );
m_panelPending->Layout();
m_actionCallback = [this]( const PACKAGE_VIEW_DATA& aData, PCM_PACKAGE_ACTION aAction,
const wxString& aVersion )
2020-10-26 03:54:36 -07:00
{
if( aAction == PPA_UPDATE && m_pcm->IsPackagePinned( aData.package.identifier ) )
{
if( wxMessageBox( wxString::Format( _( "Are you sure you want to update pinned package "
"from version %s to %s?" ),
aData.current_version, aVersion ),
_( "Confirm update" ), wxICON_QUESTION | wxYES_NO, this )
== wxNO )
2022-10-23 12:03:55 +01:00
{
return;
2022-10-23 12:03:55 +01:00
}
}
2020-10-26 03:54:36 -07:00
m_gridPendingActions->Freeze();
PCM_PACKAGE_STATE new_state;
m_gridPendingActions->AppendRows();
int row = m_gridPendingActions->GetNumberRows() - 1;
m_gridPendingActions->SetCellValue( row, PENDING_COL_NAME, aData.package.name );
m_gridPendingActions->SetCellValue( row, PENDING_COL_REPOSITORY, aData.repository_name );
switch( aAction )
2020-10-26 03:54:36 -07:00
{
default:
case PPA_INSTALL:
2020-10-26 03:54:36 -07:00
m_gridPendingActions->SetCellValue( row, PENDING_COL_ACTION, _( "Install" ) );
m_gridPendingActions->SetCellValue( row, PENDING_COL_VERSION, aVersion );
new_state = PPS_PENDING_INSTALL;
break;
case PPA_UPDATE:
m_gridPendingActions->SetCellValue( row, PENDING_COL_ACTION, _( "Update" ) );
m_gridPendingActions->SetCellValue(
row, PENDING_COL_VERSION,
wxString::Format( wxT( "%s \u279C %s" ), aData.current_version, aVersion ) );
new_state = PPS_PENDING_UPDATE;
break;
case PPA_UNINSTALL:
2020-10-26 03:54:36 -07:00
m_gridPendingActions->SetCellValue( row, PENDING_COL_ACTION, _( "Uninstall" ) );
m_gridPendingActions->SetCellValue(
row, PENDING_COL_VERSION,
m_pcm->GetInstalledPackageVersion( aData.package.identifier ) );
new_state = PPS_PENDING_UNINSTALL;
break;
2020-10-26 03:54:36 -07:00
}
m_pendingActions.emplace_back( aAction, aData.repository_id, aData.package, aVersion );
2020-10-26 03:54:36 -07:00
m_gridPendingActions->Thaw();
updatePendingActionsTab();
updatePackageState( aData.package.identifier, new_state );
};
2020-10-26 03:54:36 -07:00
m_pinCallback =
[this]( const wxString& aPackageId, const PCM_PACKAGE_STATE aState, const bool aPinned )
2022-10-23 12:03:55 +01:00
{
m_pcm->SetPinned( aPackageId, aPinned );
2022-10-23 12:03:55 +01:00
updatePackageState( aPackageId, aState );
};
2020-10-26 03:54:36 -07:00
m_installedPanel = new PANEL_PACKAGES_VIEW( m_panelInstalledHolder, m_pcm, m_actionCallback,
m_pinCallback );
m_panelInstalledHolder->GetSizer()->Add( m_installedPanel, 1, wxEXPAND );
m_panelInstalledHolder->Layout();
for( const std::pair<PCM_PACKAGE_TYPE, wxString>& entry : PACKAGE_TYPE_LIST )
{
PANEL_PACKAGES_VIEW* panel = new PANEL_PACKAGES_VIEW( m_contentNotebook, m_pcm,
m_actionCallback, m_pinCallback );
wxString label = wxGetTranslation( entry.second );
m_contentNotebook->AddPage( panel, wxString::Format( label, 0 ) );
m_repositoryContentPanels.insert( { entry.first, panel } );
}
m_dialogNotebook->SetPageText( 0, wxString::Format( _( "Repository (%d)" ), 0 ) );
2020-10-26 03:54:36 -07:00
setInstalledPackages();
updatePendingActionsTab();
m_dialogNotebook->SetSelection( 0 );
SetupStandardButtons( { { wxID_OK, _( "Close" ) },
{ wxID_APPLY, _( "Apply Pending Changes" ) },
{ wxID_CANCEL, _( "Discard Pending Changes" ) } } );
Bind( wxEVT_CLOSE_WINDOW, &DIALOG_PCM::OnCloseWindow, this );
m_sdbSizer1Cancel->Bind( wxEVT_UPDATE_UI, &DIALOG_PCM::OnUpdateEventButtons, this );
m_sdbSizer1Apply->Bind( wxEVT_UPDATE_UI, &DIALOG_PCM::OnUpdateEventButtons, this );
2020-10-26 03:54:36 -07:00
setRepositoryListFromPcm();
for( int col = 0; col < m_gridPendingActions->GetNumberCols(); col++ )
{
const wxString& heading = m_gridPendingActions->GetColLabelValue( col );
int headingWidth = GetTextExtent( heading ).x + 2 * GRID_CELL_MARGIN;
// Set the minimal width to the column label size.
m_gridPendingActions->SetColMinimalWidth( col, headingWidth );
}
// fix sizers now widgets are set.
finishDialogSettings();
2020-10-26 03:54:36 -07:00
}
DIALOG_PCM::~DIALOG_PCM()
{
m_pcm->SaveInstalledPackages();
m_pcm->SetDialogWindow( nullptr );
m_pcm->RunBackgroundUpdate();
2020-10-26 03:54:36 -07:00
m_gridPendingActions->PopEventHandler( true );
}
void DIALOG_PCM::OnUpdateEventButtons( wxUpdateUIEvent& event )
{
event.Enable( !m_pendingActions.empty() );
}
2020-10-26 03:54:36 -07:00
void DIALOG_PCM::OnCloseClicked( wxCommandEvent& event )
{
if( m_pendingActions.size() == 0
|| wxMessageBox( _( "Are you sure you want to close the package manager "
"and discard pending changes?" ),
_( "Plugin and Content Manager" ), wxICON_QUESTION | wxYES_NO, this )
== wxYES )
{
EndModal( wxID_OK );
}
}
void DIALOG_PCM::OnCloseWindow( wxCloseEvent& aEvent )
{
wxCommandEvent dummy;
OnCloseClicked( dummy );
}
2020-10-26 03:54:36 -07:00
void DIALOG_PCM::OnManageRepositoriesClicked( wxCommandEvent& event )
{
DIALOG_MANAGE_REPOSITORIES* dialog = new DIALOG_MANAGE_REPOSITORIES( this, m_pcm );
std::vector<std::pair<wxString, wxString>> dialog_data;
std::vector<std::tuple<wxString, wxString, wxString>> repo_list = m_pcm->GetRepositoryList();
2020-10-26 03:54:36 -07:00
for( const std::tuple<wxString, wxString, wxString>& repo : repo_list )
2020-10-26 03:54:36 -07:00
dialog_data.push_back( std::make_pair( std::get<1>( repo ), std::get<2>( repo ) ) );
dialog->SetData( dialog_data );
if( dialog->ShowModal() == wxID_SAVE )
{
dialog_data = dialog->GetData();
m_pcm->SetRepositoryList( dialog_data );
SETTINGS_MANAGER& mgr = Pgm().GetSettingsManager();
KICAD_SETTINGS* app_settings = mgr.GetAppSettings<KICAD_SETTINGS>();
app_settings->m_PcmRepositories = std::move( dialog_data );
setRepositoryListFromPcm();
}
dialog->Destroy();
}
void DIALOG_PCM::setRepositoryListFromPcm()
{
std::vector<std::tuple<wxString, wxString, wxString>> repositories = m_pcm->GetRepositoryList();
2020-10-26 03:54:36 -07:00
m_choiceRepository->Clear();
for( const std::tuple<wxString, wxString, wxString>& entry : repositories )
2020-10-26 03:54:36 -07:00
{
m_choiceRepository->Append( std::get<1>( entry ),
new wxStringClientData( std::get<0>( entry ) ) );
}
if( repositories.size() > 0 )
{
m_choiceRepository->SetSelection( 0 );
m_selectedRepositoryId = std::get<0>( repositories[0] );
setRepositoryData( m_selectedRepositoryId );
}
else
{
m_selectedRepositoryId = "";
}
}
void DIALOG_PCM::OnRefreshClicked( wxCommandEvent& event )
{
m_pcm->DiscardRepositoryCache( m_selectedRepositoryId );
setRepositoryData( m_selectedRepositoryId );
}
void DIALOG_PCM::OnInstallFromFileClicked( wxCommandEvent& event )
{
wxFileDialog open_file_dialog( this, _( "Choose package file" ), wxEmptyString, wxEmptyString,
2022-10-23 12:03:55 +01:00
wxT( "Zip files (*.zip)|*.zip" ),
wxFD_OPEN | wxFD_FILE_MUST_EXIST );
2020-10-26 03:54:36 -07:00
if( open_file_dialog.ShowModal() == wxID_CANCEL )
return;
PCM_TASK_MANAGER task_manager( m_pcm );
task_manager.InstallFromFile( this, open_file_dialog.GetPath() );
setInstalledPackages();
if( !m_selectedRepositoryId.IsEmpty() )
setRepositoryData( m_selectedRepositoryId );
}
void DIALOG_PCM::OnRepositoryChoice( wxCommandEvent& event )
{
wxStringClientData* data = static_cast<wxStringClientData*>(
m_choiceRepository->GetClientObject( m_choiceRepository->GetSelection() ) );
m_selectedRepositoryId = data->GetData();
setRepositoryData( m_selectedRepositoryId );
}
void DIALOG_PCM::setRepositoryData( const wxString& aRepositoryId )
{
if( m_pcm->CacheRepository( aRepositoryId ) )
{
2022-10-23 12:03:55 +01:00
for( const auto& [ packageType, packagesView ] : m_repositoryContentPanels )
packagesView->ClearData();
2020-10-26 03:54:36 -07:00
m_packageBitmaps = m_pcm->GetRepositoryPackageBitmaps( aRepositoryId );
const std::vector<PCM_PACKAGE> packages = m_pcm->GetRepositoryPackages( aRepositoryId );
std::unordered_map<PCM_PACKAGE_TYPE, std::vector<PACKAGE_VIEW_DATA>> data;
for( const PCM_PACKAGE& pkg : packages )
{
PACKAGE_VIEW_DATA package_data( pkg );
if( m_packageBitmaps.count( package_data.package.identifier ) > 0 )
package_data.bitmap = &m_packageBitmaps.at( package_data.package.identifier );
else
package_data.bitmap = &m_defaultBitmap;
package_data.state = m_pcm->GetPackageState( aRepositoryId, pkg.identifier );
if( package_data.state == PPS_INSTALLED || package_data.state == PPS_UPDATE_AVAILABLE )
{
package_data.current_version = m_pcm->GetInstalledPackageVersion( pkg.identifier );
package_data.pinned = m_pcm->IsPackagePinned( pkg.identifier );
}
if( package_data.state == PPS_UPDATE_AVAILABLE )
package_data.update_version = m_pcm->GetPackageUpdateVersion( pkg );
for( const PENDING_ACTION& action : m_pendingActions )
2020-10-26 03:54:36 -07:00
{
if( action.package.identifier != pkg.identifier )
continue;
switch( action.action )
{
case PPA_INSTALL: package_data.state = PPS_PENDING_INSTALL; break;
case PPA_UPDATE: package_data.state = PPS_PENDING_UPDATE; break;
case PPA_UNINSTALL: package_data.state = PPS_PENDING_UNINSTALL; break;
}
2020-10-26 03:54:36 -07:00
break;
}
package_data.repository_id = aRepositoryId;
package_data.repository_name = m_choiceRepository->GetStringSelection();
data[pkg.type].emplace_back( package_data );
}
for( size_t i = 0; i < PACKAGE_TYPE_LIST.size(); i++ )
{
PCM_PACKAGE_TYPE type = PACKAGE_TYPE_LIST[i].first;
const wxString& label = PACKAGE_TYPE_LIST[i].second;
m_repositoryContentPanels[type]->SetData( data[type] );
2022-10-23 12:03:55 +01:00
m_contentNotebook->SetPageText( i, wxString::Format( wxGetTranslation( label ),
(int) data[type].size() ) );
2020-10-26 03:54:36 -07:00
}
2022-10-23 12:03:55 +01:00
m_dialogNotebook->SetPageText( 0, wxString::Format( _( "Repository (%d)" ),
(int) packages.size() ) );
2020-10-26 03:54:36 -07:00
}
}
void DIALOG_PCM::OnPendingActionsCellClicked( wxGridEvent& event )
{
m_gridPendingActions->ClearSelection();
m_gridPendingActions->SelectRow( event.GetRow() );
}
void DIALOG_PCM::updatePendingActionsTab()
{
2022-10-23 12:03:55 +01:00
m_dialogNotebook->SetPageText( 2, wxString::Format( _( "Pending (%d)" ),
(int) m_pendingActions.size() ) );
2020-10-26 03:54:36 -07:00
for( int col = 0; col < m_gridPendingActions->GetNumberCols(); col++ )
{
// Set the width to see the full contents
2022-08-18 17:56:36 +01:00
m_gridPendingActions->SetColSize( col, m_gridPendingActions->GetVisibleWidth( col ) );
2020-10-26 03:54:36 -07:00
}
}
void DIALOG_PCM::setInstalledPackages()
{
m_installedPanel->ClearData();
const std::vector<PCM_INSTALLATION_ENTRY> installed = m_pcm->GetInstalledPackages();
std::vector<PACKAGE_VIEW_DATA> package_list;
m_installedBitmaps = m_pcm->GetInstalledPackageBitmaps();
for( const PCM_INSTALLATION_ENTRY& entry : installed )
2020-10-26 03:54:36 -07:00
{
PACKAGE_VIEW_DATA package_data( entry );
if( m_installedBitmaps.count( package_data.package.identifier ) > 0 )
package_data.bitmap = &m_installedBitmaps.at( package_data.package.identifier );
else
package_data.bitmap = &m_defaultBitmap;
2022-10-23 12:03:55 +01:00
package_data.state = m_pcm->GetPackageState( entry.repository_id,
entry.package.identifier );
if( package_data.state == PPS_UPDATE_AVAILABLE )
package_data.update_version = m_pcm->GetPackageUpdateVersion( entry.package );
2020-10-26 03:54:36 -07:00
package_list.emplace_back( package_data );
}
m_installedPanel->SetData( package_list );
2020-10-26 03:54:36 -07:00
2022-10-23 12:03:55 +01:00
m_dialogNotebook->SetPageText( 1, wxString::Format( _( "Installed (%d)" ),
(int) package_list.size() ) );
2020-10-26 03:54:36 -07:00
}
void DIALOG_PCM::OnApplyChangesClicked( wxCommandEvent& event )
{
if( m_pendingActions.size() == 0 )
return;
m_sdbSizer1OK->Disable();
m_sdbSizer1Apply->Disable();
m_sdbSizer1Cancel->Disable();
2020-10-26 03:54:36 -07:00
PCM_TASK_MANAGER task_manager( m_pcm );
for( const PENDING_ACTION& action : m_pendingActions )
2020-10-26 03:54:36 -07:00
{
if( action.action == PPA_UNINSTALL )
{
2020-10-26 03:54:36 -07:00
task_manager.Uninstall( action.package );
}
2020-10-26 03:54:36 -07:00
else
{
bool isUpdate = action.action == PPA_UPDATE;
task_manager.DownloadAndInstall( action.package, action.version, action.repository_id,
isUpdate );
}
2020-10-26 03:54:36 -07:00
}
task_manager.RunQueue( this );
m_sdbSizer1OK->Enable();
m_sdbSizer1Apply->Enable();
m_sdbSizer1Cancel->Enable();
2020-10-26 03:54:36 -07:00
setInstalledPackages();
wxCommandEvent dummy;
OnDiscardChangesClicked( dummy );
if( !m_selectedRepositoryId.IsEmpty() )
setRepositoryData( m_selectedRepositoryId );
}
void DIALOG_PCM::OnDiscardChangesClicked( wxCommandEvent& event )
{
m_gridPendingActions->Freeze();
for( int i = m_pendingActions.size() - 1; i >= 0; i-- )
discardAction( i );
updatePendingActionsTab();
m_gridPendingActions->Thaw();
}
void DIALOG_PCM::OnDiscardActionClicked( wxCommandEvent& event )
{
wxArrayInt rows = m_gridPendingActions->GetSelectedRows();
std::sort( rows.begin(), rows.end(),
[]( const int& a, const int& b )
{
return a > b;
} );
m_gridPendingActions->Freeze();
for( int row : rows )
discardAction( row );
updatePendingActionsTab();
m_gridPendingActions->Thaw();
}
void DIALOG_PCM::discardAction( int aIndex )
{
m_gridPendingActions->DeleteRows( aIndex );
PENDING_ACTION action = m_pendingActions[aIndex];
2022-10-23 12:03:55 +01:00
PCM_PACKAGE_STATE state = m_pcm->GetPackageState( action.repository_id,
action.package.identifier );
2020-10-26 03:54:36 -07:00
updatePackageState( action.package.identifier, state );
2020-10-26 03:54:36 -07:00
m_pendingActions.erase( m_pendingActions.begin() + aIndex );
}
void DIALOG_PCM::updatePackageState( const wxString& aPackageId, const PCM_PACKAGE_STATE aState )
{
bool pinned = m_pcm->IsPackagePinned( aPackageId );
m_installedPanel->SetPackageState( aPackageId, aState, pinned );
2022-10-23 12:03:55 +01:00
for( const auto& [ packageType, packagesView ] : m_repositoryContentPanels )
packagesView->SetPackageState( aPackageId, aState, pinned );
}
void DIALOG_PCM::OnOpenPackageDirClicked( wxCommandEvent& event )
{
LaunchExternal( m_pcm->Get3rdPartyPath() );
}