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 #if defined(__GNUG__) && !defined(__APPLE__)
19 #pragma implementation "ViewProjects.h"
20 #endif
21 
22 #include "stdwx.h"
23 #include "str_util.h"
24 #include "BOINCGUIApp.h"
25 #include "BOINCBaseFrame.h"
26 #include "MainDocument.h"
27 #include "AdvancedFrame.h"
28 #include "BOINCTaskCtrl.h"
29 #include "BOINCListCtrl.h"
30 #include "ViewProjects.h"
31 #include "Events.h"
32 #include "DlgItemProperties.h"
33 
34 
35 #include "res/proj.xpm"
36 
37 // Column IDs must be equal to the column's default
38 // position (left to right, zero-based) when all
39 // columns are shown.  However, any column may be
40 // hidden, either by default or by the user.
41 // (On MS Windows only, the user can also rearrange
42 // the columns from the default order.)
43 //
44 // Column IDs
45 #define COLUMN_PROJECT              0
46 #define COLUMN_ACCOUNTNAME          1
47 #define COLUMN_TEAMNAME             2
48 #define COLUMN_TOTALCREDIT          3
49 #define COLUMN_AVGCREDIT            4
50 #define COLUMN_RESOURCESHARE        5
51 #define COLUMN_STATUS               6
52 
53 // DefaultShownColumns is an array containing the
54 // columnIDs of the columns to be shown by default,
55 // in ascending order.  It may or may not include
56 // all columns.
57 //
58 // For now, show all columns by default
59 static int DefaultShownColumns[] = { COLUMN_PROJECT, COLUMN_ACCOUNTNAME, COLUMN_TEAMNAME,
60                                 COLUMN_TOTALCREDIT, COLUMN_AVGCREDIT,
61                                 COLUMN_RESOURCESHARE, COLUMN_STATUS};
62 
63 // groups that contain buttons
64 #define GRP_TASKS    0
65 #define GRP_WEBSITES 1
66 
67 // buttons in the "tasks" area
68 #define BTN_UPDATE       0
69 #define BTN_SUSPEND      1
70 #define BTN_NOWORK       2
71 #define BTN_RESET        3
72 #define BTN_DETACH       4
73 #define BTN_PROPERTIES   5
74 
format_total_credit(double credit,wxString & strBuffer)75 static void format_total_credit(double credit, wxString& strBuffer)  {
76     strBuffer = format_number(credit, 0);
77 }
format_avg_credit(double credit,wxString & strBuffer)78 static void format_avg_credit(double credit, wxString& strBuffer)  {
79     strBuffer = format_number(credit, 2);
80 }
81 
CProject()82 CProject::CProject() {
83     m_fTotalCredit = -1.0;
84     m_fAVGCredit = -1.0;
85     m_fResourceShare = -1.0;
86     m_fResourcePercent = -1.0;
87 }
88 
89 
~CProject()90 CProject::~CProject() {
91     m_strProjectName.Clear();
92     m_strAccountName.Clear();
93     m_strTeamName.Clear();
94     m_strStatus.Clear();
95 }
96 
97 
98 IMPLEMENT_DYNAMIC_CLASS(CViewProjects, CBOINCBaseView)
99 
100 BEGIN_EVENT_TABLE (CViewProjects, CBOINCBaseView)
101     EVT_BUTTON(ID_TASK_PROJECT_UPDATE, CViewProjects::OnProjectUpdate)
102     EVT_BUTTON(ID_TASK_PROJECT_SUSPEND, CViewProjects::OnProjectSuspend)
103     EVT_BUTTON(ID_TASK_PROJECT_NONEWWORK, CViewProjects::OnProjectNoNewWork)
104     EVT_BUTTON(ID_TASK_PROJECT_RESET, CViewProjects::OnProjectReset)
105     EVT_BUTTON(ID_TASK_PROJECT_DETACH, CViewProjects::OnProjectDetach)
106     EVT_BUTTON(ID_TASK_PROJECT_SHOW_PROPERTIES, CViewProjects::OnShowItemProperties)
107     EVT_CUSTOM_RANGE(wxEVT_COMMAND_BUTTON_CLICKED, ID_TASK_PROJECT_WEB_PROJDEF_MIN, ID_TASK_PROJECT_WEB_PROJDEF_MAX, CViewProjects::OnProjectWebsiteClicked)
108 // We currently handle EVT_LIST_CACHE_HINT on Windows or
109 // EVT_CHECK_SELECTION_CHANGED on Mac & Linux instead of EVT_LIST_ITEM_SELECTED
110 // or EVT_LIST_ITEM_DESELECTED.  See CBOINCBaseView::OnCacheHint() for info.
111 #if USE_LIST_CACHE_HINT
112     EVT_LIST_CACHE_HINT(ID_LIST_PROJECTSVIEW, CViewProjects::OnCacheHint)
113 #else
114 	EVT_CHECK_SELECTION_CHANGED(CViewProjects::OnCheckSelectionChanged)
115 #endif
116     EVT_LIST_COL_CLICK(ID_LIST_PROJECTSVIEW, CViewProjects::OnColClick)
117     EVT_LIST_COL_END_DRAG(ID_LIST_PROJECTSVIEW, CViewProjects::OnColResize)
118 END_EVENT_TABLE ()
119 
120 
121 static CViewProjects* myCViewProjects;
122 
CompareViewProjectsItems(int iRowIndex1,int iRowIndex2)123 static bool CompareViewProjectsItems(int iRowIndex1, int iRowIndex2) {
124     CProject*   project1;
125     CProject*   project2;
126     int         result = 0;
127 
128     try {
129         project1 = myCViewProjects->m_ProjectCache.at(iRowIndex1);
130     } catch ( std::out_of_range ) {
131         return 0;
132     }
133 
134     try {
135         project2 = myCViewProjects->m_ProjectCache.at(iRowIndex2);
136     } catch ( std::out_of_range ) {
137         return 0;
138     }
139 
140     switch (myCViewProjects->m_iSortColumnID) {
141     case COLUMN_PROJECT:
142         result = project1->m_strProjectName.CmpNoCase(project2->m_strProjectName);
143         break;
144     case COLUMN_ACCOUNTNAME:
145         result = project1->m_strAccountName.CmpNoCase(project2->m_strAccountName);
146         break;
147     case COLUMN_TEAMNAME:
148         result = project1->m_strTeamName.CmpNoCase(project2->m_strTeamName);
149         break;
150     case COLUMN_TOTALCREDIT:
151         if (project1->m_fTotalCredit < project2->m_fTotalCredit) {
152             result = -1;
153         } else if (project1->m_fTotalCredit > project2->m_fTotalCredit) {
154             result = 1;
155         }
156         break;
157     case COLUMN_AVGCREDIT:
158         if (project1->m_fAVGCredit < project2->m_fAVGCredit) {
159             result = -1;
160         } else if (project1->m_fAVGCredit > project2->m_fAVGCredit) {
161             result = 1;
162         }
163         break;
164     case COLUMN_RESOURCESHARE:
165         if (project1->m_fResourceShare < project2->m_fResourceShare) {
166             result = -1;
167         } else if (project1->m_fResourceShare > project2->m_fResourceShare) {
168             result = 1;
169         }
170         break;
171     case COLUMN_STATUS:
172         result = project1->m_strStatus.CmpNoCase(project2->m_strStatus);
173         break;
174     }
175 
176     // Always return FALSE for equality (result == 0)
177     return (myCViewProjects->m_bReverseSort ? (result > 0) : (result < 0));
178 }
179 
180 
CViewProjects()181 CViewProjects::CViewProjects()
182 {}
183 
184 
CViewProjects(wxNotebook * pNotebook)185 CViewProjects::CViewProjects(wxNotebook* pNotebook) :
186     CBOINCBaseView(pNotebook, ID_TASK_PROJECTSVIEW, DEFAULT_TASK_FLAGS, ID_LIST_PROJECTSVIEW, DEFAULT_LIST_FLAGS)
187 {
188     CTaskItemGroup* pGroup = NULL;
189     CTaskItem*      pItem = NULL;
190 
191     wxASSERT(m_pTaskPane);
192     wxASSERT(m_pListPane);
193 
194 
195     //
196     // Setup View
197     //
198     pGroup = new CTaskItemGroup( _("Commands") );
199     m_TaskGroups.push_back( pGroup );
200 
201     pItem = new CTaskItem(
202         _("Update"),
203         _("Report all completed tasks, get latest credit, get latest preferences, and possibly get more tasks."),
204         ID_TASK_PROJECT_UPDATE
205     );
206     pGroup->m_Tasks.push_back( pItem );
207 
208     pItem = new CTaskItem(
209         _("Suspend"),
210         _("Suspend tasks for this project."),
211         ID_TASK_PROJECT_SUSPEND
212     );
213     pGroup->m_Tasks.push_back( pItem );
214 
215     pItem = new CTaskItem(
216         _("No new tasks"),
217         _("Don't get new tasks for this project."),
218         ID_TASK_PROJECT_NONEWWORK
219     );
220     pGroup->m_Tasks.push_back( pItem );
221 
222     pItem = new CTaskItem(
223         _("Reset project"),
224         _("Delete all files and tasks associated with this project, and get new tasks.  You can update the project first to report any completed tasks."),
225         ID_TASK_PROJECT_RESET
226     );
227     pGroup->m_Tasks.push_back( pItem );
228 
229     pItem = new CTaskItem(
230         _("Remove"),
231         _("Remove this project.  Tasks in progress will be lost (use 'Update' first to report any completed tasks)."),
232         ID_TASK_PROJECT_DETACH
233     );
234     pGroup->m_Tasks.push_back( pItem );
235 
236     pItem = new CTaskItem(
237         _("Properties"),
238         _("Show project details."),
239         ID_TASK_PROJECT_SHOW_PROPERTIES
240     );
241     pGroup->m_Tasks.push_back( pItem );
242 
243     // Create Task Pane Items
244     m_pTaskPane->UpdateControls();
245 
246     // m_aStdColNameOrder is an array of all column heading labels
247     // (localized) in order of ascending Column ID.
248     // Once initialized, it should not be modified.
249     //
250     m_aStdColNameOrder = new wxArrayString;
251     m_aStdColNameOrder->Insert(_("Project"), COLUMN_PROJECT);
252     m_aStdColNameOrder->Insert(_("Account"), COLUMN_ACCOUNTNAME);
253     m_aStdColNameOrder->Insert(_("Team"), COLUMN_TEAMNAME);
254     m_aStdColNameOrder->Insert(_("Work done"), COLUMN_TOTALCREDIT);
255     m_aStdColNameOrder->Insert(_("Avg. work done"), COLUMN_AVGCREDIT);
256     m_aStdColNameOrder->Insert(_("Resource share"), COLUMN_RESOURCESHARE);
257     m_aStdColNameOrder->Insert(_("Status"), COLUMN_STATUS);
258 
259     // m_iStdColWidthOrder is an array of the width for each column.
260     // Entries must be in order of ascending Column ID.  We initalize
261     // it here to the default column widths.  It is updated by
262     // CBOINCListCtrl::OnRestoreState() and also when a user resizes
263     // a column by dragging the divider between two columns.
264     //
265     m_iStdColWidthOrder.Clear();
266     m_iStdColWidthOrder.Insert(150, COLUMN_PROJECT);
267     m_iStdColWidthOrder.Insert(80, COLUMN_ACCOUNTNAME);
268     m_iStdColWidthOrder.Insert(80, COLUMN_TEAMNAME);
269     m_iStdColWidthOrder.Insert(80, COLUMN_TOTALCREDIT);
270     m_iStdColWidthOrder.Insert(80, COLUMN_AVGCREDIT);
271     m_iStdColWidthOrder.Insert(80, COLUMN_RESOURCESHARE);
272     m_iStdColWidthOrder.Insert(80, COLUMN_STATUS);
273 
274     wxASSERT(m_iStdColWidthOrder.size() == m_aStdColNameOrder->size());
275 
276     m_iDefaultShownColumns = DefaultShownColumns;
277     m_iNumDefaultShownColumns = sizeof(DefaultShownColumns) / sizeof(int);
278     m_iProgressColumn = COLUMN_RESOURCESHARE;
279 
280     // Needed by static sort routine;
281     myCViewProjects = this;
282     m_funcSortCompare = CompareViewProjectsItems;
283 
284     UpdateSelection();
285 }
286 
287 
~CViewProjects()288 CViewProjects::~CViewProjects() {
289     EmptyCache();
290     EmptyTasks();
291 }
292 
293 
294 // Create List Pane Items
AppendColumn(int columnID)295 void CViewProjects::AppendColumn(int columnID){
296     switch(columnID) {
297         case COLUMN_PROJECT:
298             m_pListPane->AppendColumn((*m_aStdColNameOrder)[COLUMN_PROJECT],
299                     wxLIST_FORMAT_LEFT, m_iStdColWidthOrder[COLUMN_PROJECT]);
300             break;
301         case COLUMN_ACCOUNTNAME:
302             m_pListPane->AppendColumn((*m_aStdColNameOrder)[COLUMN_ACCOUNTNAME],
303                     wxLIST_FORMAT_LEFT, m_iStdColWidthOrder[COLUMN_ACCOUNTNAME]);
304             break;
305         case COLUMN_TEAMNAME:
306             m_pListPane->AppendColumn((*m_aStdColNameOrder)[COLUMN_TEAMNAME],
307                     wxLIST_FORMAT_LEFT, m_iStdColWidthOrder[COLUMN_TEAMNAME]);
308             break;
309         case COLUMN_TOTALCREDIT:
310             m_pListPane->AppendColumn((*m_aStdColNameOrder)[COLUMN_TOTALCREDIT],
311                     wxLIST_FORMAT_RIGHT, m_iStdColWidthOrder[COLUMN_TOTALCREDIT]);
312             break;
313         case COLUMN_AVGCREDIT:
314             m_pListPane->AppendColumn((*m_aStdColNameOrder)[COLUMN_AVGCREDIT],
315                     wxLIST_FORMAT_RIGHT, m_iStdColWidthOrder[COLUMN_AVGCREDIT]);
316             break;
317         case COLUMN_RESOURCESHARE:
318             m_pListPane->AppendColumn((*m_aStdColNameOrder)[COLUMN_RESOURCESHARE],
319                     wxLIST_FORMAT_CENTRE, m_iStdColWidthOrder[COLUMN_RESOURCESHARE]);
320             break;
321         case COLUMN_STATUS:
322             m_pListPane->AppendColumn((*m_aStdColNameOrder)[COLUMN_STATUS],
323                     wxLIST_FORMAT_LEFT, m_iStdColWidthOrder[COLUMN_STATUS]);
324             break;
325     }
326 }
327 
328 
GetViewName()329 wxString& CViewProjects::GetViewName() {
330     static wxString strViewName(wxT("Projects"));
331     return strViewName;
332 }
333 
334 
GetViewDisplayName()335 wxString& CViewProjects::GetViewDisplayName() {
336     static wxString strViewName(_("Projects"));
337     return strViewName;
338 }
339 
340 
GetViewIcon()341 const char** CViewProjects::GetViewIcon() {
342     return proj_xpm;
343 }
344 
345 
GetViewCurrentViewPage()346 int CViewProjects::GetViewCurrentViewPage() {
347     return VW_PROJ;
348 }
349 
350 
GetKeyValue1(int iRowIndex)351 wxString CViewProjects::GetKeyValue1(int iRowIndex) {
352     CProject*   project;
353 
354     if (GetProjectCacheAtIndex(project, m_iSortedIndexes[iRowIndex])) {
355         return wxEmptyString;
356     }
357 
358     if (m_iColumnIDToColumnIndex[COLUMN_PROJECT] < 0) {
359         // Column is hidden, so SynchronizeCacheItem() did not set its value
360         GetDocProjectURL(m_iSortedIndexes[iRowIndex], project->m_strProjectURL);
361     }
362 
363     return project->m_strProjectURL;
364 }
365 
366 
FindRowIndexByKeyValues(wxString & key1,wxString &)367 int CViewProjects::FindRowIndexByKeyValues(wxString& key1, wxString&) {
368     CProject* project;
369     unsigned int iRowIndex, n = GetCacheCount();
370     for(iRowIndex=0; iRowIndex < n; iRowIndex++) {
371         if (GetProjectCacheAtIndex(project, m_iSortedIndexes[iRowIndex])) {
372             continue;
373         }
374         if((project->m_strProjectURL).IsSameAs(key1)) return iRowIndex;
375     }
376     return -1;
377 }
378 
379 
OnProjectUpdate(wxCommandEvent & WXUNUSED (event))380 void CViewProjects::OnProjectUpdate( wxCommandEvent& WXUNUSED(event) ) {
381     wxLogTrace(wxT("Function Start/End"), wxT("CViewProjects::OnProjectUpdate - Function Begin"));
382 
383     CMainDocument*  pDoc   = wxGetApp().GetDocument();
384     CAdvancedFrame* pFrame = wxDynamicCast(GetParent()->GetParent()->GetParent(), CAdvancedFrame);
385     int row;
386 
387     wxASSERT(pDoc);
388     wxASSERT(wxDynamicCast(pDoc, CMainDocument));
389     wxASSERT(pFrame);
390     wxASSERT(wxDynamicCast(pFrame, CAdvancedFrame));
391     wxASSERT(m_pListPane);
392 
393     row = -1;
394     while (1) {
395         // Step through all selected items
396         row = m_pListPane->GetNextItem(row, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
397         if (row < 0) break;
398 
399         pDoc->ProjectUpdate(m_iSortedIndexes[row]);
400     }
401 
402     m_bForceUpdateSelection = true;
403     UpdateSelection();
404     pFrame->ResetReminderTimers();
405     pFrame->FireRefreshView();
406 
407     wxLogTrace(wxT("Function Start/End"), wxT("CViewProjects::OnProjectUpdate - Function End"));
408 }
409 
410 
OnProjectSuspend(wxCommandEvent & WXUNUSED (event))411 void CViewProjects::OnProjectSuspend( wxCommandEvent& WXUNUSED(event) ) {
412     wxLogTrace(wxT("Function Start/End"), wxT("CViewProjects::OnProjectSuspend - Function Begin"));
413 
414     CMainDocument*  pDoc   = wxGetApp().GetDocument();
415     CAdvancedFrame* pFrame = wxDynamicCast(GetParent()->GetParent()->GetParent(), CAdvancedFrame);
416     int row;
417 
418     wxASSERT(pDoc);
419     wxASSERT(wxDynamicCast(pDoc, CMainDocument));
420     wxASSERT(pFrame);
421     wxASSERT(wxDynamicCast(pFrame, CAdvancedFrame));
422     wxASSERT(m_pListPane);
423 
424     row = -1;
425     while (1) {
426         // Step through all selected items
427         row = m_pListPane->GetNextItem(row, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
428         if (row < 0) break;
429 
430         PROJECT* project = pDoc->project(m_iSortedIndexes[row]);
431         if (project) {
432             if (project->suspended_via_gui) {
433                 pDoc->ProjectResume(m_iSortedIndexes[row]);
434             } else {
435                 pDoc->ProjectSuspend(m_iSortedIndexes[row]);
436             }
437         }
438     }
439 
440     m_bForceUpdateSelection = true;
441     UpdateSelection();
442     pFrame->FireRefreshView();
443 
444     wxLogTrace(wxT("Function Start/End"), wxT("CViewProjects::OnProjectSuspend - Function End"));
445 }
446 
447 
OnProjectNoNewWork(wxCommandEvent & WXUNUSED (event))448 void CViewProjects::OnProjectNoNewWork( wxCommandEvent& WXUNUSED(event) ) {
449     wxLogTrace(wxT("Function Start/End"), wxT("CViewProjects::OnProjectNoNewWork - Function Begin"));
450 
451     CMainDocument*  pDoc   = wxGetApp().GetDocument();
452     CAdvancedFrame* pFrame = wxDynamicCast(GetParent()->GetParent()->GetParent(), CAdvancedFrame);
453     int row;
454 
455     wxASSERT(pDoc);
456     wxASSERT(wxDynamicCast(pDoc, CMainDocument));
457     wxASSERT(pFrame);
458     wxASSERT(wxDynamicCast(pFrame, CAdvancedFrame));
459     wxASSERT(m_pListPane);
460 
461     row = -1;
462     while (1) {
463         // Step through all selected items
464         row = m_pListPane->GetNextItem(row, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
465         if (row < 0) break;
466 
467         PROJECT* project = pDoc->project(m_iSortedIndexes[row]);
468         if (project) {
469             if (project->dont_request_more_work) {
470                 pDoc->ProjectAllowMoreWork(m_iSortedIndexes[row]);
471             } else {
472                 pDoc->ProjectNoMoreWork(m_iSortedIndexes[row]);
473             }
474         }
475     }
476 
477     m_bForceUpdateSelection = true;
478     UpdateSelection();
479     pFrame->FireRefreshView();
480 
481     wxLogTrace(wxT("Function Start/End"), wxT("CViewProjects::OnProjectNoNewWork - Function End"));
482 }
483 
484 
485 
486 
OnProjectReset(wxCommandEvent & WXUNUSED (event))487 void CViewProjects::OnProjectReset( wxCommandEvent& WXUNUSED(event) ) {
488     wxLogTrace(wxT("Function Start/End"), wxT("CViewProjects::OnProjectReset - Function Begin"));
489 
490     wxInt32         iAnswer        = 0;
491     wxString        strMessage     = wxEmptyString;
492     CMainDocument*  pDoc           = wxGetApp().GetDocument();
493     CAdvancedFrame* pFrame         = wxDynamicCast(GetParent()->GetParent()->GetParent(), CAdvancedFrame);
494     CProject*       pProject       = NULL;
495     int row;
496 
497     wxASSERT(pDoc);
498     wxASSERT(wxDynamicCast(pDoc, CMainDocument));
499     wxASSERT(pFrame);
500     wxASSERT(wxDynamicCast(pFrame, CAdvancedFrame));
501     wxASSERT(m_pListPane);
502 
503     if (!pDoc->IsUserAuthorized())
504         return;
505 
506     row = -1;
507     while (1) {
508         // Step through all selected items
509         row = m_pListPane->GetNextItem(row, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
510         if (row < 0) break;
511 
512         if (GetProjectCacheAtIndex(pProject, m_iSortedIndexes[row])) {
513             return;
514         }
515 
516         strMessage.Printf(
517             _("Are you sure you want to reset project '%s'?"),
518             pProject->m_strProjectName.c_str()
519         );
520 
521         iAnswer = wxGetApp().SafeMessageBox(
522             strMessage,
523             _("Reset Project"),
524             wxYES_NO | wxICON_QUESTION,
525             this
526         );
527 
528         if (wxYES == iAnswer) {
529             pDoc->ProjectReset(m_iSortedIndexes[row]);
530         }
531     }
532 
533     m_bForceUpdateSelection = true;
534     UpdateSelection();
535     pFrame->FireRefreshView();
536 
537     wxLogTrace(wxT("Function Start/End"), wxT("CViewProjects::OnProjectReset - Function End"));
538 }
539 
540 
OnProjectDetach(wxCommandEvent & WXUNUSED (event))541 void CViewProjects::OnProjectDetach( wxCommandEvent& WXUNUSED(event) ) {
542     wxLogTrace(wxT("Function Start/End"), wxT("CViewProjects::OnProjectDetach - Function Begin"));
543 
544     wxInt32         iAnswer        = 0;
545     wxString        strMessage     = wxEmptyString;
546     CMainDocument*  pDoc           = wxGetApp().GetDocument();
547     CAdvancedFrame* pFrame         = wxDynamicCast(GetParent()->GetParent()->GetParent(), CAdvancedFrame);
548     CProject*       pProject       = NULL;
549     int row;
550 
551     wxASSERT(pDoc);
552     wxASSERT(wxDynamicCast(pDoc, CMainDocument));
553     wxASSERT(pFrame);
554     wxASSERT(wxDynamicCast(pFrame, CAdvancedFrame));
555     wxASSERT(m_pListPane);
556 
557     if (!pDoc->IsUserAuthorized())
558         return;
559 
560     row = -1;
561     while (1) {
562         // Step through all selected items
563         row = m_pListPane->GetNextItem(row, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
564         if (row < 0) break;
565 
566         if (GetProjectCacheAtIndex(pProject, m_iSortedIndexes[row])) {
567             return;
568         }
569 
570         strMessage.Printf(
571             _("Are you sure you want to remove project '%s'?"),
572             pProject->m_strProjectName.c_str()
573         );
574 
575         iAnswer = wxGetApp().SafeMessageBox(
576             strMessage,
577             _("Remove Project"),
578             wxYES_NO | wxICON_QUESTION,
579             this
580         );
581 
582         if (wxYES == iAnswer) {
583             pDoc->ProjectDetach(m_iSortedIndexes[row]);
584         }
585     }
586 
587     m_bForceUpdateSelection = true;
588     UpdateSelection();
589     pFrame->FireRefreshView();
590 
591     wxLogTrace(wxT("Function Start/End"), wxT("CViewProjects::OnProjectDetach - Function End"));
592 }
593 
594 
OnShowItemProperties(wxCommandEvent & WXUNUSED (event))595 void CViewProjects::OnShowItemProperties( wxCommandEvent& WXUNUSED(event) ) {
596     wxASSERT(m_pListPane);
597 
598     long item = m_pListPane->GetFirstSelected();
599     PROJECT* project = wxGetApp().GetDocument()->project(m_iSortedIndexes[item]);
600 
601     if(!project) return;     // TODO: display some sort of error alert?
602     //displaying the infos on a dialog
603     CDlgItemProperties dlg(this);
604     dlg.renderInfos(project);
605     dlg.ShowModal();
606 }
607 
608 
OnProjectWebsiteClicked(wxEvent & event)609 void CViewProjects::OnProjectWebsiteClicked( wxEvent& event ) {
610     wxLogTrace(wxT("Function Start/End"), wxT("CViewProjects::OnProjectWebsiteClicked - Function Begin"));
611 
612     CAdvancedFrame* pFrame      = wxDynamicCast(GetParent()->GetParent()->GetParent(), CAdvancedFrame);
613 
614     wxASSERT(pFrame);
615     wxASSERT(wxDynamicCast(pFrame, CAdvancedFrame));
616 
617     int website_task_index = event.GetId() - ID_TASK_PROJECT_WEB_PROJDEF_MIN;
618     wxLaunchDefaultBrowser(
619         m_TaskGroups[1]->m_Tasks[website_task_index]->m_strWebSiteLink
620     );
621 
622     UpdateSelection();
623     pFrame->FireRefreshView();
624 
625     wxLogTrace(wxT("Function Start/End"), wxT("CViewProjects::OnProjectWebsiteClicked - Function End"));
626 }
627 
OnColResize(wxListEvent &)628 void CViewProjects::OnColResize( wxListEvent& ) {
629     // Register the new column widths immediately
630     CAdvancedFrame* pFrame = wxDynamicCast(GetParent()->GetParent()->GetParent(), CAdvancedFrame);
631 
632     wxASSERT(pFrame);
633     wxASSERT(wxDynamicCast(pFrame, CAdvancedFrame));
634     pFrame->SaveState();
635 }
636 
637 
GetDocCount()638 wxInt32 CViewProjects::GetDocCount() {
639     CMainDocument* pDoc = wxGetApp().GetDocument();
640     if (pDoc) {
641         return pDoc->GetProjectCount();
642     }
643     return -1;
644 }
645 
646 
OnListGetItemText(long item,long column) const647 wxString CViewProjects::OnListGetItemText(long item, long column) const {
648     CProject* project     = NULL;
649     wxString       strBuffer = wxEmptyString;
650 
651     m_pListPane->AddPendingProgressBar(item);
652 
653     try {
654         project = m_ProjectCache.at(m_iSortedIndexes[item]);
655     } catch ( std::out_of_range ) {
656         project = NULL;
657     }
658 
659     if (project && (column >= 0)) {
660         switch(m_iColumnIndexToColumnID[column]) {
661             case COLUMN_PROJECT:
662                 strBuffer = project->m_strProjectName;
663                 break;
664             case COLUMN_ACCOUNTNAME:
665                 strBuffer = project->m_strAccountName;
666                 break;
667             case COLUMN_TEAMNAME:
668                 strBuffer = project->m_strTeamName;
669                 break;
670             case COLUMN_TOTALCREDIT:
671                 strBuffer = project->m_strTotalCredit;
672                 break;
673             case COLUMN_AVGCREDIT:
674                 strBuffer = project->m_strAVGCredit;
675                 break;
676             case COLUMN_RESOURCESHARE:
677                 // CBOINCListCtrl::DrawProgressBars() will draw this using
678                 // data provided by GetProgressText() and GetProgressValue(),
679                 // but we need it here for accessibility programs.
680                 strBuffer = project->m_strResourceShare;
681                 break;
682             case COLUMN_STATUS:
683                 strBuffer = project->m_strStatus;
684                 break;
685         }
686     }
687 
688      return strBuffer;
689 }
690 
691 
AddCacheElement()692 wxInt32 CViewProjects::AddCacheElement() {
693     CProject* pItem = new CProject();
694     wxASSERT(pItem);
695     if (pItem) {
696         m_ProjectCache.push_back(pItem);
697         m_iSortedIndexes.Add((int)m_ProjectCache.size()-1);
698         return 0;
699     }
700     return -1;
701 }
702 
703 
EmptyCache()704 wxInt32 CViewProjects::EmptyCache() {
705     unsigned int i;
706     for (i=0; i<m_ProjectCache.size(); i++) {
707         delete m_ProjectCache[i];
708     }
709     m_ProjectCache.clear();
710     m_iSortedIndexes.Clear();
711     return 0;
712 }
713 
714 
GetCacheCount()715 wxInt32 CViewProjects::GetCacheCount() {
716     return (wxInt32)m_ProjectCache.size();
717 }
718 
719 
RemoveCacheElement()720 wxInt32 CViewProjects::RemoveCacheElement() {
721     unsigned int i;
722     delete m_ProjectCache.back();
723     m_ProjectCache.erase(m_ProjectCache.end() - 1);
724     m_iSortedIndexes.Clear();
725     for (i=0; i<m_ProjectCache.size(); i++) {
726         m_iSortedIndexes.Add(i);
727     }
728     return 0;
729 }
730 
731 
IsSelectionManagementNeeded()732 bool CViewProjects::IsSelectionManagementNeeded() {
733     return true;
734 }
735 
UpdateSelection()736 void CViewProjects::UpdateSelection() {
737     CTaskItemGroup*     pGroup = NULL;
738     PROJECT*            project = NULL;
739     CMainDocument*      pDoc = wxGetApp().GetDocument();
740     int                 i, n, row;
741     bool                wasSuspended=false, wasNoNewWork=false;
742     bool                enableUpdate = false;
743     bool                enableSuspendResume = false;
744     bool                enableNoNewTasks = false;
745     bool                enableReset = false;
746     bool                enableDetach = false;
747     bool                enableProperties = false;
748 
749     wxASSERT(pDoc);
750     wxASSERT(wxDynamicCast(pDoc, CMainDocument));
751     wxASSERT(m_pTaskPane);
752     wxASSERT(m_pListPane);
753 
754     CBOINCBaseView::PreUpdateSelection();
755 
756     // Update the tasks static box buttons
757     //
758     pGroup = m_TaskGroups[0];
759 
760     n = m_pListPane->GetSelectedItemCount();
761     if (n > 0) {
762         enableUpdate = true;
763         enableSuspendResume = true;
764         enableNoNewTasks = true;
765         enableReset = true;
766         enableDetach = true;
767     }
768 
769     row = -1;
770     for (i=0; i<n; i++) {
771         // Step through all selected items
772         row = m_pListPane->GetNextItem(row, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
773         if (row < 0) break;     // Should never happen
774 
775         project = pDoc->project(m_iSortedIndexes[row]);
776         if (!project) {
777             m_pTaskPane->DisableTaskGroupTasks(pGroup);
778             if(m_TaskGroups.size()>1) {
779                 m_pTaskPane->DisableTaskGroupTasks(m_TaskGroups[1]);
780             }
781             return;
782         }
783 
784         if (i == 0) {
785             wasSuspended = project->suspended_via_gui;
786              if (project->suspended_via_gui) {
787                 m_pTaskPane->UpdateTask(
788                     pGroup->m_Tasks[BTN_SUSPEND], _("Resume"), _("Resume tasks for this project.")
789                 );
790             } else {
791                 m_pTaskPane->UpdateTask(
792                     pGroup->m_Tasks[BTN_SUSPEND], _("Suspend"), _("Suspend tasks for this project.")
793                 );
794             }
795         } else {
796             if (wasSuspended != project->suspended_via_gui) {
797                 // Disable Suspend / Resume button if the multiple selection
798                 // has a mix of suspended and not suspended projects
799                 enableSuspendResume = false;
800             }
801         }
802 
803         if (i == 0) {
804             wasNoNewWork = project->dont_request_more_work;
805             if (project->dont_request_more_work) {
806                 m_pTaskPane->UpdateTask(
807                     pGroup->m_Tasks[BTN_NOWORK], _("Allow new tasks"), _("Allow fetching new tasks for this project.")
808                 );
809             } else {
810                 m_pTaskPane->UpdateTask(
811                     pGroup->m_Tasks[BTN_NOWORK], _("No new tasks"), _("Don't fetch new tasks for this project.")
812                 );
813             }
814         } else {
815             if (wasNoNewWork != project->dont_request_more_work) {
816                 // Disable Allow New Work / No New Work button if the multiple
817                 // selection has a mix of Allow New Work and No New Work projects
818                 enableNoNewTasks = false;
819             }
820         }
821 
822         if (project->attached_via_acct_mgr) {
823             enableDetach = false;
824         }
825     }
826 
827     if (n == 1) {
828         enableProperties = true;
829 
830         UpdateWebsiteSelection(GRP_WEBSITES, project);
831         if(m_TaskGroups.size()>1) {
832             m_pTaskPane->EnableTaskGroupTasks(m_TaskGroups[1]);
833         }
834     } else {
835         UpdateWebsiteSelection(GRP_WEBSITES, NULL);
836         if(m_TaskGroups.size()>1) {
837             m_pTaskPane->DisableTaskGroupTasks(m_TaskGroups[1]);
838         }
839     }
840 
841     // To minimize flicker, set each button only once to the final desired state
842     pGroup->m_Tasks[BTN_UPDATE]->m_pButton->Enable(enableUpdate);
843     pGroup->m_Tasks[BTN_SUSPEND]->m_pButton->Enable(enableSuspendResume);
844     pGroup->m_Tasks[BTN_NOWORK]->m_pButton->Enable(enableNoNewTasks);
845     pGroup->m_Tasks[BTN_RESET]->m_pButton->Enable(enableReset);
846     pGroup->m_Tasks[BTN_DETACH]->m_pButton->Enable(enableDetach);
847     pGroup->m_Tasks[BTN_PROPERTIES]->m_pButton->Enable(enableProperties);
848 
849     CBOINCBaseView::PostUpdateSelection();
850 }
851 
852 
SynchronizeCacheItem(wxInt32 iRowIndex,wxInt32 iColumnIndex)853 bool CViewProjects::SynchronizeCacheItem(wxInt32 iRowIndex, wxInt32 iColumnIndex) {
854     wxString    strDocumentText  = wxEmptyString;
855     wxString    strDocumentText2 = wxEmptyString;
856     double       x = 0.0;
857     double       fDocumentPercent = 0.0;
858     CProject*   project;
859     bool        dirty = false;
860 
861     if (GetProjectCacheAtIndex(project, m_iSortedIndexes[iRowIndex])) {
862             return false;
863     }
864 
865     if (iColumnIndex < 0) return false;
866 
867     strDocumentText.Empty();
868 
869    switch (m_iColumnIndexToColumnID[iColumnIndex]) {
870         case COLUMN_PROJECT:
871             GetDocProjectName(m_iSortedIndexes[iRowIndex], strDocumentText);
872             GetDocProjectURL(m_iSortedIndexes[iRowIndex], strDocumentText2);
873             if (!strDocumentText.IsSameAs(project->m_strProjectName) || !strDocumentText2.IsSameAs(project->m_strProjectURL)) {
874                 project->m_strProjectName = strDocumentText;
875                 project->m_strProjectURL = strDocumentText2;
876                 return true;
877             }
878             break;
879         case COLUMN_ACCOUNTNAME:
880             GetDocAccountName(m_iSortedIndexes[iRowIndex], strDocumentText);
881             if (!strDocumentText.IsSameAs(project->m_strAccountName)) {
882                 project->m_strAccountName = strDocumentText;
883                 return true;
884             }
885            break;
886         case COLUMN_TEAMNAME:
887             GetDocTeamName(m_iSortedIndexes[iRowIndex], strDocumentText);
888             if (!strDocumentText.IsSameAs(project->m_strTeamName)) {
889                 project->m_strTeamName = strDocumentText;
890                 return true;
891             }
892             break;
893         case COLUMN_TOTALCREDIT:
894             GetDocTotalCredit(m_iSortedIndexes[iRowIndex], x);
895             if (x != project->m_fTotalCredit) {
896                 project->m_fTotalCredit = x;
897                 format_total_credit(x, project->m_strTotalCredit);
898                 return true;
899             }
900             break;
901         case COLUMN_AVGCREDIT:
902             GetDocAVGCredit(m_iSortedIndexes[iRowIndex], x);
903             if (x != project->m_fAVGCredit) {
904                 project->m_fAVGCredit = x;
905                 format_avg_credit(x, project->m_strAVGCredit);
906                 return true;
907             }
908             break;
909         case COLUMN_RESOURCESHARE:
910             GetDocResourceShare(m_iSortedIndexes[iRowIndex], x);
911             if (x != project->m_fResourceShare) {
912                 project->m_fResourceShare = x;
913                 dirty = true;
914             }
915             GetDocResourcePercent(m_iSortedIndexes[iRowIndex], fDocumentPercent);
916             if (fDocumentPercent != project->m_fResourcePercent) {
917                 project->m_fResourcePercent = fDocumentPercent;
918                 dirty = true;
919             }
920             if (dirty) {
921                 FormatResourceShare(x, fDocumentPercent, project->m_strResourceShare);
922                 return true;
923             }
924             break;
925         case COLUMN_STATUS:
926             GetDocStatus(m_iSortedIndexes[iRowIndex], strDocumentText);
927             if (!strDocumentText.IsSameAs(project->m_strStatus)) {
928                 project->m_strStatus = strDocumentText;
929                 return true;
930             }
931             break;
932     }
933 
934     return false;
935 }
936 
937 
GetDocProjectName(wxInt32 item,wxString & strBuffer) const938 void CViewProjects::GetDocProjectName(wxInt32 item, wxString& strBuffer) const {
939     PROJECT* project = NULL;
940     CMainDocument* pDoc = wxGetApp().GetDocument();
941     std::string project_name;
942 
943     if (pDoc) {
944         project = pDoc->project(item);
945     }
946 
947     if (project) {
948         project->get_name(project_name);
949         strBuffer = HtmlEntityDecode(wxString(project_name.c_str(), wxConvUTF8));
950     } else {
951         strBuffer = wxEmptyString;
952     }
953 }
954 
955 
FormatProjectName(wxInt32 item,wxString & strBuffer) const956 wxInt32 CViewProjects::FormatProjectName(wxInt32 item, wxString& strBuffer) const {
957     CProject* project;
958 
959     try {
960         project = m_ProjectCache.at(m_iSortedIndexes[item]);
961     } catch ( std::out_of_range ) {
962         project = NULL;
963     }
964 
965     if (project) {
966         strBuffer = project->m_strProjectName;
967     } else {
968         strBuffer = wxEmptyString;
969     }
970 
971     return 0;
972 }
973 
974 
GetDocAccountName(wxInt32 item,wxString & strBuffer) const975 void CViewProjects::GetDocAccountName(wxInt32 item, wxString& strBuffer) const {
976     PROJECT* project = NULL;
977     CMainDocument* pDoc = wxGetApp().GetDocument();
978 
979     if (pDoc) {
980         project = pDoc->project(item);
981     }
982 
983     if (project) {
984         strBuffer = HtmlEntityDecode(wxString(project->user_name.c_str(), wxConvUTF8));
985     } else {
986         strBuffer = wxEmptyString;
987     }
988 }
989 
990 
FormatAccountName(wxInt32 item,wxString & strBuffer) const991 wxInt32 CViewProjects::FormatAccountName(wxInt32 item, wxString& strBuffer) const {
992     CProject* project;
993 
994     try {
995         project = m_ProjectCache.at(m_iSortedIndexes[item]);
996     } catch ( std::out_of_range ) {
997         project = NULL;
998     }
999     if (project) {
1000         strBuffer = project->m_strAccountName;
1001     } else {
1002         strBuffer = wxEmptyString;
1003     }
1004 
1005     return 0;
1006 }
1007 
1008 
GetDocTeamName(wxInt32 item,wxString & strBuffer) const1009 void CViewProjects::GetDocTeamName(wxInt32 item, wxString& strBuffer) const {
1010     PROJECT* project = NULL;
1011     CMainDocument* pDoc = wxGetApp().GetDocument();
1012 
1013     if (pDoc) {
1014         project = pDoc->project(item);
1015     }
1016 
1017     if (project) {
1018         strBuffer = HtmlEntityDecode(wxString(project->team_name.c_str(), wxConvUTF8));
1019     } else {
1020         strBuffer = wxEmptyString;
1021     }
1022 }
1023 
1024 
FormatTeamName(wxInt32 item,wxString & strBuffer) const1025 wxInt32 CViewProjects::FormatTeamName(wxInt32 item, wxString& strBuffer) const {
1026     CProject* project;
1027 
1028     try {
1029         project = m_ProjectCache.at(m_iSortedIndexes[item]);
1030     } catch ( std::out_of_range ) {
1031         project = NULL;
1032     }
1033 
1034     if (project) {
1035         strBuffer = project->m_strTeamName;
1036     } else {
1037         strBuffer = wxEmptyString;
1038     }
1039 
1040     return 0;
1041 }
1042 
1043 
GetDocTotalCredit(wxInt32 item,double & fBuffer) const1044 void CViewProjects::GetDocTotalCredit(wxInt32 item, double& fBuffer) const {
1045     PROJECT* project = NULL;
1046     CMainDocument* pDoc = wxGetApp().GetDocument();
1047 
1048     if (pDoc) {
1049         project = pDoc->project(item);
1050     }
1051 
1052     if (project) {
1053         fBuffer = project->user_total_credit;
1054     } else {
1055         fBuffer = 0.0;
1056     }
1057 }
1058 
1059 
GetDocAVGCredit(wxInt32 item,double & fBuffer) const1060 void CViewProjects::GetDocAVGCredit(wxInt32 item, double& fBuffer) const {
1061     PROJECT* project = NULL;
1062     CMainDocument* pDoc = wxGetApp().GetDocument();
1063 
1064     if (pDoc) {
1065         project = pDoc->project(item);
1066     }
1067 
1068     if (project) {
1069         fBuffer = project->user_expavg_credit;
1070     } else {
1071         fBuffer = 0.0;
1072     }
1073 }
1074 
GetDocResourceShare(wxInt32 item,double & fBuffer) const1075 void CViewProjects::GetDocResourceShare(wxInt32 item, double& fBuffer) const {
1076     PROJECT* project = NULL;
1077     CMainDocument* pDoc = wxGetApp().GetDocument();
1078 
1079     if (pDoc) {
1080         project = pDoc->project(item);
1081     }
1082 
1083     if (project) {
1084         fBuffer = project->resource_share;
1085     } else {
1086         fBuffer = 0.0;
1087     }
1088 }
1089 
1090 
GetDocResourcePercent(wxInt32 item,double & fBuffer) const1091 void CViewProjects::GetDocResourcePercent(wxInt32 item, double& fBuffer) const {
1092     PROJECT* project = NULL;
1093     CMainDocument* pDoc = wxGetApp().GetDocument();
1094 
1095     if (pDoc) {
1096         project = pDoc->project(item);
1097     }
1098 
1099     if (project && pDoc) {
1100         fBuffer = (project->resource_share / pDoc->m_fProjectTotalResourceShare) * 100;
1101     } else {
1102         fBuffer = 0.0;
1103     }
1104 }
1105 
1106 
FormatResourceShare(double share,double share_pct,wxString & strBuffer) const1107 wxInt32 CViewProjects::FormatResourceShare(double share, double share_pct, wxString& strBuffer) const {
1108     strBuffer.Printf(wxT("%s (%s%%)"), format_number(share, 0), format_number(share_pct, 2));
1109 
1110     return 0;
1111 }
1112 
rpc_reason_string_translated(int reason)1113 wxString rpc_reason_string_translated(int reason) {
1114     switch (reason) {
1115     case RPC_REASON_USER_REQ: return _("Requested by user");
1116     case RPC_REASON_NEED_WORK: return _("To fetch work");
1117     case RPC_REASON_RESULTS_DUE: return _("To report completed tasks");
1118     case RPC_REASON_TRICKLE_UP: return _("To send trickle-up message");
1119     case RPC_REASON_ACCT_MGR_REQ: return _("Requested by account manager");
1120     case RPC_REASON_INIT: return _("Project initialization");
1121     case RPC_REASON_PROJECT_REQ: return _("Requested by project");
1122     default: return _("Unknown reason");
1123     }
1124 }
1125 
GetDocStatus(wxInt32 item,wxString & strBuffer) const1126 void CViewProjects::GetDocStatus(wxInt32 item, wxString& strBuffer) const {
1127     PROJECT* project = NULL;
1128     CMainDocument* pDoc = wxGetApp().GetDocument();
1129 
1130     if (pDoc) {
1131         project = pDoc->project(item);
1132     }
1133 
1134     if (project) {
1135         if (project->suspended_via_gui) {
1136             append_to_status(strBuffer, _("Suspended by user"));
1137         }
1138         if (project->dont_request_more_work) {
1139             append_to_status(strBuffer, _("Won't get new tasks"));
1140         }
1141         if (project->ended) {
1142             append_to_status(strBuffer, _("Project ended - OK to remove"));
1143         }
1144         if (project->detach_when_done) {
1145             append_to_status(strBuffer, _("Will remove when tasks done"));
1146         }
1147         if (project->sched_rpc_pending) {
1148             append_to_status(strBuffer, _("Scheduler request pending"));
1149             append_to_status(strBuffer,
1150                 rpc_reason_string_translated(project->sched_rpc_pending)
1151             );
1152         }
1153         if (project->scheduler_rpc_in_progress) {
1154             append_to_status(strBuffer, _("Scheduler request in progress"));
1155         }
1156         if (project->trickle_up_pending) {
1157             append_to_status(strBuffer, _("Trickle up message pending"));
1158         }
1159         wxDateTime dtNextRPC((time_t)project->min_rpc_time);
1160         wxDateTime dtNow(wxDateTime::Now());
1161         if (dtNextRPC > dtNow) {
1162             wxTimeSpan tsNextRPC(dtNextRPC - dtNow);
1163             append_to_status(strBuffer, _("Communication deferred") + wxString(" ") + tsNextRPC.Format());
1164         }
1165     }
1166 }
1167 
1168 
FormatStatus(wxInt32 item,wxString & strBuffer) const1169 wxInt32 CViewProjects::FormatStatus(wxInt32 item, wxString& strBuffer) const {
1170     CProject* project;
1171 
1172     try {
1173         project = m_ProjectCache.at(m_iSortedIndexes[item]);
1174     } catch ( std::out_of_range ) {
1175         project = NULL;
1176     }
1177 
1178     if (project) {
1179         strBuffer = project->m_strStatus;
1180     } else {
1181         strBuffer = wxEmptyString;
1182     }
1183 
1184     return 0;
1185 }
1186 
1187 
GetDocProjectURL(wxInt32 item,wxString & strBuffer) const1188 void CViewProjects::GetDocProjectURL(wxInt32 item, wxString& strBuffer) const {
1189     PROJECT* project = NULL;
1190     CMainDocument* pDoc = wxGetApp().GetDocument();
1191 
1192     if (pDoc) {
1193         project = pDoc->project(item);
1194     }
1195 
1196     if (project) {
1197         strBuffer = wxString(project->master_url, wxConvUTF8);
1198     } else {
1199         strBuffer = wxEmptyString;
1200     }
1201 }
1202 
1203 
GetProgressValue(long item)1204 double CViewProjects::GetProgressValue(long item) {
1205     CProject* project;
1206 
1207     try {
1208         project = m_ProjectCache.at(m_iSortedIndexes[item]);
1209     } catch ( std::out_of_range ) {
1210         project = NULL;
1211     }
1212 
1213     if (project) {
1214         return (project->m_fResourcePercent) / 100.0;
1215     }
1216 
1217     return 0.0;
1218 }
1219 
1220 
GetProgressText(long item)1221 wxString CViewProjects::GetProgressText( long item) {
1222     CProject* project     = NULL;
1223     wxString       strBuffer = wxEmptyString;
1224 
1225     try {
1226         project = m_ProjectCache.at(m_iSortedIndexes[item]);
1227     } catch ( std::out_of_range ) {
1228         project = NULL;
1229     }
1230 
1231     if (project) {
1232         strBuffer = project->m_strResourceShare;
1233     }
1234     return strBuffer;
1235 }
1236 
1237 
IsWebsiteLink(const wxString & strLink)1238 bool CViewProjects::IsWebsiteLink(const wxString& strLink) {
1239     bool bReturnValue = false;
1240 
1241     if (strLink.StartsWith(wxT("web:")))
1242         bReturnValue = true;
1243 
1244     return bReturnValue;
1245 }
1246 
1247 
ConvertWebsiteIndexToLink(wxInt32 iProjectIndex,wxInt32 iWebsiteIndex,wxString & strLink)1248 wxInt32 CViewProjects::ConvertWebsiteIndexToLink(wxInt32 iProjectIndex, wxInt32 iWebsiteIndex, wxString& strLink) {
1249     strLink.Printf(wxT("web:%d:%d"), iProjectIndex, iWebsiteIndex);
1250     return 0;
1251 }
1252 
1253 
ConvertLinkToWebsiteIndex(const wxString & strLink,wxInt32 & iProjectIndex,wxInt32 & iWebsiteIndex)1254 wxInt32 CViewProjects::ConvertLinkToWebsiteIndex(const wxString& strLink, wxInt32& iProjectIndex, wxInt32& iWebsiteIndex) {
1255     wxString strTemplate = strLink;
1256     wxString strBuffer = wxEmptyString;
1257     long lProjectIndex, lWebsiteIndex;
1258 
1259     strTemplate.Replace(wxT("web:"), wxEmptyString);
1260 
1261     strBuffer = strTemplate;
1262     strBuffer.Remove(strBuffer.Find(wxT(":")));
1263     strBuffer.ToLong((long*) &lProjectIndex);
1264     iProjectIndex = lProjectIndex;
1265 
1266     strBuffer = strTemplate;
1267     strBuffer = strBuffer.Mid(strBuffer.Find(wxT(":")) + 1);
1268     strBuffer.ToLong((long*) &lWebsiteIndex);
1269     iWebsiteIndex = lWebsiteIndex;
1270 
1271     return 0;
1272 }
1273 
1274 
GetProjectCacheAtIndex(CProject * & projectPtr,int index)1275 int CViewProjects::GetProjectCacheAtIndex(CProject*& projectPtr, int index) {
1276     try {
1277         projectPtr = m_ProjectCache.at(index);
1278     } catch ( std::out_of_range ) {
1279         projectPtr = NULL;
1280         return -1;
1281     }
1282 
1283     return 0;
1284 }
1285 
1286