1 // This file is part of BOINC.
2 // http://boinc.berkeley.edu
3 // Copyright (C) 2008 University of California
4 //
5 // BOINC is free software; you can redistribute it and/or modify it
6 // under the terms of the GNU Lesser General Public License
7 // as published by the Free Software Foundation,
8 // either version 3 of the License, or (at your option) any later version.
9 //
10 // BOINC is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 // See the GNU Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public License
16 // along with BOINC. If not, see <http://www.gnu.org/licenses/>.
17
18 #include "stdwx.h"
19 #include "miofile.h"
20 #include "Events.h"
21 #include "BOINCGUIApp.h"
22 #include "SkinManager.h"
23 #include "MainDocument.h"
24 #include "sg_TaskCommandPopup.h"
25 #include "sg_TaskPanel.h"
26 #include "boinc_api.h"
27 #include "filesys.h"
28 #include "str_replace.h"
29
30
31 #define SORTTASKLIST 1 /* TRUE to sort task selection control alphabetically */
32 #define SLIDESHOWWIDTH ADJUSTFORXDPI(290)
33 #define SLIDESHOWHEIGHT ADJUSTFORYDPI(126)
34 #define SLIDESHOWBORDER 1
35 #define HIDEDEFAULTSLIDE 1
36 #define TESTALLDESCRIPTIONS 0
37 #define SCROLLBARSPACER 8
38
39
40 enum { suspendedIcon, waitingIcon, runningIcon };
41
42
IMPLEMENT_DYNAMIC_CLASS(CScrolledTextBox,wxScrolledWindow)43 IMPLEMENT_DYNAMIC_CLASS(CScrolledTextBox, wxScrolledWindow)
44
45 BEGIN_EVENT_TABLE(CScrolledTextBox, wxScrolledWindow)
46 EVT_ERASE_BACKGROUND(CScrolledTextBox::OnEraseBackground)
47 END_EVENT_TABLE()
48
49 CScrolledTextBox::CScrolledTextBox() {
50 }
51
52
CScrolledTextBox(wxWindow * parent)53 CScrolledTextBox::CScrolledTextBox( wxWindow* parent) :
54 wxScrolledWindow( parent, ID_SGPROJECTDESCRIPTION, wxDefaultPosition, wxDefaultSize, wxVSCROLL)
55 {
56 SetForegroundColour(*wxBLACK);
57
58 m_TextSizer = new wxBoxSizer( wxVERTICAL );
59 m_hLine = GetCharHeight();
60
61 this->SetSizerAndFit( m_TextSizer );
62 this->Layout();
63 this->FitInside();
64 }
65
66
~CScrolledTextBox()67 CScrolledTextBox::~CScrolledTextBox() {
68 // Delete sizer & its children (CTransparentStaticText objects)
69 m_TextSizer->Clear(true);
70 }
71
72
SetValue(const wxString & s)73 void CScrolledTextBox::SetValue(const wxString& s) {
74 int lineHeight, totalLines, totalWidth;
75 wxString t = s;
76
77 // Delete sizer & its children (CTransparentStaticText objects)
78 m_TextSizer->Clear(true);
79
80 // Change all occurrences of "<sup>n</sup>" to "^n"
81 t.Replace(wxT("<sup>"), wxT("^"), true);
82 t.Replace(wxT("</sup>"), wxT(""), true);
83 t.Replace(wxT("<"), wxT("<"), true);
84
85 // First see if it fits without vertical scroll bar
86 totalWidth = GetSize().GetWidth();
87 totalLines = Wrap(t, totalWidth, &lineHeight);
88 m_TextSizer->FitInside(this);
89 SetScrollRate(1, lineHeight);
90 int scrollLines = GetScrollLines(wxVERTICAL); // Returns 0 if no scrollbar
91 if (scrollLines > 0) {
92 int sbwidth = wxSystemSettings::GetMetric(wxSYS_VSCROLL_X);
93 // It has a vertical scroll bar, so wrap again for reduced width
94 m_TextSizer->Clear(true);
95 totalLines = Wrap(t, totalWidth - sbwidth - SCROLLBARSPACER, &lineHeight);
96 m_TextSizer->FitInside(this);
97 }
98 }
99
100
OnEraseBackground(wxEraseEvent & event)101 void CScrolledTextBox::OnEraseBackground(wxEraseEvent& event) {
102 wxDC *dc = event.GetDC();
103 wxPoint p = GetParent()->GetPosition();
104 wxRect r = GetRect();
105 r.Offset(p);
106 wxBitmap backgroundBitmap = ((CSimpleTaskPanel*)GetGrandParent())->GetBackgroundBmp().GetSubBitmap(r);
107 dc->DrawBitmap(backgroundBitmap, 0, 0);
108 }
109
110
111 // Text wrapping code adapted from wxWindows dlgcmn.cpp
IsStartOfNewLine()112 bool CScrolledTextBox::IsStartOfNewLine() {
113 if ( !m_eol ) return false;
114 m_eol = false;
115 return true;
116 }
117
118
OnOutputLine(const wxString & line)119 void CScrolledTextBox::OnOutputLine(const wxString& line) {
120 if ( !line.empty() ) {
121 m_TextSizer->Add(new CTransparentStaticText(this, wxID_ANY, line));
122 } else { // empty line, no need to create a control for it
123 m_TextSizer->Add(5, m_hLine/3);
124 }
125 }
126
127
128 // Returns the number of lines
Wrap(const wxString & text,int widthMax,int * lineHeight)129 int CScrolledTextBox::Wrap(const wxString& text, int widthMax, int *lineHeight) {
130 const wxChar *lastSpace = NULL;
131 wxString line;
132 int height = 0, numLines = 0;
133
134 const wxChar *lineStart = text.c_str();
135 for ( const wxChar *p = lineStart; ; p++ ) {
136 if ( IsStartOfNewLine() ) {
137 m_text += _T('\n');
138
139 lastSpace = NULL;
140 line.clear();
141 lineStart = p;
142 }
143
144 if ( *p == _T('\n') || *p == _T('\0') ) {
145 line.Trim();
146 OnOutputLine(line);
147 m_eol = true;
148 ++numLines;
149
150 if ( *p == _T('\0') )
151 break;
152 } else { // not EOL
153 if ( *p == _T(' ') ) {
154 lastSpace = p;
155 }
156 line += *p;
157
158 if ( widthMax >= 0 && lastSpace ) {
159 int width;
160 GetTextExtent(line, &width, &height);
161
162 if ( width > widthMax ) {
163 // remove the last word from this line
164 line.erase(lastSpace - lineStart, p + 1 - lineStart);
165 line.Trim();
166 OnOutputLine(line);
167 m_eol = true;
168 ++numLines;
169
170 // go back to the last word of this line which we didn't
171 // output yet
172 p = lastSpace;
173 }
174 }
175 //else: no wrapping at all or impossible to wrap
176 }
177 }
178 *lineHeight = height;
179 return numLines;
180 }
181
182
183
IMPLEMENT_DYNAMIC_CLASS(CSlideShowPanel,wxPanel)184 IMPLEMENT_DYNAMIC_CLASS(CSlideShowPanel, wxPanel)
185
186 BEGIN_EVENT_TABLE(CSlideShowPanel, wxPanel)
187 EVT_ERASE_BACKGROUND(CSlideShowPanel::OnEraseBackground)
188 EVT_TIMER(ID_CHANGE_SLIDE_TIMER, CSlideShowPanel::OnSlideShowTimer)
189 EVT_PAINT(CSlideShowPanel::OnPaint)
190 END_EVENT_TABLE()
191
192 CSlideShowPanel::CSlideShowPanel() {
193 }
194
195
CSlideShowPanel(wxWindow * parent)196 CSlideShowPanel::CSlideShowPanel( wxWindow* parent ) :
197 wxPanel( parent, wxID_ANY, wxDefaultPosition,
198 wxSize(SLIDESHOWWIDTH+(2*SLIDESHOWBORDER),
199 SLIDESHOWHEIGHT+(2*SLIDESHOWBORDER)), wxBORDER_NONE )
200 {
201 int w, h;
202 wxBoxSizer* bSizer1;
203 bSizer1 = new wxBoxSizer( wxVERTICAL );
204
205 m_description = new CScrolledTextBox( this );
206 GetSize(&w, &h);
207 m_description->SetMinSize(wxSize(w, h));
208 bSizer1->Add( m_description, 1, wxEXPAND, 0 );
209
210 this->SetSizer( bSizer1 );
211 this->Layout();
212
213 m_SlideBitmap = wxNullBitmap;
214 m_bCurrentSlideIsDefault = false;
215 m_bGotAllProjectsList = false;
216 m_bHasBeenDrawn = false;
217
218 #ifdef __WXMAC__
219 // Tell accessibility aids to ignore this panel (but not its contents)
220 HIObjectSetAccessibilityIgnored((HIObjectRef)GetHandle(), true);
221 #endif
222
223 m_ChangeSlideTimer = new wxTimer(this, ID_CHANGE_SLIDE_TIMER);
224 m_ChangeSlideTimer->Start(10000);
225 }
226
~CSlideShowPanel()227 CSlideShowPanel::~CSlideShowPanel()
228 {
229 if ( m_ChangeSlideTimer->IsRunning() ) {
230 m_ChangeSlideTimer->Stop();
231 }
232 delete m_ChangeSlideTimer;
233 }
234
235
OnSlideShowTimer(wxTimerEvent & WXUNUSED (event))236 void CSlideShowPanel::OnSlideShowTimer(wxTimerEvent& WXUNUSED(event)) {
237 AdvanceSlideShow(true, false);
238 }
239
SetDescriptionText(void)240 void CSlideShowPanel::SetDescriptionText(void) {
241 unsigned int i;
242 wxString s, ss;
243
244 TaskSelectionData* selData = ((CSimpleTaskPanel*)GetParent())->GetTaskSelectionData();
245 if (selData == NULL) return;
246 for (i=0; i<m_AllProjectsList.projects.size(); i++) {
247 if (!strcmp(m_AllProjectsList.projects[i]->url.c_str(), selData->project_url)) {
248 s = wxString(m_AllProjectsList.projects[i]->home.c_str(), wxConvUTF8);
249 ss = wxGetTranslation(s);
250 ss.Append("\n\n");
251 s = wxString(m_AllProjectsList.projects[i]->specific_area.c_str(), wxConvUTF8);
252 ss += wxGetTranslation(s);
253 ss.Append("\n\n");
254 s = wxString(m_AllProjectsList.projects[i]->description.c_str(), wxConvUTF8);
255 ss += wxGetTranslation(s);
256 m_description->SetValue(ss);
257
258 m_description->Show(true);
259 Enable( true );
260 m_description->Enable();
261 this->Layout();
262 break;
263 }
264 }
265 }
266
267
AdvanceSlideShow(bool changeSlide,bool reload)268 void CSlideShowPanel::AdvanceSlideShow(bool changeSlide, bool reload) {
269 double xRatio, yRatio, ratio;
270 TaskSelectionData* selData = ((CSimpleTaskPanel*)GetParent())->GetTaskSelectionData();
271 if (selData == NULL) return;
272
273 if (reload) {
274 m_bCurrentSlideIsDefault = false;
275 selData->lastSlideShown = -1;
276 }
277
278 int numSlides = (int)selData->slideShowFileNames.size();
279 #if TESTALLDESCRIPTIONS // For testing
280 numSlides = 0;
281 #endif
282 if (numSlides <= 0) {
283 #if HIDEDEFAULTSLIDE
284 if (!reload) {
285 return;
286 }
287 wxRect r = GetRect();
288 wxBitmap backgroundBitmap = ((CSimpleTaskPanel*)GetParent())->GetBackgroundBmp().GetSubBitmap(r);
289 wxWindowDC dc(this);
290 dc.DrawBitmap(backgroundBitmap, 0, 0);
291
292 // Force redraws if text unchanged; hide all if not in all-projects list
293 m_description->Show(false);
294 Enable( false );
295
296 if (!m_bGotAllProjectsList) {
297 CMainDocument* pDoc = wxGetApp().GetDocument();
298 wxASSERT(pDoc);
299
300 pDoc->rpc.get_all_projects_list(m_AllProjectsList);
301 m_bGotAllProjectsList = true;
302 }
303
304 SetDescriptionText();
305
306 return;
307 #else // HIDEDEFAULTSLIDE
308 SetBackgroundColour(*wxBLACK);
309
310 if (m_bCurrentSlideIsDefault) return;
311
312 CSkinSimple* pSkinSimple = wxGetApp().GetSkinManager()->GetSimple();
313 wxASSERT(pSkinSimple);
314 wxASSERT(wxDynamicCast(pSkinSimple, CSkinSimple));
315
316 m_SlideBitmap = *pSkinSimple->GetWorkunitAnimationImage()->GetBitmap();
317 if (m_SlideBitmap.Ok()) {
318 m_bCurrentSlideIsDefault = true;
319 }
320 #endif // HIDEDEFAULTSLIDE
321 } else {
322 #if HIDEDEFAULTSLIDE
323 m_description->Show(false);
324 Enable( false );
325
326 #endif // HIDEDEFAULTSLIDE
327 // TODO: Should we allow slide show to advance if task is not running?
328 int newSlide = selData->lastSlideShown;
329
330 if (selData->dotColor == runningIcon) { // Advance only if running
331 if (changeSlide) {
332 if (++newSlide >= numSlides) {
333 newSlide = 0;
334 }
335 }
336 }
337 if (newSlide < 0) {
338 newSlide = 0;
339 }
340
341 if (selData->lastSlideShown != newSlide) { // Don't update if only one slide
342
343 selData->lastSlideShown = newSlide;
344
345 wxBitmap *bm = new wxBitmap();
346 bm->LoadFile(selData->slideShowFileNames[newSlide], wxBITMAP_TYPE_ANY);
347 if (bm->Ok()) {
348 m_SlideBitmap = *bm;
349 delete bm;
350 m_bCurrentSlideIsDefault = false;
351 }
352 }
353 }
354 if (m_SlideBitmap.Ok()) {
355 // Check to see if they need to be rescaled to fit in the window
356 ratio = 1.0;
357 xRatio = (double)SLIDESHOWWIDTH / (double)m_SlideBitmap.GetWidth();
358 yRatio = (double)SLIDESHOWHEIGHT / (double)m_SlideBitmap.GetHeight();
359 ratio = xRatio;
360 if ( yRatio < ratio ) {
361 ratio = yRatio;
362 }
363 if ( (ratio < 0.95) || (ratio > 1.05) ) {
364 wxImage img = m_SlideBitmap.ConvertToImage();
365 img.Rescale((int) (m_SlideBitmap.GetWidth()*ratio),
366 (int) (m_SlideBitmap.GetHeight()*ratio),
367 (ratio > 1.0) ? wxIMAGE_QUALITY_BILINEAR : wxIMAGE_QUALITY_BOX_AVERAGE
368 );
369 wxBitmap *bm = new wxBitmap(img);
370 m_SlideBitmap = *bm;
371 delete bm;
372 }
373
374 Refresh();
375 }
376 }
377
378
OnPaint(wxPaintEvent & WXUNUSED (event))379 void CSlideShowPanel::OnPaint(wxPaintEvent& WXUNUSED(event))
380 {
381 wxPaintDC dc(this);
382 #if HIDEDEFAULTSLIDE
383 int numSlides = 0;
384 TaskSelectionData* selData = ((CSimpleTaskPanel*)GetParent())->GetTaskSelectionData();
385 if (selData) {
386 numSlides = (int)selData->slideShowFileNames.size();
387 }
388 #if TESTALLDESCRIPTIONS // For testing
389 numSlides = 0;
390 #endif // TESTALLDESCRIPTIONS
391
392 if (numSlides > 0)
393 #endif // HIDEDEFAULTSLIDE
394 {
395 int w, h, x;
396 wxPen oldPen = dc.GetPen();
397 wxBrush oldBrush = dc.GetBrush();
398 int oldMode = dc.GetBackgroundMode();
399 wxPen bgPen(*wxBLACK, 2*SLIDESHOWBORDER+1);
400 dc.SetBackgroundMode(wxSOLID);
401 dc.SetPen(bgPen);
402 dc.SetBrush(*wxBLACK_BRUSH);
403
404 GetSize(&w, &h);
405 x = (w - SLIDESHOWWIDTH) / 2;
406 dc.DrawRectangle(x, SLIDESHOWBORDER, SLIDESHOWWIDTH, SLIDESHOWHEIGHT);
407 // Restore Mode, Pen and Brush
408 dc.SetBackgroundMode(oldMode);
409 dc.SetPen(oldPen);
410 dc.SetBrush(oldBrush);
411
412 if(m_SlideBitmap.Ok())
413 {
414 dc.DrawBitmap(m_SlideBitmap,
415 (w - m_SlideBitmap.GetWidth())/2,
416 (h - m_SlideBitmap.GetHeight())/2
417 );
418 }
419 }
420
421 if (!m_bHasBeenDrawn) {
422 m_bHasBeenDrawn = true;
423 if (numSlides <= 0) {
424 SetDescriptionText();
425 }
426 }
427 }
428
429
OnEraseBackground(wxEraseEvent & event)430 void CSlideShowPanel::OnEraseBackground(wxEraseEvent& event) {
431 wxDC *dc = event.GetDC();
432 wxRect r = GetRect();
433 wxBitmap backgroundBitmap = ((CSimpleTaskPanel*)GetParent())->GetBackgroundBmp().GetSubBitmap(r);
434 dc->DrawBitmap(backgroundBitmap, 0, 0);
435 }
436
437
438
439
IMPLEMENT_DYNAMIC_CLASS(CSimpleTaskPanel,CSimplePanelBase)440 IMPLEMENT_DYNAMIC_CLASS(CSimpleTaskPanel, CSimplePanelBase)
441
442 BEGIN_EVENT_TABLE(CSimpleTaskPanel, CSimplePanelBase)
443 #ifdef __WXMAC__
444 EVT_CHOICE(ID_SGTASKSELECTOR, CSimpleTaskPanel::OnTaskSelection)
445 #else
446 EVT_COMBOBOX(ID_SGTASKSELECTOR, CSimpleTaskPanel::OnTaskSelection)
447 #if 0 // This is apparently no longer needed with wxCocoa 3.0
448 EVT_ERASE_BACKGROUND(CSimpleTaskPanel::OnEraseBackground)
449 #endif
450 #endif
451 END_EVENT_TABLE()
452
453 CSimpleTaskPanel::CSimpleTaskPanel() {
454 }
455
456
CSimpleTaskPanel(wxWindow * parent)457 CSimpleTaskPanel::CSimpleTaskPanel( wxWindow* parent ) :
458 CSimplePanelBase( parent )
459 {
460 wxSize sz;
461 int w, h;
462 wxString str = wxEmptyString;
463
464 m_oldWorkCount = -1;
465 error_time = 0;
466 m_GotBGBitMap = false; // Can't be made until parent has been laid out.
467 m_bStableTaskInfoChanged = false;
468 m_CurrentTaskSelection = -1;
469 m_sNoProjectsString = _("You don't have any projects. Please Add a Project.");
470 m_sNotAvailableString = _("Not available");
471 m_progressBarRect = NULL;
472
473 SetForegroundColour(*wxBLACK);
474
475 wxBoxSizer* bSizer1;
476 bSizer1 = new wxBoxSizer( wxVERTICAL );
477
478 wxBoxSizer* bSizer2;
479 bSizer2 = new wxBoxSizer( wxHORIZONTAL );
480
481 m_myTasksLabel = new CTransparentStaticText( this, wxID_ANY, _("Tasks:"), wxDefaultPosition, wxDefaultSize, 0 );
482 m_myTasksLabel->Wrap( -1 );
483 bSizer2->Add( m_myTasksLabel, 0, wxRIGHT, ADJUSTFORXDPI(5) );
484
485 m_TaskSelectionCtrl = new CBOINCBitmapComboBox( this, ID_SGTASKSELECTOR, wxT(""), wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_READONLY );
486 // TODO: Might want better wording for Task Selection Combo Box tooltip
487 str = _("Select a task to access");
488 m_TaskSelectionCtrl->SetToolTip(str);
489 bSizer2->Add( m_TaskSelectionCtrl, 1, wxRIGHT | wxEXPAND, SIDEMARGINS );
490
491 bSizer1->Add( bSizer2, 0, wxEXPAND | wxTOP | wxLEFT, ADJUSTFORXDPI(10) );
492
493 bSizer1->AddSpacer(ADJUSTFORYDPI(5));
494
495 wxBoxSizer* bSizer3;
496 bSizer3 = new wxBoxSizer( wxHORIZONTAL );
497
498 // what project the task is from, e.g. "From: SETI@home"
499 m_TaskProjectLabel = new CTransparentStaticText( this, wxID_ANY, _("From:"), wxDefaultPosition, wxDefaultSize, 0 );
500 m_TaskProjectLabel->Wrap( -1 );
501 bSizer3->Add( m_TaskProjectLabel, 0, wxRIGHT, ADJUSTFORXDPI(5) );
502
503 m_TaskProjectName = new CTransparentStaticText( this, wxID_ANY, wxT("SETI@home"), wxDefaultPosition, wxDefaultSize, wxST_NO_AUTORESIZE );
504 m_TaskProjectName->Wrap( -1 );
505 wxFont theFont = m_TaskProjectName->GetFont();
506 theFont.SetWeight(wxFONTWEIGHT_BOLD);
507 m_TaskProjectName->SetFont(theFont);
508 bSizer3->Add( m_TaskProjectName, 1, 0, 0 );
509
510 bSizer1->Add( bSizer3, 0, wxLEFT | wxRIGHT | wxEXPAND, SIDEMARGINS );
511
512 #if SELECTBYRESULTNAME
513 m_TaskApplicationName = new CTransparentStaticText( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxST_NO_AUTORESIZE );
514 m_TaskApplicationName->Wrap( -1 );
515
516 bSizer1->Add( m_TaskApplicationName, 0, wxLEFT | wxRIGHT | wxEXPAND, SIDEMARGINS );
517 #endif // SELECTBYRESULTNAME
518
519 bSizer1->AddSpacer(ADJUSTFORYDPI(10));
520
521 m_SlideShowArea = new CSlideShowPanel(this);
522 m_SlideShowArea->SetMinSize(wxSize(SLIDESHOWWIDTH+(2*SLIDESHOWBORDER), SLIDESHOWHEIGHT+(2*SLIDESHOWBORDER)));
523 m_SlideShowArea->Enable( false );
524
525 bSizer1->Add( m_SlideShowArea, 0, wxLEFT | wxRIGHT | wxEXPAND, SIDEMARGINS );
526
527 bSizer1->AddSpacer(ADJUSTFORYDPI(10));
528
529 m_ElapsedTimeValue = new CTransparentStaticText( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxST_NO_AUTORESIZE );
530 m_ElapsedTimeValue->Wrap( -1 );
531 bSizer1->Add( m_ElapsedTimeValue, 0, wxLEFT | wxRIGHT | wxEXPAND, SIDEMARGINS );
532
533 bSizer1->AddSpacer(ADJUSTFORYDPI(7));
534
535 m_TimeRemainingValue = new CTransparentStaticText( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxST_NO_AUTORESIZE );
536 m_TimeRemainingValue->Wrap( -1 );
537 bSizer1->Add( m_TimeRemainingValue, 0, wxLEFT | wxRIGHT | wxEXPAND, SIDEMARGINS );
538
539 bSizer1->AddSpacer(ADJUSTFORYDPI(7));
540
541 wxBoxSizer* bSizer4;
542 bSizer4 = new wxBoxSizer( wxHORIZONTAL );
543
544 // TODO: Standard Mac progress indicator's animation uses lots of CPU
545 // time, and also triggers unnecessary Erase events. Should we use a
546 // non-standard progress indicator on Mac? See also optimizations in
547 // CSimpleGUIPanel::OnEraseBackground and CSimpleTaskPanel::OnEraseBackground.
548 m_ProgressBar = new wxGauge( this, wxID_ANY, 100, wxDefaultPosition, wxDefaultSize, wxGA_HORIZONTAL );
549 m_ipctDoneX1000 = 100000;
550 m_ProgressBar->SetValue( 100 );
551 GetTextExtent(wxT("0"), &w, &h);
552 m_ProgressBar->SetMinSize(wxSize(ADJUSTFORXDPI(245), h));
553 m_ProgressBar->SetToolTip(_("This task's progress"));
554 bSizer4->Add( m_ProgressBar, 0, wxRIGHT, ADJUSTFORXDPI(5) );
555
556 m_ProgressValueText = new CTransparentStaticText( this, wxID_ANY, wxT("100.000%"), wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT | wxST_NO_AUTORESIZE );
557 m_ProgressValueText->Wrap( -1 );
558 bSizer4->Add( m_ProgressValueText, 0, wxALIGN_RIGHT, 0 );
559
560 bSizer1->Add( bSizer4, 0, wxLEFT | wxRIGHT | wxEXPAND, SIDEMARGINS );
561
562 bSizer1->AddSpacer(ADJUSTFORYDPI(7));
563
564 // TODO: Can we determine the longest status string and initialize with it?
565 m_StatusValueText = new CTransparentStaticText( this, wxID_ANY, m_sNoProjectsString, wxDefaultPosition, wxDefaultSize, wxST_NO_AUTORESIZE );
566 m_StatusValueText->Wrap( -1 );
567 bSizer1->Add( m_StatusValueText, 0, wxLEFT | wxRIGHT | wxEXPAND, SIDEMARGINS );
568
569 bSizer1->AddSpacer(ADJUSTFORYDPI(7));
570
571 m_TaskCommandsButton = new CSimpleTaskPopupButton( this, ID_TASKSCOMMANDBUTTON, _("Task Commands"), wxDefaultPosition, wxDefaultSize, 0 );
572 m_TaskCommandsButton->SetToolTip(_("Pop up a menu of commands to apply to this task"));
573 bSizer1->Add( m_TaskCommandsButton, 0, wxLEFT | wxRIGHT | wxEXPAND | wxALIGN_CENTER_HORIZONTAL, SIDEMARGINS );
574
575 bSizer1->AddSpacer(ADJUSTFORYDPI(10));
576
577 this->SetSizer( bSizer1 );
578 this->Layout();
579
580 m_ProgressRect = m_ProgressBar->GetRect();
581 #ifdef __WXMAC__
582 m_ProgressRect.Inflate(0, -2);
583 m_ProgressRect.Offset(0, -2);
584 #endif
585 }
586
587
~CSimpleTaskPanel()588 CSimpleTaskPanel::~CSimpleTaskPanel()
589 {
590 TaskSelectionData *selData;
591 int count = m_TaskSelectionCtrl->GetCount();
592 for(int j = count-1; j >=0; --j) {
593 selData = (TaskSelectionData*)m_TaskSelectionCtrl->GetClientData(j);
594 selData->slideShowFileNames.Clear();
595 delete selData;
596 // Indicate to Clear() we have cleaned up the Selection Data
597 m_TaskSelectionCtrl->SetClientData(j, NULL);
598 }
599 m_TaskSelectionCtrl->Clear();
600
601 if (m_progressBarRect) {
602 delete m_progressBarRect;
603 }
604 }
605
606
OnTaskSelection(wxCommandEvent &)607 void CSimpleTaskPanel::OnTaskSelection(wxCommandEvent& /*event*/)
608 {
609 int sel = m_TaskSelectionCtrl->GetSelection();
610 if (sel != m_CurrentTaskSelection) {
611 m_CurrentTaskSelection = sel;
612 m_bStableTaskInfoChanged = true;
613 UpdatePanel();
614 }
615 }
616
617
UpdatePanel(bool delayShow)618 void CSimpleTaskPanel::UpdatePanel(bool delayShow) {
619 wxString s = wxEmptyString;
620 wxString projName = wxEmptyString;
621 TaskSelectionData *selData;
622 CMainDocument* pDoc = wxGetApp().GetDocument();
623 wxASSERT(pDoc);
624 int workCount = pDoc->GetSimpleGUIWorkCount();
625
626 // Workaround for Linux refresh problem
627 static bool wasDelayed = false;
628
629 #ifndef __WXMAC__
630 Freeze();
631 #endif
632
633 if ((workCount <= 0) || delayShow) {
634 if ((workCount != m_oldWorkCount) || delayShow) {
635 wasDelayed = true;
636 m_myTasksLabel->Hide();
637 m_TaskSelectionCtrl->Hide();
638 m_TaskProjectLabel->Hide();
639 m_TaskProjectName->Hide();
640 #if SELECTBYRESULTNAME
641 m_TaskApplicationName->Hide();
642 #endif // SELECTBYRESULTNAME
643 m_SlideShowArea->Hide();
644 m_ElapsedTimeValue->Hide();
645 m_TimeRemainingValue->Hide();
646 if (m_ipctDoneX1000 >= 0) {
647 m_ipctDoneX1000 = -1;
648 m_ProgressBar->Hide();
649 }
650 m_ProgressValueText->Hide();
651 m_TaskCommandsButton->Hide();
652 this->Layout();
653
654 #ifdef __WXMAC__
655 m_ProgressRect = m_ProgressBar->GetRect();
656 m_ProgressRect.Inflate(0, -2);
657 m_ProgressRect.Offset(0, -2);
658 #endif
659 }
660
661 DisplayIdleState();
662
663 } else {
664 if ((m_oldWorkCount == 0) || wasDelayed) {
665 wasDelayed = false;
666 m_myTasksLabel->Show();
667 m_TaskSelectionCtrl->Show();
668 m_TaskProjectLabel->Show();
669 m_TaskProjectName->Show();
670 #if SELECTBYRESULTNAME
671 m_TaskApplicationName->Show();
672 #endif // SELECTBYRESULTNAME
673 m_SlideShowArea->Show();
674 m_ElapsedTimeValue->Show();
675 m_TimeRemainingValue->Show();
676 m_ProgressBar->Show();
677 m_ProgressValueText->Show();
678 m_TaskCommandsButton->Show();
679 this->Layout();
680
681 #ifdef __WXMAC__
682 m_ProgressRect = m_ProgressBar->GetRect();
683 m_ProgressRect.Inflate(0, -2);
684 m_ProgressRect.Offset(0, -2);
685 #endif
686 }
687
688 UpdateTaskSelectionList(false);
689
690 // We now have valid result pointers, so extract our data
691 int count = m_TaskSelectionCtrl->GetCount();
692 if (count <= 0) {
693 m_CurrentTaskSelection = -1;
694 } else {
695 if ((m_CurrentTaskSelection < 0) || (m_CurrentTaskSelection > count -1)) {
696 m_TaskSelectionCtrl->SetSelection(0);
697 m_CurrentTaskSelection = 0;
698 m_bStableTaskInfoChanged = true;
699 }
700 selData = (TaskSelectionData*)m_TaskSelectionCtrl->GetClientData(m_CurrentTaskSelection);
701 RESULT* result = selData->result;
702 if (result) {
703 if (m_bStableTaskInfoChanged) {
704 #if SELECTBYRESULTNAME
705 wxString str = wxEmptyString;
706 GetApplicationAndProjectNames(result, &s, &projName);
707 str.Printf(_("Application: %s"), s.c_str());
708 UpdateStaticText(&m_TaskApplicationName, str);
709 UpdateStaticText(&m_TaskProjectName, projName);
710 #else // SELECTBYRESULTNAME
711 GetApplicationAndProjectNames(result, NULL, &projName);
712 #endif // SELECTBYRESULTNAME
713 UpdateStaticText(&m_TaskProjectName, projName);
714 m_SlideShowArea->AdvanceSlideShow(false, true);
715 m_bStableTaskInfoChanged = false;
716 }
717 double f = result->elapsed_time;
718 if (f == 0.) f = result->current_cpu_time;
719 // f = result->final_elapsed_time;
720 UpdateStaticText(&m_ElapsedTimeValue, GetElapsedTimeString(f));
721 UpdateStaticText(&m_TimeRemainingValue, GetTimeRemainingString(result->estimated_cpu_time_remaining));
722 // fraction_done ranges from 0.0 to 1.0 so % done = fraction_done * 100.
723 int pctDoneX1000 = result->fraction_done * 100000.0;
724 // Update progress only if visible part has changed (xx.xxx)
725 if (m_ipctDoneX1000 != pctDoneX1000) {
726 int pctDone = pctDoneX1000 / 1000;
727 if (m_ipctDoneX1000 != (pctDone * 1000)) {
728 m_ProgressBar->SetValue(pctDone);
729 }
730 s.Printf(_("%.3f%%"), result->fraction_done*100);
731 m_ipctDoneX1000 = pctDoneX1000;
732 UpdateStaticText(&m_ProgressValueText, s);
733 }
734 UpdateStaticText(&m_StatusValueText, GetStatusString(result));
735 } else {
736 UpdateStaticText(&m_TaskProjectName, m_sNotAvailableString);
737 #if SELECTBYRESULTNAME
738 UpdateStaticText(&m_TaskApplicationName, _("Application: Not available") );
739 #endif // SELECTBYRESULTNAME
740 UpdateStaticText(&m_ElapsedTimeValue, GetElapsedTimeString(-1.0));
741 UpdateStaticText(&m_TimeRemainingValue, GetTimeRemainingString(-1.0));
742 if (m_ipctDoneX1000 >= 0) {
743 m_ipctDoneX1000 = -1;
744 m_ProgressBar->Hide();
745 }
746 UpdateStaticText(&m_ProgressValueText, wxEmptyString);
747 UpdateStaticText(&m_StatusValueText, GetStatusString(NULL));
748 }
749 }
750 }
751 m_oldWorkCount = workCount;
752
753 #ifndef __WXMAC__
754 Thaw();
755 #endif
756 }
757
758
GetProgressRect()759 wxRect* CSimpleTaskPanel::GetProgressRect() {
760 if (m_ProgressBar->IsShown()) {
761 return &m_ProgressRect;
762 } else {
763 return NULL;
764 }
765 }
766
767
ReskinInterface()768 void CSimpleTaskPanel::ReskinInterface() {
769 wxLogTrace(wxT("Function Start/End"), wxT("CSimpleTaskPanel::ReskinInterface - Function Begin"));
770 CSimplePanelBase::ReskinInterface();
771 m_SlideShowArea->AdvanceSlideShow(false, false);
772 UpdateTaskSelectionList(true);
773 wxLogTrace(wxT("Function Start/End"), wxT("CSimpleTaskPanel::ReskinInterface - Function Begin"));
774 }
775
776
GetTaskSelectionData()777 TaskSelectionData* CSimpleTaskPanel::GetTaskSelectionData() {
778 int count = m_TaskSelectionCtrl->GetCount();
779 if (count <= 0) {
780 return NULL;
781 }
782
783 int n = m_TaskSelectionCtrl->GetSelection();
784 return (TaskSelectionData*)m_TaskSelectionCtrl->GetClientData(n);
785 }
786
787
788 // Either appName argument or projName argument may be NULL
GetApplicationAndProjectNames(RESULT * result,wxString * appName,wxString * projName)789 void CSimpleTaskPanel::GetApplicationAndProjectNames(RESULT* result, wxString* appName, wxString* projName) {
790 CMainDocument* pDoc = wxGetApp().GetDocument();
791 RESULT* state_result = NULL;
792 wxString strAppBuffer = wxEmptyString;
793 wxString strGPUBuffer = wxEmptyString;
794
795 wxASSERT(pDoc);
796 wxASSERT(wxDynamicCast(pDoc, CMainDocument));
797
798 state_result = pDoc->state.lookup_result(result->project_url, result->name);
799 if (!state_result) {
800 pDoc->ForceCacheUpdate();
801 state_result = pDoc->state.lookup_result(result->project_url, result->name);
802 }
803
804 if (!state_result) return;
805
806 if (appName != NULL) {
807 WORKUNIT* wup = state_result->wup;
808 if (!wup) return;
809 APP* app = wup->app;
810 if (!app) return;
811 APP_VERSION* avp = state_result->avp;
812 if (!avp) return;
813
814 if (strlen(app->user_friendly_name)) {
815 strAppBuffer = wxString(state_result->app->user_friendly_name, wxConvUTF8);
816 } else {
817 strAppBuffer = wxString(state_result->avp->app_name, wxConvUTF8);
818 }
819
820 if (avp->gpu_type) {
821 strGPUBuffer.Printf(
822 wxT(" (%s)"),
823 wxString(proc_type_name(avp->gpu_type), wxConvUTF8).c_str()
824 );
825 }
826
827 appName->Printf(
828 wxT("%s%s%s"),
829 state_result->project->anonymous_platform?_("Local: "):wxT(""),
830 strAppBuffer.c_str(),
831 strGPUBuffer.c_str()
832 );
833 }
834
835 if (projName != NULL) {
836 *projName = wxString(state_result->project->project_name.c_str(), wxConvUTF8 );
837 if (projName->IsEmpty()) {
838 *projName = _("Not Available");
839 }
840 }
841 }
842
843
GetElapsedTimeString(double f)844 wxString CSimpleTaskPanel::GetElapsedTimeString(double f) {
845 wxString s = wxEmptyString;
846 wxString str = wxEmptyString;
847
848 if (f < 0) {
849 s = m_sNotAvailableString;
850 } else {
851 s = FormatTime(f);
852 }
853 str.Printf(_("Elapsed: %s"), s.c_str());
854 return str;
855 }
856
857
GetTimeRemainingString(double f)858 wxString CSimpleTaskPanel::GetTimeRemainingString(double f) {
859 wxString s = wxEmptyString;
860 wxString str = wxEmptyString;
861
862 if (f < 0) {
863 s = m_sNotAvailableString;
864 } else {
865 s = FormatTime(f);
866 }
867 str.Printf(_("Remaining (estimated): %s"), s.c_str());
868 return str;
869 }
870
871
GetStatusString(RESULT * result)872 wxString CSimpleTaskPanel::GetStatusString(RESULT* result) {
873 wxString s = wxEmptyString;
874 wxString str = wxEmptyString;
875
876 if (result == NULL) {
877 s = m_sNotAvailableString;
878 } else {
879 s = result_description(result, false);
880 }
881
882 str.Printf(_("Status: %s"), s.c_str());
883 return str;
884 }
885
FindSlideShowFiles(TaskSelectionData * selData)886 void CSimpleTaskPanel::FindSlideShowFiles(TaskSelectionData *selData) {
887 RESULT* state_result;
888 char proj_dir[1024];
889 char fileName[1024];
890 char resolvedFileName[1024];
891 int j;
892 CMainDocument* pDoc = wxGetApp().GetDocument();
893 wxASSERT(pDoc);
894
895
896 selData->slideShowFileNames.Clear();
897 state_result = pDoc->state.lookup_result(selData->result->project_url, selData->result->name);
898 if (!state_result) {
899 pDoc->ForceCacheUpdate();
900 state_result = pDoc->state.lookup_result(selData->result->project_url, selData->result->name);
901 }
902 if (state_result) {
903 url_to_project_dir(state_result->project->master_url, proj_dir, sizeof(proj_dir));
904 for(j=0; j<99; ++j) {
905 snprintf(fileName, sizeof(fileName), "%s/slideshow_%s_%02d", proj_dir, state_result->app->name, j);
906 if(boinc_resolve_filename(fileName, resolvedFileName, sizeof(resolvedFileName)) == 0) {
907 if (boinc_file_exists(resolvedFileName)) {
908 selData->slideShowFileNames.Add(wxString(resolvedFileName,wxConvUTF8));
909 }
910 } else {
911 break;
912 }
913 }
914
915 if ( selData->slideShowFileNames.size() == 0 ) {
916 for(j=0; j<99; ++j) {
917 snprintf(fileName, sizeof(fileName), "%s/slideshow_%02d", proj_dir, j);
918 if(boinc_resolve_filename(fileName, resolvedFileName, sizeof(resolvedFileName)) == 0) {
919 if (boinc_file_exists(resolvedFileName)) {
920 selData->slideShowFileNames.Add(wxString(resolvedFileName,wxConvUTF8));
921 }
922 } else {
923 break;
924 }
925 }
926 }
927 }
928 selData->lastSlideShown = -1;
929 }
930
931
UpdateTaskSelectionList(bool reskin)932 void CSimpleTaskPanel::UpdateTaskSelectionList(bool reskin) {
933 wxLogTrace(wxT("Function Start/End"), wxT("CSimpleTaskPanel::UpdateTaskSelectionList - Function Begin"));
934 int i, j, count, newIcon;
935 TaskSelectionData *selData;
936 RESULT* result;
937 RESULT* ctrlResult;
938 PROJECT* project;
939 std::vector<bool>is_alive;
940 bool needRefresh = false;
941 wxString resname;
942 CC_STATUS status;
943 CMainDocument* pDoc = wxGetApp().GetDocument();
944 CSkinSimple* pSkinSimple = wxGetApp().GetSkinManager()->GetSimple();
945 wxASSERT(pDoc);
946
947 static bool bAlreadyRunning = false;
948
949 wxASSERT(pDoc);
950 wxASSERT(pSkinSimple);
951 wxASSERT(wxDynamicCast(pSkinSimple, CSkinSimple));
952
953 if (bAlreadyRunning) {
954 return;
955 }
956 bAlreadyRunning = true;
957
958 count = m_TaskSelectionCtrl->GetCount();
959 // Mark all inactive (this lets us loop only once)
960 for (i=0; i<count; ++i) {
961 is_alive.push_back(false);
962 }
963
964 // First update existing entries and add new ones
965 for(i = 0; i < (int) pDoc->results.results.size(); i++) {
966 bool found = false;
967
968 result = pDoc->result(i);
969 // only check tasks that are active
970 if ( result == NULL || !result->active_task ) {
971 continue;
972 }
973
974 resname = wxEmptyString;
975 #if SELECTBYRESULTNAME
976 resname = wxString::FromUTF8(result->name);
977 #else // SELECTBYRESULTNAME
978 GetApplicationAndProjectNames(result, &resname, NULL);
979 #endif // SELECTBYRESULTNAME
980
981 // loop through the items already in Task Selection Control to find this result
982 count = m_TaskSelectionCtrl->GetCount();
983 for(j = 0; j < count; ++j) {
984 selData = (TaskSelectionData*)m_TaskSelectionCtrl->GetClientData(j);
985 if (!strcmp(result->name, selData->result_name) &&
986 !strcmp(result->project_url, selData->project_url)
987 ) {
988 selData->result = result;
989 found = true;
990 is_alive.at(j) = true;
991 break; // skip out of this loop
992 }
993 }
994
995 // if it isn't currently in the list then we have a new one! lets add it
996 if (!found) {
997 int alphaOrder;
998 for(j = 0; j < count; ++j) {
999 alphaOrder = (m_TaskSelectionCtrl->GetString(j)).CmpNoCase(resname);
1000 #if SORTTASKLIST
1001 if (alphaOrder > 0) {
1002 break; // Insert the new item here (sorted by item label)
1003 }
1004 #endif
1005 // wxComboBox and wxBitmapComboBox have bugs on Windows when multiple
1006 // entries have identical text, so add enough spaces to make each
1007 // entry's text unique.
1008 if (alphaOrder == 0) {
1009 resname.Append((const wxChar *)wxT(" "));
1010 #if !SORTTASKLIST
1011 j = -1; // If not sorted, check new name from start for duplicate
1012 #endif
1013 }
1014 }
1015
1016 selData = new TaskSelectionData;
1017 selData->result = result;
1018 strlcpy(selData->result_name, result->name, sizeof(selData->result_name));
1019 strlcpy(selData->project_url, result->project_url, sizeof(selData->project_url));
1020 selData->dotColor = -1;
1021 FindSlideShowFiles(selData);
1022 project = pDoc->state.lookup_project(result->project_url);
1023 if (project) {
1024 selData->project_files_downloaded_time = project->project_files_downloaded_time;
1025 } else {
1026 selData->project_files_downloaded_time = 0.0;
1027 }
1028
1029 #if SORTTASKLIST
1030 if (j < count) {
1031 std::vector<bool>::iterator iter = is_alive.begin();
1032 m_TaskSelectionCtrl->Insert(resname, wxNullBitmap, j, (void*)selData);
1033 is_alive.insert(iter+j, true);
1034 if (j <= m_CurrentTaskSelection) {
1035 ++m_CurrentTaskSelection;
1036 m_TaskSelectionCtrl->SetSelection(m_CurrentTaskSelection);
1037 }
1038 } else
1039 #endif
1040 {
1041 m_TaskSelectionCtrl->Append(resname, wxNullBitmap, (void*)selData);
1042 is_alive.push_back(true);
1043 }
1044 ++count;
1045 } // End if (!found)
1046 } // End for (i) loop
1047
1048 // Check items in descending order so deletion won't change indexes of items yet to be checked
1049 for(j = count-1; j >=0; --j) {
1050 if(!is_alive.at(j)) {
1051 wxLogTrace(wxT("Function Status"), wxT("CSimpleTaskPanel::UpdateTaskSelectionList - Task '%d' no longer alive"), j);
1052 selData = (TaskSelectionData*)m_TaskSelectionCtrl->GetClientData(j);
1053 wxLogTrace(wxT("Function Status"), wxT("CSimpleTaskPanel::UpdateTaskSelectionList - selData '%p' "), selData);
1054 wxLogTrace(wxT("Function Status"), wxT("CSimpleTaskPanel::UpdateTaskSelectionList - result_name '%s' "), selData->result_name);
1055 selData->slideShowFileNames.Clear();
1056 wxLogTrace(wxT("Function Status"), wxT("CSimpleTaskPanel::UpdateTaskSelectionList - Deleting selData"));
1057 delete selData;
1058 wxLogTrace(wxT("Function Status"), wxT("CSimpleTaskPanel::UpdateTaskSelectionList - Deleting control data"));
1059 // Indicate to Delete() we have cleaned up the Selection Data
1060 m_TaskSelectionCtrl->SetClientData(j, NULL);
1061 m_TaskSelectionCtrl->Delete(j);
1062 if (j == m_CurrentTaskSelection) {
1063 int newCount = m_TaskSelectionCtrl->GetCount();
1064 if (m_CurrentTaskSelection < newCount) {
1065 // Select the next item if one exists
1066 m_TaskSelectionCtrl->SetSelection(m_CurrentTaskSelection);
1067 } else if (newCount > 0) {
1068 // Select the previous item if one exists
1069 m_CurrentTaskSelection = newCount-1;
1070 m_TaskSelectionCtrl->SetSelection(m_CurrentTaskSelection);
1071 } else {
1072 m_CurrentTaskSelection = -1;
1073 m_TaskSelectionCtrl->SetSelection(wxNOT_FOUND);
1074 }
1075 m_bStableTaskInfoChanged = true;
1076 needRefresh = true;
1077 } else if (j < m_CurrentTaskSelection) {
1078 --m_CurrentTaskSelection;
1079 m_TaskSelectionCtrl->SetSelection(m_CurrentTaskSelection);
1080 }
1081 }
1082 }
1083
1084 if ((m_CurrentTaskSelection >= 0) && !m_bStableTaskInfoChanged) {
1085 selData = (TaskSelectionData*)m_TaskSelectionCtrl->GetClientData(m_CurrentTaskSelection);
1086 project = pDoc->state.lookup_project(selData->project_url);
1087 if ( project && (project->project_files_downloaded_time > selData->project_files_downloaded_time) ) {
1088 FindSlideShowFiles(selData);
1089 selData->project_files_downloaded_time = project->project_files_downloaded_time;
1090 }
1091 }
1092
1093 pDoc->GetCoreClientStatus(status);
1094
1095 count = m_TaskSelectionCtrl->GetCount();
1096 for(j = 0; j < count; ++j) {
1097 selData = (TaskSelectionData*)m_TaskSelectionCtrl->GetClientData(j);
1098 ctrlResult = selData->result;
1099 if (isRunning(ctrlResult)) {
1100 newIcon = runningIcon;
1101 } else if (Suspended() ||
1102 ctrlResult->suspended_via_gui ||
1103 ctrlResult->project_suspended_via_gui ||
1104 // kludge. But ctrlResult->avp isn't populated.
1105 (status.gpu_suspend_reason && (strstr(ctrlResult->resources, "GPU") != NULL)))
1106 {
1107 newIcon = suspendedIcon;
1108 } else {
1109 newIcon = waitingIcon;
1110 }
1111
1112 if (reskin || (newIcon != selData->dotColor)) {
1113 switch (newIcon) {
1114 case runningIcon:
1115 m_TaskSelectionCtrl->SetItemBitmap(j, *pSkinSimple->GetWorkunitRunningImage()->GetBitmap());
1116 break;
1117 case waitingIcon:
1118 m_TaskSelectionCtrl->SetItemBitmap(j, *pSkinSimple->GetWorkunitWaitingImage()->GetBitmap());
1119 break;
1120 case suspendedIcon:
1121 m_TaskSelectionCtrl->SetItemBitmap(j, *pSkinSimple->GetWorkunitSuspendedImage()->GetBitmap());
1122 break;
1123 }
1124 selData->dotColor = newIcon;
1125 needRefresh = true;
1126 }
1127 }
1128 if (needRefresh) {
1129 m_TaskSelectionCtrl->Refresh();
1130 }
1131
1132 bAlreadyRunning = false;
1133
1134 wxLogTrace(wxT("Function Start/End"), wxT("CSimpleTaskPanel::UpdateTaskSelectionList - Function End"));
1135 }
1136
1137
isRunning(RESULT * result)1138 bool CSimpleTaskPanel::isRunning(RESULT* result) {
1139
1140 // It must be scheduled to be running
1141 if ( result->scheduler_state != CPU_SCHED_SCHEDULED ) {
1142 return false;
1143 }
1144 // If either the project or task have been suspended, then it cannot be running
1145 if (result->suspended_via_gui || result->project_suspended_via_gui ) {
1146 return false;
1147 }
1148 CC_STATUS status;
1149 CMainDocument* pDoc = wxGetApp().GetDocument();
1150 wxASSERT(pDoc);
1151
1152 pDoc->GetCoreClientStatus(status);
1153 // Make sure that the core client isn't global suspended for some reason
1154 if (status.task_suspend_reason == 0 || status.task_suspend_reason == SUSPEND_REASON_CPU_THROTTLE) {
1155 return true;
1156 }
1157 if (result->active_task_state == PROCESS_EXECUTING) {
1158 return true;
1159 }
1160 return false;
1161 }
1162
1163
DownloadingResults()1164 bool CSimpleTaskPanel::DownloadingResults() {
1165 bool return_value = false;
1166 CMainDocument* pDoc = wxGetApp().GetDocument();
1167 wxASSERT(pDoc);
1168
1169 if ( pDoc->results.results.size() > 0 ) {
1170 RESULT* result;
1171 for(unsigned int i=0; !return_value && i < pDoc->results.results.size(); i++ ) {
1172 result = pDoc->result(i);
1173 if ( result != NULL && result->state == RESULT_FILES_DOWNLOADING ) {
1174 return_value = true;
1175 }
1176 }
1177 }
1178 return return_value;
1179 }
1180
Suspended()1181 bool CSimpleTaskPanel::Suspended() {
1182 CC_STATUS status;
1183 CMainDocument* pDoc = wxGetApp().GetDocument();
1184 wxASSERT(pDoc);
1185
1186 bool result = false;
1187 pDoc->GetCoreClientStatus(status);
1188 if ( pDoc->IsConnected() && status.task_suspend_reason > 0 && status.task_suspend_reason != SUSPEND_REASON_CPU_THROTTLE ) {
1189 result = true;
1190 }
1191 return result;
1192 }
1193
1194 // Check to see if a project update is scheduled or in progress
ProjectUpdateScheduled()1195 bool CSimpleTaskPanel::ProjectUpdateScheduled() {
1196 PROJECT* project;
1197 CMainDocument* pDoc = wxGetApp().GetDocument();
1198 wxASSERT(pDoc);
1199
1200 int prjCount = pDoc->GetSimpleProjectCount();
1201 for(int i=0; i<prjCount; i++) {
1202 project = pDoc->state.projects[i];
1203 if ( project->sched_rpc_pending || project->master_url_fetch_pending || project->scheduler_rpc_in_progress ) {
1204 return true;
1205 }
1206 }
1207 return false;
1208 }
1209
DisplayIdleState()1210 void CSimpleTaskPanel::DisplayIdleState() {
1211 CMainDocument* pDoc = wxGetApp().GetDocument();
1212 wxASSERT(pDoc);
1213
1214 if ( pDoc->IsReconnecting() ) {
1215 error_time = 0;
1216 UpdateStaticText(&m_StatusValueText, _("Retrieving current status."));
1217 } else if ( pDoc->IsConnected() && pDoc->state.projects.size() == 0) {
1218 error_time = 0;
1219 UpdateStaticText(&m_StatusValueText, m_sNoProjectsString);
1220 } else if ( DownloadingResults() ) {
1221 error_time = 0;
1222 UpdateStaticText(&m_StatusValueText, _("Downloading work from the server."));
1223 } else if ( Suspended() ) {
1224 CC_STATUS status;
1225 pDoc->GetCoreClientStatus(status);
1226 if ( status.task_suspend_reason & SUSPEND_REASON_BATTERIES ) {
1227 UpdateStaticText(&m_StatusValueText, _("Processing Suspended: Running On Batteries."));
1228 } else if ( status.task_suspend_reason & SUSPEND_REASON_USER_ACTIVE ) {
1229 UpdateStaticText(&m_StatusValueText, _("Processing Suspended: User Active."));
1230 } else if ( status.task_suspend_reason & SUSPEND_REASON_USER_REQ ) {
1231 UpdateStaticText(&m_StatusValueText, _("Processing Suspended: User paused processing."));
1232 } else if ( status.task_suspend_reason & SUSPEND_REASON_TIME_OF_DAY ) {
1233 UpdateStaticText(&m_StatusValueText, _("Processing Suspended: Time of Day."));
1234 } else if ( status.task_suspend_reason & SUSPEND_REASON_BENCHMARKS ) {
1235 UpdateStaticText(&m_StatusValueText, _("Processing Suspended: Benchmarks Running."));
1236 } else if ( status.task_suspend_reason & SUSPEND_REASON_DISK_SIZE ) {
1237 UpdateStaticText(&m_StatusValueText, _("Processing Suspended: need disk space."));
1238 } else {
1239 UpdateStaticText(&m_StatusValueText, _("Processing Suspended."));
1240 }
1241 } else if ( ProjectUpdateScheduled() ) {
1242 error_time = 0;
1243 UpdateStaticText(&m_StatusValueText, _("Waiting to contact project servers."));
1244 } else {
1245 if ( error_time == 0 ) {
1246 error_time = time(NULL) + 10;
1247 UpdateStaticText(&m_StatusValueText, _("Retrieving current status"));
1248 } else if ( error_time < time(NULL) ) {
1249 // TODO: should we display "ERROR" like old Simple GUI?
1250 if ( pDoc->IsConnected() ) {
1251 UpdateStaticText(&m_StatusValueText, _("No work available to process"));
1252 } else {
1253 UpdateStaticText(&m_StatusValueText, _("Unable to connect to the core client"));
1254 }
1255 } else {
1256 UpdateStaticText(&m_StatusValueText, _("Retrieving current status"));
1257 }
1258 }
1259 }
1260
1261
1262 #ifdef __WXMAC__
1263 // Avoid unnecessary drawing due to Mac progress indicator's animation
OnEraseBackground(wxEraseEvent & event)1264 void CSimpleTaskPanel::OnEraseBackground(wxEraseEvent& event) {
1265 wxRect clipRect;
1266 wxDC *dc = event.GetDC();
1267
1268 if (m_ProgressBar->IsShown()) {
1269 // if (m_progressBarRect == NULL) {
1270 m_progressBarRect = new wxRect(m_ProgressBar->GetRect());
1271 m_progressBarRect->Inflate(1, 0);
1272 // }
1273 dc->GetClippingBox(&clipRect.x, &clipRect.y, &clipRect.width, &clipRect.height);
1274 if (clipRect.IsEmpty() || m_progressBarRect->Contains(clipRect)) {
1275 return;
1276 }
1277 }
1278
1279 // CSimplePanelBase::OnEraseBackground(event);
1280 event.Skip();
1281 }
1282 #endif
1283