PCM: package update functionality
Adds package update available state, package update operation and all the corresponding logic around it to make updating package to another version easy.
This commit is contained in:
parent
1e4d0aa1c0
commit
955c5d039e
|
@ -22,9 +22,9 @@
|
|||
// at least on Windows/msys2
|
||||
#include "kicad_curl/kicad_curl_easy.h"
|
||||
|
||||
#include "dialog_pcm.h"
|
||||
#include "bitmaps.h"
|
||||
#include "dialog_manage_repositories.h"
|
||||
#include "dialog_pcm.h"
|
||||
#include "grid_tricks.h"
|
||||
#include "ki_exception.h"
|
||||
#include "kicad_settings.h"
|
||||
|
@ -35,11 +35,11 @@
|
|||
#include "widgets/wx_grid.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <launch_ext.h>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <wx/filedlg.h>
|
||||
#include <wx/msgdlg.h>
|
||||
#include <launch_ext.h>
|
||||
|
||||
|
||||
#define GRID_CELL_MARGIN 4
|
||||
|
@ -71,7 +71,7 @@ DIALOG_PCM::DIALOG_PCM( wxWindow* parent ) : DIALOG_PCM_BASE( parent )
|
|||
for( const std::pair<PCM_PACKAGE_TYPE, wxString>& entry : PACKAGE_TYPE_LIST )
|
||||
{
|
||||
PANEL_PACKAGES_VIEW* panel = new PANEL_PACKAGES_VIEW( m_contentNotebook, m_pcm );
|
||||
wxString label = wxGetTranslation( entry.second );
|
||||
wxString label = wxGetTranslation( entry.second );
|
||||
m_contentNotebook->AddPage( panel, wxString::Format( label, 0 ) );
|
||||
m_repositoryContentPanels.insert( { entry.first, panel } );
|
||||
}
|
||||
|
@ -91,27 +91,31 @@ DIALOG_PCM::DIALOG_PCM( wxWindow* parent ) : DIALOG_PCM_BASE( parent )
|
|||
m_gridPendingActions->SetCellValue( row, PENDING_COL_NAME, aData.package.name );
|
||||
m_gridPendingActions->SetCellValue( row, PENDING_COL_REPOSITORY, aData.repository_name );
|
||||
|
||||
if( aAction == PPA_INSTALL )
|
||||
switch( aAction )
|
||||
{
|
||||
case PPA_INSTALL:
|
||||
m_gridPendingActions->SetCellValue( row, PENDING_COL_ACTION, _( "Install" ) );
|
||||
m_gridPendingActions->SetCellValue( row, PENDING_COL_VERSION, aVersion );
|
||||
|
||||
m_pendingActions.emplace_back( aAction, aData.repository_id, aData.package, aVersion );
|
||||
|
||||
new_state = PPS_PENDING_INSTALL;
|
||||
}
|
||||
else
|
||||
{
|
||||
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:
|
||||
m_gridPendingActions->SetCellValue( row, PENDING_COL_ACTION, _( "Uninstall" ) );
|
||||
m_gridPendingActions->SetCellValue(
|
||||
row, PENDING_COL_VERSION,
|
||||
m_pcm->GetInstalledPackageVersion( aData.package.identifier ) );
|
||||
|
||||
m_pendingActions.emplace_back( aAction, aData.repository_id, aData.package, aVersion );
|
||||
|
||||
new_state = PPS_PENDING_UNINSTALL;
|
||||
break;
|
||||
}
|
||||
|
||||
m_pendingActions.emplace_back( aAction, aData.repository_id, aData.package, aVersion );
|
||||
|
||||
m_gridPendingActions->Thaw();
|
||||
|
||||
updatePendingActionsTab();
|
||||
|
@ -127,8 +131,8 @@ DIALOG_PCM::DIALOG_PCM( wxWindow* parent ) : DIALOG_PCM_BASE( parent )
|
|||
|
||||
m_dialogNotebook->SetSelection( 0 );
|
||||
|
||||
SetupStandardButtons( { { wxID_OK, _( "Close" ) },
|
||||
{ wxID_APPLY, _( "Apply Pending Changes" ) },
|
||||
SetupStandardButtons( { { wxID_OK, _( "Close" ) },
|
||||
{ wxID_APPLY, _( "Apply Pending Changes" ) },
|
||||
{ wxID_CANCEL, _( "Discard Pending Changes" ) } } );
|
||||
|
||||
Bind( wxEVT_CLOSE_WINDOW, &DIALOG_PCM::OnCloseWindow, this );
|
||||
|
@ -303,15 +307,24 @@ void DIALOG_PCM::setRepositoryData( const wxString& aRepositoryId )
|
|||
|
||||
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 );
|
||||
|
||||
if( package_data.state == PPS_UPDATE_AVAILABLE )
|
||||
package_data.update_version = m_pcm->GetPackageUpdateVersion( pkg );
|
||||
|
||||
|
||||
for( const PENDING_ACTION& action : m_pendingActions )
|
||||
{
|
||||
if( action.package.identifier != pkg.identifier )
|
||||
continue;
|
||||
|
||||
if( action.action == PPA_INSTALL )
|
||||
package_data.state = PPS_PENDING_INSTALL;
|
||||
else
|
||||
package_data.state = PPS_PENDING_UNINSTALL;
|
||||
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;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
@ -327,12 +340,12 @@ void DIALOG_PCM::setRepositoryData( const wxString& aRepositoryId )
|
|||
PCM_PACKAGE_TYPE type = PACKAGE_TYPE_LIST[i].first;
|
||||
const wxString& label = PACKAGE_TYPE_LIST[i].second;
|
||||
m_repositoryContentPanels[type]->SetData( data[type], m_callback );
|
||||
m_contentNotebook->SetPageText( i, wxString::Format( wxGetTranslation( label ),
|
||||
(int) data[type].size() ) );
|
||||
m_contentNotebook->SetPageText(
|
||||
i, wxString::Format( wxGetTranslation( label ), (int) data[type].size() ) );
|
||||
}
|
||||
|
||||
m_dialogNotebook->SetPageText( 0, wxString::Format( _( "Repository (%d)" ),
|
||||
(int) packages.size() ) );
|
||||
m_dialogNotebook->SetPageText(
|
||||
0, wxString::Format( _( "Repository (%d)" ), (int) packages.size() ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -381,8 +394,8 @@ void DIALOG_PCM::setInstalledPackages()
|
|||
|
||||
m_installedPanel->SetData( package_list, m_callback );
|
||||
|
||||
m_dialogNotebook->SetPageText( 1, wxString::Format( _( "Installed (%d)" ),
|
||||
(int) package_list.size() ) );
|
||||
m_dialogNotebook->SetPageText(
|
||||
1, wxString::Format( _( "Installed (%d)" ), (int) package_list.size() ) );
|
||||
}
|
||||
|
||||
|
||||
|
@ -400,9 +413,15 @@ void DIALOG_PCM::OnApplyChangesClicked( wxCommandEvent& event )
|
|||
for( const PENDING_ACTION& action : m_pendingActions )
|
||||
{
|
||||
if( action.action == PPA_UNINSTALL )
|
||||
{
|
||||
task_manager.Uninstall( action.package );
|
||||
}
|
||||
else
|
||||
task_manager.DownloadAndInstall( action.package, action.version, action.repository_id );
|
||||
{
|
||||
bool isUpdate = action.action == PPA_UPDATE;
|
||||
task_manager.DownloadAndInstall( action.package, action.version, action.repository_id,
|
||||
isUpdate );
|
||||
}
|
||||
}
|
||||
|
||||
task_manager.RunQueue( this );
|
||||
|
@ -458,8 +477,8 @@ void DIALOG_PCM::discardAction( int aIndex )
|
|||
|
||||
PENDING_ACTION action = m_pendingActions[aIndex];
|
||||
|
||||
PCM_PACKAGE_STATE state = m_pcm->GetPackageState( action.repository_id,
|
||||
action.package.identifier );
|
||||
PCM_PACKAGE_STATE state =
|
||||
m_pcm->GetPackageState( action.repository_id, action.package.identifier );
|
||||
|
||||
m_installedPanel->SetPackageState( action.package.identifier, state );
|
||||
|
||||
|
|
|
@ -18,16 +18,15 @@
|
|||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <wx/dcclient.h>
|
||||
#include <math/util.h>
|
||||
#include <wx/dcclient.h>
|
||||
|
||||
#include "panel_package.h"
|
||||
|
||||
PANEL_PACKAGE::PANEL_PACKAGE( wxWindow* parent, const ActionCallback& aCallback,
|
||||
const PACKAGE_VIEW_DATA& aData ) :
|
||||
PANEL_PACKAGE_BASE( parent ),
|
||||
m_actionCallback( aCallback ),
|
||||
m_data( aData )
|
||||
m_actionCallback( aCallback ), m_data( aData )
|
||||
{
|
||||
// Propagate clicks on static elements to the panel handler.
|
||||
m_name->Connect( wxEVT_LEFT_DOWN, wxMouseEventHandler( PANEL_PACKAGE::OnClick ), NULL, this );
|
||||
|
@ -53,20 +52,26 @@ PANEL_PACKAGE::PANEL_PACKAGE( wxWindow* parent, const ActionCallback& aCallback,
|
|||
m_desc->SetLabel( m_data.package.description );
|
||||
descLineHeight = wxSplit( m_desc->GetLabel(), '\n' ).size() * descLineHeight;
|
||||
|
||||
int nameLineHeight = m_name->GetTextExtent( "X" ).GetHeight();
|
||||
int nameLineHeight = m_name->GetTextExtent( "X" ).GetHeight();
|
||||
wxSize minSize = GetMinSize();
|
||||
minSize.y = std::max( nameLineHeight + KiROUND( descLineHeight ) + 15, m_minHeight );
|
||||
SetMinSize( minSize );
|
||||
|
||||
wxSizeEvent dummy;
|
||||
OnSize( dummy );
|
||||
m_splitButton->SetLabel( _( "Update" ) );
|
||||
m_splitButton->Bind( wxEVT_BUTTON, &PANEL_PACKAGE::OnButtonClicked, this );
|
||||
|
||||
wxMenu* splitMenu = m_splitButton->GetSplitButtonMenu();
|
||||
wxMenuItem* menuItem = splitMenu->Append( wxID_ANY, _( "Uninstall" ) );
|
||||
|
||||
splitMenu->Bind( wxEVT_COMMAND_MENU_SELECTED, &PANEL_PACKAGE::OnUninstallClick, this,
|
||||
menuItem->GetId() );
|
||||
|
||||
SetState( m_data.state );
|
||||
}
|
||||
|
||||
|
||||
void PANEL_PACKAGE::OnSize( wxSizeEvent& event )
|
||||
{
|
||||
{
|
||||
Layout();
|
||||
}
|
||||
|
||||
|
@ -78,30 +83,50 @@ void PANEL_PACKAGE::SetState( PCM_PACKAGE_STATE aState )
|
|||
switch( aState )
|
||||
{
|
||||
case PCM_PACKAGE_STATE::PPS_AVAILABLE:
|
||||
m_splitButton->Hide();
|
||||
m_button->Show();
|
||||
m_button->SetLabel( _( "Install" ) );
|
||||
m_button->Enable();
|
||||
break;
|
||||
case PCM_PACKAGE_STATE::PPS_UNAVAILABLE:
|
||||
m_splitButton->Hide();
|
||||
m_button->Show();
|
||||
m_button->SetLabel( _( "Install" ) );
|
||||
m_button->Disable();
|
||||
break;
|
||||
case PCM_PACKAGE_STATE::PPS_INSTALLED:
|
||||
m_splitButton->Hide();
|
||||
m_button->Show();
|
||||
m_button->SetLabel( _( "Uninstall" ) );
|
||||
m_button->Enable();
|
||||
break;
|
||||
case PCM_PACKAGE_STATE::PPS_PENDING_INSTALL:
|
||||
m_splitButton->Hide();
|
||||
m_button->Show();
|
||||
m_button->SetLabel( _( "Install Pending" ) );
|
||||
m_button->Disable();
|
||||
break;
|
||||
case PCM_PACKAGE_STATE::PPS_PENDING_UNINSTALL:
|
||||
m_splitButton->Hide();
|
||||
m_button->Show();
|
||||
m_button->SetLabel( _( "Uninstall Pending" ) );
|
||||
m_button->Disable();
|
||||
break;
|
||||
case PCM_PACKAGE_STATE::PPS_UPDATE_AVAILABLE:
|
||||
// The only state where the split button is shown instead of the normal one
|
||||
m_button->Hide();
|
||||
m_splitButton->Show();
|
||||
break;
|
||||
case PCM_PACKAGE_STATE::PPS_PENDING_UPDATE:
|
||||
m_splitButton->Hide();
|
||||
m_button->Show();
|
||||
m_button->SetLabel( _( "Update Pending" ) );
|
||||
m_button->Disable();
|
||||
break;
|
||||
}
|
||||
|
||||
// Relayout to change button size to fit the label.
|
||||
wxSizeEvent dummy;
|
||||
OnSize( dummy );
|
||||
Layout();
|
||||
}
|
||||
|
||||
|
||||
|
@ -116,6 +141,10 @@ void PANEL_PACKAGE::OnButtonClicked( wxCommandEvent& event )
|
|||
|
||||
m_actionCallback( m_data, PPA_INSTALL, version );
|
||||
}
|
||||
else if( m_data.state == PPS_UPDATE_AVAILABLE )
|
||||
{
|
||||
m_actionCallback( m_data, PPA_UPDATE, m_data.update_version );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_actionCallback( m_data, PPA_UNINSTALL, m_data.current_version );
|
||||
|
@ -123,6 +152,21 @@ void PANEL_PACKAGE::OnButtonClicked( wxCommandEvent& event )
|
|||
}
|
||||
|
||||
|
||||
void PANEL_PACKAGE::OnUninstallClick( wxCommandEvent& event )
|
||||
{
|
||||
if( m_data.state == PPS_UPDATE_AVAILABLE )
|
||||
{
|
||||
m_actionCallback( m_data, PPA_UNINSTALL, m_data.current_version );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Clicking uninstall menu item of the split button should not be possible
|
||||
// for any state other than UPDATE_AVAILABLE
|
||||
wxLogError( wxT( "Uninstall clicked in unexpected state" ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PANEL_PACKAGE::SetSelectCallback( const std::function<void()>& aCallback )
|
||||
{
|
||||
m_selectCallback = aCallback;
|
||||
|
@ -185,5 +229,3 @@ wxString PANEL_PACKAGE::GetPreferredVersion() const
|
|||
|
||||
return ver_it->version;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@ struct PACKAGE_VIEW_DATA
|
|||
wxString repository_id;
|
||||
wxString repository_name;
|
||||
wxString current_version;
|
||||
wxString update_version;
|
||||
PACKAGE_VIEW_DATA( const PCM_PACKAGE aPackage ) :
|
||||
package( std::move( aPackage ) ), bitmap( nullptr ), state( PPS_INSTALLED ){};
|
||||
PACKAGE_VIEW_DATA( const PCM_INSTALLATION_ENTRY& aEntry ) :
|
||||
|
@ -73,6 +74,8 @@ public:
|
|||
///< Called when anywhere on the panel is clicked (except install button)
|
||||
void OnClick( wxMouseEvent& event ) override;
|
||||
|
||||
void OnUninstallClick( wxCommandEvent& event );
|
||||
|
||||
void OnSize( wxSizeEvent& event ) override;
|
||||
|
||||
///< Get preferred version. If criteria are not met, return wxEmptyString
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
///////////////////////////////////////////////////////////////////////////
|
||||
// C++ code generated with wxFormBuilder (version Oct 26 2018)
|
||||
// C++ code generated with wxFormBuilder (version 3.10.1-0-g8feb16b3)
|
||||
// http://www.wxformbuilder.org/
|
||||
//
|
||||
// PLEASE DO *NOT* EDIT THIS FILE!
|
||||
|
@ -67,6 +67,9 @@ PANEL_PACKAGE_BASE::PANEL_PACKAGE_BASE( wxWindow* parent, wxWindowID id, const w
|
|||
m_button = new wxButton( this, wxID_ANY, _("Install"), wxDefaultPosition, wxDefaultSize, 0 );
|
||||
bSizer4->Add( m_button, 0, wxTOP|wxBOTTOM|wxRIGHT, 5 );
|
||||
|
||||
m_splitButton = new SPLIT_BUTTON( this, wxID_ANY, _( "Update" ) );
|
||||
bSizer4->Add( m_splitButton, 0, wxBOTTOM|wxRIGHT|wxTOP, 5 );
|
||||
|
||||
|
||||
bSizer3->Add( bSizer4, 0, wxEXPAND, 5 );
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
||||
<wxFormBuilder_Project>
|
||||
<FileVersion major="1" minor="15" />
|
||||
<FileVersion major="1" minor="16" />
|
||||
<object class="Project" expanded="1">
|
||||
<property name="class_decoration">; </property>
|
||||
<property name="code_generation">C++</property>
|
||||
|
@ -14,6 +14,7 @@
|
|||
<property name="file">panel_package_base</property>
|
||||
<property name="first_id">1000</property>
|
||||
<property name="help_provider">none</property>
|
||||
<property name="image_path_wrapper_function_name"></property>
|
||||
<property name="indent_with_spaces"></property>
|
||||
<property name="internationalize">1</property>
|
||||
<property name="name">panel_package_base</property>
|
||||
|
@ -25,6 +26,7 @@
|
|||
<property name="skip_php_events">1</property>
|
||||
<property name="skip_python_events">1</property>
|
||||
<property name="ui_table">UI</property>
|
||||
<property name="use_array_enum">0</property>
|
||||
<property name="use_enum">0</property>
|
||||
<property name="use_microsoft_bom">0</property>
|
||||
<object class="Panel" expanded="1">
|
||||
|
@ -46,6 +48,7 @@
|
|||
<property name="size">-1,-1</property>
|
||||
<property name="subclass">; ; forward_declare</property>
|
||||
<property name="tooltip"></property>
|
||||
<property name="two_step_creation">0</property>
|
||||
<property name="window_extra_style"></property>
|
||||
<property name="window_name"></property>
|
||||
<property name="window_style">wxBORDER_NONE|wxTAB_TRAVERSAL</property>
|
||||
|
@ -349,6 +352,7 @@
|
|||
<property name="aui_name"></property>
|
||||
<property name="aui_position"></property>
|
||||
<property name="aui_row"></property>
|
||||
<property name="auth_needed">0</property>
|
||||
<property name="best_size"></property>
|
||||
<property name="bg"></property>
|
||||
<property name="bitmap"></property>
|
||||
|
@ -409,6 +413,68 @@
|
|||
<event name="OnButtonClick">OnButtonClicked</event>
|
||||
</object>
|
||||
</object>
|
||||
<object class="sizeritem" expanded="1">
|
||||
<property name="border">5</property>
|
||||
<property name="flag">wxBOTTOM|wxRIGHT|wxTOP</property>
|
||||
<property name="proportion">0</property>
|
||||
<object class="CustomControl" expanded="1">
|
||||
<property name="BottomDockable">1</property>
|
||||
<property name="LeftDockable">1</property>
|
||||
<property name="RightDockable">1</property>
|
||||
<property name="TopDockable">1</property>
|
||||
<property name="aui_layer"></property>
|
||||
<property name="aui_name"></property>
|
||||
<property name="aui_position"></property>
|
||||
<property name="aui_row"></property>
|
||||
<property name="best_size"></property>
|
||||
<property name="bg"></property>
|
||||
<property name="caption"></property>
|
||||
<property name="caption_visible">1</property>
|
||||
<property name="center_pane">0</property>
|
||||
<property name="class">SPLIT_BUTTON</property>
|
||||
<property name="close_button">1</property>
|
||||
<property name="construction">m_splitButton = new SPLIT_BUTTON( this, wxID_ANY, _( "Update" ) );</property>
|
||||
<property name="context_help"></property>
|
||||
<property name="context_menu">1</property>
|
||||
<property name="declaration"></property>
|
||||
<property name="default_pane">0</property>
|
||||
<property name="dock">Dock</property>
|
||||
<property name="dock_fixed">0</property>
|
||||
<property name="docking">Left</property>
|
||||
<property name="enabled">1</property>
|
||||
<property name="fg"></property>
|
||||
<property name="floatable">1</property>
|
||||
<property name="font"></property>
|
||||
<property name="gripper">0</property>
|
||||
<property name="hidden">0</property>
|
||||
<property name="id">wxID_ANY</property>
|
||||
<property name="include">#include <widgets/split_button.h></property>
|
||||
<property name="max_size"></property>
|
||||
<property name="maximize_button">0</property>
|
||||
<property name="maximum_size"></property>
|
||||
<property name="min_size"></property>
|
||||
<property name="minimize_button">0</property>
|
||||
<property name="minimum_size"></property>
|
||||
<property name="moveable">1</property>
|
||||
<property name="name">m_splitButton</property>
|
||||
<property name="pane_border">1</property>
|
||||
<property name="pane_position"></property>
|
||||
<property name="pane_size"></property>
|
||||
<property name="permission">protected</property>
|
||||
<property name="pin_button">1</property>
|
||||
<property name="pos"></property>
|
||||
<property name="resize">Resizable</property>
|
||||
<property name="settings"></property>
|
||||
<property name="show">1</property>
|
||||
<property name="size"></property>
|
||||
<property name="subclass">; ; forward_declare</property>
|
||||
<property name="toolbar_pane">0</property>
|
||||
<property name="tooltip"></property>
|
||||
<property name="window_extra_style"></property>
|
||||
<property name="window_name"></property>
|
||||
<property name="window_style"></property>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
///////////////////////////////////////////////////////////////////////////
|
||||
// C++ code generated with wxFormBuilder (version Oct 26 2018)
|
||||
// C++ code generated with wxFormBuilder (version 3.10.1-0-g8feb16b3)
|
||||
// http://www.wxformbuilder.org/
|
||||
//
|
||||
// PLEASE DO *NOT* EDIT THIS FILE!
|
||||
|
@ -22,6 +22,7 @@
|
|||
#include <wx/sizer.h>
|
||||
#include <wx/stattext.h>
|
||||
#include <wx/button.h>
|
||||
#include <widgets/split_button.h>
|
||||
#include <wx/panel.h>
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
@ -39,8 +40,9 @@ class PANEL_PACKAGE_BASE : public wxPanel
|
|||
wxStaticText* m_name;
|
||||
wxStaticText* m_desc;
|
||||
wxButton* m_button;
|
||||
SPLIT_BUTTON* m_splitButton;
|
||||
|
||||
// Virtual event handlers, overide them in your derived class
|
||||
// Virtual event handlers, override them in your derived class
|
||||
virtual void OnClick( wxMouseEvent& event ) { event.Skip(); }
|
||||
virtual void OnPaint( wxPaintEvent& event ) { event.Skip(); }
|
||||
virtual void OnSize( wxSizeEvent& event ) { event.Skip(); }
|
||||
|
@ -50,6 +52,7 @@ class PANEL_PACKAGE_BASE : public wxPanel
|
|||
public:
|
||||
|
||||
PANEL_PACKAGE_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxBORDER_NONE|wxTAB_TRAVERSAL, const wxString& name = wxEmptyString );
|
||||
|
||||
~PANEL_PACKAGE_BASE();
|
||||
|
||||
};
|
||||
|
|
|
@ -20,14 +20,14 @@
|
|||
|
||||
#include "panel_packages_view.h"
|
||||
#include <grid_tricks.h>
|
||||
#include <html_window.h>
|
||||
#include <kicad_settings.h>
|
||||
#include <pgm_base.h>
|
||||
#include <settings/settings_manager.h>
|
||||
#include <settings/common_settings.h>
|
||||
#include <widgets/wx_splitter_window.h>
|
||||
#include <widgets/wx_panel.h>
|
||||
#include <settings/settings_manager.h>
|
||||
#include <string_utils.h>
|
||||
#include <html_window.h>
|
||||
#include <widgets/wx_panel.h>
|
||||
#include <widgets/wx_splitter_window.h>
|
||||
|
||||
#include <cmath>
|
||||
#include <fstream>
|
||||
|
@ -82,8 +82,8 @@ PANEL_PACKAGES_VIEW::PANEL_PACKAGES_VIEW( wxWindow*
|
|||
|
||||
// Set the minimal width to the column label size.
|
||||
m_gridVersions->SetColMinimalWidth( col, headingWidth );
|
||||
m_gridVersions->SetColSize( col, m_gridVersions->GetVisibleWidth( col, true, true,
|
||||
false ) );
|
||||
m_gridVersions->SetColSize( col,
|
||||
m_gridVersions->GetVisibleWidth( col, true, true, false ) );
|
||||
}
|
||||
|
||||
// Most likely should be changed to wxGridSelectNone once WxWidgets>=3.1.5 is mandatory.
|
||||
|
@ -163,48 +163,47 @@ void PANEL_PACKAGES_VIEW::setPackageDetails( const PACKAGE_VIEW_DATA& aPackageDa
|
|||
|
||||
details << "<h5>" + package.name + "</h5>";
|
||||
|
||||
auto format_desc =
|
||||
[]( const wxString& text ) -> wxString
|
||||
auto format_desc = []( const wxString& text ) -> wxString
|
||||
{
|
||||
wxString result;
|
||||
bool inURL = false;
|
||||
wxString url;
|
||||
|
||||
for( unsigned i = 0; i < text.length(); ++i )
|
||||
{
|
||||
wxUniChar c = text[i];
|
||||
|
||||
if( inURL )
|
||||
{
|
||||
wxString result;
|
||||
bool inURL = false;
|
||||
wxString url;
|
||||
|
||||
for( unsigned i = 0; i < text.length(); ++i )
|
||||
if( c == ' ' )
|
||||
{
|
||||
wxUniChar c = text[i];
|
||||
result += wxString::Format( "<a href='%s'>%s</a>", url, url );
|
||||
inURL = false;
|
||||
|
||||
if( inURL )
|
||||
{
|
||||
if( c == ' ' )
|
||||
{
|
||||
result += wxString::Format( "<a href='%s'>%s</a>", url, url );
|
||||
inURL = false;
|
||||
|
||||
result += c;
|
||||
}
|
||||
else
|
||||
{
|
||||
url += c;
|
||||
}
|
||||
}
|
||||
else if( text.Mid( i, 5 ) == "http:" || text.Mid( i, 6 ) == "https:" )
|
||||
{
|
||||
url = c;
|
||||
inURL = true;
|
||||
}
|
||||
else if( c == '\n' )
|
||||
{
|
||||
result += "</p><p>";
|
||||
}
|
||||
else
|
||||
{
|
||||
result += c;
|
||||
}
|
||||
result += c;
|
||||
}
|
||||
else
|
||||
{
|
||||
url += c;
|
||||
}
|
||||
}
|
||||
else if( text.Mid( i, 5 ) == "http:" || text.Mid( i, 6 ) == "https:" )
|
||||
{
|
||||
url = c;
|
||||
inURL = true;
|
||||
}
|
||||
else if( c == '\n' )
|
||||
{
|
||||
result += "</p><p>";
|
||||
}
|
||||
else
|
||||
{
|
||||
result += c;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
return result;
|
||||
};
|
||||
|
||||
wxString desc = package.description_full;
|
||||
details << "<p>" + format_desc( desc ) + "</p>";
|
||||
|
@ -229,30 +228,28 @@ void PANEL_PACKAGES_VIEW::setPackageDetails( const PACKAGE_VIEW_DATA& aPackageDa
|
|||
details << "<li>" + _( "Tags: " ) + tags_str + "</li>";
|
||||
}
|
||||
|
||||
auto format_entry =
|
||||
[]( const std::pair<const std::string, wxString>& entry ) -> wxString
|
||||
{
|
||||
wxString name = entry.first;
|
||||
wxString url = EscapeHTML( entry.second );
|
||||
auto format_entry = []( const std::pair<const std::string, wxString>& entry ) -> wxString
|
||||
{
|
||||
wxString name = entry.first;
|
||||
wxString url = EscapeHTML( entry.second );
|
||||
|
||||
if( name == "email" )
|
||||
return wxString::Format( "<a href='mailto:%s'>%s</a>", url, url );
|
||||
else if( url.StartsWith( "http:" ) || url.StartsWith( "https:" ) )
|
||||
return wxString::Format( "<a href='%s'>%s</a>", url, url );
|
||||
else
|
||||
return entry.second;
|
||||
};
|
||||
if( name == "email" )
|
||||
return wxString::Format( "<a href='mailto:%s'>%s</a>", url, url );
|
||||
else if( url.StartsWith( "http:" ) || url.StartsWith( "https:" ) )
|
||||
return wxString::Format( "<a href='%s'>%s</a>", url, url );
|
||||
else
|
||||
return entry.second;
|
||||
};
|
||||
|
||||
auto write_contact =
|
||||
[&]( const wxString& type, const PCM_CONTACT& contact )
|
||||
{
|
||||
details << "<li>" + type + ": " + contact.name + "<ul>";
|
||||
auto write_contact = [&]( const wxString& type, const PCM_CONTACT& contact )
|
||||
{
|
||||
details << "<li>" + type + ": " + contact.name + "<ul>";
|
||||
|
||||
for( const std::pair<const std::string, wxString>& entry : contact.contact )
|
||||
details << "<li>" + entry.first + ": " + format_entry( entry ) + "</li>";
|
||||
for( const std::pair<const std::string, wxString>& entry : contact.contact )
|
||||
details << "<li>" + entry.first + ": " + format_entry( entry ) + "</li>";
|
||||
|
||||
details << "</ul>";
|
||||
};
|
||||
details << "</ul>";
|
||||
};
|
||||
|
||||
write_contact( _( "Author" ), package.author );
|
||||
|
||||
|
@ -285,7 +282,7 @@ void PANEL_PACKAGES_VIEW::setPackageDetails( const PACKAGE_VIEW_DATA& aPackageDa
|
|||
int row = 0;
|
||||
wxString current_version;
|
||||
|
||||
if( aPackageData.state == PPS_INSTALLED )
|
||||
if( aPackageData.state == PPS_INSTALLED || aPackageData.state == PPS_UPDATE_AVAILABLE )
|
||||
current_version = m_pcm->GetInstalledPackageVersion( package.identifier );
|
||||
|
||||
wxFont bold_font = m_gridVersions->GetDefaultCellFont().Bold();
|
||||
|
@ -320,13 +317,17 @@ void PANEL_PACKAGES_VIEW::setPackageDetails( const PACKAGE_VIEW_DATA& aPackageDa
|
|||
for( int col = 0; col < m_gridVersions->GetNumberCols(); col++ )
|
||||
{
|
||||
// Set the width to see the full contents
|
||||
m_gridVersions->SetColSize( col, m_gridVersions->GetVisibleWidth( col, true, true,
|
||||
false ) );
|
||||
m_gridVersions->SetColSize( col,
|
||||
m_gridVersions->GetVisibleWidth( col, true, true, false ) );
|
||||
}
|
||||
|
||||
// Autoselect preferred or installed version
|
||||
if( m_gridVersions->GetNumberRows() >= 1 )
|
||||
{
|
||||
wxString version = m_currentSelected->GetPreferredVersion();
|
||||
wxString version = m_currentSelected->GetPackageData().current_version;
|
||||
|
||||
if( version.IsEmpty() )
|
||||
version = m_currentSelected->GetPreferredVersion();
|
||||
|
||||
if( !version.IsEmpty() )
|
||||
{
|
||||
|
@ -335,6 +336,7 @@ void PANEL_PACKAGES_VIEW::setPackageDetails( const PACKAGE_VIEW_DATA& aPackageDa
|
|||
if( m_gridVersions->GetCellValue( i, COL_VERSION ) == version )
|
||||
{
|
||||
m_gridVersions->SelectRow( i );
|
||||
m_gridVersions->SetGridCursor( i, COL_VERSION );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -404,15 +406,20 @@ bool PANEL_PACKAGES_VIEW::canDownload() const
|
|||
}
|
||||
|
||||
|
||||
bool PANEL_PACKAGES_VIEW::canInstall() const
|
||||
bool PANEL_PACKAGES_VIEW::canRunAction() const
|
||||
{
|
||||
if( !m_currentSelected )
|
||||
return false;
|
||||
|
||||
const PACKAGE_VIEW_DATA& packageData = m_currentSelected->GetPackageData();
|
||||
|
||||
if( packageData.state != PPS_AVAILABLE && packageData.state != PPS_UNAVAILABLE )
|
||||
return false;
|
||||
switch( packageData.state )
|
||||
{
|
||||
case PPS_PENDING_INSTALL:
|
||||
case PPS_PENDING_UNINSTALL:
|
||||
case PPS_PENDING_UPDATE: return false;
|
||||
default: break;
|
||||
}
|
||||
|
||||
return m_gridVersions->GetNumberRows() == 1 || m_gridVersions->GetSelectedRows().size() == 1;
|
||||
}
|
||||
|
@ -529,14 +536,23 @@ void PANEL_PACKAGES_VIEW::OnDownloadVersionClicked( wxCommandEvent& event )
|
|||
}
|
||||
|
||||
|
||||
void PANEL_PACKAGES_VIEW::OnInstallVersionClicked( wxCommandEvent& event )
|
||||
void PANEL_PACKAGES_VIEW::OnVersionActionClicked( wxCommandEvent& event )
|
||||
{
|
||||
if( !canInstall() )
|
||||
if( !canRunAction() )
|
||||
{
|
||||
wxBell();
|
||||
return;
|
||||
}
|
||||
|
||||
PCM_PACKAGE_ACTION action = getAction();
|
||||
|
||||
if( action == PPA_UNINSTALL )
|
||||
{
|
||||
m_actionCallback( m_currentSelected->GetPackageData(), PPA_UNINSTALL,
|
||||
m_currentSelected->GetPackageData().current_version );
|
||||
return;
|
||||
}
|
||||
|
||||
if( m_gridVersions->GetNumberRows() == 1 )
|
||||
m_gridVersions->SelectRow( 0 );
|
||||
|
||||
|
@ -562,7 +578,7 @@ void PANEL_PACKAGES_VIEW::OnInstallVersionClicked( wxCommandEvent& event )
|
|||
return;
|
||||
}
|
||||
|
||||
m_actionCallback( m_currentSelected->GetPackageData(), PPA_INSTALL, version );
|
||||
m_actionCallback( m_currentSelected->GetPackageData(), action, version );
|
||||
}
|
||||
|
||||
|
||||
|
@ -648,7 +664,56 @@ void PANEL_PACKAGES_VIEW::updatePackageList()
|
|||
void PANEL_PACKAGES_VIEW::updateDetailsButtons()
|
||||
{
|
||||
m_buttonDownload->Enable( canDownload() );
|
||||
m_buttonInstall->Enable( canInstall() );
|
||||
|
||||
if( canRunAction() )
|
||||
{
|
||||
m_buttonAction->Enable();
|
||||
|
||||
PCM_PACKAGE_ACTION action = getAction();
|
||||
|
||||
switch( action )
|
||||
{
|
||||
case PPA_INSTALL: m_buttonAction->SetLabel( _( "Install" ) ); break;
|
||||
case PPA_UNINSTALL: m_buttonAction->SetLabel( _( "Uninstall" ) ); break;
|
||||
case PPA_UPDATE: m_buttonAction->SetLabel( _( "Update" ) ); break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_buttonAction->Disable();
|
||||
m_buttonAction->SetLabel( _( "Pending" ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PCM_PACKAGE_ACTION PANEL_PACKAGES_VIEW::getAction() const
|
||||
{
|
||||
wxASSERT_MSG( m_gridVersions->GetNumberRows() == 1
|
||||
|| m_gridVersions->GetSelectedRows().size() == 1,
|
||||
"getAction() called with ambiguous version selection" );
|
||||
|
||||
int selected_row = 0;
|
||||
|
||||
if( m_gridVersions->GetSelectedRows().size() == 1 )
|
||||
selected_row = m_gridVersions->GetSelectedRows()[0];
|
||||
|
||||
wxString version = m_gridVersions->GetCellValue( selected_row, COL_VERSION );
|
||||
const PACKAGE_VIEW_DATA& package = m_currentSelected->GetPackageData();
|
||||
|
||||
switch( package.state )
|
||||
{
|
||||
case PPS_AVAILABLE:
|
||||
case PPS_UNAVAILABLE:
|
||||
return PPA_INSTALL; // Only action for not installed package is to install it
|
||||
case PPS_INSTALLED:
|
||||
case PPS_UPDATE_AVAILABLE:
|
||||
if( version == package.current_version )
|
||||
return PPA_UNINSTALL;
|
||||
else
|
||||
return PPA_UPDATE;
|
||||
default:
|
||||
return PPA_INSTALL; // For pending states return value does not matter as button will be disabled
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -692,6 +757,6 @@ void PANEL_PACKAGES_VIEW::SetSashOnIdle( wxIdleEvent& aEvent )
|
|||
|
||||
m_packageListWindow->FitInside();
|
||||
|
||||
m_splitter1->Disconnect( wxEVT_IDLE, wxIdleEventHandler( PANEL_PACKAGES_VIEW::SetSashOnIdle ),
|
||||
m_splitter1->Disconnect( wxEVT_IDLE, wxIdleEventHandler( PANEL_PACKAGES_VIEW::SetSashOnIdle ),
|
||||
NULL, this );
|
||||
}
|
||||
|
|
|
@ -63,8 +63,8 @@ public:
|
|||
///< Opens file chooser dialog and downloads selected package version archive
|
||||
void OnDownloadVersionClicked( wxCommandEvent& event ) override;
|
||||
|
||||
///< Schedules installation of selected package version
|
||||
void OnInstallVersionClicked( wxCommandEvent& event ) override;
|
||||
///< Schedules relevant action for selected package version
|
||||
void OnVersionActionClicked( wxCommandEvent& event ) override;
|
||||
|
||||
///< Shows all versions including incompatible ones
|
||||
void OnShowAllVersionsClicked( wxCommandEvent& event ) override;
|
||||
|
@ -102,8 +102,11 @@ private:
|
|||
///< Returns true if it the download operation can be performed
|
||||
bool canDownload() const;
|
||||
|
||||
///< Returns true if the install operation can be performed
|
||||
bool canInstall() const;
|
||||
///< Returns true if the package action can be performed
|
||||
bool canRunAction() const;
|
||||
|
||||
///< Returns implied action for the action button
|
||||
PCM_PACKAGE_ACTION getAction() const;
|
||||
|
||||
private:
|
||||
ActionCallback m_actionCallback;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
///////////////////////////////////////////////////////////////////////////
|
||||
// C++ code generated with wxFormBuilder (version Oct 26 2018)
|
||||
// C++ code generated with wxFormBuilder (version 3.10.1-0-g8feb16b3)
|
||||
// http://www.wxformbuilder.org/
|
||||
//
|
||||
// PLEASE DO *NOT* EDIT THIS FILE!
|
||||
|
@ -78,12 +78,12 @@ PANEL_PACKAGES_VIEW_BASE::PANEL_PACKAGES_VIEW_BASE( wxWindow* parent, wxWindowID
|
|||
m_gridVersions->AutoSizeColumns();
|
||||
m_gridVersions->EnableDragColMove( false );
|
||||
m_gridVersions->EnableDragColSize( true );
|
||||
m_gridVersions->SetColLabelSize( 22 );
|
||||
m_gridVersions->SetColLabelValue( 0, _("Version") );
|
||||
m_gridVersions->SetColLabelValue( 1, _("Download Size") );
|
||||
m_gridVersions->SetColLabelValue( 2, _("Install Size") );
|
||||
m_gridVersions->SetColLabelValue( 3, _("Compatible") );
|
||||
m_gridVersions->SetColLabelValue( 4, _("Status") );
|
||||
m_gridVersions->SetColLabelSize( 22 );
|
||||
m_gridVersions->SetColLabelAlignment( wxALIGN_CENTER, wxALIGN_CENTER );
|
||||
|
||||
// Rows
|
||||
|
@ -109,8 +109,8 @@ PANEL_PACKAGES_VIEW_BASE::PANEL_PACKAGES_VIEW_BASE( wxWindow* parent, wxWindowID
|
|||
m_buttonDownload = new wxButton( m_infoScrollWindow, wxID_ANY, _("Download"), wxDefaultPosition, wxDefaultSize, 0 );
|
||||
bSizerVersionButtons->Add( m_buttonDownload, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
|
||||
|
||||
m_buttonInstall = new wxButton( m_infoScrollWindow, wxID_ANY, _("Install"), wxDefaultPosition, wxDefaultSize, 0 );
|
||||
bSizerVersionButtons->Add( m_buttonInstall, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 5 );
|
||||
m_buttonAction = new wxButton( m_infoScrollWindow, wxID_ANY, _("Install"), wxDefaultPosition, wxDefaultSize, 0 );
|
||||
bSizerVersionButtons->Add( m_buttonAction, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 5 );
|
||||
|
||||
|
||||
m_sizerVersions->Add( bSizerVersionButtons, 0, wxEXPAND|wxRIGHT, 5 );
|
||||
|
@ -143,7 +143,7 @@ PANEL_PACKAGES_VIEW_BASE::PANEL_PACKAGES_VIEW_BASE( wxWindow* parent, wxWindowID
|
|||
m_gridVersions->Connect( wxEVT_GRID_CELL_LEFT_CLICK, wxGridEventHandler( PANEL_PACKAGES_VIEW_BASE::OnVersionsCellClicked ), NULL, this );
|
||||
m_showAllVersions->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( PANEL_PACKAGES_VIEW_BASE::OnShowAllVersionsClicked ), NULL, this );
|
||||
m_buttonDownload->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_PACKAGES_VIEW_BASE::OnDownloadVersionClicked ), NULL, this );
|
||||
m_buttonInstall->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_PACKAGES_VIEW_BASE::OnInstallVersionClicked ), NULL, this );
|
||||
m_buttonAction->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_PACKAGES_VIEW_BASE::OnVersionActionClicked ), NULL, this );
|
||||
}
|
||||
|
||||
PANEL_PACKAGES_VIEW_BASE::~PANEL_PACKAGES_VIEW_BASE()
|
||||
|
@ -155,6 +155,6 @@ PANEL_PACKAGES_VIEW_BASE::~PANEL_PACKAGES_VIEW_BASE()
|
|||
m_gridVersions->Disconnect( wxEVT_GRID_CELL_LEFT_CLICK, wxGridEventHandler( PANEL_PACKAGES_VIEW_BASE::OnVersionsCellClicked ), NULL, this );
|
||||
m_showAllVersions->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( PANEL_PACKAGES_VIEW_BASE::OnShowAllVersionsClicked ), NULL, this );
|
||||
m_buttonDownload->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_PACKAGES_VIEW_BASE::OnDownloadVersionClicked ), NULL, this );
|
||||
m_buttonInstall->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_PACKAGES_VIEW_BASE::OnInstallVersionClicked ), NULL, this );
|
||||
m_buttonAction->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_PACKAGES_VIEW_BASE::OnVersionActionClicked ), NULL, this );
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
||||
<wxFormBuilder_Project>
|
||||
<FileVersion major="1" minor="15" />
|
||||
<FileVersion major="1" minor="16" />
|
||||
<object class="Project" expanded="1">
|
||||
<property name="class_decoration">; </property>
|
||||
<property name="code_generation">C++</property>
|
||||
|
@ -14,6 +14,7 @@
|
|||
<property name="file">panel_packages_view_base</property>
|
||||
<property name="first_id">1000</property>
|
||||
<property name="help_provider">none</property>
|
||||
<property name="image_path_wrapper_function_name"></property>
|
||||
<property name="indent_with_spaces"></property>
|
||||
<property name="internationalize">1</property>
|
||||
<property name="name">panel_packages_view_base</property>
|
||||
|
@ -25,6 +26,7 @@
|
|||
<property name="skip_php_events">1</property>
|
||||
<property name="skip_python_events">1</property>
|
||||
<property name="ui_table">UI</property>
|
||||
<property name="use_array_enum">0</property>
|
||||
<property name="use_enum">0</property>
|
||||
<property name="use_microsoft_bom">0</property>
|
||||
<object class="Panel" expanded="1">
|
||||
|
@ -46,6 +48,7 @@
|
|||
<property name="size">-1,-1</property>
|
||||
<property name="subclass">; ; forward_declare</property>
|
||||
<property name="tooltip"></property>
|
||||
<property name="two_step_creation">0</property>
|
||||
<property name="window_extra_style"></property>
|
||||
<property name="window_name"></property>
|
||||
<property name="window_style">wxTAB_TRAVERSAL</property>
|
||||
|
@ -680,6 +683,7 @@
|
|||
<property name="aui_name"></property>
|
||||
<property name="aui_position"></property>
|
||||
<property name="aui_row"></property>
|
||||
<property name="auth_needed">0</property>
|
||||
<property name="best_size"></property>
|
||||
<property name="bg"></property>
|
||||
<property name="bitmap"></property>
|
||||
|
@ -753,6 +757,7 @@
|
|||
<property name="aui_name"></property>
|
||||
<property name="aui_position"></property>
|
||||
<property name="aui_row"></property>
|
||||
<property name="auth_needed">0</property>
|
||||
<property name="best_size"></property>
|
||||
<property name="bg"></property>
|
||||
<property name="bitmap"></property>
|
||||
|
@ -787,7 +792,7 @@
|
|||
<property name="minimize_button">0</property>
|
||||
<property name="minimum_size"></property>
|
||||
<property name="moveable">1</property>
|
||||
<property name="name">m_buttonInstall</property>
|
||||
<property name="name">m_buttonAction</property>
|
||||
<property name="pane_border">1</property>
|
||||
<property name="pane_position"></property>
|
||||
<property name="pane_size"></property>
|
||||
|
@ -810,7 +815,7 @@
|
|||
<property name="window_extra_style"></property>
|
||||
<property name="window_name"></property>
|
||||
<property name="window_style"></property>
|
||||
<event name="OnButtonClick">OnInstallVersionClicked</event>
|
||||
<event name="OnButtonClick">OnVersionActionClicked</event>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
///////////////////////////////////////////////////////////////////////////
|
||||
// C++ code generated with wxFormBuilder (version Oct 26 2018)
|
||||
// C++ code generated with wxFormBuilder (version 3.10.1-0-g8feb16b3)
|
||||
// http://www.wxformbuilder.org/
|
||||
//
|
||||
// PLEASE DO *NOT* EDIT THIS FILE!
|
||||
|
@ -27,10 +27,10 @@ class WX_PANEL;
|
|||
#include <wx/html/htmlwin.h>
|
||||
#include <wx/grid.h>
|
||||
#include <wx/checkbox.h>
|
||||
#include <wx/button.h>
|
||||
#include <wx/bitmap.h>
|
||||
#include <wx/image.h>
|
||||
#include <wx/icon.h>
|
||||
#include <wx/button.h>
|
||||
#include <wx/splitter.h>
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
@ -55,21 +55,22 @@ class PANEL_PACKAGES_VIEW_BASE : public wxPanel
|
|||
WX_GRID* m_gridVersions;
|
||||
wxCheckBox* m_showAllVersions;
|
||||
wxButton* m_buttonDownload;
|
||||
wxButton* m_buttonInstall;
|
||||
wxButton* m_buttonAction;
|
||||
|
||||
// Virtual event handlers, overide them in your derived class
|
||||
// Virtual event handlers, override them in your derived class
|
||||
virtual void OnSizeInfoBox( wxSizeEvent& event ) { event.Skip(); }
|
||||
virtual void OnURLClicked( wxHtmlLinkEvent& event ) { event.Skip(); }
|
||||
virtual void OnInfoMouseWheel( wxMouseEvent& event ) { event.Skip(); }
|
||||
virtual void OnVersionsCellClicked( wxGridEvent& event ) { event.Skip(); }
|
||||
virtual void OnShowAllVersionsClicked( wxCommandEvent& event ) { event.Skip(); }
|
||||
virtual void OnDownloadVersionClicked( wxCommandEvent& event ) { event.Skip(); }
|
||||
virtual void OnInstallVersionClicked( wxCommandEvent& event ) { event.Skip(); }
|
||||
virtual void OnVersionActionClicked( wxCommandEvent& event ) { event.Skip(); }
|
||||
|
||||
|
||||
public:
|
||||
|
||||
PANEL_PACKAGES_VIEW_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxTAB_TRAVERSAL, const wxString& name = wxEmptyString );
|
||||
|
||||
~PANEL_PACKAGES_VIEW_BASE();
|
||||
|
||||
void m_splitter1OnIdle( wxIdleEvent& )
|
||||
|
|
|
@ -616,6 +616,9 @@ void PLUGIN_CONTENT_MANAGER::DiscardRepositoryCache( const wxString& aRepository
|
|||
void PLUGIN_CONTENT_MANAGER::MarkInstalled( const PCM_PACKAGE& aPackage, const wxString& aVersion,
|
||||
const wxString& aRepositoryId )
|
||||
{
|
||||
// In case of package update remove old data
|
||||
MarkUninstalled( aPackage );
|
||||
|
||||
PCM_INSTALLATION_ENTRY entry;
|
||||
entry.package = aPackage;
|
||||
entry.current_version = aVersion;
|
||||
|
@ -641,11 +644,10 @@ void PLUGIN_CONTENT_MANAGER::MarkUninstalled( const PCM_PACKAGE& aPackage )
|
|||
PCM_PACKAGE_STATE PLUGIN_CONTENT_MANAGER::GetPackageState( const wxString& aRepositoryId,
|
||||
const wxString& aPackageId )
|
||||
{
|
||||
if( m_installed.find( aPackageId ) != m_installed.end() )
|
||||
return PPS_INSTALLED;
|
||||
bool installed = m_installed.find( aPackageId ) != m_installed.end();
|
||||
|
||||
if( aRepositoryId.IsEmpty() || !CacheRepository( aRepositoryId ) )
|
||||
return PPS_UNAVAILABLE;
|
||||
return installed ? PPS_INSTALLED : PPS_UNAVAILABLE;
|
||||
|
||||
const PCM_REPOSITORY& repo = getCachedRepository( aRepositoryId );
|
||||
|
||||
|
@ -656,23 +658,59 @@ PCM_PACKAGE_STATE PLUGIN_CONTENT_MANAGER::GetPackageState( const wxString& aRepo
|
|||
} );
|
||||
|
||||
if( pkg_it == repo.package_list.end() )
|
||||
return PPS_UNAVAILABLE;
|
||||
return installed ? PPS_INSTALLED : PPS_UNAVAILABLE;
|
||||
|
||||
const PCM_PACKAGE& pkg = *pkg_it;
|
||||
|
||||
auto ver_it = std::find_if( pkg.versions.begin(), pkg.versions.end(),
|
||||
[]( const PACKAGE_VERSION& ver )
|
||||
{
|
||||
return ver.compatible;
|
||||
} );
|
||||
if( installed )
|
||||
{
|
||||
// Package is installed, check for available updates at the same or
|
||||
// higher (numerically lower) version stability level
|
||||
wxString update_version = GetPackageUpdateVersion( pkg );
|
||||
|
||||
if( ver_it == pkg.versions.end() )
|
||||
return PPS_UNAVAILABLE;
|
||||
return update_version.IsEmpty() ? PPS_INSTALLED : PPS_UPDATE_AVAILABLE;
|
||||
}
|
||||
else
|
||||
return PPS_AVAILABLE;
|
||||
{
|
||||
// Find any compatible version
|
||||
auto ver_it = std::find_if( pkg.versions.begin(), pkg.versions.end(),
|
||||
[]( const PACKAGE_VERSION& ver )
|
||||
{
|
||||
return ver.compatible;
|
||||
} );
|
||||
|
||||
return ver_it == pkg.versions.end() ? PPS_UNAVAILABLE : PPS_AVAILABLE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const wxString PLUGIN_CONTENT_MANAGER::GetPackageUpdateVersion( const PCM_PACKAGE& aPackage )
|
||||
{
|
||||
wxASSERT_MSG( m_installed.find( aPackage.identifier ) != m_installed.end(),
|
||||
"GetPackageUpdateVersion called on a not installed package" );
|
||||
|
||||
const PCM_INSTALLATION_ENTRY& installation_entry = m_installed.at( aPackage.identifier );
|
||||
|
||||
auto installed_ver_it = std::find_if(
|
||||
installation_entry.package.versions.begin(), installation_entry.package.versions.end(),
|
||||
[&]( const PACKAGE_VERSION& ver )
|
||||
{
|
||||
return ver.version == installation_entry.current_version;
|
||||
} );
|
||||
|
||||
wxASSERT_MSG( installed_ver_it != installation_entry.package.versions.end(),
|
||||
"Installed package version not found" );
|
||||
|
||||
auto ver_it = std::find_if( aPackage.versions.begin(), aPackage.versions.end(),
|
||||
[&]( const PACKAGE_VERSION& ver )
|
||||
{
|
||||
return ver.compatible && installed_ver_it->status >= ver.status
|
||||
&& installed_ver_it->parsed_version < ver.parsed_version;
|
||||
} );
|
||||
|
||||
return ver_it == aPackage.versions.end() ? wxT( "" ) : ver_it->version;
|
||||
}
|
||||
|
||||
time_t PLUGIN_CONTENT_MANAGER::getCurrentTimestamp() const
|
||||
{
|
||||
return std::chrono::duration_cast<std::chrono::seconds>(
|
||||
|
|
|
@ -55,7 +55,9 @@ enum PCM_PACKAGE_STATE
|
|||
PPS_UNAVAILABLE = 1,
|
||||
PPS_INSTALLED = 2,
|
||||
PPS_PENDING_INSTALL = 3,
|
||||
PPS_PENDING_UNINSTALL = 4
|
||||
PPS_PENDING_UNINSTALL = 4,
|
||||
PPS_UPDATE_AVAILABLE = 5,
|
||||
PPS_PENDING_UPDATE = 6,
|
||||
};
|
||||
|
||||
|
||||
|
@ -63,7 +65,8 @@ enum PCM_PACKAGE_STATE
|
|||
enum PCM_PACKAGE_ACTION
|
||||
{
|
||||
PPA_INSTALL = 0,
|
||||
PPA_UNINSTALL = 1
|
||||
PPA_UNINSTALL = 1,
|
||||
PPA_UPDATE = 2,
|
||||
};
|
||||
|
||||
|
||||
|
@ -224,6 +227,18 @@ public:
|
|||
*/
|
||||
PCM_PACKAGE_STATE GetPackageState( const wxString& aRepositoryId, const wxString& aPackageId );
|
||||
|
||||
/**
|
||||
* @brief Get the preferred package update version or empty string if there is none
|
||||
*
|
||||
* Works only for installed packages and returns highest compatible version greater
|
||||
* than currently installed that is at the same or higher (numerically lower)
|
||||
* version stability level.
|
||||
*
|
||||
* @param aPackage package
|
||||
* @return package version string
|
||||
*/
|
||||
const wxString GetPackageUpdateVersion( const PCM_PACKAGE& aPackage );
|
||||
|
||||
/**
|
||||
* @brief Downloads url to an output stream
|
||||
*
|
||||
|
|
|
@ -59,6 +59,9 @@ void to_json( json& j, const PACKAGE_VERSION& v )
|
|||
|
||||
if( v.kicad_version_max )
|
||||
j["kicad_version_max"] = v.kicad_version_max.get();
|
||||
|
||||
if( v.keep_on_update.size() > 0 )
|
||||
nlohmann::to_json( j["keep_on_update"], v.keep_on_update );
|
||||
}
|
||||
|
||||
|
||||
|
@ -77,6 +80,9 @@ void from_json( const json& j, PACKAGE_VERSION& v )
|
|||
|
||||
if( j.contains( "platforms" ) )
|
||||
j.at( "platforms" ).get_to( v.platforms );
|
||||
|
||||
if( j.contains( "keep_on_update" ) )
|
||||
j.at( "keep_on_update" ).get_to( v.keep_on_update );
|
||||
}
|
||||
|
||||
|
||||
|
@ -97,6 +103,9 @@ void to_json( json& j, const PCM_PACKAGE& p )
|
|||
|
||||
if( p.tags.size() > 0 )
|
||||
j["tags"] = p.tags;
|
||||
|
||||
if( p.keep_on_update.size() > 0 )
|
||||
j["keep_on_update"] = p.keep_on_update;
|
||||
}
|
||||
|
||||
|
||||
|
@ -116,6 +125,9 @@ void from_json( const json& j, PCM_PACKAGE& p )
|
|||
|
||||
if( j.contains( "tags" ) )
|
||||
j.at( "tags" ).get_to( p.tags );
|
||||
|
||||
if( j.contains( "keep_on_update" ) )
|
||||
j.at( "keep_on_update" ).get_to( p.keep_on_update );
|
||||
}
|
||||
|
||||
|
||||
|
@ -133,7 +145,7 @@ void from_json( const json& j, PCM_RESOURCE_REFERENCE& r )
|
|||
j.at( "url" ).get_to( r.url );
|
||||
j.at( "update_timestamp" ).get_to( r.update_timestamp );
|
||||
|
||||
to_optional(j, "sha256", r.sha256 );
|
||||
to_optional( j, "sha256", r.sha256 );
|
||||
}
|
||||
|
||||
|
||||
|
@ -157,7 +169,7 @@ void from_json( const json& j, PCM_REPOSITORY& r )
|
|||
j.at( "name" ).get_to( r.name );
|
||||
j.at( "packages" ).get_to( r.packages );
|
||||
|
||||
to_optional(j, "resources", r.resources );
|
||||
to_optional(j, "manifests", r.manifests );
|
||||
to_optional(j, "maintainer", r.maintainer );
|
||||
to_optional( j, "resources", r.resources );
|
||||
to_optional( j, "manifests", r.manifests );
|
||||
to_optional( j, "maintainer", r.maintainer );
|
||||
}
|
||||
|
|
|
@ -82,6 +82,7 @@ struct PACKAGE_VERSION
|
|||
std::vector<std::string> platforms;
|
||||
wxString kicad_version;
|
||||
boost::optional<wxString> kicad_version_max;
|
||||
std::vector<std::string> keep_on_update;
|
||||
|
||||
// Not serialized fields
|
||||
std::tuple<int, int, int, int> parsed_version; // Full version tuple for sorting
|
||||
|
@ -102,6 +103,7 @@ struct PCM_PACKAGE
|
|||
wxString license;
|
||||
STRING_MAP resources;
|
||||
std::vector<std::string> tags;
|
||||
std::vector<std::string> keep_on_update;
|
||||
std::vector<PACKAGE_VERSION> versions;
|
||||
};
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
*/
|
||||
// kicad_curl_easy.h **must be** included before any wxWidgets header to avoid conflicts
|
||||
// at least on Windows/msys2
|
||||
#include <kicad_curl/kicad_curl.h>
|
||||
#include "kicad_curl/kicad_curl.h"
|
||||
#include "kicad_curl/kicad_curl_easy.h"
|
||||
|
||||
#include "pcm_task_manager.h"
|
||||
|
@ -37,92 +37,62 @@
|
|||
#include <wx/zipstrm.h>
|
||||
|
||||
|
||||
void PCM_TASK_MANAGER::DownloadAndInstall( const PCM_PACKAGE& aPackage, const wxString& aVersion,
|
||||
const wxString& aRepositoryId )
|
||||
void compile_keep_on_update_regex( const PCM_PACKAGE& pkg, const PACKAGE_VERSION& ver,
|
||||
std::forward_list<wxRegEx>& aKeepOnUpdate )
|
||||
{
|
||||
PCM_TASK download_task = [aPackage, aVersion, aRepositoryId, this]()
|
||||
auto compile_regex = [&]( const wxString& regex )
|
||||
{
|
||||
aKeepOnUpdate.emplace_front( regex, wxRE_DEFAULT );
|
||||
|
||||
if( !aKeepOnUpdate.front().IsValid() )
|
||||
aKeepOnUpdate.pop_front();
|
||||
};
|
||||
|
||||
std::for_each( pkg.keep_on_update.begin(), pkg.keep_on_update.end(), compile_regex );
|
||||
std::for_each( ver.keep_on_update.begin(), ver.keep_on_update.end(), compile_regex );
|
||||
}
|
||||
|
||||
|
||||
void PCM_TASK_MANAGER::DownloadAndInstall( const PCM_PACKAGE& aPackage, const wxString& aVersion,
|
||||
const wxString& aRepositoryId, const bool isUpdate )
|
||||
{
|
||||
PCM_TASK download_task = [aPackage, aVersion, aRepositoryId, isUpdate, this]()
|
||||
{
|
||||
wxFileName file_path( m_pcm->Get3rdPartyPath(), "" );
|
||||
file_path.AppendDir( "cache" );
|
||||
file_path.SetFullName( wxString::Format( "%s_v%s.zip", aPackage.identifier, aVersion ) );
|
||||
|
||||
auto find_pkgver = std::find_if( aPackage.versions.begin(), aPackage.versions.end(),
|
||||
[&aVersion]( const PACKAGE_VERSION& pv )
|
||||
{
|
||||
return pv.version == aVersion;
|
||||
} );
|
||||
[&aVersion]( const PACKAGE_VERSION& pv )
|
||||
{
|
||||
return pv.version == aVersion;
|
||||
} );
|
||||
|
||||
wxASSERT_MSG( find_pkgver != aPackage.versions.end(), "Package version not found" );
|
||||
|
||||
if( !wxDirExists( file_path.GetPath() )
|
||||
&& !wxFileName::Mkdir( file_path.GetPath(), wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
|
||||
{
|
||||
m_reporter->PCMReport( _( "Unable to create download directory!" ), RPT_SEVERITY_ERROR );
|
||||
m_reporter->PCMReport( _( "Unable to create download directory!" ),
|
||||
RPT_SEVERITY_ERROR );
|
||||
return;
|
||||
}
|
||||
|
||||
int code = downloadFile( file_path.GetFullPath(), find_pkgver->download_url.get() );
|
||||
|
||||
if( code == CURLE_OK )
|
||||
if( code != CURLE_OK )
|
||||
{
|
||||
PCM_TASK install_task = [aPackage, aVersion, aRepositoryId, file_path, this]()
|
||||
{
|
||||
auto get_pkgver = std::find_if( aPackage.versions.begin(), aPackage.versions.end(),
|
||||
[&aVersion]( const PACKAGE_VERSION& pv )
|
||||
{
|
||||
return pv.version == aVersion;
|
||||
} );
|
||||
|
||||
const boost::optional<wxString>& hash = get_pkgver->download_sha256;
|
||||
bool hash_match = true;
|
||||
|
||||
if( hash )
|
||||
{
|
||||
std::ifstream stream( file_path.GetFullPath().ToUTF8(), std::ios::binary );
|
||||
hash_match = m_pcm->VerifyHash( stream, hash.get() );
|
||||
}
|
||||
|
||||
if( !hash_match )
|
||||
{
|
||||
m_reporter->PCMReport( wxString::Format( _( "Downloaded archive hash for package "
|
||||
"%s does not match repository entry. "
|
||||
"This may indicate a problem with the "
|
||||
"package, if the issue persists "
|
||||
"report this to repository maintainers." ),
|
||||
aPackage.identifier ),
|
||||
RPT_SEVERITY_ERROR );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_reporter->PCMReport( wxString::Format( _( "Extracting package '%s'." ),
|
||||
aPackage.identifier ),
|
||||
RPT_SEVERITY_INFO );
|
||||
|
||||
if( extract( file_path.GetFullPath(), aPackage.identifier, true ) )
|
||||
{
|
||||
m_pcm->MarkInstalled( aPackage, get_pkgver->version, aRepositoryId );
|
||||
// TODO register libraries.
|
||||
}
|
||||
else
|
||||
{
|
||||
// Cleanup possibly partially extracted package
|
||||
deletePackageDirectories( aPackage.identifier );
|
||||
}
|
||||
}
|
||||
|
||||
m_reporter->PCMReport( wxString::Format( _( "Removing downloaded archive '%s'." ),
|
||||
file_path.GetFullName() ),
|
||||
RPT_SEVERITY_INFO );
|
||||
wxRemoveFile( file_path.GetFullPath() );
|
||||
};
|
||||
|
||||
m_install_queue.push( install_task );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Cleanup after ourselves.
|
||||
// Cleanup after ourselves and exit
|
||||
wxRemoveFile( file_path.GetFullPath() );
|
||||
return;
|
||||
}
|
||||
|
||||
PCM_TASK install_task = [aPackage, aVersion, aRepositoryId, file_path, isUpdate, this]()
|
||||
{
|
||||
installDownloadedPackage( aPackage, aVersion, aRepositoryId, file_path, isUpdate );
|
||||
};
|
||||
|
||||
m_install_queue.push( install_task );
|
||||
};
|
||||
|
||||
m_download_queue.push( download_task );
|
||||
|
@ -150,7 +120,7 @@ int PCM_TASK_MANAGER::downloadFile( const wxString& aFilePath, const wxString& u
|
|||
curl.SetTransferCallback( callback, 250000L );
|
||||
|
||||
m_reporter->PCMReport( wxString::Format( _( "Downloading package url: '%s'" ), url ),
|
||||
RPT_SEVERITY_INFO );
|
||||
RPT_SEVERITY_INFO );
|
||||
|
||||
int code = curl.Perform();
|
||||
|
||||
|
@ -164,14 +134,88 @@ int PCM_TASK_MANAGER::downloadFile( const wxString& aFilePath, const wxString& u
|
|||
if( code != CURLE_OK && code != CURLE_ABORTED_BY_CALLBACK )
|
||||
{
|
||||
m_reporter->PCMReport( wxString::Format( _( "Failed to download url %s\n%s" ), url,
|
||||
curl.GetErrorText( code ) ),
|
||||
RPT_SEVERITY_ERROR );
|
||||
curl.GetErrorText( code ) ),
|
||||
RPT_SEVERITY_ERROR );
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
|
||||
void PCM_TASK_MANAGER::installDownloadedPackage( const PCM_PACKAGE& aPackage,
|
||||
const wxString& aVersion,
|
||||
const wxString& aRepositoryId,
|
||||
const wxFileName& aFilePath, const bool isUpdate )
|
||||
{
|
||||
auto pkgver = std::find_if( aPackage.versions.begin(), aPackage.versions.end(),
|
||||
[&aVersion]( const PACKAGE_VERSION& pv )
|
||||
{
|
||||
return pv.version == aVersion;
|
||||
} );
|
||||
|
||||
wxASSERT_MSG( pkgver != aPackage.versions.end(), "Package version not found" );
|
||||
|
||||
// wxRegEx is not CopyConstructible hence the weird choice of forward_list
|
||||
std::forward_list<wxRegEx> keep_on_update;
|
||||
|
||||
if( isUpdate )
|
||||
compile_keep_on_update_regex( aPackage, *pkgver, keep_on_update );
|
||||
|
||||
const boost::optional<wxString>& hash = pkgver->download_sha256;
|
||||
bool hash_match = true;
|
||||
|
||||
if( hash )
|
||||
{
|
||||
std::ifstream stream( aFilePath.GetFullPath().ToUTF8(), std::ios::binary );
|
||||
hash_match = m_pcm->VerifyHash( stream, hash.get() );
|
||||
}
|
||||
|
||||
if( !hash_match )
|
||||
{
|
||||
m_reporter->PCMReport( wxString::Format( _( "Downloaded archive hash for package "
|
||||
"%s does not match repository entry. "
|
||||
"This may indicate a problem with the "
|
||||
"package, if the issue persists "
|
||||
"report this to repository maintainers." ),
|
||||
aPackage.identifier ),
|
||||
RPT_SEVERITY_ERROR );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( isUpdate )
|
||||
{
|
||||
m_reporter->PCMReport(
|
||||
wxString::Format( _( "Removing previous version of package '%s'." ),
|
||||
aPackage.identifier ),
|
||||
RPT_SEVERITY_INFO );
|
||||
|
||||
deletePackageDirectories( aPackage.identifier, keep_on_update );
|
||||
}
|
||||
|
||||
m_reporter->PCMReport(
|
||||
wxString::Format( _( "Extracting package '%s'." ), aPackage.identifier ),
|
||||
RPT_SEVERITY_INFO );
|
||||
|
||||
if( extract( aFilePath.GetFullPath(), aPackage.identifier, true ) )
|
||||
{
|
||||
m_pcm->MarkInstalled( aPackage, pkgver->version, aRepositoryId );
|
||||
// TODO register libraries.
|
||||
}
|
||||
else
|
||||
{
|
||||
// Cleanup possibly partially extracted package
|
||||
deletePackageDirectories( aPackage.identifier );
|
||||
}
|
||||
}
|
||||
|
||||
m_reporter->PCMReport(
|
||||
wxString::Format( _( "Removing downloaded archive '%s'." ), aFilePath.GetFullName() ),
|
||||
RPT_SEVERITY_INFO );
|
||||
|
||||
wxRemoveFile( aFilePath.GetFullPath() );
|
||||
}
|
||||
|
||||
|
||||
bool PCM_TASK_MANAGER::extract( const wxString& aFilePath, const wxString& aPackageId,
|
||||
bool isMultiThreaded )
|
||||
{
|
||||
|
@ -319,7 +363,11 @@ void PCM_TASK_MANAGER::InstallFromFile( wxWindow* aParent, const wxString& aFile
|
|||
return;
|
||||
}
|
||||
|
||||
const auto installed_packages = m_pcm->GetInstalledPackages();
|
||||
bool isUpdate = false;
|
||||
// wxRegEx is not CopyConstructible hence the weird choice of forward_list
|
||||
std::forward_list<wxRegEx> keep_on_update;
|
||||
const std::vector<PCM_INSTALLATION_ENTRY> installed_packages = m_pcm->GetInstalledPackages();
|
||||
|
||||
if( std::find_if( installed_packages.begin(), installed_packages.end(),
|
||||
[&]( const PCM_INSTALLATION_ENTRY& entry )
|
||||
{
|
||||
|
@ -327,15 +375,32 @@ void PCM_TASK_MANAGER::InstallFromFile( wxWindow* aParent, const wxString& aFile
|
|||
} )
|
||||
!= installed_packages.end() )
|
||||
{
|
||||
wxLogError( wxString::Format( _( "Package with identifier %s is already installed, you "
|
||||
"must first uninstall this package." ),
|
||||
package.identifier ) );
|
||||
return;
|
||||
if( wxMessageBox(
|
||||
wxString::Format(
|
||||
_( "Package with identifier %s is already installed. "
|
||||
"Would you like to update it to the version from selected file?" ),
|
||||
package.identifier ),
|
||||
_( "Update package" ), wxICON_EXCLAMATION | wxYES_NO, aParent )
|
||||
== wxNO )
|
||||
return;
|
||||
|
||||
isUpdate = true;
|
||||
|
||||
compile_keep_on_update_regex( package, package.versions[0], keep_on_update );
|
||||
}
|
||||
|
||||
m_reporter = std::make_unique<DIALOG_PCM_PROGRESS>( aParent, false );
|
||||
m_reporter->Show();
|
||||
|
||||
if( isUpdate )
|
||||
{
|
||||
m_reporter->PCMReport( wxString::Format( _( "Removing previous version of package '%s'." ),
|
||||
package.identifier ),
|
||||
RPT_SEVERITY_INFO );
|
||||
|
||||
deletePackageDirectories( package.identifier, keep_on_update );
|
||||
}
|
||||
|
||||
if( extract( aFilePath, package.identifier, false ) )
|
||||
m_pcm->MarkInstalled( package, package.versions[0].version, "" );
|
||||
|
||||
|
@ -346,23 +411,68 @@ void PCM_TASK_MANAGER::InstallFromFile( wxWindow* aParent, const wxString& aFile
|
|||
}
|
||||
|
||||
|
||||
void PCM_TASK_MANAGER::deletePackageDirectories( const wxString& aPackageId )
|
||||
class PATH_COLLECTOR : public wxDirTraverser
|
||||
{
|
||||
private:
|
||||
std::vector<wxString>& m_files;
|
||||
std::vector<wxString>& m_dirs;
|
||||
|
||||
public:
|
||||
explicit PATH_COLLECTOR( std::vector<wxString>& aFiles, std::vector<wxString>& aDirs ) :
|
||||
m_files( aFiles ), m_dirs( aDirs )
|
||||
{
|
||||
}
|
||||
|
||||
wxDirTraverseResult OnFile( const wxString& aFilePath ) override
|
||||
{
|
||||
m_files.push_back( aFilePath );
|
||||
return wxDIR_CONTINUE;
|
||||
}
|
||||
|
||||
wxDirTraverseResult OnDir( const wxString& dirPath ) override
|
||||
{
|
||||
m_dirs.push_back( dirPath );
|
||||
return wxDIR_CONTINUE;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void PCM_TASK_MANAGER::deletePackageDirectories( const wxString& aPackageId,
|
||||
const std::forward_list<wxRegEx>& aKeep )
|
||||
{
|
||||
// Namespace delimiter changed on disk to allow flat loading of Python modules
|
||||
wxString clean_package_id = aPackageId;
|
||||
clean_package_id.Replace( '.', '_' );
|
||||
|
||||
int path_prefix_len = m_pcm->Get3rdPartyPath().Length();
|
||||
|
||||
auto sort_func = []( const wxString& a, const wxString& b )
|
||||
{
|
||||
if( a.length() > b.length() )
|
||||
return true;
|
||||
if( a.length() < b.length() )
|
||||
return false;
|
||||
|
||||
if( a != b )
|
||||
return a < b;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
for( const wxString& dir : PCM_PACKAGE_DIRECTORIES )
|
||||
{
|
||||
wxFileName d( m_pcm->Get3rdPartyPath(), "" );
|
||||
d.AppendDir( dir );
|
||||
d.AppendDir( clean_package_id );
|
||||
|
||||
if( d.DirExists() )
|
||||
{
|
||||
m_reporter->PCMReport( wxString::Format( _( "Removing directory %s" ), d.GetPath() ),
|
||||
RPT_SEVERITY_INFO );
|
||||
if( !d.DirExists() )
|
||||
continue;
|
||||
|
||||
m_reporter->PCMReport( wxString::Format( _( "Removing directory %s" ), d.GetPath() ),
|
||||
RPT_SEVERITY_INFO );
|
||||
|
||||
if( aKeep.empty() )
|
||||
{
|
||||
if( !d.Rmdir( wxPATH_RMDIR_RECURSIVE ) )
|
||||
{
|
||||
m_reporter->PCMReport(
|
||||
|
@ -370,6 +480,50 @@ void PCM_TASK_MANAGER::deletePackageDirectories( const wxString& aPackageId )
|
|||
RPT_SEVERITY_ERROR );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<wxString> files;
|
||||
std::vector<wxString> dirs;
|
||||
PATH_COLLECTOR collector( files, dirs );
|
||||
|
||||
wxDir( d.GetFullPath() )
|
||||
.Traverse( collector, wxEmptyString, wxDIR_DEFAULT | wxDIR_NO_FOLLOW );
|
||||
|
||||
// Do a poor mans post order traversal by sorting paths in reverse length order
|
||||
std::sort( files.begin(), files.end(), sort_func );
|
||||
std::sort( dirs.begin(), dirs.end(), sort_func );
|
||||
|
||||
// Delete files that don't match any of the aKeep regexs
|
||||
for( const wxString& file : files )
|
||||
{
|
||||
bool del = true;
|
||||
|
||||
for( const wxRegEx& re : aKeep )
|
||||
{
|
||||
wxString tmp = file.Mid( path_prefix_len );
|
||||
tmp.Replace( "\\", "/" );
|
||||
|
||||
if( re.Matches( tmp ) )
|
||||
{
|
||||
// m_reporter->PCMReport( wxString::Format( _( "Keeping file '%s'." ), tmp ),
|
||||
// RPT_SEVERITY_INFO );
|
||||
|
||||
del = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( del )
|
||||
wxRemoveFile( file );
|
||||
}
|
||||
|
||||
// Delete any empty dirs
|
||||
for( const wxString& dir : dirs )
|
||||
{
|
||||
wxFileName d( dir, "" );
|
||||
d.Rmdir(); // not passing any flags here will only remove empty directories
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -382,8 +536,9 @@ void PCM_TASK_MANAGER::Uninstall( const PCM_PACKAGE& aPackage )
|
|||
|
||||
m_pcm->MarkUninstalled( aPackage );
|
||||
|
||||
m_reporter->PCMReport( wxString::Format( _( "Package %s uninstalled" ), aPackage.identifier ),
|
||||
RPT_SEVERITY_INFO );
|
||||
m_reporter->PCMReport(
|
||||
wxString::Format( _( "Package %s uninstalled" ), aPackage.identifier ),
|
||||
RPT_SEVERITY_INFO );
|
||||
};
|
||||
|
||||
m_install_queue.push( task );
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include <mutex>
|
||||
#include <nlohmann/json-schema.hpp>
|
||||
#include <widgets/wx_progress_reporters.h>
|
||||
#include <wx/regex.h>
|
||||
#include <wx/string.h>
|
||||
|
||||
|
||||
|
@ -67,7 +68,7 @@ public:
|
|||
* @param aRepositoryId id of the source repository
|
||||
*/
|
||||
void DownloadAndInstall( const PCM_PACKAGE& aPackage, const wxString& aVersion,
|
||||
const wxString& aRepositoryId );
|
||||
const wxString& aRepositoryId, const bool isUpdate );
|
||||
|
||||
/**
|
||||
* @brief Enqueue package uninstallation
|
||||
|
@ -113,6 +114,19 @@ private:
|
|||
*/
|
||||
int downloadFile( const wxString& aFilePath, const wxString& aUrl );
|
||||
|
||||
/**
|
||||
* @brief Installs downloaded package archive
|
||||
*
|
||||
* @param aPackage package metadata
|
||||
* @param aVersion version to be installed
|
||||
* @param aRepositoryId id of the source repository
|
||||
* @param aFilePath path to the archive
|
||||
* @param isUpdate true if this is an update operation
|
||||
*/
|
||||
void installDownloadedPackage( const PCM_PACKAGE& aPackage, const wxString& aVersion,
|
||||
const wxString& aRepositoryId, const wxFileName& aFilePath,
|
||||
const bool isUpdate );
|
||||
|
||||
/**
|
||||
* @brief Extract package archive
|
||||
*
|
||||
|
@ -127,8 +141,10 @@ private:
|
|||
* @brief Delete all package files
|
||||
*
|
||||
* @param aPackageId id of the package
|
||||
* @param aKeep list of regex indicating which files should not be deleted
|
||||
*/
|
||||
void deletePackageDirectories( const wxString& aPackageId );
|
||||
void deletePackageDirectories( const wxString& aPackageId,
|
||||
const std::forward_list<wxRegEx>& aKeep = {} );
|
||||
|
||||
std::unique_ptr<DIALOG_PCM_PROGRESS> m_reporter;
|
||||
SYNC_QUEUE<PCM_TASK> m_download_queue;
|
||||
|
|
|
@ -70,6 +70,14 @@
|
|||
"minItems": 1,
|
||||
"uniqueItems": true
|
||||
},
|
||||
"keep_on_update": {
|
||||
"type": "array",
|
||||
"description": "List of regular expressions describing which files should be kept on package update",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"uniqueItems": true
|
||||
},
|
||||
"versions": {
|
||||
"type": "array",
|
||||
"description": "Package versions",
|
||||
|
@ -183,6 +191,14 @@
|
|||
"type": "string",
|
||||
"description": "Maximum supported KiCad version",
|
||||
"pattern": "^\\d{1,2}(\\.\\d{1,2}(\\.\\d{1,2})?)?$"
|
||||
},
|
||||
"keep_on_update": {
|
||||
"type": "array",
|
||||
"description": "List of regular expressions describing which files should be kept on package update",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"uniqueItems": true
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
|
Loading…
Reference in New Issue