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
215{
217 m_job->m_maxProgress = 1000;
218 m_job->m_currentProgress = ( 1000 * aProgress );
219 m_monitor->jobUpdated( m_job );
220}
221
222
227
228
229std::shared_ptr<BACKGROUND_JOB> BACKGROUND_JOBS_MONITOR::Create( const wxString& aName )
230{
231 std::shared_ptr<BACKGROUND_JOB> job = std::make_shared<BACKGROUND_JOB>();
232
233 job->m_name = aName;
234 job->m_reporter = std::make_shared<BACKGROUND_JOB_REPORTER>( this, job );
235
236 std::lock_guard<std::shared_mutex> lock( m_mutex );
237 m_jobs.push_back( job );
238
239 if( m_shownDialogs.size() > 0 )
240 {
241 // update dialogs
243 {
244 list->CallAfter(
245 [=]()
246 {
247 list->Add( job );
248 } );
249 }
250 }
251
252 return job;
253}
254
255
256void BACKGROUND_JOBS_MONITOR::Remove( std::shared_ptr<BACKGROUND_JOB> aJob )
257{
258 if( m_shownDialogs.size() > 0 )
259 {
260 // update dialogs
261
263 {
264 list->CallAfter(
265 [=]()
266 {
267 list->Remove( aJob );
268 } );
269 }
270 }
271
272 std::lock_guard<std::shared_mutex> lock( m_mutex );
273 m_jobs.erase( std::remove_if( m_jobs.begin(), m_jobs.end(),
274 [&]( std::shared_ptr<BACKGROUND_JOB> job )
275 {
276 return job == aJob;
277 } ) );
278
279 if( m_jobs.size() > 0 )
280 {
281 jobUpdated( m_jobs.front() );
282 }
283 else
284 {
285 for( KISTATUSBAR* statusBar : m_statusBars )
286 {
287 statusBar->CallAfter(
288 [=]()
289 {
290 statusBar->HideBackgroundProgressBar();
291 statusBar->SetBackgroundStatusText( wxT( "" ) );
292 } );
293 }
294 }
295}
296
297
299{
300 BACKGROUND_JOB_LIST* evtWindow = dynamic_cast<BACKGROUND_JOB_LIST*>( aEvent.GetEventObject() );
301
302 m_shownDialogs.erase( std::remove_if( m_shownDialogs.begin(), m_shownDialogs.end(),
303 [&]( BACKGROUND_JOB_LIST* dialog )
304 {
305 return dialog == evtWindow;
306 } ) );
307
308 aEvent.Skip();
309}
310
311
312void BACKGROUND_JOBS_MONITOR::ShowList( wxWindow* aParent, wxPoint aPos )
313{
314 BACKGROUND_JOB_LIST* list = new BACKGROUND_JOB_LIST( aParent, aPos );
315
316 std::shared_lock<std::shared_mutex> lock( m_mutex, std::try_to_lock );
317
318 for( const std::shared_ptr<BACKGROUND_JOB>& job : m_jobs )
319 list->Add( job );
320
321 lock.unlock();
322
323 m_shownDialogs.push_back( list );
324
325 list->Bind( wxEVT_CLOSE_WINDOW, &BACKGROUND_JOBS_MONITOR::onListWindowClosed, this );
326
327 // correct the position
328 wxSize windowSize = list->GetSize();
329 list->SetPosition( aPos - windowSize );
330
331 list->Show();
332}
333
334
335void BACKGROUND_JOBS_MONITOR::jobUpdated( std::shared_ptr<BACKGROUND_JOB> aJob )
336{
337 std::shared_lock<std::shared_mutex> lock( m_mutex, std::try_to_lock );
338
339 // this method is called from the reporters from potentially other threads
340 // we have to guard ui calls with CallAfter
341 if( m_jobs.size() > 0 )
342 {
343 //for now, we go and update the status bar if its the first job in the vector
344 if( m_jobs.front() == aJob )
345 {
346 // update all status bar entries
347 for( KISTATUSBAR* statusBar : m_statusBars )
348 {
349 statusBar->CallAfter(
350 [=]()
351 {
352 statusBar->ShowBackgroundProgressBar();
353 statusBar->SetBackgroundProgressMax( aJob->m_maxProgress );
354 statusBar->SetBackgroundProgress( aJob->m_currentProgress );
355 statusBar->SetBackgroundStatusText( aJob->m_status );
356 } );
357 }
358 }
359 }
360
361
363 {
364 list->CallAfter(
365 [=]()
366 {
367 list->UpdateJob( aJob );
368 } );
369 }
370}
371
372
374{
375 m_statusBars.push_back( aStatusBar );
376
377 // Make sure the newly-registered bar gets the active job, if any
378 if( !m_jobs.empty() )
379 jobUpdated( m_jobs.front() );
380}
381
382
384{
385 m_statusBars.erase( std::remove_if( m_statusBars.begin(), m_statusBars.end(),
386 [&]( KISTATUSBAR* statusBar )
387 {
388 return statusBar == aStatusBar;
389 } ) );
390}
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.
void SetCurrentProgress(double aProgress) override
Set the progress value to aProgress (0..1).
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:46
virtual void AdvancePhase() override
Use the next available virtual zone of the dialog progress bar.
virtual void SetCurrentProgress(double aProgress) override
Set the progress value to aProgress (0..1).
void SetNumPhases(int aNumPhases) override
Set the number of phases.
#define _(s)