You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

377 lines
11 KiB

2 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2023 Mark Roszko <mark.roszko@gmail.com>
  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. #include <unordered_map>
  25. #include <wx/gauge.h>
  26. #include <wx/frame.h>
  27. #include <wx/panel.h>
  28. #include <wx/settings.h>
  29. #include <wx/scrolwin.h>
  30. #include <wx/sizer.h>
  31. #include <wx/stattext.h>
  32. #include <wx/string.h>
  33. #include <background_jobs_monitor.h>
  34. #include <widgets/kistatusbar.h>
  35. class BACKGROUND_JOB_PANEL : public wxPanel
  36. {
  37. public:
  38. BACKGROUND_JOB_PANEL( wxWindow* aParent, std::shared_ptr<BACKGROUND_JOB> aJob ) :
  39. wxPanel( aParent, wxID_ANY, wxDefaultPosition, wxSize( -1, 75 ),
  40. wxBORDER_SIMPLE ),
  41. m_job( aJob )
  42. {
  43. SetSizeHints( wxDefaultSize, wxDefaultSize );
  44. wxBoxSizer* mainSizer;
  45. mainSizer = new wxBoxSizer( wxVERTICAL );
  46. SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_3DLIGHT ) );
  47. m_stName = new wxStaticText( this, wxID_ANY, aJob->m_name );
  48. m_stName->Wrap( -1 );
  49. m_stName->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT,
  50. wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false,
  51. wxEmptyString ) );
  52. mainSizer->Add( m_stName, 0, wxALL | wxEXPAND, 1 );
  53. m_stStatus = new wxStaticText( this, wxID_ANY, aJob->m_status, wxDefaultPosition,
  54. wxDefaultSize, 0 );
  55. m_stStatus->Wrap( -1 );
  56. mainSizer->Add( m_stStatus, 0, wxALL | wxEXPAND, 1 );
  57. m_progress = new wxGauge( this, wxID_ANY, aJob->m_maxProgress, wxDefaultPosition,
  58. wxDefaultSize, wxGA_HORIZONTAL );
  59. m_progress->SetValue( 0 );
  60. mainSizer->Add( m_progress, 0, wxALL | wxEXPAND, 1 );
  61. SetSizer( mainSizer );
  62. Layout();
  63. UpdateFromJob();
  64. }
  65. void UpdateFromJob()
  66. {
  67. m_stStatus->SetLabelText( m_job->m_status );
  68. m_progress->SetValue( m_job->m_currentProgress );
  69. m_progress->SetRange( m_job->m_maxProgress );
  70. }
  71. private:
  72. wxGauge* m_progress;
  73. wxStaticText* m_stName;
  74. wxStaticText* m_stStatus;
  75. std::shared_ptr<BACKGROUND_JOB> m_job;
  76. };
  77. class BACKGROUND_JOB_LIST : public wxFrame
  78. {
  79. public:
  80. BACKGROUND_JOB_LIST( wxWindow* parent, const wxPoint& pos ) :
  81. wxFrame( parent, wxID_ANY, _( "Background Jobs" ), pos, wxSize( 300, 150 ),
  82. wxFRAME_NO_TASKBAR | wxBORDER_SIMPLE )
  83. {
  84. SetSizeHints( wxDefaultSize, wxDefaultSize );
  85. wxBoxSizer* bSizer1;
  86. bSizer1 = new wxBoxSizer( wxVERTICAL );
  87. m_scrolledWindow = new wxScrolledWindow( this, wxID_ANY, wxDefaultPosition,
  88. wxSize( -1, -1 ), wxVSCROLL );
  89. m_scrolledWindow->SetScrollRate( 5, 5 );
  90. m_contentSizer = new wxBoxSizer( wxVERTICAL );
  91. m_scrolledWindow->SetSizer( m_contentSizer );
  92. m_scrolledWindow->Layout();
  93. m_contentSizer->Fit( m_scrolledWindow );
  94. bSizer1->Add( m_scrolledWindow, 1, wxEXPAND | wxALL, 0 );
  95. Bind( wxEVT_KILL_FOCUS, &BACKGROUND_JOB_LIST::onFocusLoss, this );
  96. SetSizer( bSizer1 );
  97. Layout();
  98. SetFocus();
  99. }
  100. void onFocusLoss( wxFocusEvent& aEvent )
  101. {
  102. Close( true );
  103. aEvent.Skip();
  104. }
  105. void Add( std::shared_ptr<BACKGROUND_JOB> aJob )
  106. {
  107. BACKGROUND_JOB_PANEL* panel = new BACKGROUND_JOB_PANEL( m_scrolledWindow, aJob );
  108. m_contentSizer->Add( panel, 0, wxEXPAND | wxALL, 2 );
  109. m_scrolledWindow->Layout();
  110. m_contentSizer->Fit( m_scrolledWindow );
  111. // call this at this window otherwise the child panels don't resize width properly
  112. Layout();
  113. m_jobPanels[aJob] = panel;
  114. }
  115. void Remove( std::shared_ptr<BACKGROUND_JOB> aJob )
  116. {
  117. auto it = m_jobPanels.find( aJob );
  118. if( it != m_jobPanels.end() )
  119. {
  120. BACKGROUND_JOB_PANEL* panel = m_jobPanels[aJob];
  121. m_contentSizer->Detach( panel );
  122. panel->Destroy();
  123. m_jobPanels.erase( it );
  124. }
  125. }
  126. void UpdateJob( std::shared_ptr<BACKGROUND_JOB> aJob )
  127. {
  128. auto it = m_jobPanels.find( aJob );
  129. if( it != m_jobPanels.end() )
  130. {
  131. BACKGROUND_JOB_PANEL* panel = m_jobPanels[aJob];
  132. panel->UpdateFromJob();
  133. }
  134. }
  135. private:
  136. wxScrolledWindow* m_scrolledWindow;
  137. wxBoxSizer* m_contentSizer;
  138. std::unordered_map<std::shared_ptr<BACKGROUND_JOB>, BACKGROUND_JOB_PANEL*> m_jobPanels;
  139. };
  140. BACKGROUND_JOB_REPORTER::BACKGROUND_JOB_REPORTER( BACKGROUND_JOBS_MONITOR* aMonitor,
  141. const std::shared_ptr<BACKGROUND_JOB>& aJob ) :
  142. PROGRESS_REPORTER_BASE( 1 ),
  143. m_monitor( aMonitor ),
  144. m_job( aJob )
  145. {
  146. }
  147. bool BACKGROUND_JOB_REPORTER::updateUI()
  148. {
  149. return !m_cancelled;
  150. }
  151. void BACKGROUND_JOB_REPORTER::Report( const wxString& aMessage )
  152. {
  153. m_job->m_status = aMessage;
  154. m_monitor->jobUpdated( m_job );
  155. }
  156. void BACKGROUND_JOB_REPORTER::SetNumPhases( int aNumPhases )
  157. {
  158. PROGRESS_REPORTER_BASE::SetNumPhases( aNumPhases );
  159. m_job->m_maxProgress = m_numPhases;
  160. m_monitor->jobUpdated( m_job );
  161. }
  162. void BACKGROUND_JOB_REPORTER::AdvancePhase()
  163. {
  164. PROGRESS_REPORTER_BASE::AdvancePhase();
  165. m_job->m_currentProgress = m_phase;
  166. m_monitor->jobUpdated( m_job );
  167. }
  168. BACKGROUND_JOBS_MONITOR::BACKGROUND_JOBS_MONITOR()
  169. {
  170. }
  171. std::shared_ptr<BACKGROUND_JOB> BACKGROUND_JOBS_MONITOR::Create( const wxString& aName )
  172. {
  173. std::shared_ptr<BACKGROUND_JOB> job = std::make_shared<BACKGROUND_JOB>();
  174. job->m_name = aName;
  175. job->m_reporter = std::make_shared<BACKGROUND_JOB_REPORTER>( this, job );
  176. std::lock_guard<std::shared_mutex> lock( m_mutex );
  177. m_jobs.push_back( job );
  178. if( m_shownDialogs.size() > 0 )
  179. {
  180. // update dialogs
  181. for( BACKGROUND_JOB_LIST* list : m_shownDialogs )
  182. {
  183. list->CallAfter(
  184. [=]()
  185. {
  186. list->Add( job );
  187. } );
  188. }
  189. }
  190. return job;
  191. }
  192. void BACKGROUND_JOBS_MONITOR::Remove( std::shared_ptr<BACKGROUND_JOB> aJob )
  193. {
  194. if( m_shownDialogs.size() > 0 )
  195. {
  196. // update dialogs
  197. for( BACKGROUND_JOB_LIST* list : m_shownDialogs )
  198. {
  199. list->CallAfter(
  200. [=]()
  201. {
  202. list->Remove( aJob );
  203. } );
  204. }
  205. }
  206. std::lock_guard<std::shared_mutex> lock( m_mutex );
  207. m_jobs.erase( std::remove_if( m_jobs.begin(), m_jobs.end(),
  208. [&]( std::shared_ptr<BACKGROUND_JOB> job )
  209. {
  210. return job == aJob;
  211. } ) );
  212. if( m_jobs.size() > 0 )
  213. {
  214. jobUpdated( m_jobs.front() );
  215. }
  216. else
  217. {
  218. for( KISTATUSBAR* statusBar : m_statusBars )
  219. {
  220. statusBar->CallAfter(
  221. [=]()
  222. {
  223. statusBar->HideBackgroundProgressBar();
  224. statusBar->SetBackgroundStatusText( wxT( "" ) );
  225. } );
  226. }
  227. }
  228. }
  229. void BACKGROUND_JOBS_MONITOR::onListWindowClosed( wxCloseEvent& aEvent )
  230. {
  231. BACKGROUND_JOB_LIST* evtWindow = dynamic_cast<BACKGROUND_JOB_LIST*>( aEvent.GetEventObject() );
  232. m_shownDialogs.erase( std::remove_if( m_shownDialogs.begin(), m_shownDialogs.end(),
  233. [&]( BACKGROUND_JOB_LIST* dialog )
  234. {
  235. return dialog == evtWindow;
  236. } ) );
  237. aEvent.Skip();
  238. }
  239. void BACKGROUND_JOBS_MONITOR::ShowList( wxWindow* aParent, wxPoint aPos )
  240. {
  241. BACKGROUND_JOB_LIST* list = new BACKGROUND_JOB_LIST( aParent, aPos );
  242. std::shared_lock<std::shared_mutex> lock( m_mutex, std::try_to_lock );
  243. for( const std::shared_ptr<BACKGROUND_JOB>& job : m_jobs )
  244. list->Add( job );
  245. lock.unlock();
  246. m_shownDialogs.push_back( list );
  247. list->Bind( wxEVT_CLOSE_WINDOW, &BACKGROUND_JOBS_MONITOR::onListWindowClosed, this );
  248. // correct the position
  249. wxSize windowSize = list->GetSize();
  250. list->SetPosition( aPos - windowSize );
  251. list->Show();
  252. }
  253. void BACKGROUND_JOBS_MONITOR::jobUpdated( std::shared_ptr<BACKGROUND_JOB> aJob )
  254. {
  255. std::shared_lock<std::shared_mutex> lock( m_mutex, std::try_to_lock );
  256. // this method is called from the reporters from potentially other threads
  257. // we have to guard ui calls with CallAfter
  258. if( m_jobs.size() > 0 )
  259. {
  260. //for now, we go and update the status bar if its the first job in the vector
  261. if( m_jobs.front() == aJob )
  262. {
  263. // update all status bar entries
  264. for( KISTATUSBAR* statusBar : m_statusBars )
  265. {
  266. statusBar->CallAfter(
  267. [=]()
  268. {
  269. statusBar->ShowBackgroundProgressBar();
  270. statusBar->SetBackgroundProgress( aJob->m_currentProgress );
  271. statusBar->SetBackgroundProgressMax( aJob->m_maxProgress );
  272. statusBar->SetBackgroundStatusText( aJob->m_status );
  273. } );
  274. }
  275. }
  276. }
  277. for( BACKGROUND_JOB_LIST* list : m_shownDialogs )
  278. {
  279. list->CallAfter(
  280. [=]()
  281. {
  282. list->UpdateJob( aJob );
  283. } );
  284. }
  285. }
  286. void BACKGROUND_JOBS_MONITOR::RegisterStatusBar( KISTATUSBAR* aStatusBar )
  287. {
  288. m_statusBars.push_back( aStatusBar );
  289. }
  290. void BACKGROUND_JOBS_MONITOR::UnregisterStatusBar( KISTATUSBAR* aStatusBar )
  291. {
  292. m_statusBars.erase( std::remove_if( m_statusBars.begin(), m_statusBars.end(),
  293. [&]( KISTATUSBAR* statusBar )
  294. {
  295. return statusBar == aStatusBar;
  296. } ) );
  297. }