mirror of https://github.com/BOINC/boinc.git
1275 lines
44 KiB
C++
1275 lines
44 KiB
C++
// This file is part of BOINC.
|
|
// http://boinc.berkeley.edu
|
|
// Copyright (C) 2018 University of California
|
|
//
|
|
// BOINC is free software; you can redistribute it and/or modify it
|
|
// under the terms of the GNU Lesser General Public License
|
|
// as published by the Free Software Foundation,
|
|
// either version 3 of the License, or (at your option) any later version.
|
|
//
|
|
// BOINC is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
// See the GNU Lesser General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Lesser General Public License
|
|
// along with BOINC. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
#include "stdwx.h"
|
|
#include "miofile.h"
|
|
#include "Events.h"
|
|
#include "BOINCGUIApp.h"
|
|
#include "SkinManager.h"
|
|
#include "MainDocument.h"
|
|
#include "sg_TaskCommandPopup.h"
|
|
#include "sg_TaskPanel.h"
|
|
#include "boinc_api.h"
|
|
#include "filesys.h"
|
|
#include "str_replace.h"
|
|
|
|
|
|
#define SORTTASKLIST 1 /* TRUE to sort task selection control alphabetically */
|
|
#define SLIDESHOWWIDTH ADJUSTFORXDPI(290)
|
|
#define SLIDESHOWHEIGHT ADJUSTFORYDPI(126)
|
|
#define SLIDESHOWBORDER 1
|
|
#define HIDEDEFAULTSLIDE 1
|
|
#define TESTALLDESCRIPTIONS 0
|
|
#define SCROLLBARSPACER 8
|
|
|
|
|
|
enum { suspendedIcon, waitingIcon, runningIcon };
|
|
|
|
|
|
IMPLEMENT_DYNAMIC_CLASS(CScrolledTextBox, wxScrolledWindow)
|
|
|
|
BEGIN_EVENT_TABLE(CScrolledTextBox, wxScrolledWindow)
|
|
EVT_ERASE_BACKGROUND(CScrolledTextBox::OnEraseBackground)
|
|
END_EVENT_TABLE()
|
|
|
|
CScrolledTextBox::CScrolledTextBox() {
|
|
}
|
|
|
|
|
|
CScrolledTextBox::CScrolledTextBox( wxWindow* parent) :
|
|
wxScrolledWindow( parent, ID_SGPROJECTDESCRIPTION, wxDefaultPosition, wxDefaultSize, wxVSCROLL)
|
|
{
|
|
SetForegroundColour(*wxBLACK);
|
|
|
|
m_TextSizer = new wxBoxSizer( wxVERTICAL );
|
|
m_hLine = GetCharHeight();
|
|
|
|
this->SetSizerAndFit( m_TextSizer );
|
|
this->Layout();
|
|
this->FitInside();
|
|
}
|
|
|
|
|
|
CScrolledTextBox::~CScrolledTextBox() {
|
|
// Delete sizer & its children (CTransparentStaticText objects)
|
|
m_TextSizer->Clear(true);
|
|
}
|
|
|
|
|
|
void CScrolledTextBox::SetValue(const wxString& s) {
|
|
int lineHeight, totalLines, totalWidth;
|
|
wxString t = s;
|
|
|
|
// Delete sizer & its children (CTransparentStaticText objects)
|
|
m_TextSizer->Clear(true);
|
|
|
|
// Change all occurrences of "<sup>n</sup>" to "^n"
|
|
t.Replace(wxT("<sup>"), wxT("^"), true);
|
|
t.Replace(wxT("</sup>"), wxT(""), true);
|
|
t.Replace(wxT("<"), wxT("<"), true);
|
|
|
|
// First see if it fits without vertical scroll bar
|
|
totalWidth = GetSize().GetWidth();
|
|
totalLines = Wrap(t, totalWidth, &lineHeight);
|
|
m_TextSizer->FitInside(this);
|
|
SetScrollRate(1, lineHeight);
|
|
int scrollLines = GetScrollLines(wxVERTICAL); // Returns 0 if no scrollbar
|
|
if (scrollLines > 0) {
|
|
int sbwidth = wxSystemSettings::GetMetric(wxSYS_VSCROLL_X);
|
|
// It has a vertical scroll bar, so wrap again for reduced width
|
|
m_TextSizer->Clear(true);
|
|
totalLines = Wrap(t, totalWidth - sbwidth - SCROLLBARSPACER, &lineHeight);
|
|
m_TextSizer->FitInside(this);
|
|
}
|
|
}
|
|
|
|
|
|
void CScrolledTextBox::OnEraseBackground(wxEraseEvent& event) {
|
|
wxDC *dc = event.GetDC();
|
|
wxPoint p = GetParent()->GetPosition();
|
|
wxRect r = GetRect();
|
|
r.Offset(p);
|
|
wxBitmap backgroundBitmap = ((CSimpleTaskPanel*)GetGrandParent())->GetBackgroundBmp().GetSubBitmap(r);
|
|
dc->DrawBitmap(backgroundBitmap, 0, 0);
|
|
}
|
|
|
|
|
|
// Text wrapping code adapted from wxWindows dlgcmn.cpp
|
|
bool CScrolledTextBox::IsStartOfNewLine() {
|
|
if ( !m_eol ) return false;
|
|
m_eol = false;
|
|
return true;
|
|
}
|
|
|
|
|
|
void CScrolledTextBox::OnOutputLine(const wxString& line) {
|
|
if ( !line.empty() ) {
|
|
m_TextSizer->Add(new CTransparentStaticText(this, wxID_ANY, line));
|
|
} else { // empty line, no need to create a control for it
|
|
m_TextSizer->Add(5, m_hLine/3);
|
|
}
|
|
}
|
|
|
|
|
|
// Returns the number of lines
|
|
int CScrolledTextBox::Wrap(const wxString& text, int widthMax, int *lineHeight) {
|
|
const wxChar *lastSpace = NULL;
|
|
wxString line;
|
|
int height = 0, numLines = 0;
|
|
|
|
const wxChar *lineStart = text.c_str();
|
|
for ( const wxChar *p = lineStart; ; p++ ) {
|
|
if ( IsStartOfNewLine() ) {
|
|
m_text += _T('\n');
|
|
|
|
lastSpace = NULL;
|
|
line.clear();
|
|
lineStart = p;
|
|
}
|
|
|
|
if ( *p == _T('\n') || *p == _T('\0') ) {
|
|
line.Trim();
|
|
OnOutputLine(line);
|
|
m_eol = true;
|
|
++numLines;
|
|
|
|
if ( *p == _T('\0') )
|
|
break;
|
|
} else { // not EOL
|
|
if ( *p == _T(' ') ) {
|
|
lastSpace = p;
|
|
}
|
|
line += *p;
|
|
|
|
if ( widthMax >= 0 && lastSpace ) {
|
|
int width;
|
|
GetTextExtent(line, &width, &height);
|
|
|
|
if ( width > widthMax ) {
|
|
// remove the last word from this line
|
|
line.erase(lastSpace - lineStart, p + 1 - lineStart);
|
|
line.Trim();
|
|
OnOutputLine(line);
|
|
m_eol = true;
|
|
++numLines;
|
|
|
|
// go back to the last word of this line which we didn't
|
|
// output yet
|
|
p = lastSpace;
|
|
}
|
|
}
|
|
//else: no wrapping at all or impossible to wrap
|
|
}
|
|
}
|
|
*lineHeight = height;
|
|
return numLines;
|
|
}
|
|
|
|
|
|
|
|
IMPLEMENT_DYNAMIC_CLASS(CSlideShowPanel, wxPanel)
|
|
|
|
BEGIN_EVENT_TABLE(CSlideShowPanel, wxPanel)
|
|
EVT_ERASE_BACKGROUND(CSlideShowPanel::OnEraseBackground)
|
|
EVT_TIMER(ID_CHANGE_SLIDE_TIMER, CSlideShowPanel::OnSlideShowTimer)
|
|
EVT_PAINT(CSlideShowPanel::OnPaint)
|
|
END_EVENT_TABLE()
|
|
|
|
CSlideShowPanel::CSlideShowPanel() {
|
|
}
|
|
|
|
|
|
CSlideShowPanel::CSlideShowPanel( wxWindow* parent ) :
|
|
wxPanel( parent, wxID_ANY, wxDefaultPosition,
|
|
wxSize(SLIDESHOWWIDTH+(2*SLIDESHOWBORDER),
|
|
SLIDESHOWHEIGHT+(2*SLIDESHOWBORDER)), wxBORDER_NONE )
|
|
{
|
|
int w, h;
|
|
wxBoxSizer* bSizer1;
|
|
bSizer1 = new wxBoxSizer( wxVERTICAL );
|
|
|
|
m_description = new CScrolledTextBox( this );
|
|
GetSize(&w, &h);
|
|
m_description->SetMinSize(wxSize(w, h));
|
|
bSizer1->Add( m_description, 1, wxEXPAND, 0 );
|
|
|
|
this->SetSizer( bSizer1 );
|
|
this->Layout();
|
|
|
|
m_SlideBitmap = wxNullBitmap;
|
|
m_bCurrentSlideIsDefault = false;
|
|
m_bGotAllProjectsList = false;
|
|
m_bHasBeenDrawn = false;
|
|
|
|
m_ChangeSlideTimer = new wxTimer(this, ID_CHANGE_SLIDE_TIMER);
|
|
m_ChangeSlideTimer->Start(10000);
|
|
}
|
|
|
|
CSlideShowPanel::~CSlideShowPanel()
|
|
{
|
|
if ( m_ChangeSlideTimer->IsRunning() ) {
|
|
m_ChangeSlideTimer->Stop();
|
|
}
|
|
delete m_ChangeSlideTimer;
|
|
}
|
|
|
|
|
|
void CSlideShowPanel::OnSlideShowTimer(wxTimerEvent& WXUNUSED(event)) {
|
|
AdvanceSlideShow(true, false);
|
|
}
|
|
|
|
void CSlideShowPanel::SetDescriptionText(void) {
|
|
unsigned int i;
|
|
wxString s, ss;
|
|
|
|
TaskSelectionData* selData = ((CSimpleTaskPanel*)GetParent())->GetTaskSelectionData();
|
|
if (selData == NULL) return;
|
|
for (i=0; i<m_AllProjectsList.projects.size(); i++) {
|
|
if (!strcmp(m_AllProjectsList.projects[i]->url.c_str(), selData->project_url)) {
|
|
s = wxString(m_AllProjectsList.projects[i]->home.c_str(), wxConvUTF8);
|
|
ss = wxGetTranslation(s);
|
|
ss.Append("\n\n");
|
|
s = wxString(m_AllProjectsList.projects[i]->specific_area.c_str(), wxConvUTF8);
|
|
ss += wxGetTranslation(s);
|
|
ss.Append("\n\n");
|
|
s = wxString(m_AllProjectsList.projects[i]->description.c_str(), wxConvUTF8);
|
|
ss += wxGetTranslation(s);
|
|
m_description->SetValue(ss);
|
|
|
|
m_description->Show(true);
|
|
Enable( true );
|
|
m_description->Enable();
|
|
this->Layout();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void CSlideShowPanel::AdvanceSlideShow(bool changeSlide, bool reload) {
|
|
double xRatio, yRatio, ratio;
|
|
TaskSelectionData* selData = ((CSimpleTaskPanel*)GetParent())->GetTaskSelectionData();
|
|
if (selData == NULL) return;
|
|
|
|
if (reload) {
|
|
m_bCurrentSlideIsDefault = false;
|
|
selData->lastSlideShown = -1;
|
|
}
|
|
|
|
int numSlides = (int)selData->slideShowFileNames.size();
|
|
#if TESTALLDESCRIPTIONS // For testing
|
|
numSlides = 0;
|
|
#endif
|
|
if (numSlides <= 0) {
|
|
#if HIDEDEFAULTSLIDE
|
|
if (!reload) {
|
|
return;
|
|
}
|
|
wxRect r = GetRect();
|
|
wxBitmap backgroundBitmap = ((CSimpleTaskPanel*)GetParent())->GetBackgroundBmp().GetSubBitmap(r);
|
|
wxWindowDC dc(this);
|
|
dc.DrawBitmap(backgroundBitmap, 0, 0);
|
|
|
|
// Force redraws if text unchanged; hide all if not in all-projects list
|
|
m_description->Show(false);
|
|
Enable( false );
|
|
|
|
if (!m_bGotAllProjectsList) {
|
|
CMainDocument* pDoc = wxGetApp().GetDocument();
|
|
wxASSERT(pDoc);
|
|
|
|
pDoc->rpc.get_all_projects_list(m_AllProjectsList);
|
|
m_bGotAllProjectsList = true;
|
|
}
|
|
|
|
SetDescriptionText();
|
|
|
|
return;
|
|
#else // HIDEDEFAULTSLIDE
|
|
SetBackgroundColour(*wxBLACK);
|
|
|
|
if (m_bCurrentSlideIsDefault) return;
|
|
|
|
CSkinSimple* pSkinSimple = wxGetApp().GetSkinManager()->GetSimple();
|
|
wxASSERT(pSkinSimple);
|
|
wxASSERT(wxDynamicCast(pSkinSimple, CSkinSimple));
|
|
|
|
m_SlideBitmap = *pSkinSimple->GetWorkunitAnimationImage()->GetBitmap();
|
|
if (m_SlideBitmap.Ok()) {
|
|
m_bCurrentSlideIsDefault = true;
|
|
}
|
|
#endif // HIDEDEFAULTSLIDE
|
|
} else {
|
|
#if HIDEDEFAULTSLIDE
|
|
m_description->Show(false);
|
|
Enable( false );
|
|
|
|
#endif // HIDEDEFAULTSLIDE
|
|
int newSlide = selData->lastSlideShown;
|
|
|
|
if (changeSlide) {
|
|
if (++newSlide >= numSlides) {
|
|
newSlide = 0;
|
|
}
|
|
}
|
|
if (newSlide < 0) {
|
|
newSlide = 0;
|
|
}
|
|
|
|
if (selData->lastSlideShown != newSlide) { // Don't update if only one slide
|
|
|
|
selData->lastSlideShown = newSlide;
|
|
|
|
wxBitmap *bm = new wxBitmap();
|
|
bm->LoadFile(selData->slideShowFileNames[newSlide], wxBITMAP_TYPE_ANY);
|
|
if (bm->Ok()) {
|
|
m_SlideBitmap = *bm;
|
|
delete bm;
|
|
m_bCurrentSlideIsDefault = false;
|
|
}
|
|
}
|
|
}
|
|
if (m_SlideBitmap.Ok()) {
|
|
// Check to see if they need to be rescaled to fit in the window
|
|
ratio = 1.0;
|
|
xRatio = (double)SLIDESHOWWIDTH / (double)m_SlideBitmap.GetWidth();
|
|
yRatio = (double)SLIDESHOWHEIGHT / (double)m_SlideBitmap.GetHeight();
|
|
ratio = xRatio;
|
|
if ( yRatio < ratio ) {
|
|
ratio = yRatio;
|
|
}
|
|
if ( (ratio < 0.95) || (ratio > 1.05) ) {
|
|
wxImage img = m_SlideBitmap.ConvertToImage();
|
|
img.Rescale((int) (m_SlideBitmap.GetWidth()*ratio),
|
|
(int) (m_SlideBitmap.GetHeight()*ratio),
|
|
(ratio > 1.0) ? wxIMAGE_QUALITY_BILINEAR : wxIMAGE_QUALITY_BOX_AVERAGE
|
|
);
|
|
wxBitmap *bm = new wxBitmap(img);
|
|
m_SlideBitmap = *bm;
|
|
delete bm;
|
|
}
|
|
|
|
Refresh();
|
|
}
|
|
}
|
|
|
|
|
|
void CSlideShowPanel::OnPaint(wxPaintEvent& WXUNUSED(event))
|
|
{
|
|
wxPaintDC dc(this);
|
|
#if HIDEDEFAULTSLIDE
|
|
int numSlides = 0;
|
|
TaskSelectionData* selData = ((CSimpleTaskPanel*)GetParent())->GetTaskSelectionData();
|
|
if (selData) {
|
|
numSlides = (int)selData->slideShowFileNames.size();
|
|
}
|
|
#if TESTALLDESCRIPTIONS // For testing
|
|
numSlides = 0;
|
|
#endif // TESTALLDESCRIPTIONS
|
|
|
|
if (numSlides > 0)
|
|
#endif // HIDEDEFAULTSLIDE
|
|
{
|
|
int w, h, x;
|
|
wxPen oldPen = dc.GetPen();
|
|
wxBrush oldBrush = dc.GetBrush();
|
|
int oldMode = dc.GetBackgroundMode();
|
|
wxPen bgPen(*wxBLACK, 2*SLIDESHOWBORDER+1);
|
|
dc.SetBackgroundMode(wxSOLID);
|
|
dc.SetPen(bgPen);
|
|
dc.SetBrush(*wxBLACK_BRUSH);
|
|
|
|
GetSize(&w, &h);
|
|
x = (w - SLIDESHOWWIDTH) / 2;
|
|
dc.DrawRectangle(x, SLIDESHOWBORDER, SLIDESHOWWIDTH, SLIDESHOWHEIGHT);
|
|
// Restore Mode, Pen and Brush
|
|
dc.SetBackgroundMode(oldMode);
|
|
dc.SetPen(oldPen);
|
|
dc.SetBrush(oldBrush);
|
|
|
|
if(m_SlideBitmap.Ok())
|
|
{
|
|
dc.DrawBitmap(m_SlideBitmap,
|
|
(w - m_SlideBitmap.GetWidth())/2,
|
|
(h - m_SlideBitmap.GetHeight())/2
|
|
);
|
|
}
|
|
}
|
|
|
|
if (!m_bHasBeenDrawn) {
|
|
m_bHasBeenDrawn = true;
|
|
if (numSlides <= 0) {
|
|
SetDescriptionText();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void CSlideShowPanel::OnEraseBackground(wxEraseEvent& event) {
|
|
wxDC *dc = event.GetDC();
|
|
wxRect r = GetRect();
|
|
wxBitmap backgroundBitmap = ((CSimpleTaskPanel*)GetParent())->GetBackgroundBmp().GetSubBitmap(r);
|
|
dc->DrawBitmap(backgroundBitmap, 0, 0);
|
|
}
|
|
|
|
|
|
|
|
|
|
IMPLEMENT_DYNAMIC_CLASS(CSimpleTaskPanel, CSimplePanelBase)
|
|
|
|
BEGIN_EVENT_TABLE(CSimpleTaskPanel, CSimplePanelBase)
|
|
#ifdef __WXMAC__
|
|
EVT_CHOICE(ID_SGTASKSELECTOR, CSimpleTaskPanel::OnTaskSelection)
|
|
#else
|
|
EVT_COMBOBOX(ID_SGTASKSELECTOR, CSimpleTaskPanel::OnTaskSelection)
|
|
#if 0 // This is apparently no longer needed with wxCocoa 3.0
|
|
EVT_ERASE_BACKGROUND(CSimpleTaskPanel::OnEraseBackground)
|
|
#endif
|
|
#endif
|
|
END_EVENT_TABLE()
|
|
|
|
CSimpleTaskPanel::CSimpleTaskPanel() {
|
|
}
|
|
|
|
|
|
CSimpleTaskPanel::CSimpleTaskPanel( wxWindow* parent ) :
|
|
CSimplePanelBase( parent )
|
|
{
|
|
wxSize sz;
|
|
int w, h;
|
|
wxString str = wxEmptyString;
|
|
|
|
m_oldWorkCount = -1;
|
|
error_time = 0;
|
|
m_GotBGBitMap = false; // Can't be made until parent has been laid out.
|
|
m_bStableTaskInfoChanged = false;
|
|
m_CurrentTaskSelection = -1;
|
|
m_sNoProjectsString = _("You don't have any projects. Please Add a Project.");
|
|
m_sNotAvailableString = _("Not available");
|
|
m_progressBarRect = NULL;
|
|
|
|
SetForegroundColour(*wxBLACK);
|
|
|
|
wxBoxSizer* bSizer1;
|
|
bSizer1 = new wxBoxSizer( wxVERTICAL );
|
|
|
|
wxBoxSizer* bSizer2;
|
|
bSizer2 = new wxBoxSizer( wxHORIZONTAL );
|
|
|
|
m_myTasksLabel = new CTransparentStaticText( this, wxID_ANY, _("Tasks:"), wxDefaultPosition, wxDefaultSize, 0 );
|
|
m_myTasksLabel->Wrap( -1 );
|
|
bSizer2->Add( m_myTasksLabel, 0, wxRIGHT, ADJUSTFORXDPI(5) );
|
|
|
|
m_TaskSelectionCtrl = new CBOINCBitmapComboBox( this, ID_SGTASKSELECTOR, wxT(""), wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_READONLY );
|
|
// TODO: Might want better wording for Task Selection Combo Box tooltip
|
|
str = _("Select a task to access");
|
|
m_TaskSelectionCtrl->SetToolTip(str);
|
|
bSizer2->Add( m_TaskSelectionCtrl, 1, wxRIGHT | wxEXPAND, SIDEMARGINS );
|
|
|
|
bSizer1->Add( bSizer2, 0, wxEXPAND | wxTOP | wxLEFT, ADJUSTFORXDPI(10) );
|
|
|
|
bSizer1->AddSpacer(ADJUSTFORYDPI(5));
|
|
|
|
wxBoxSizer* bSizer3;
|
|
bSizer3 = new wxBoxSizer( wxHORIZONTAL );
|
|
|
|
// what project the task is from, e.g. "From: SETI@home"
|
|
m_TaskProjectLabel = new CTransparentStaticText( this, wxID_ANY, _("From:"), wxDefaultPosition, wxDefaultSize, 0 );
|
|
m_TaskProjectLabel->Wrap( -1 );
|
|
bSizer3->Add( m_TaskProjectLabel, 0, wxRIGHT, ADJUSTFORXDPI(5) );
|
|
|
|
m_TaskProjectName = new CTransparentStaticText( this, wxID_ANY, wxT("SETI@home"), wxDefaultPosition, wxDefaultSize, wxST_NO_AUTORESIZE );
|
|
m_TaskProjectName->Wrap( -1 );
|
|
wxFont theFont = m_TaskProjectName->GetFont();
|
|
theFont.SetWeight(wxFONTWEIGHT_BOLD);
|
|
m_TaskProjectName->SetFont(theFont);
|
|
bSizer3->Add( m_TaskProjectName, 1, 0, 0 );
|
|
|
|
bSizer1->Add( bSizer3, 0, wxLEFT | wxRIGHT | wxEXPAND, SIDEMARGINS );
|
|
|
|
#if SELECTBYRESULTNAME
|
|
m_TaskApplicationName = new CTransparentStaticText( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxST_NO_AUTORESIZE );
|
|
m_TaskApplicationName->Wrap( -1 );
|
|
|
|
bSizer1->Add( m_TaskApplicationName, 0, wxLEFT | wxRIGHT | wxEXPAND, SIDEMARGINS );
|
|
#endif // SELECTBYRESULTNAME
|
|
|
|
bSizer1->AddSpacer(ADJUSTFORYDPI(10));
|
|
|
|
m_SlideShowArea = new CSlideShowPanel(this);
|
|
m_SlideShowArea->SetMinSize(wxSize(SLIDESHOWWIDTH+(2*SLIDESHOWBORDER), SLIDESHOWHEIGHT+(2*SLIDESHOWBORDER)));
|
|
m_SlideShowArea->Enable( false );
|
|
|
|
bSizer1->Add( m_SlideShowArea, 0, wxLEFT | wxRIGHT | wxEXPAND, SIDEMARGINS );
|
|
|
|
bSizer1->AddSpacer(ADJUSTFORYDPI(10));
|
|
|
|
m_ElapsedTimeValue = new CTransparentStaticText( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxST_NO_AUTORESIZE );
|
|
m_ElapsedTimeValue->Wrap( -1 );
|
|
bSizer1->Add( m_ElapsedTimeValue, 0, wxLEFT | wxRIGHT | wxEXPAND, SIDEMARGINS );
|
|
|
|
bSizer1->AddSpacer(ADJUSTFORYDPI(7));
|
|
|
|
m_TimeRemainingValue = new CTransparentStaticText( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxST_NO_AUTORESIZE );
|
|
m_TimeRemainingValue->Wrap( -1 );
|
|
bSizer1->Add( m_TimeRemainingValue, 0, wxLEFT | wxRIGHT | wxEXPAND, SIDEMARGINS );
|
|
|
|
bSizer1->AddSpacer(ADJUSTFORYDPI(7));
|
|
|
|
wxBoxSizer* bSizer4;
|
|
bSizer4 = new wxBoxSizer( wxHORIZONTAL );
|
|
|
|
// TODO: Standard Mac progress indicator's animation uses lots of CPU
|
|
// time, and also triggers unnecessary Erase events. Should we use a
|
|
// non-standard progress indicator on Mac? See also optimizations in
|
|
// CSimpleGUIPanel::OnEraseBackground and CSimpleTaskPanel::OnEraseBackground.
|
|
m_ProgressBar = new wxGauge( this, wxID_ANY, 100, wxDefaultPosition, wxDefaultSize, wxGA_HORIZONTAL );
|
|
m_ipctDoneX1000 = 100000;
|
|
m_ProgressBar->SetValue( 100 );
|
|
GetTextExtent(wxT("0"), &w, &h);
|
|
m_ProgressBar->SetMinSize(wxSize(ADJUSTFORXDPI(245), h));
|
|
m_ProgressBar->SetToolTip(_("This task's progress"));
|
|
bSizer4->Add( m_ProgressBar, 0, wxRIGHT, ADJUSTFORXDPI(5) );
|
|
|
|
m_ProgressValueText = new CTransparentStaticText( this, wxID_ANY, wxT("100.000%"), wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT | wxST_NO_AUTORESIZE );
|
|
m_ProgressValueText->Wrap( -1 );
|
|
bSizer4->Add( m_ProgressValueText, 0, wxALIGN_LEFT, 0 );
|
|
|
|
bSizer1->Add( bSizer4, 0, wxLEFT | wxRIGHT | wxEXPAND, SIDEMARGINS );
|
|
|
|
bSizer1->AddSpacer(ADJUSTFORYDPI(7));
|
|
|
|
// TODO: Can we determine the longest status string and initialize with it?
|
|
m_StatusValueText = new CTransparentStaticText( this, wxID_ANY, m_sNoProjectsString, wxDefaultPosition, wxDefaultSize, wxST_NO_AUTORESIZE );
|
|
m_StatusValueText->Wrap( -1 );
|
|
bSizer1->Add( m_StatusValueText, 0, wxLEFT | wxRIGHT | wxEXPAND, SIDEMARGINS );
|
|
|
|
bSizer1->AddSpacer(ADJUSTFORYDPI(7));
|
|
|
|
m_TaskCommandsButton = new CSimpleTaskPopupButton( this, ID_TASKSCOMMANDBUTTON, _("Task Commands"), wxDefaultPosition, wxDefaultSize, 0 );
|
|
m_TaskCommandsButton->SetToolTip(_("Pop up a menu of commands to apply to this task"));
|
|
bSizer1->Add( m_TaskCommandsButton, 0, wxLEFT | wxRIGHT | wxEXPAND, SIDEMARGINS );
|
|
|
|
bSizer1->AddSpacer(ADJUSTFORYDPI(10));
|
|
|
|
this->SetSizer( bSizer1 );
|
|
this->Layout();
|
|
|
|
m_ProgressRect = m_ProgressBar->GetRect();
|
|
#ifdef __WXMAC__
|
|
m_ProgressRect.Inflate(0, -2);
|
|
m_ProgressRect.Offset(0, -2);
|
|
#endif
|
|
}
|
|
|
|
|
|
CSimpleTaskPanel::~CSimpleTaskPanel()
|
|
{
|
|
TaskSelectionData *selData;
|
|
int count = m_TaskSelectionCtrl->GetCount();
|
|
for(int j = count-1; j >=0; --j) {
|
|
selData = (TaskSelectionData*)m_TaskSelectionCtrl->GetClientData(j);
|
|
selData->slideShowFileNames.Clear();
|
|
delete selData;
|
|
// Indicate to Clear() we have cleaned up the Selection Data
|
|
m_TaskSelectionCtrl->SetClientData(j, NULL);
|
|
}
|
|
m_TaskSelectionCtrl->Clear();
|
|
|
|
if (m_progressBarRect) {
|
|
delete m_progressBarRect;
|
|
}
|
|
}
|
|
|
|
|
|
void CSimpleTaskPanel::OnTaskSelection(wxCommandEvent& /*event*/)
|
|
{
|
|
int sel = m_TaskSelectionCtrl->GetSelection();
|
|
if (sel != m_CurrentTaskSelection) {
|
|
m_CurrentTaskSelection = sel;
|
|
m_bStableTaskInfoChanged = true;
|
|
UpdatePanel();
|
|
}
|
|
}
|
|
|
|
|
|
void CSimpleTaskPanel::UpdatePanel(bool delayShow) {
|
|
wxString s = wxEmptyString;
|
|
wxString projName = wxEmptyString;
|
|
TaskSelectionData *selData;
|
|
CMainDocument* pDoc = wxGetApp().GetDocument();
|
|
wxASSERT(pDoc);
|
|
int workCount = pDoc->GetSimpleGUIWorkCount();
|
|
|
|
// Workaround for Linux refresh problem
|
|
static bool wasDelayed = false;
|
|
|
|
#ifndef __WXMAC__
|
|
Freeze();
|
|
#endif
|
|
|
|
if ((workCount <= 0) || delayShow) {
|
|
if ((workCount != m_oldWorkCount) || delayShow) {
|
|
wasDelayed = true;
|
|
m_myTasksLabel->Hide();
|
|
m_TaskSelectionCtrl->Hide();
|
|
m_TaskProjectLabel->Hide();
|
|
m_TaskProjectName->Hide();
|
|
#if SELECTBYRESULTNAME
|
|
m_TaskApplicationName->Hide();
|
|
#endif // SELECTBYRESULTNAME
|
|
m_SlideShowArea->Hide();
|
|
m_ElapsedTimeValue->Hide();
|
|
m_TimeRemainingValue->Hide();
|
|
if (m_ipctDoneX1000 >= 0) {
|
|
m_ipctDoneX1000 = -1;
|
|
m_ProgressBar->Hide();
|
|
}
|
|
m_ProgressValueText->Hide();
|
|
m_TaskCommandsButton->Hide();
|
|
this->Layout();
|
|
|
|
#ifdef __WXMAC__
|
|
m_ProgressRect = m_ProgressBar->GetRect();
|
|
m_ProgressRect.Inflate(0, -2);
|
|
m_ProgressRect.Offset(0, -2);
|
|
#endif
|
|
}
|
|
|
|
DisplayIdleState();
|
|
|
|
} else {
|
|
if ((m_oldWorkCount == 0) || wasDelayed) {
|
|
wasDelayed = false;
|
|
m_myTasksLabel->Show();
|
|
m_TaskSelectionCtrl->Show();
|
|
m_TaskProjectLabel->Show();
|
|
m_TaskProjectName->Show();
|
|
#if SELECTBYRESULTNAME
|
|
m_TaskApplicationName->Show();
|
|
#endif // SELECTBYRESULTNAME
|
|
m_SlideShowArea->Show();
|
|
m_ElapsedTimeValue->Show();
|
|
m_TimeRemainingValue->Show();
|
|
m_ProgressBar->Show();
|
|
m_ProgressValueText->Show();
|
|
m_TaskCommandsButton->Show();
|
|
this->Layout();
|
|
|
|
#ifdef __WXMAC__
|
|
m_ProgressRect = m_ProgressBar->GetRect();
|
|
m_ProgressRect.Inflate(0, -2);
|
|
m_ProgressRect.Offset(0, -2);
|
|
#endif
|
|
}
|
|
|
|
UpdateTaskSelectionList(false);
|
|
|
|
// We now have valid result pointers, so extract our data
|
|
int count = m_TaskSelectionCtrl->GetCount();
|
|
if (count <= 0) {
|
|
m_CurrentTaskSelection = -1;
|
|
} else {
|
|
if ((m_CurrentTaskSelection < 0) || (m_CurrentTaskSelection > count -1)) {
|
|
m_TaskSelectionCtrl->SetSelection(0);
|
|
m_CurrentTaskSelection = 0;
|
|
m_bStableTaskInfoChanged = true;
|
|
}
|
|
selData = (TaskSelectionData*)m_TaskSelectionCtrl->GetClientData(m_CurrentTaskSelection);
|
|
RESULT* result = selData->result;
|
|
if (result) {
|
|
if (m_bStableTaskInfoChanged) {
|
|
#if SELECTBYRESULTNAME
|
|
wxString str = wxEmptyString;
|
|
GetApplicationAndProjectNames(result, &s, &projName);
|
|
str.Printf(_("Application: %s"), s.c_str());
|
|
UpdateStaticText(&m_TaskApplicationName, str);
|
|
UpdateStaticText(&m_TaskProjectName, projName);
|
|
#else // SELECTBYRESULTNAME
|
|
GetApplicationAndProjectNames(result, NULL, &projName);
|
|
#endif // SELECTBYRESULTNAME
|
|
UpdateStaticText(&m_TaskProjectName, projName);
|
|
m_SlideShowArea->AdvanceSlideShow(false, true);
|
|
m_bStableTaskInfoChanged = false;
|
|
}
|
|
double f = result->elapsed_time;
|
|
if (f == 0.) f = result->current_cpu_time;
|
|
// f = result->final_elapsed_time;
|
|
UpdateStaticText(&m_ElapsedTimeValue, GetElapsedTimeString(f));
|
|
UpdateStaticText(&m_TimeRemainingValue, GetTimeRemainingString(result->estimated_cpu_time_remaining));
|
|
// fraction_done ranges from 0.0 to 1.0 so % done = fraction_done * 100.
|
|
int pctDoneX1000 = result->fraction_done * 100000.0;
|
|
// Update progress only if visible part has changed (xx.xxx)
|
|
if (m_ipctDoneX1000 != pctDoneX1000) {
|
|
int pctDone = pctDoneX1000 / 1000;
|
|
if (m_ipctDoneX1000 != (pctDone * 1000)) {
|
|
m_ProgressBar->SetValue(pctDone);
|
|
}
|
|
s.Printf(_("%.3f%%"), result->fraction_done*100);
|
|
m_ipctDoneX1000 = pctDoneX1000;
|
|
UpdateStaticText(&m_ProgressValueText, s);
|
|
}
|
|
UpdateStaticText(&m_StatusValueText, GetStatusString(result));
|
|
} else {
|
|
UpdateStaticText(&m_TaskProjectName, m_sNotAvailableString);
|
|
#if SELECTBYRESULTNAME
|
|
UpdateStaticText(&m_TaskApplicationName, _("Application: Not available") );
|
|
#endif // SELECTBYRESULTNAME
|
|
UpdateStaticText(&m_ElapsedTimeValue, GetElapsedTimeString(-1.0));
|
|
UpdateStaticText(&m_TimeRemainingValue, GetTimeRemainingString(-1.0));
|
|
if (m_ipctDoneX1000 >= 0) {
|
|
m_ipctDoneX1000 = -1;
|
|
m_ProgressBar->Hide();
|
|
}
|
|
UpdateStaticText(&m_ProgressValueText, wxEmptyString);
|
|
UpdateStaticText(&m_StatusValueText, GetStatusString(NULL));
|
|
}
|
|
}
|
|
}
|
|
m_oldWorkCount = workCount;
|
|
|
|
#ifndef __WXMAC__
|
|
Thaw();
|
|
#endif
|
|
}
|
|
|
|
|
|
wxRect* CSimpleTaskPanel::GetProgressRect() {
|
|
if (m_ProgressBar->IsShown()) {
|
|
return &m_ProgressRect;
|
|
} else {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
void CSimpleTaskPanel::ReskinInterface() {
|
|
wxLogTrace(wxT("Function Start/End"), wxT("CSimpleTaskPanel::ReskinInterface - Function Begin"));
|
|
CSimplePanelBase::ReskinInterface();
|
|
m_SlideShowArea->AdvanceSlideShow(false, false);
|
|
UpdateTaskSelectionList(true);
|
|
wxLogTrace(wxT("Function Start/End"), wxT("CSimpleTaskPanel::ReskinInterface - Function Begin"));
|
|
}
|
|
|
|
|
|
TaskSelectionData* CSimpleTaskPanel::GetTaskSelectionData() {
|
|
int count = m_TaskSelectionCtrl->GetCount();
|
|
if (count <= 0) {
|
|
return NULL;
|
|
}
|
|
|
|
int n = m_TaskSelectionCtrl->GetSelection();
|
|
return (TaskSelectionData*)m_TaskSelectionCtrl->GetClientData(n);
|
|
}
|
|
|
|
|
|
// Either appName argument or projName argument may be NULL
|
|
void CSimpleTaskPanel::GetApplicationAndProjectNames(RESULT* result, wxString* appName, wxString* projName) {
|
|
CMainDocument* pDoc = wxGetApp().GetDocument();
|
|
RESULT* state_result = NULL;
|
|
wxString strAppBuffer = wxEmptyString;
|
|
wxString strGPUBuffer = wxEmptyString;
|
|
|
|
wxASSERT(pDoc);
|
|
wxASSERT(wxDynamicCast(pDoc, CMainDocument));
|
|
|
|
state_result = pDoc->state.lookup_result(result->project_url, result->name);
|
|
if (!state_result) {
|
|
pDoc->ForceCacheUpdate();
|
|
state_result = pDoc->state.lookup_result(result->project_url, result->name);
|
|
}
|
|
|
|
if (!state_result) return;
|
|
|
|
if (appName != NULL) {
|
|
WORKUNIT* wup = state_result->wup;
|
|
if (!wup) return;
|
|
APP* app = wup->app;
|
|
if (!app) return;
|
|
APP_VERSION* avp = state_result->avp;
|
|
if (!avp) return;
|
|
|
|
if (strlen(app->user_friendly_name)) {
|
|
strAppBuffer = wxString(state_result->app->user_friendly_name, wxConvUTF8);
|
|
} else {
|
|
strAppBuffer = wxString(state_result->avp->app_name, wxConvUTF8);
|
|
}
|
|
|
|
if (avp->gpu_type) {
|
|
strGPUBuffer.Printf(
|
|
wxT(" (%s)"),
|
|
wxString(proc_type_name(avp->gpu_type), wxConvUTF8).c_str()
|
|
);
|
|
}
|
|
|
|
appName->Printf(
|
|
wxT("%s%s%s"),
|
|
state_result->project->anonymous_platform?_("Local: "):wxT(""),
|
|
strAppBuffer.c_str(),
|
|
strGPUBuffer.c_str()
|
|
);
|
|
}
|
|
|
|
if (projName != NULL) {
|
|
*projName = wxString(state_result->project->project_name.c_str(), wxConvUTF8 );
|
|
if (projName->IsEmpty()) {
|
|
*projName = _("Not Available");
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
wxString CSimpleTaskPanel::GetElapsedTimeString(double f) {
|
|
wxString s = wxEmptyString;
|
|
wxString str = wxEmptyString;
|
|
|
|
if (f < 0) {
|
|
s = m_sNotAvailableString;
|
|
} else {
|
|
s = FormatTime(f);
|
|
}
|
|
str.Printf(_("Elapsed: %s"), s.c_str());
|
|
return str;
|
|
}
|
|
|
|
|
|
wxString CSimpleTaskPanel::GetTimeRemainingString(double f) {
|
|
wxString s = wxEmptyString;
|
|
wxString str = wxEmptyString;
|
|
|
|
if (f < 0) {
|
|
s = m_sNotAvailableString;
|
|
} else {
|
|
s = FormatTime(f);
|
|
}
|
|
str.Printf(_("Remaining (estimated): %s"), s.c_str());
|
|
return str;
|
|
}
|
|
|
|
|
|
wxString CSimpleTaskPanel::GetStatusString(RESULT* result) {
|
|
wxString s = wxEmptyString;
|
|
wxString str = wxEmptyString;
|
|
|
|
if (result == NULL) {
|
|
s = m_sNotAvailableString;
|
|
} else {
|
|
s = result_description(result, false);
|
|
}
|
|
|
|
str.Printf(_("Status: %s"), s.c_str());
|
|
return str;
|
|
}
|
|
|
|
void CSimpleTaskPanel::FindSlideShowFiles(TaskSelectionData *selData) {
|
|
RESULT* state_result;
|
|
char proj_dir[1024];
|
|
char fileName[1024];
|
|
char resolvedFileName[1024];
|
|
int j;
|
|
CMainDocument* pDoc = wxGetApp().GetDocument();
|
|
wxASSERT(pDoc);
|
|
|
|
|
|
selData->slideShowFileNames.Clear();
|
|
state_result = pDoc->state.lookup_result(selData->result->project_url, selData->result->name);
|
|
if (!state_result) {
|
|
pDoc->ForceCacheUpdate();
|
|
state_result = pDoc->state.lookup_result(selData->result->project_url, selData->result->name);
|
|
}
|
|
if (state_result) {
|
|
url_to_project_dir(state_result->project->master_url, proj_dir, sizeof(proj_dir));
|
|
for(j=0; j<99; ++j) {
|
|
snprintf(fileName, sizeof(fileName), "%s/slideshow_%s_%02d", proj_dir, state_result->app->name, j);
|
|
if(boinc_resolve_filename(fileName, resolvedFileName, sizeof(resolvedFileName)) == 0) {
|
|
if (boinc_file_exists(resolvedFileName)) {
|
|
selData->slideShowFileNames.Add(wxString(resolvedFileName,wxConvUTF8));
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( selData->slideShowFileNames.size() == 0 ) {
|
|
for(j=0; j<99; ++j) {
|
|
snprintf(fileName, sizeof(fileName), "%s/slideshow_%02d", proj_dir, j);
|
|
if(boinc_resolve_filename(fileName, resolvedFileName, sizeof(resolvedFileName)) == 0) {
|
|
if (boinc_file_exists(resolvedFileName)) {
|
|
selData->slideShowFileNames.Add(wxString(resolvedFileName,wxConvUTF8));
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
selData->lastSlideShown = -1;
|
|
}
|
|
|
|
|
|
void CSimpleTaskPanel::UpdateTaskSelectionList(bool reskin) {
|
|
wxLogTrace(wxT("Function Start/End"), wxT("CSimpleTaskPanel::UpdateTaskSelectionList - Function Begin"));
|
|
int i, j, count, newIcon;
|
|
TaskSelectionData *selData;
|
|
RESULT* result;
|
|
RESULT* ctrlResult;
|
|
PROJECT* project;
|
|
std::vector<bool>is_alive;
|
|
bool needRefresh = false;
|
|
wxString resname;
|
|
CC_STATUS status;
|
|
CMainDocument* pDoc = wxGetApp().GetDocument();
|
|
CSkinSimple* pSkinSimple = wxGetApp().GetSkinManager()->GetSimple();
|
|
wxASSERT(pDoc);
|
|
|
|
static bool bAlreadyRunning = false;
|
|
|
|
wxASSERT(pDoc);
|
|
wxASSERT(pSkinSimple);
|
|
wxASSERT(wxDynamicCast(pSkinSimple, CSkinSimple));
|
|
|
|
if (bAlreadyRunning) {
|
|
return;
|
|
}
|
|
bAlreadyRunning = true;
|
|
|
|
count = m_TaskSelectionCtrl->GetCount();
|
|
// Mark all inactive (this lets us loop only once)
|
|
for (i=0; i<count; ++i) {
|
|
is_alive.push_back(false);
|
|
}
|
|
|
|
// First update existing entries and add new ones
|
|
for(i = 0; i < (int) pDoc->results.results.size(); i++) {
|
|
bool found = false;
|
|
|
|
result = pDoc->result(i);
|
|
// only check tasks that are active
|
|
if ( result == NULL || !result->active_task ) {
|
|
continue;
|
|
}
|
|
|
|
resname = wxEmptyString;
|
|
#if SELECTBYRESULTNAME
|
|
resname = wxString::FromUTF8(result->name);
|
|
#else // SELECTBYRESULTNAME
|
|
GetApplicationAndProjectNames(result, &resname, NULL);
|
|
#endif // SELECTBYRESULTNAME
|
|
|
|
// loop through the items already in Task Selection Control to find this result
|
|
count = m_TaskSelectionCtrl->GetCount();
|
|
for(j = 0; j < count; ++j) {
|
|
selData = (TaskSelectionData*)m_TaskSelectionCtrl->GetClientData(j);
|
|
if (!strcmp(result->name, selData->result_name) &&
|
|
!strcmp(result->project_url, selData->project_url)
|
|
) {
|
|
selData->result = result;
|
|
found = true;
|
|
is_alive.at(j) = true;
|
|
break; // skip out of this loop
|
|
}
|
|
}
|
|
|
|
// if it isn't currently in the list then we have a new one! lets add it
|
|
if (!found) {
|
|
int alphaOrder;
|
|
for(j = 0; j < count; ++j) {
|
|
alphaOrder = (m_TaskSelectionCtrl->GetString(j)).CmpNoCase(resname);
|
|
#if SORTTASKLIST
|
|
if (alphaOrder > 0) {
|
|
break; // Insert the new item here (sorted by item label)
|
|
}
|
|
#endif
|
|
// wxComboBox and wxBitmapComboBox have bugs on Windows when multiple
|
|
// entries have identical text, so add enough spaces to make each
|
|
// entry's text unique.
|
|
if (alphaOrder == 0) {
|
|
resname.Append((const wxChar *)wxT(" "));
|
|
#if !SORTTASKLIST
|
|
j = -1; // If not sorted, check new name from start for duplicate
|
|
#endif
|
|
}
|
|
}
|
|
|
|
selData = new TaskSelectionData;
|
|
selData->result = result;
|
|
strlcpy(selData->result_name, result->name, sizeof(selData->result_name));
|
|
strlcpy(selData->project_url, result->project_url, sizeof(selData->project_url));
|
|
selData->dotColor = -1;
|
|
FindSlideShowFiles(selData);
|
|
project = pDoc->state.lookup_project(result->project_url);
|
|
if (project) {
|
|
selData->project_files_downloaded_time = project->project_files_downloaded_time;
|
|
} else {
|
|
selData->project_files_downloaded_time = 0.0;
|
|
}
|
|
|
|
#if SORTTASKLIST
|
|
if (j < count) {
|
|
std::vector<bool>::iterator iter = is_alive.begin();
|
|
m_TaskSelectionCtrl->Insert(resname, wxNullBitmap, j, (void*)selData);
|
|
is_alive.insert(iter+j, true);
|
|
if (j <= m_CurrentTaskSelection) {
|
|
++m_CurrentTaskSelection;
|
|
m_TaskSelectionCtrl->SetSelection(m_CurrentTaskSelection);
|
|
}
|
|
} else
|
|
#endif
|
|
{
|
|
m_TaskSelectionCtrl->Append(resname, wxNullBitmap, (void*)selData);
|
|
is_alive.push_back(true);
|
|
}
|
|
++count;
|
|
} // End if (!found)
|
|
} // End for (i) loop
|
|
|
|
// Check items in descending order so deletion won't change indexes of items yet to be checked
|
|
for(j = count-1; j >=0; --j) {
|
|
if(!is_alive.at(j)) {
|
|
wxLogTrace(wxT("Function Status"), wxT("CSimpleTaskPanel::UpdateTaskSelectionList - Task '%d' no longer alive"), j);
|
|
selData = (TaskSelectionData*)m_TaskSelectionCtrl->GetClientData(j);
|
|
wxLogTrace(wxT("Function Status"), wxT("CSimpleTaskPanel::UpdateTaskSelectionList - selData '%p' "), selData);
|
|
wxLogTrace(wxT("Function Status"), wxT("CSimpleTaskPanel::UpdateTaskSelectionList - result_name '%s' "), selData->result_name);
|
|
selData->slideShowFileNames.Clear();
|
|
wxLogTrace(wxT("Function Status"), wxT("CSimpleTaskPanel::UpdateTaskSelectionList - Deleting selData"));
|
|
delete selData;
|
|
wxLogTrace(wxT("Function Status"), wxT("CSimpleTaskPanel::UpdateTaskSelectionList - Deleting control data"));
|
|
// Indicate to Delete() we have cleaned up the Selection Data
|
|
m_TaskSelectionCtrl->SetClientData(j, NULL);
|
|
m_TaskSelectionCtrl->Delete(j);
|
|
if (j == m_CurrentTaskSelection) {
|
|
int newCount = m_TaskSelectionCtrl->GetCount();
|
|
if (m_CurrentTaskSelection < newCount) {
|
|
// Select the next item if one exists
|
|
m_TaskSelectionCtrl->SetSelection(m_CurrentTaskSelection);
|
|
} else if (newCount > 0) {
|
|
// Select the previous item if one exists
|
|
m_CurrentTaskSelection = newCount-1;
|
|
m_TaskSelectionCtrl->SetSelection(m_CurrentTaskSelection);
|
|
} else {
|
|
m_CurrentTaskSelection = -1;
|
|
m_TaskSelectionCtrl->SetSelection(wxNOT_FOUND);
|
|
}
|
|
m_bStableTaskInfoChanged = true;
|
|
needRefresh = true;
|
|
} else if (j < m_CurrentTaskSelection) {
|
|
--m_CurrentTaskSelection;
|
|
m_TaskSelectionCtrl->SetSelection(m_CurrentTaskSelection);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((m_CurrentTaskSelection >= 0) && !m_bStableTaskInfoChanged) {
|
|
selData = (TaskSelectionData*)m_TaskSelectionCtrl->GetClientData(m_CurrentTaskSelection);
|
|
project = pDoc->state.lookup_project(selData->project_url);
|
|
if ( project && (project->project_files_downloaded_time > selData->project_files_downloaded_time) ) {
|
|
FindSlideShowFiles(selData);
|
|
selData->project_files_downloaded_time = project->project_files_downloaded_time;
|
|
}
|
|
}
|
|
|
|
pDoc->GetCoreClientStatus(status);
|
|
|
|
count = m_TaskSelectionCtrl->GetCount();
|
|
for(j = 0; j < count; ++j) {
|
|
selData = (TaskSelectionData*)m_TaskSelectionCtrl->GetClientData(j);
|
|
ctrlResult = selData->result;
|
|
if (isRunning(ctrlResult)) {
|
|
newIcon = runningIcon;
|
|
} else if (Suspended() ||
|
|
ctrlResult->suspended_via_gui ||
|
|
ctrlResult->project_suspended_via_gui ||
|
|
// kludge. But ctrlResult->avp isn't populated.
|
|
(status.gpu_suspend_reason && (strstr(ctrlResult->resources, "GPU") != NULL)))
|
|
{
|
|
newIcon = suspendedIcon;
|
|
} else {
|
|
newIcon = waitingIcon;
|
|
}
|
|
|
|
if (reskin || (newIcon != selData->dotColor)) {
|
|
switch (newIcon) {
|
|
case runningIcon:
|
|
m_TaskSelectionCtrl->SetItemBitmap(j, *pSkinSimple->GetWorkunitRunningImage()->GetBitmap());
|
|
break;
|
|
case waitingIcon:
|
|
m_TaskSelectionCtrl->SetItemBitmap(j, *pSkinSimple->GetWorkunitWaitingImage()->GetBitmap());
|
|
break;
|
|
case suspendedIcon:
|
|
m_TaskSelectionCtrl->SetItemBitmap(j, *pSkinSimple->GetWorkunitSuspendedImage()->GetBitmap());
|
|
break;
|
|
}
|
|
selData->dotColor = newIcon;
|
|
needRefresh = true;
|
|
}
|
|
}
|
|
if (needRefresh) {
|
|
m_TaskSelectionCtrl->Refresh();
|
|
}
|
|
|
|
bAlreadyRunning = false;
|
|
|
|
wxLogTrace(wxT("Function Start/End"), wxT("CSimpleTaskPanel::UpdateTaskSelectionList - Function End"));
|
|
}
|
|
|
|
|
|
bool CSimpleTaskPanel::isRunning(RESULT* result) {
|
|
|
|
// It must be scheduled to be running
|
|
if ( result->scheduler_state != CPU_SCHED_SCHEDULED ) {
|
|
return false;
|
|
}
|
|
// If either the project or task have been suspended, then it cannot be running
|
|
if (result->suspended_via_gui || result->project_suspended_via_gui ) {
|
|
return false;
|
|
}
|
|
CC_STATUS status;
|
|
CMainDocument* pDoc = wxGetApp().GetDocument();
|
|
wxASSERT(pDoc);
|
|
|
|
pDoc->GetCoreClientStatus(status);
|
|
// Make sure that the core client isn't global suspended for some reason
|
|
if (status.task_suspend_reason == 0 || status.task_suspend_reason == SUSPEND_REASON_CPU_THROTTLE) {
|
|
return true;
|
|
}
|
|
if (result->active_task_state == PROCESS_EXECUTING) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
bool CSimpleTaskPanel::DownloadingResults() {
|
|
bool return_value = false;
|
|
CMainDocument* pDoc = wxGetApp().GetDocument();
|
|
wxASSERT(pDoc);
|
|
|
|
if ( pDoc->results.results.size() > 0 ) {
|
|
RESULT* result;
|
|
for(unsigned int i=0; !return_value && i < pDoc->results.results.size(); i++ ) {
|
|
result = pDoc->result(i);
|
|
if ( result != NULL && result->state == RESULT_FILES_DOWNLOADING ) {
|
|
return_value = true;
|
|
}
|
|
}
|
|
}
|
|
return return_value;
|
|
}
|
|
|
|
bool CSimpleTaskPanel::Suspended() {
|
|
CC_STATUS status;
|
|
CMainDocument* pDoc = wxGetApp().GetDocument();
|
|
wxASSERT(pDoc);
|
|
|
|
bool result = false;
|
|
pDoc->GetCoreClientStatus(status);
|
|
if ( pDoc->IsConnected() && status.task_suspend_reason > 0 && status.task_suspend_reason != SUSPEND_REASON_CPU_THROTTLE ) {
|
|
result = true;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// Check to see if a project update is scheduled or in progress
|
|
bool CSimpleTaskPanel::ProjectUpdateScheduled() {
|
|
PROJECT* project;
|
|
CMainDocument* pDoc = wxGetApp().GetDocument();
|
|
wxASSERT(pDoc);
|
|
|
|
int prjCount = pDoc->GetSimpleProjectCount();
|
|
for(int i=0; i<prjCount; i++) {
|
|
project = pDoc->state.projects[i];
|
|
if ( project->sched_rpc_pending || project->master_url_fetch_pending || project->scheduler_rpc_in_progress ) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void CSimpleTaskPanel::DisplayIdleState() {
|
|
CMainDocument* pDoc = wxGetApp().GetDocument();
|
|
wxASSERT(pDoc);
|
|
|
|
if ( pDoc->IsReconnecting() ) {
|
|
error_time = 0;
|
|
UpdateStaticText(&m_StatusValueText, _("Retrieving current status."));
|
|
} else if ( pDoc->IsConnected() && pDoc->state.projects.size() == 0) {
|
|
error_time = 0;
|
|
UpdateStaticText(&m_StatusValueText, m_sNoProjectsString);
|
|
} else if ( DownloadingResults() ) {
|
|
error_time = 0;
|
|
UpdateStaticText(&m_StatusValueText, _("Downloading work from the server."));
|
|
} else if ( Suspended() ) {
|
|
CC_STATUS status;
|
|
pDoc->GetCoreClientStatus(status);
|
|
if ( status.task_suspend_reason & SUSPEND_REASON_BATTERIES ) {
|
|
UpdateStaticText(&m_StatusValueText, _("Processing Suspended: Running On Batteries."));
|
|
} else if ( status.task_suspend_reason & SUSPEND_REASON_USER_ACTIVE ) {
|
|
UpdateStaticText(&m_StatusValueText, _("Processing Suspended: User Active."));
|
|
} else if ( status.task_suspend_reason & SUSPEND_REASON_USER_REQ ) {
|
|
UpdateStaticText(&m_StatusValueText, _("Processing Suspended: User paused processing."));
|
|
} else if ( status.task_suspend_reason & SUSPEND_REASON_TIME_OF_DAY ) {
|
|
UpdateStaticText(&m_StatusValueText, _("Processing Suspended: Time of Day."));
|
|
} else if ( status.task_suspend_reason & SUSPEND_REASON_BENCHMARKS ) {
|
|
UpdateStaticText(&m_StatusValueText, _("Processing Suspended: Benchmarks Running."));
|
|
} else if ( status.task_suspend_reason & SUSPEND_REASON_DISK_SIZE ) {
|
|
UpdateStaticText(&m_StatusValueText, _("Processing Suspended: need disk space."));
|
|
} else {
|
|
UpdateStaticText(&m_StatusValueText, _("Processing Suspended."));
|
|
}
|
|
} else if ( ProjectUpdateScheduled() ) {
|
|
error_time = 0;
|
|
UpdateStaticText(&m_StatusValueText, _("Waiting to contact project servers."));
|
|
} else {
|
|
if ( error_time == 0 ) {
|
|
error_time = time(NULL) + 10;
|
|
UpdateStaticText(&m_StatusValueText, _("Retrieving current status"));
|
|
} else if ( error_time < time(NULL) ) {
|
|
// TODO: should we display "ERROR" like old Simple GUI?
|
|
if ( pDoc->IsConnected() ) {
|
|
UpdateStaticText(&m_StatusValueText, _("No work available to process"));
|
|
} else {
|
|
UpdateStaticText(&m_StatusValueText, _("Unable to connect to the core client"));
|
|
}
|
|
} else {
|
|
UpdateStaticText(&m_StatusValueText, _("Retrieving current status"));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef __WXMAC__
|
|
// Avoid unnecessary drawing due to Mac progress indicator's animation
|
|
void CSimpleTaskPanel::OnEraseBackground(wxEraseEvent& event) {
|
|
wxRect clipRect;
|
|
wxDC *dc = event.GetDC();
|
|
|
|
if (m_ProgressBar->IsShown()) {
|
|
// if (m_progressBarRect == NULL) {
|
|
m_progressBarRect = new wxRect(m_ProgressBar->GetRect());
|
|
m_progressBarRect->Inflate(1, 0);
|
|
// }
|
|
dc->GetClippingBox(&clipRect.x, &clipRect.y, &clipRect.width, &clipRect.height);
|
|
if (clipRect.IsEmpty() || m_progressBarRect->Contains(clipRect)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
// CSimplePanelBase::OnEraseBackground(event);
|
|
event.Skip();
|
|
}
|
|
#endif
|