KiCad PCB EDA Suite
dialog_pcm.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2021 Andrew Lutsenko, anlutsenko at gmail dot com
5  * Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * This program is free software: you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License as published by the
9  * Free Software Foundation, either version 3 of the License, or (at your
10  * option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program. If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 // kicad_curl_easy.h **must be** included before any wxWidgets header to avoid conflicts
22 // at least on Windows/msys2
24 
25 #include "dialog_pcm.h"
26 #include "bitmaps.h"
28 #include "grid_tricks.h"
29 #include "ki_exception.h"
30 #include "kicad_settings.h"
31 #include "pcm_task_manager.h"
32 #include "pgm_base.h"
34 #include "thread"
36 #include "widgets/wx_grid.h"
37 
38 #include <fstream>
39 #include <sstream>
40 #include <vector>
41 #include <wx/filedlg.h>
42 #include <wx/msgdlg.h>
43 
44 
45 #define GRID_CELL_MARGIN 4
46 
47 // Notes: These strings are static, so wxGetTranslation must be called to display the
48 // transalted text
49 static std::vector<std::pair<PCM_PACKAGE_TYPE, wxString>> PACKAGE_TYPE_LIST = {
50  { PT_PLUGIN, _( "Plugins (%d)" ) },
51  { PT_LIBRARY, _( "Libraries (%d)" ) },
52  { PT_COLORTHEME, _( "Color themes (%d)" ) },
53 };
54 
55 
56 DIALOG_PCM::DIALOG_PCM( wxWindow* parent ) : DIALOG_PCM_BASE( parent )
57 {
59 
60  m_pcm = std::make_shared<PLUGIN_CONTENT_MANAGER>( this );
61 
62  m_gridPendingActions->PushEventHandler( new GRID_TRICKS( m_gridPendingActions ) );
63 
65  m_panelPending->Layout();
66 
68  m_panelInstalledHolder->GetSizer()->Add( m_installedPanel, 1, wxEXPAND );
69  m_panelInstalledHolder->Layout();
70 
71  for( const auto& entry : PACKAGE_TYPE_LIST )
72  {
74  wxString msg = wxGetTranslation( std::get<1>( entry ) );
75  m_contentNotebook->AddPage( panel, wxString::Format( msg, 0 ) );
76  m_repositoryContentPanels.insert( { std::get<0>( entry ), panel } );
77  }
78 
79  m_dialogNotebook->SetPageText( 0, wxString::Format( _( "Repository (%d)" ), 0 ) );
80 
81  m_callback = [this]( const PACKAGE_VIEW_DATA& aData, PCM_PACKAGE_ACTION aAction,
82  const wxString& aVersion )
83  {
84  m_gridPendingActions->Freeze();
85 
86  PCM_PACKAGE_STATE new_state;
87 
88  m_gridPendingActions->AppendRows();
89  int row = m_gridPendingActions->GetNumberRows() - 1;
90 
91  m_gridPendingActions->SetCellValue( row, PENDING_COL_NAME, aData.package.name );
92  m_gridPendingActions->SetCellValue( row, PENDING_COL_REPOSITORY, aData.repository_name );
93 
94  if( aAction == PPA_INSTALL )
95  {
96  m_gridPendingActions->SetCellValue( row, PENDING_COL_ACTION, _( "Install" ) );
97  m_gridPendingActions->SetCellValue( row, PENDING_COL_VERSION, aVersion );
98 
99  m_pendingActions.emplace_back( aAction, aData.repository_id, aData.package, aVersion );
100 
101  new_state = PPS_PENDING_INSTALL;
102  }
103  else
104  {
105  m_gridPendingActions->SetCellValue( row, PENDING_COL_ACTION, _( "Uninstall" ) );
106  m_gridPendingActions->SetCellValue(
107  row, PENDING_COL_VERSION,
108  m_pcm->GetInstalledPackageVersion( aData.package.identifier ) );
109 
110  m_pendingActions.emplace_back( aAction, aData.repository_id, aData.package, aVersion );
111 
112  new_state = PPS_PENDING_UNINSTALL;
113  }
114 
115  m_gridPendingActions->Thaw();
116 
118 
119  m_installedPanel->SetPackageState( aData.package.identifier, new_state );
120 
121  for( const auto& entry : m_repositoryContentPanels )
122  entry.second->SetPackageState( aData.package.identifier, new_state );
123  };
124 
127 
128  m_dialogNotebook->SetSelection( 0 );
129 
130 
131  SETTINGS_MANAGER& mgr = Pgm().GetSettingsManager();
132  KICAD_SETTINGS* app_settings = mgr.GetAppSettings<KICAD_SETTINGS>();
133 
134  m_pcm->SetRepositoryList( app_settings->m_PcmRepositories );
135 
137 
138  SetDefaultItem( m_closeButton );
139 
140  for( int col = 0; col < m_gridPendingActions->GetNumberCols(); col++ )
141  {
142  const wxString& heading = m_gridPendingActions->GetColLabelValue( col );
143  int headingWidth = GetTextExtent( heading ).x + 2 * GRID_CELL_MARGIN;
144 
145  // Set the minimal width to the column label size.
146  m_gridPendingActions->SetColMinimalWidth( col, headingWidth );
147  }
148 }
149 
150 
152 {
153  m_gridPendingActions->PopEventHandler( true );
154 }
155 
156 
157 void DIALOG_PCM::OnCloseClicked( wxCommandEvent& event )
158 {
159  if( m_pendingActions.size() == 0
160  || wxMessageBox( _( "Are you sure you want to close the package manager "
161  "and discard pending changes?" ),
162  _( "Plugin and Content Manager" ), wxICON_QUESTION | wxYES_NO, this )
163  == wxYES )
164  {
165  EndModal( wxID_OK );
166  }
167 }
168 
169 
170 void DIALOG_PCM::OnManageRepositoriesClicked( wxCommandEvent& event )
171 {
173 
174  STRING_PAIR_LIST dialog_data;
175  STRING_TUPLE_LIST repo_list = m_pcm->GetRepositoryList();
176 
177  for( const auto& repo : repo_list )
178  {
179  dialog_data.push_back( std::make_pair( std::get<1>( repo ), std::get<2>( repo ) ) );
180  }
181 
182  dialog->SetData( dialog_data );
183 
184  if( dialog->ShowModal() == wxID_SAVE )
185  {
186  dialog_data = dialog->GetData();
187  m_pcm->SetRepositoryList( dialog_data );
188 
189  SETTINGS_MANAGER& mgr = Pgm().GetSettingsManager();
190  KICAD_SETTINGS* app_settings = mgr.GetAppSettings<KICAD_SETTINGS>();
191 
192  app_settings->m_PcmRepositories = std::move( dialog_data );
193 
195  }
196 
197  dialog->Destroy();
198 }
199 
200 
202 {
203  STRING_TUPLE_LIST repositories = m_pcm->GetRepositoryList();
204 
205  m_choiceRepository->Clear();
206 
207  for( const auto& entry : repositories )
208  {
209  m_choiceRepository->Append( std::get<1>( entry ),
210  new wxStringClientData( std::get<0>( entry ) ) );
211  }
212 
213  if( repositories.size() > 0 )
214  {
215  m_choiceRepository->SetSelection( 0 );
216  m_selectedRepositoryId = std::get<0>( repositories[0] );
218  }
219  else
220  {
222  }
223 }
224 
225 
226 void DIALOG_PCM::OnRefreshClicked( wxCommandEvent& event )
227 {
228  m_pcm->DiscardRepositoryCache( m_selectedRepositoryId );
230 }
231 
232 
233 void DIALOG_PCM::OnInstallFromFileClicked( wxCommandEvent& event )
234 {
235  wxFileDialog open_file_dialog( this, _( "Choose package file" ), wxEmptyString, wxEmptyString,
236  "Zip files (*.zip)|*.zip", wxFD_OPEN | wxFD_FILE_MUST_EXIST );
237 
238  if( open_file_dialog.ShowModal() == wxID_CANCEL )
239  return;
240 
241  PCM_TASK_MANAGER task_manager( m_pcm );
242  task_manager.InstallFromFile( this, open_file_dialog.GetPath() );
243 
245 
246  if( !m_selectedRepositoryId.IsEmpty() )
248 }
249 
250 
251 void DIALOG_PCM::OnRepositoryChoice( wxCommandEvent& event )
252 {
253  wxStringClientData* data = static_cast<wxStringClientData*>(
254  m_choiceRepository->GetClientObject( m_choiceRepository->GetSelection() ) );
255 
256  m_selectedRepositoryId = data->GetData();
257 
259 }
260 
261 
262 void DIALOG_PCM::setRepositoryData( const wxString& aRepositoryId )
263 {
264  if( m_pcm->CacheRepository( aRepositoryId ) )
265  {
266  for( const auto& entry : m_repositoryContentPanels )
267  entry.second->ClearData();
268 
269  m_packageBitmaps = m_pcm->GetRepositoryPackageBitmaps( aRepositoryId );
270 
271  const std::vector<PCM_PACKAGE> packages = m_pcm->GetRepositoryPackages( aRepositoryId );
272 
273  std::unordered_map<PCM_PACKAGE_TYPE, std::vector<PACKAGE_VIEW_DATA>> data;
274 
275  for( const PCM_PACKAGE& pkg : packages )
276  {
277  PACKAGE_VIEW_DATA package_data( pkg );
278 
279  if( m_packageBitmaps.count( package_data.package.identifier ) > 0 )
280  package_data.bitmap = &m_packageBitmaps.at( package_data.package.identifier );
281  else
282  package_data.bitmap = &m_defaultBitmap;
283 
284  package_data.state = m_pcm->GetPackageState( aRepositoryId, pkg.identifier );
285 
286  for( const auto& action : m_pendingActions )
287  {
288  if( action.package.identifier != pkg.identifier )
289  continue;
290 
291  if( action.action == PPA_INSTALL )
292  package_data.state = PPS_PENDING_INSTALL;
293  else
294  package_data.state = PPS_PENDING_UNINSTALL;
295 
296  break;
297  }
298 
299  package_data.repository_id = aRepositoryId;
300  package_data.repository_name = m_choiceRepository->GetStringSelection();
301 
302  data[pkg.type].emplace_back( package_data );
303  }
304 
305  for( size_t i = 0; i < PACKAGE_TYPE_LIST.size(); i++ )
306  {
307  PCM_PACKAGE_TYPE type = PACKAGE_TYPE_LIST[i].first;
308  m_repositoryContentPanels[type]->SetData( data[type], m_callback );
309  m_contentNotebook->SetPageText(
310  i, wxString::Format( wxGetTranslation( PACKAGE_TYPE_LIST[i].second ),
311  (int) data[type].size() ) );
312  }
313 
314  m_dialogNotebook->SetPageText(
315  0, wxString::Format( _( "Repository (%d)" ), (int) packages.size() ) );
316  }
317 }
318 
319 
320 void DIALOG_PCM::OnPendingActionsCellClicked( wxGridEvent& event )
321 {
322  m_gridPendingActions->ClearSelection();
323  m_gridPendingActions->SelectRow( event.GetRow() );
324 }
325 
326 
328 {
329  m_dialogNotebook->SetPageText(
330  2, wxString::Format( _( "Pending (%d)" ), (int) m_pendingActions.size() ) );
331 
332  for( int col = 0; col < m_gridPendingActions->GetNumberCols(); col++ )
333  {
334  // Set the width to see the full contents
335  m_gridPendingActions->SetColSize(
336  col, m_gridPendingActions->GetVisibleWidth( col, true, true, false ) );
337  }
338 }
339 
340 
342 {
344 
345  const std::vector<PCM_INSTALLATION_ENTRY> installed = m_pcm->GetInstalledPackages();
346  std::vector<PACKAGE_VIEW_DATA> package_list;
347 
348  m_installedBitmaps = m_pcm->GetInstalledPackageBitmaps();
349 
350  for( const auto& entry : installed )
351  {
352  PACKAGE_VIEW_DATA package_data( entry );
353 
354  if( m_installedBitmaps.count( package_data.package.identifier ) > 0 )
355  package_data.bitmap = &m_installedBitmaps.at( package_data.package.identifier );
356  else
357  package_data.bitmap = &m_defaultBitmap;
358 
359  package_list.emplace_back( package_data );
360  }
361 
362  m_installedPanel->SetData( package_list, m_callback );
363 
364  m_dialogNotebook->SetPageText(
365  1, wxString::Format( _( "Installed (%d)" ), (int) package_list.size() ) );
366 }
367 
368 
369 void DIALOG_PCM::OnApplyChangesClicked( wxCommandEvent& event )
370 {
371  if( m_pendingActions.size() == 0 )
372  return;
373 
374  PCM_TASK_MANAGER task_manager( m_pcm );
375 
376  for( const auto& action : m_pendingActions )
377  {
378  if( action.action == PPA_UNINSTALL )
379  task_manager.Uninstall( action.package );
380  else
381  task_manager.DownloadAndInstall( action.package, action.version, action.repository_id );
382  }
383 
384  task_manager.RunQueue( this );
385 
387  wxCommandEvent dummy;
389 
390  if( !m_selectedRepositoryId.IsEmpty() )
392 }
393 
394 
395 void DIALOG_PCM::OnDiscardChangesClicked( wxCommandEvent& event )
396 {
397  m_gridPendingActions->Freeze();
398 
399  for( int i = m_pendingActions.size() - 1; i >= 0; i-- )
400  discardAction( i );
401 
403  m_gridPendingActions->Thaw();
404 }
405 
406 
407 void DIALOG_PCM::OnDiscardActionClicked( wxCommandEvent& event )
408 {
409  wxArrayInt rows = m_gridPendingActions->GetSelectedRows();
410 
411  std::sort( rows.begin(), rows.end(),
412  []( const int& a, const int& b )
413  {
414  return a > b;
415  } );
416 
417  m_gridPendingActions->Freeze();
418 
419  for( int row : rows )
420  discardAction( row );
421 
423  m_gridPendingActions->Thaw();
424 }
425 
426 
427 void DIALOG_PCM::discardAction( int aIndex )
428 {
429  m_gridPendingActions->DeleteRows( aIndex );
430 
431  PENDING_ACTION action = m_pendingActions[aIndex];
432 
433  PCM_PACKAGE_STATE state =
434  m_pcm->GetPackageState( action.repository_id, action.package.identifier );
435 
437 
438  for( const auto& entry : m_repositoryContentPanels )
439  entry.second->SetPackageState( action.package.identifier, state );
440 
441  m_pendingActions.erase( m_pendingActions.begin() + aIndex );
442 }
void SetData(const std::vector< PACKAGE_VIEW_DATA > &aPackageData, ActionCallback aCallback)
Recreates package panels and displays daya.
void setRepositoryData(const wxString &aRepositoryId)
Gets package data from PCM and displays it on repository tab.
Definition: dialog_pcm.cpp:262
void setRepositoryListFromPcm()
Updates pending actions tab caption and content-fits the grid.
Definition: dialog_pcm.cpp:201
std::vector< PENDING_ACTION > m_pendingActions
Definition: dialog_pcm.h:112
wxNotebook * m_dialogNotebook
PCM_PACKAGE_TYPE
< Supported package types
Definition: pcm_data.h:40
void updatePendingActionsTab()
Gets installed packages list from PCM and displays it on installed tab.
Definition: dialog_pcm.cpp:327
void Uninstall(const PCM_PACKAGE &aPackage)
Enqueue package uninstallation.
wxString repository_id
Definition: panel_package.h:35
void SetData(const STRING_PAIR_LIST &aData)
void setInstalledPackages()
Discards specified pending action.
Definition: dialog_pcm.cpp:341
void OnPendingActionsCellClicked(wxGridEvent &event) override
Discards selected pending actions.
Definition: dialog_pcm.cpp:320
Add mouse and command handling (such as cut, copy, and paste) to a WX_GRID instance.
Definition: grid_tricks.h:55
std::unordered_map< wxString, wxBitmap > m_installedBitmaps
Definition: dialog_pcm.h:95
void ClearData()
Selects full row of the clicked cell.
void OnInstallFromFileClicked(wxCommandEvent &event) override
Enqueues current pending actions in PCM_TASK_MANAGER and runs the queue.
Definition: dialog_pcm.cpp:233
void discardAction(int aIndex)
Definition: dialog_pcm.cpp:427
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Definition: single_top.cpp:106
const PCM_PACKAGE package
Definition: panel_package.h:32
#define GRID_CELL_MARGIN
Definition: dialog_pcm.cpp:45
wxNotebook * m_contentNotebook
int GetVisibleWidth(int aCol, bool aHeader=true, bool aContents=false, bool aKeep=true)
Calculates the specified column based on the actual size of the text on screen.
Definition: wx_grid.cpp:246
< Collection of data relevant to the package display panel Callback for (un)install button
Definition: panel_package.h:30
void InstallFromFile(wxWindow *aParent, const wxString &aFilePath)
Installs package from an archive file on disk.
static LIB_SYMBOL * dummy()
Used to draw a dummy shape when a LIB_SYMBOL is not found in library.
Definition: sch_symbol.cpp:72
void SetPackageState(const wxString &aPackageId, const PCM_PACKAGE_STATE aState) const
Set the state of package.
Implementing DIALOG_MANAGE_REPOSITORIES_BASE.
void OnDiscardActionClicked(wxCommandEvent &event) override
Definition: dialog_pcm.cpp:407
void OnManageRepositoriesClicked(wxCommandEvent &event) override
Discards current repo cache, fetches it anew and displays.
Definition: dialog_pcm.cpp:170
wxChoice * m_choiceRepository
DIALOG_PCM(wxWindow *parent)
Constructor.
Definition: dialog_pcm.cpp:56
Repository reference to a resource.
Definition: pcm_data.h:93
wxBitmapButton * m_discardActionButton
PANEL_PACKAGES_VIEW * m_installedPanel
Definition: dialog_pcm.h:91
T * GetAppSettings(bool aLoadNow=true)
Returns a handle to the a given settings by type If the settings have already been loaded,...
static std::vector< std::pair< PCM_PACKAGE_TYPE, wxString > > PACKAGE_TYPE_LIST
Definition: dialog_pcm.cpp:49
wxPanel * m_panelInstalledHolder
#define _(s)
~DIALOG_PCM()
Closes the window, asks user confirmation if there are pending actions.
Definition: dialog_pcm.cpp:151
wxBitmap * bitmap
Definition: panel_package.h:33
std::unordered_map< wxString, wxBitmap > m_packageBitmaps
Definition: dialog_pcm.h:94
wxButton * m_closeButton
void DownloadAndInstall(const PCM_PACKAGE &aPackage, const wxString &aVersion, const wxString &aRepositoryId)
Enqueue package download and installation.
wxString name
Definition: pcm_data.h:95
wxBitmap KiBitmap(BITMAPS aBitmap, int aHeightTag)
Construct a wxBitmap from an image identifier Returns the image from the active theme if the image ha...
Definition: bitmap.cpp:105
wxString repository_name
Definition: panel_package.h:36
void OnRepositoryChoice(wxCommandEvent &event) override
Selects the whole row in the grid if a cell is clicked.
Definition: dialog_pcm.cpp:251
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
void OnRefreshClicked(wxCommandEvent &event) override
Opens file selection dialog and installs selected package archive.
Definition: dialog_pcm.cpp:226
std::vector< std::pair< wxString, wxString > > STRING_PAIR_LIST
Definition: pcm.h:69
PCM_PACKAGE_STATE
Definition: pcm.h:51
ActionCallback m_callback
Definition: dialog_pcm.h:90
PCM_PACKAGE_STATE state
Definition: panel_package.h:34
wxString m_selectedRepositoryId
Definition: dialog_pcm.h:93
void OnCloseClicked(wxCommandEvent &event) override
Opens repository management dialog, saves changes to PCM.
Definition: dialog_pcm.cpp:157
see class PGM_BASE
void RunQueue(wxWindow *aParent)
Run queue of pending actions.
Class DIALOG_PCM_BASE.
Helper class that handles package (un)installation.
wxString identifier
Definition: pcm_data.h:98
wxPanel * m_panelPending
std::unordered_map< PCM_PACKAGE_TYPE, PANEL_PACKAGES_VIEW * > m_repositoryContentPanels
Definition: dialog_pcm.h:92
std::vector< std::tuple< wxString, wxString, wxString > > STRING_TUPLE_LIST
Definition: pcm.h:70
void OnDiscardChangesClicked(wxCommandEvent &event) override
Switches to another repository.
Definition: dialog_pcm.cpp:395
std::shared_ptr< PLUGIN_CONTENT_MANAGER > m_pcm
Definition: dialog_pcm.h:89
WX_GRID * m_gridPendingActions
void OnApplyChangesClicked(wxCommandEvent &event) override
Discards all pending changes.
Definition: dialog_pcm.cpp:369
wxBitmap m_defaultBitmap
Definition: dialog_pcm.h:96
PCM_PACKAGE_ACTION
Definition: pcm.h:62