KiCad PCB EDA Suite
Loading...
Searching...
No Matches
background_jobs_monitor.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) 2023 Mark Roszko <[email protected]>
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU 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, you may find one here:
19 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20 * or you may search the http://www.gnu.org website for the version 2 license,
21 * or you may write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
25#include <unordered_map>
26
27#include <wx/gauge.h>
28#include <wx/frame.h>
29#include <wx/panel.h>
30#include <wx/settings.h>
31#include <wx/scrolwin.h>
32#include <wx/sizer.h>
33#include <wx/stattext.h>
34#include <wx/string.h>
35
37#include <widgets/kistatusbar.h>
38
39
40class BACKGROUND_JOB_PANEL : public wxPanel
41{
42public:
43 BACKGROUND_JOB_PANEL( wxWindow* aParent, std::shared_ptr<BACKGROUND_JOB> aJob ) :
44 wxPanel( aParent, wxID_ANY, wxDefaultPosition, wxSize( -1, 75 ),
45 wxBORDER_SIMPLE ),
46 m_job( aJob )
47 {
48 SetSizeHints( wxDefaultSize, wxDefaultSize );
49
50 wxBoxSizer* mainSizer;
51 mainSizer = new wxBoxSizer( wxVERTICAL );
52
53 SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_3DLIGHT ) );
54
55 m_stName = new wxStaticText( this, wxID_ANY, aJob->m_name );
56 m_stName->Wrap( -1 );
57 m_stName->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT,
58 wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false,
59 wxEmptyString ) );
60 mainSizer->Add( m_stName, 0, wxALL | wxEXPAND, 1 );
61
62 m_stStatus = new wxStaticText( this, wxID_ANY, aJob->m_status, wxDefaultPosition,
63 wxDefaultSize, 0 );
64 m_stStatus->Wrap( -1 );
65 mainSizer->Add( m_stStatus, 0, wxALL | wxEXPAND, 1 );
66
67 m_progress = new wxGauge( this, wxID_ANY, aJob->m_maxProgress, wxDefaultPosition,
68 wxDefaultSize, wxGA_HORIZONTAL );
69 m_progress->SetValue( 0 );
70 mainSizer->Add( m_progress, 0, wxALL | wxEXPAND, 1 );
71
72 SetSizer( mainSizer );
73 Layout();
74
76 }
77
78
80 {
81 m_stStatus->SetLabelText( m_job->m_status );
82 m_progress->SetValue( m_job->m_currentProgress );
83 m_progress->SetRange( m_job->m_maxProgress );
84 }
85
86private:
87 wxGauge* m_progress;
88 wxStaticText* m_stName;
89 wxStaticText* m_stStatus;
90 std::shared_ptr<BACKGROUND_JOB> m_job;
91};
92
93
94class BACKGROUND_JOB_LIST : public wxFrame
95{
96public:
97 BACKGROUND_JOB_LIST( wxWindow* parent, const wxPoint& pos ) :
98 wxFrame( parent, wxID_ANY, _( "Background Jobs" ), pos, wxSize( 300, 150 ),
99 wxFRAME_NO_TASKBAR | wxBORDER_SIMPLE )
100 {
101 SetSizeHints( wxDefaultSize, wxDefaultSize );
102
103 wxBoxSizer* bSizer1;
104 bSizer1 = new wxBoxSizer( wxVERTICAL );
105
106 m_scrolledWindow = new wxScrolledWindow( this, wxID_ANY, wxDefaultPosition,
107 wxSize( -1, -1 ), wxVSCROLL );
108 m_scrolledWindow->SetScrollRate( 5, 5 );
109 m_contentSizer = new wxBoxSizer( wxVERTICAL );
110
111 m_scrolledWindow->SetSizer( m_contentSizer );
112 m_scrolledWindow->Layout();
114 bSizer1->Add( m_scrolledWindow, 1, wxEXPAND | wxALL, 0 );
115
116 Bind( wxEVT_KILL_FOCUS, &BACKGROUND_JOB_LIST::onFocusLoss, this );
117
118 SetSizer( bSizer1 );
119 Layout();
120
121 SetFocus();
122 }
123
124 void onFocusLoss( wxFocusEvent& aEvent )
125 {
126 Close( true );
127 aEvent.Skip();
128 }
129
130
131 void Add( std::shared_ptr<BACKGROUND_JOB> aJob )
132 {
134 m_contentSizer->Add( panel, 0, wxEXPAND | wxALL, 2 );
135 m_scrolledWindow->Layout();
137
138 // call this at this window otherwise the child panels don't resize width properly
139 Layout();
140
141 m_jobPanels[aJob] = panel;
142 }
143
144
145 void Remove( std::shared_ptr<BACKGROUND_JOB> aJob )
146 {
147 auto it = m_jobPanels.find( aJob );
148 if( it != m_jobPanels.end() )
149 {
150 BACKGROUND_JOB_PANEL* panel = m_jobPanels[aJob];
151 m_contentSizer->Detach( panel );
152 panel->Destroy();
153
154 m_jobPanels.erase( it );
155 }
156 }
157
158 void UpdateJob( std::shared_ptr<BACKGROUND_JOB> aJob )
159 {
160 auto it = m_jobPanels.find( aJob );
161 if( it != m_jobPanels.end() )
162 {
163 BACKGROUND_JOB_PANEL* panel = m_jobPanels[aJob];
164 panel->UpdateFromJob();
165 }
166 }
167
168private:
169 wxScrolledWindow* m_scrolledWindow;
170 wxBoxSizer* m_contentSizer;
171 std::unordered_map<std::shared_ptr<BACKGROUND_JOB>, BACKGROUND_JOB_PANEL*> m_jobPanels;
172};
173
174
176 const std::shared_ptr<BACKGROUND_JOB>& aJob ) :
178 m_monitor( aMonitor ),
179 m_job( aJob )
180{
181
182}
183
184
186{
187 return !m_cancelled;
188}
189
190
191void BACKGROUND_JOB_REPORTER::Report( const wxString& aMessage )
192{
193 m_job->m_status = aMessage;
194 m_monitor->jobUpdated( m_job );
195}
196
197
199{
201 m_job->m_maxProgress = m_numPhases;
202 m_monitor->jobUpdated( m_job );
203}
204
205
207{
209 m_job->m_currentProgress = m_phase;
210 m_monitor->jobUpdated( m_job );
211}
212
213
218
219
220std::shared_ptr<BACKGROUND_JOB> BACKGROUND_JOBS_MONITOR::Create( const wxString& aName )
221{
222 std::shared_ptr<BACKGROUND_JOB> job = std::make_shared<BACKGROUND_JOB>();
223
224 job->m_name = aName;
225 job->m_reporter = std::make_shared<BACKGROUND_JOB_REPORTER>( this, job );
226
227 std::lock_guard<std::shared_mutex> lock( m_mutex );
228 m_jobs.push_back( job );
229
230 if( m_shownDialogs.size() > 0 )
231 {
232 // update dialogs
234 {
235 list->CallAfter(
236 [=]()
237 {
238 list->Add( job );
239 } );
240 }
241 }
242
243 return job;
244}
245
246
247void BACKGROUND_JOBS_MONITOR::Remove( std::shared_ptr<BACKGROUND_JOB> aJob )
248{
249 if( m_shownDialogs.size() > 0 )
250 {
251 // update dialogs
252
254 {
255 list->CallAfter(
256 [=]()
257 {
258 list->Remove( aJob );
259 } );
260 }
261 }
262
263 std::lock_guard<std::shared_mutex> lock( m_mutex );
264 m_jobs.erase( std::remove_if( m_jobs.begin(), m_jobs.end(),
265 [&]( std::shared_ptr<BACKGROUND_JOB> job )
266 {
267 return job == aJob;
268 } ) );
269
270 if( m_jobs.size() > 0 )
271 {
272 jobUpdated( m_jobs.front() );
273 }
274 else
275 {
276 for( KISTATUSBAR* statusBar : m_statusBars )
277 {
278 statusBar->CallAfter(
279 [=]()
280 {
281 statusBar->HideBackgroundProgressBar();
282 statusBar->SetBackgroundStatusText( wxT( "" ) );
283 } );
284 }
285 }
286}
287
288
290{
291 BACKGROUND_JOB_LIST* evtWindow = dynamic_cast<BACKGROUND_JOB_LIST*>( aEvent.GetEventObject() );
292
293 m_shownDialogs.erase( std::remove_if( m_shownDialogs.begin(), m_shownDialogs.end(),
294 [&]( BACKGROUND_JOB_LIST* dialog )
295 {
296 return dialog == evtWindow;
297 } ) );
298
299 aEvent.Skip();
300}
301
302
303void BACKGROUND_JOBS_MONITOR::ShowList( wxWindow* aParent, wxPoint aPos )
304{
305 BACKGROUND_JOB_LIST* list = new BACKGROUND_JOB_LIST( aParent, aPos );
306
307 std::shared_lock<std::shared_mutex> lock( m_mutex, std::try_to_lock );
308
309 for( const std::shared_ptr<BACKGROUND_JOB>& job : m_jobs )
310 list->Add( job );
311
312 lock.unlock();
313
314 m_shownDialogs.push_back( list );
315
316 list->Bind( wxEVT_CLOSE_WINDOW, &BACKGROUND_JOBS_MONITOR::onListWindowClosed, this );
317
318 // correct the position
319 wxSize windowSize = list->GetSize();
320 list->SetPosition( aPos - windowSize );
321
322 list->Show();
323}
324
325
326void BACKGROUND_JOBS_MONITOR::jobUpdated( std::shared_ptr<BACKGROUND_JOB> aJob )
327{
328 std::shared_lock<std::shared_mutex> lock( m_mutex, std::try_to_lock );
329
330 // this method is called from the reporters from potentially other threads
331 // we have to guard ui calls with CallAfter
332 if( m_jobs.size() > 0 )
333 {
334 //for now, we go and update the status bar if its the first job in the vector
335 if( m_jobs.front() == aJob )
336 {
337 // update all status bar entries
338 for( KISTATUSBAR* statusBar : m_statusBars )
339 {
340 statusBar->CallAfter(
341 [=]()
342 {
343 statusBar->ShowBackgroundProgressBar();
344 statusBar->SetBackgroundProgress( aJob->m_currentProgress );
345 statusBar->SetBackgroundProgressMax( aJob->m_maxProgress );
346 statusBar->SetBackgroundStatusText( aJob->m_status );
347 } );
348 }
349 }
350 }
351
352
354 {
355 list->CallAfter(
356 [=]()
357 {
358 list->UpdateJob( aJob );
359 } );
360 }
361}
362
363
365{
366 m_statusBars.push_back( aStatusBar );
367}
368
369
371{
372 m_statusBars.erase( std::remove_if( m_statusBars.begin(), m_statusBars.end(),
373 [&]( KISTATUSBAR* statusBar )
374 {
375 return statusBar == aStatusBar;
376 } ) );
377}
void UnregisterStatusBar(KISTATUSBAR *aStatusBar)
Removes status bar from handling.
std::vector< std::shared_ptr< BACKGROUND_JOB > > m_jobs
Holds a reference to all active background jobs Access to this vector should be protected by locks si...
void ShowList(wxWindow *aParent, wxPoint aPos)
Shows the background job list.
std::shared_mutex m_mutex
Mutex to protect access to the m_jobs vector.
std::shared_ptr< BACKGROUND_JOB > Create(const wxString &aName)
Creates a background job with the given name.
void jobUpdated(std::shared_ptr< BACKGROUND_JOB > aJob)
Handles job status updates, intended to be called by BACKGROUND_JOB_REPORTER only.
void Remove(std::shared_ptr< BACKGROUND_JOB > job)
Removes the given background job from any lists and frees it.
void onListWindowClosed(wxCloseEvent &aEvent)
Handles removing the shown list window from our list of shown windows.
std::vector< KISTATUSBAR * > m_statusBars
std::vector< BACKGROUND_JOB_LIST * > m_shownDialogs
void RegisterStatusBar(KISTATUSBAR *aStatusBar)
Add a status bar for handling.
void Remove(std::shared_ptr< BACKGROUND_JOB > aJob)
void onFocusLoss(wxFocusEvent &aEvent)
void Add(std::shared_ptr< BACKGROUND_JOB > aJob)
BACKGROUND_JOB_LIST(wxWindow *parent, const wxPoint &pos)
void UpdateJob(std::shared_ptr< BACKGROUND_JOB > aJob)
std::unordered_map< std::shared_ptr< BACKGROUND_JOB >, BACKGROUND_JOB_PANEL * > m_jobPanels
wxScrolledWindow * m_scrolledWindow
BACKGROUND_JOB_PANEL(wxWindow *aParent, std::shared_ptr< BACKGROUND_JOB > aJob)
std::shared_ptr< BACKGROUND_JOB > m_job
void SetNumPhases(int aNumPhases) override
Set the number of phases.
void AdvancePhase() override
Use the next available virtual zone of the dialog progress bar.
std::shared_ptr< BACKGROUND_JOB > m_job
void Report(const wxString &aMessage) override
Display aMessage in the progress bar dialog.
BACKGROUND_JOBS_MONITOR * m_monitor
BACKGROUND_JOB_REPORTER(BACKGROUND_JOBS_MONITOR *aMonitor, const std::shared_ptr< BACKGROUND_JOB > &aJob)
KISTATUSBAR is a wxStatusBar suitable for Kicad manager.
Definition kistatusbar.h:45
virtual void AdvancePhase() override
Use the next available virtual zone of the dialog progress bar.
void SetNumPhases(int aNumPhases) override
Set the number of phases.
#define _(s)