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("&lt;"), 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