KiCad PCB EDA Suite
Loading...
Searching...
No Matches
panel_pcbnew_action_plugins.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) 2018 Andrew Lutsenko, anlutsenko at gmail dot com
5 * Copyright The 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
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
21#include <api/api_plugin.h>
22#include <bitmaps.h>
23#include <bitmap_store.h>
25#include <grid_tricks.h>
26#include <kiface_base.h>
27#include <kiplatform/ui.h>
29#include <paths.h>
30#include <pcb_edit_frame.h>
31#include <pcbnew_settings.h>
32#include <pgm_base.h>
33#include <reporter.h>
37#include <launch_ext.h>
38#include <widgets/kistatusbar.h>
41#include <widgets/wx_grid.h>
44#include <wx/app.h>
45
46#include <algorithm>
47
48
49#define GRID_CELL_MARGIN 4
50
51enum
52{
54};
55
57{
58public:
60 GRID_TRICKS( aGrid )
61 {}
62
63protected:
64 void showPopupMenu( wxMenu& menu, wxGridEvent& aEvent ) override;
65 void doPopupSelection( wxCommandEvent& event ) override;
66};
67
68
69void PLUGINS_GRID_TRICKS::showPopupMenu( wxMenu& menu, wxGridEvent& aEvent )
70{
71 const int clickedRow = aEvent.GetRow();
72
73 if( clickedRow >= 0 )
74 {
75 m_grid->SetGridCursor( clickedRow, m_grid->GetGridCursorCol() );
76 m_grid->ClearSelection();
77 m_grid->SelectRow( clickedRow );
78
80 wxString id = m_grid->GetCellValue( clickedRow,
82
83 if( std::optional<const PLUGIN_ACTION*> action = mgr.GetAction( id );
84 action && ( *action )->plugin.Runtime().type == PLUGIN_RUNTIME_TYPE::PYTHON )
85 {
86 menu.Append( MYID_RECREATE_ENV, _( "Recreate Plugin Environment" ), _( "Recreate Plugin Environment" ) );
87 menu.AppendSeparator();
88 }
89 }
90
91 GRID_TRICKS::showPopupMenu( menu, aEvent );
92}
93
94
95void PLUGINS_GRID_TRICKS::doPopupSelection( wxCommandEvent& event )
96{
97 if( event.GetId() == MYID_RECREATE_ENV )
98 {
100 wxString id = m_grid->GetCellValue( m_grid->GetGridCursorRow(),
102
103 if( std::optional<const PLUGIN_ACTION*> action = mgr.GetAction( id );
104 action && ( *action )->plugin.Runtime().type == PLUGIN_RUNTIME_TYPE::PYTHON )
105 {
106 if( mgr.RecreatePluginEnvironment( ( *action )->plugin.Identifier() ) )
107 {
108 wxCommandEvent* evt = new wxCommandEvent( EDA_EVT_PLUGIN_MANAGER_JOB_FINISHED, wxID_ANY );
109 mgr.QueueEvent( evt );
110 }
111 }
112 }
113 else
114 {
116 }
117}
118
119
122{
124 m_grid->PushEventHandler( new PLUGINS_GRID_TRICKS( m_grid ) );
125 m_grid->SetUseNativeColLabels();
126
127 // Pin best size before TransferDataToWindow grows columns past the screen (#24408).
128 m_grid->OverrideMinSize( 1.0, 1.0 );
129
135
136 m_errorDialog = new DIALOG_HTML_REPORTER( aParent );
137 m_allowErrorDialog = false;
138
141}
142
143
151
152
154{
155 m_grid->Enable();
157
158 if( m_allowErrorDialog && m_errorDialog->m_Reporter->HasMessage() )
159 {
160 m_errorDialog->m_Reporter->Flush();
161 m_allowErrorDialog = false;
162 m_errorDialog->ShowModal();
163 }
164
165 aEvt.Skip();
166}
167
168
170{
171 SelectRow( event.GetRow() );
172}
173
174
176{
177 m_grid->ClearSelection();
178 m_grid->SelectRow( aRow );
179}
180
181
183{
184 m_grid->OnMoveRowUp(
185 [&]( int row )
186 {
187 SwapRows( row, row - 1 );
188 } );
189}
190
191
193{
194 m_grid->OnMoveRowDown(
195 [&]( int row )
196 {
197 SwapRows( row, row + 1 );
198 } );
199}
200
201
202void PANEL_PCBNEW_ACTION_PLUGINS::SwapRows( int aRowA, int aRowB )
203{
204 m_grid->Freeze();
205
206 m_grid->SwapRows( aRowA, aRowB );
207
208 // Swap icon column renderers
209 auto cellRenderer = m_grid->GetCellRenderer( aRowA, COLUMN_ACTION_NAME );
210 m_grid->SetCellRenderer( aRowA, COLUMN_ACTION_NAME, m_grid->GetCellRenderer( aRowB, COLUMN_ACTION_NAME ) );
211 m_grid->SetCellRenderer( aRowB, COLUMN_ACTION_NAME, cellRenderer );
212
213 m_grid->Thaw();
214}
215
216
218{
220 m_errorDialog->m_Reporter->Clear();
221 auto reporter = std::make_shared<REDIRECT_REPORTER>( m_errorDialog->m_Reporter );
222 m_allowErrorDialog = true;
223 mgr.ReloadPlugins( std::nullopt, reporter );
224 m_grid->Disable();
225}
226
227
229{
230 PCBNEW_SETTINGS* settings = dynamic_cast<PCBNEW_SETTINGS*>( Kiface().KifaceSettings() );
231 wxASSERT( settings );
232
234
235 if( settings )
236 {
237 settings->m_Plugins.actions.clear();
238
239 for( int ii = 0; ii < m_grid->GetNumberRows(); ii++ )
240 {
241 wxString id = m_grid->GetCellValue( ii, COLUMN_SETTINGS_IDENTIFIER );
242
243 if( mgr.GetAction( id ) != std::nullopt )
244 {
245 settings->m_Plugins.actions.emplace_back( std::make_pair(
246 id, m_grid->GetCellValue( ii, COLUMN_VISIBLE ) == wxT( "1" ) ) );
247 }
248 }
249 }
250
251 return true;
252}
253
254
256{
257 m_grid->Freeze();
258
259 m_grid->ClearRows();
260
261 const std::vector<const PLUGIN_ACTION*>& orderedPlugins = PCB_EDIT_FRAME::GetOrderedPluginActions();
262 m_grid->AppendRows( orderedPlugins.size() );
263
265 wxSize iconSize( size, size );
266
267 for( size_t row = 0; row < orderedPlugins.size(); row++ )
268 {
269 const PLUGIN_ACTION* action = orderedPlugins[row];
270
271 const std::vector<wxImage>& images = KIPLATFORM::UI::IsDarkTheme() && !action->icon_dark.empty()
272 ? action->icon_dark
273 : action->icon_light;
274
275 wxBitmapBundle icon;
276
277 if( !images.empty() )
278 {
279 wxVector<wxBitmap> bitmaps;
280
281 for( const wxImage& img : images )
282 bitmaps.push_back( wxBitmap( img ) );
283
284 icon = BITMAP_STORE::MakeBitmapBundleDef( bitmaps, size );
285 }
286
287 m_grid->SetCellRenderer( row, COLUMN_ACTION_NAME, new GRID_CELL_ICON_TEXT_RENDERER(
288 icon.IsOk() ? icon : m_genericIcon, iconSize ) );
289 m_grid->SetCellValue( row, COLUMN_ACTION_NAME, action->name );
290 m_grid->SetCellValue( row, COLUMN_SETTINGS_IDENTIFIER, action->identifier );
291
292 // Toolbar button checkbox
293 m_grid->SetCellRenderer( row, COLUMN_VISIBLE, new wxGridCellBoolRenderer() );
294 m_grid->SetCellAlignment( row, COLUMN_VISIBLE, wxALIGN_CENTER, wxALIGN_CENTER );
295
297
298 m_grid->SetCellValue( row, COLUMN_VISIBLE, show ? wxT( "1" ) : wxEmptyString );
299
300 m_grid->SetCellValue( row, COLUMN_PLUGIN_NAME, action->plugin.Name() );
301 m_grid->SetCellValue( row, COLUMN_DESCRIPTION, action->description );
302 }
303
304 const int colMaxWidth = FromDIP( 400 );
305
306 for( int col = 0; col < m_grid->GetNumberCols(); col++ )
307 {
308 const wxString& heading = m_grid->GetColLabelValue( col );
309 int headingWidth = GetTextExtent( heading ).x + 2 * GRID_CELL_MARGIN;
310
311 m_grid->SetColMinimalWidth( col, headingWidth );
312 int width = std::min( m_grid->GetVisibleWidth( col ), colMaxWidth );
313 m_grid->SetColSize( col, std::max( headingWidth, width ) );
314 }
315
316 m_grid->AutoSizeRows();
317 // AutoSizeColumns() would re-expand columns to full content width (setAsMin=true) and undo
318 // the cap above (#24408).
320
321 m_grid->Thaw();
322
323 // Show errors button should be disabled if there are no errors.
324 wxString trace;
325
326 if( trace.empty() )
327 {
328 m_showErrorsButton->Disable();
329 m_showErrorsButton->Hide();
330 }
331 else
332 {
333 m_showErrorsButton->Enable();
334 m_showErrorsButton->Show();
335 }
336
337 return true;
338}
339
340
342{
343 wxString dir( PATHS::GetUserPluginsPath() );
344 LaunchExternal( dir );
345}
346
347
349{
350 wxString trace;
351
352 // Now display the filtered trace in our dialog
353 // (a simple wxMessageBox is really not suitable for long messages)
354 DIALOG_FOOTPRINT_WIZARD_LOG logWindow( wxGetTopLevelParent( this ) );
355 logWindow.m_Message->SetValue( trace );
356 logWindow.ShowModal();
357}
const KICOMMON_API wxEventTypeTag< wxCommandEvent > EDA_EVT_PLUGIN_AVAILABILITY_CHANGED
Notifies other parts of KiCad when plugin availability changes.
const KICOMMON_API wxEventTypeTag< wxCommandEvent > EDA_EVT_PLUGIN_MANAGER_JOB_FINISHED
Event used for marking async plugin setup tasks as done.
KIFACE_BASE & Kiface()
Global KIFACE_BASE "get" accessor.
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
Definition bitmap.cpp:106
Responsible for loading plugin definitions for API-based plugins (ones that do not run inside KiCad i...
bool RecreatePluginEnvironment(const wxString &aIdentifier)
Removes the given plugin's virtual environment (if any) and schedules a new one to be created with th...
void ReloadPlugins(std::optional< wxString > aDirectoryToScan=std::nullopt, std::shared_ptr< REPORTER > aReporter=nullptr)
Clears the loaded plugins and actions and re-scans the filesystem to register new ones.
std::optional< const PLUGIN_ACTION * > GetAction(const wxString &aIdentifier)
const wxString & Name() const
static wxBitmapBundle MakeBitmapBundleDef(const wxVector< wxBitmap > &aBitmaps, int aDefHeight)
Constructs a bitmap bundle from the given bitmaps.
APPEARANCE m_Appearance
Class DIALOG_FOOTPRINT_WIZARD_LOG.
Class DIALOG_HTML_REPORTER.
int ShowModal() override
GRID_TRICKS(WX_GRID *aGrid)
virtual void doPopupSelection(wxCommandEvent &event)
virtual void showPopupMenu(wxMenu &menu, wxGridEvent &aEvent)
WX_GRID * m_grid
I don't own the grid, but he owns me.
APP_SETTINGS_BASE * KifaceSettings() const
Definition kiface_base.h:92
PANEL_PCBNEW_ACTION_PLUGINS_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)
void onPluginAvailabilityChanged(wxCommandEvent &aEvt)
void OnShowErrorsButtonClick(wxCommandEvent &event) override
Shows plugin import errors.
void OnMoveUpButtonClick(wxCommandEvent &event) override
Moves plugin up in the grid.
void OnGridCellClick(wxGridEvent &event) override
Selects a whole row.
void OnMoveDownButtonClick(wxCommandEvent &event) override
Moves plugin down in the grid.
void OnOpenDirectoryButtonClick(wxCommandEvent &event) override
Opens user's action plugin directory.
void OnReloadButtonClick(wxCommandEvent &event) override
Reloads plugins and updates grid.
static wxString GetUserPluginsPath()
Gets the user path for plugins.
Definition paths.cpp:49
static bool GetPluginActionButtonVisible(const wxString &aPluginPath, bool aPluginDefault)
Return true if button visibility action plugin setting was set to true or it is unset and plugin defa...
static std::vector< const PLUGIN_ACTION * > GetOrderedPluginActions()
Return ordered list of plugins in sequence in which they should appear on toolbar or in settings.
virtual COMMON_SETTINGS * GetCommonSettings() const
Definition pgm_base.cpp:546
virtual API_PLUGIN_MANAGER & GetPluginManager() const
Definition pgm_base.h:139
void showPopupMenu(wxMenu &menu, wxGridEvent &aEvent) override
void doPopupSelection(wxCommandEvent &event) override
#define GRID_CELL_MARGIN
#define _(s)
@ GRIDTRICKS_FIRST_CLIENT_ID
Definition grid_tricks.h:44
bool LaunchExternal(const wxString &aPath)
Launches the given file or folder in the host OS.
bool IsDarkTheme()
Determine if the desktop interface is currently using a dark theme or a light theme.
Definition wxgtk/ui.cpp:51
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
std::vector< std::pair< wxString, bool > > actions
Ordered list of plugin actions mapped to whether or not they are shown in the toolbar.
An action performed by a plugin via the IPC API.
Definition api_plugin.h:74
const API_PLUGIN & plugin
Definition api_plugin.h:89
wxString name
Definition api_plugin.h:80
wxString description
Definition api_plugin.h:81
wxString identifier
Definition api_plugin.h:79
std::vector< wxImage > icon_light
Definition api_plugin.h:86
std::vector< wxImage > icon_dark
Definition api_plugin.h:87
IbisParser parser & reporter