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 (C) 2023 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, wxEmptyString ) );
59 mainSizer->Add( m_stName, 0, wxALL | wxEXPAND, 1 );
60
61 m_stStatus = new wxStaticText( this, wxID_ANY, aJob->m_status, wxDefaultPosition,
62 wxDefaultSize, 0 );
63 m_stStatus->Wrap( -1 );
64 mainSizer->Add( m_stStatus, 0, wxALL | wxEXPAND, 1 );
65
66 m_progress = new wxGauge( this, wxID_ANY, aJob->m_maxProgress, wxDefaultPosition, wxDefaultSize,
67 wxGA_HORIZONTAL );
68 m_progress->SetValue( 0 );
69 mainSizer->Add( m_progress, 0, wxALL | wxEXPAND, 1 );
70
71 SetSizer( mainSizer );
72 Layout();
73
75 }
76
77
79 {
80 m_stStatus->SetLabelText( m_job->m_status );
81 m_progress->SetValue( m_job->m_currentProgress );
82 m_progress->SetRange( m_job->m_maxProgress );
83 }
84
85private:
86 wxGauge* m_progress;
87 wxStaticText* m_stName;
88 wxStaticText* m_stStatus;
89 std::shared_ptr<BACKGROUND_JOB> m_job;
90};
91
92
93class BACKGROUND_JOB_LIST : public wxFrame
94{
95public:
96 BACKGROUND_JOB_LIST( wxWindow* parent, const wxPoint& pos ) :
97 wxFrame( parent, wxID_ANY, _( "Background Jobs" ), pos, wxSize( 300, 150 ),
98 wxFRAME_NO_TASKBAR | wxBORDER_SIMPLE )
99 {
100 SetSizeHints( wxDefaultSize, wxDefaultSize );
101
102 wxBoxSizer* bSizer1;
103 bSizer1 = new wxBoxSizer( wxVERTICAL );
104
105 m_scrolledWindow = new wxScrolledWindow( this, wxID_ANY, wxDefaultPosition,
106 wxSize( -1, -1 ), wxVSCROLL );
107 m_scrolledWindow->SetScrollRate( 5, 5 );
108 m_contentSizer = new wxBoxSizer( wxVERTICAL );
109
110 m_scrolledWindow->SetSizer( m_contentSizer );
111 m_scrolledWindow->Layout();
113 bSizer1->Add( m_scrolledWindow, 1, wxEXPAND | wxALL, 0 );
114
115 Bind( wxEVT_KILL_FOCUS, &BACKGROUND_JOB_LIST::onFocusLoss, this );
116
117 SetSizer( bSizer1 );
118 Layout();
119
120 SetFocus();
121 }
122
123 void onFocusLoss( wxFocusEvent& aEvent )
124 {
125 Close( true );
126 aEvent.Skip();
127 }
128
129
130 void Add( std::shared_ptr<BACKGROUND_JOB> aJob )
131 {
133 m_contentSizer->Add( panel, 0, wxEXPAND | wxALL, 2 );
134 m_scrolledWindow->Layout();
136
137 // call this at this window otherwise the child panels dont resize width properly
138 Layout();
139
140 m_jobPanels[aJob] = panel;
141 }
142
143
144 void Remove( std::shared_ptr<BACKGROUND_JOB> aJob )
145 {
146 auto it = m_jobPanels.find( aJob );
147 if( it != m_jobPanels.end() )
148 {
149 BACKGROUND_JOB_PANEL* panel = m_jobPanels[aJob];
150 m_contentSizer->Detach( panel );
151 panel->Destroy();
152
153 m_jobPanels.erase( it );
154 }
155 }
156
157 void UpdateJob( std::shared_ptr<BACKGROUND_JOB> aJob )
158 {
159 auto it = m_jobPanels.find( aJob );
160 if( it != m_jobPanels.end() )
161 {
162 BACKGROUND_JOB_PANEL* panel = m_jobPanels[aJob];
163 panel->UpdateFromJob();
164 }
165 }
166
167private:
168 wxScrolledWindow* m_scrolledWindow;
169 wxBoxSizer* m_contentSizer;
170 std::unordered_map<std::shared_ptr<BACKGROUND_JOB>, BACKGROUND_JOB_PANEL*> m_jobPanels;
171};
172
173
175 std::shared_ptr<BACKGROUND_JOB> aJob ) :
177 m_monitor( aMonitor ), m_job( aJob )
178{
179
180}
181
182
184{
185 return true;
186}
187
188
189void BACKGROUND_JOB_REPORTER::Report( const wxString& aMessage )
190{
191 m_job->m_status = aMessage;
193}
194
195
197{
199 m_job->m_maxProgress = m_numPhases;
201}
202
203
205{
207 m_job->m_currentProgress = m_phase;
209}
210
211
213{
214
215}
216
217
218std::shared_ptr<BACKGROUND_JOB> BACKGROUND_JOBS_MONITOR::Create( const wxString& aName )
219{
220 std::shared_ptr<BACKGROUND_JOB> job = std::make_shared<BACKGROUND_JOB>();
221
222 job->m_name = aName;
223 job->m_reporter = std::make_shared<BACKGROUND_JOB_REPORTER>( this, job );
224
225 std::lock_guard<std::shared_mutex> lock( m_mutex );
226 m_jobs.push_back( job );
227
228 if( m_shownDialogs.size() > 0 )
229 {
230 // update dialogs
232 {
233 list->CallAfter(
234 [=]()
235 {
236 list->Add( job );
237 } );
238 }
239 }
240
241 return job;
242}
243
244
245void BACKGROUND_JOBS_MONITOR::Remove( std::shared_ptr<BACKGROUND_JOB> aJob )
246{
247 if( m_shownDialogs.size() > 0 )
248 {
249 // update dialogs
250
252 {
253 list->CallAfter(
254 [=]()
255 {
256 list->Remove( aJob );
257 } );
258 }
259 }
260
261 std::lock_guard<std::shared_mutex> lock( m_mutex );
262 m_jobs.erase( std::remove_if( m_jobs.begin(), m_jobs.end(),
263 [&]( std::shared_ptr<BACKGROUND_JOB> job )
264 {
265 return job == aJob;
266 } ) );
267
268 if( m_jobs.size() == 0 )
269 {
270 for( KISTATUSBAR* statusBar : m_statusBars )
271 {
272 statusBar->CallAfter(
273 [=]()
274 {
275 statusBar->HideBackgroundProgressBar();
276 statusBar->SetBackgroundStatusText( wxT( "" ) );
277 } );
278 }
279 }
280}
281
282
284{
285 BACKGROUND_JOB_LIST* evtWindow = dynamic_cast<BACKGROUND_JOB_LIST*>( aEvent.GetEventObject() );
286
287 m_shownDialogs.erase( std::remove_if( m_shownDialogs.begin(), m_shownDialogs.end(),
288 [&]( BACKGROUND_JOB_LIST* dialog )
289 {
290 return dialog == evtWindow;
291 } ) );
292
293 aEvent.Skip();
294}
295
296
297void BACKGROUND_JOBS_MONITOR::ShowList( wxWindow* aParent, wxPoint aPos )
298{
299 BACKGROUND_JOB_LIST* list = new BACKGROUND_JOB_LIST( aParent, aPos );
300
301 std::shared_lock<std::shared_mutex> lock( m_mutex, std::try_to_lock );
302
303 for( std::shared_ptr<BACKGROUND_JOB> job : m_jobs )
304 {
305 list->Add( job );
306 }
307
308 lock.unlock();
309
310 m_shownDialogs.push_back( list );
311
312 list->Bind( wxEVT_CLOSE_WINDOW, &BACKGROUND_JOBS_MONITOR::onListWindowClosed, this );
313
314 // correct the position
315 wxSize windowSize = list->GetSize();
316 list->SetPosition( aPos - windowSize );
317
318 list->Show();
319}
320
321
322void BACKGROUND_JOBS_MONITOR::jobUpdated( std::shared_ptr<BACKGROUND_JOB> aJob )
323{
324 std::shared_lock<std::shared_mutex> lock( m_mutex, std::try_to_lock );
325
326 // this method is called from the reporters from potentially other threads
327 // we have to guard ui calls with CallAfter
328 if( m_jobs.size() > 0 )
329 {
330 //for now, we go and update the status bar if its the first job in the vector
331 if( m_jobs.front() == aJob )
332 {
333 // update all status bar entries
334 for( KISTATUSBAR* statusBar : m_statusBars )
335 {
336 statusBar->CallAfter(
337 [=]()
338 {
339 statusBar->ShowBackgroundProgressBar();
340 statusBar->SetBackgroundProgress( aJob->m_currentProgress );
341 statusBar->SetBackgroundProgressMax( aJob->m_maxProgress );
342 statusBar->SetBackgroundStatusText( aJob->m_status );
343 } );
344 }
345 }
346 }
347
348
350 {
351 list->CallAfter(
352 [=]()
353 {
354 list->UpdateJob( aJob );
355 } );
356 }
357}
358
359
361{
362 m_statusBars.push_back( aStatusBar );
363}
364
365
367{
368 m_statusBars.erase( std::remove_if( m_statusBars.begin(), m_statusBars.end(),
369 [&]( KISTATUSBAR* statusBar )
370 {
371 return statusBar == aStatusBar;
372 } ) );
373}
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, std::shared_ptr< BACKGROUND_JOB > aJob)
This implements all the tricky bits for thread safety, but the GUI is left to derived classes.
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)