1 // This file is part of BOINC.
2 // http://boinc.berkeley.edu
3 // Copyright (C) 2015 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 "BOINCBaseView.h"
20 #endif
21 
22 #include "stdwx.h"
23 #include "BOINCGUIApp.h"
24 #include "MainDocument.h"
25 #include "BOINCBaseView.h"
26 #include "BOINCTaskCtrl.h"
27 #include "BOINCListCtrl.h"
28 #include "Events.h"
29 
30 #include "res/boinc.xpm"
31 #include "res/sortascending.xpm"
32 #include "res/sortdescending.xpm"
33 
34 
IMPLEMENT_DYNAMIC_CLASS(CBOINCBaseView,wxPanel)35 IMPLEMENT_DYNAMIC_CLASS(CBOINCBaseView, wxPanel)
36 
37 CBOINCBaseView::CBOINCBaseView() {}
38 
CBOINCBaseView(wxNotebook * pNotebook)39 CBOINCBaseView::CBOINCBaseView(wxNotebook* pNotebook) :
40     wxPanel(pNotebook, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL)
41 {
42     m_bProcessingTaskRenderEvent = false;
43     m_bProcessingListRenderEvent = false;
44 
45     m_bForceUpdateSelection = true;
46     m_bIgnoreUIEvents = false;
47     m_bNeedSort = false;
48 
49     m_iPreviousSelectionCount = 0;
50     m_lPreviousFirstSelection = -1;
51 
52     //
53     // Setup View
54     //
55     m_pTaskPane = NULL;
56     m_pListPane = NULL;
57     m_iProgressColumn = -1;
58     m_iSortColumnID = -1;
59     m_SortArrows = NULL;
60 
61     m_aStdColNameOrder = NULL;
62     m_iDefaultShownColumns = NULL;
63     m_iNumDefaultShownColumns = 0;
64 
65     SetName(GetViewName());
66     SetAutoLayout(TRUE);
67 }
68 
69 
CBOINCBaseView(wxNotebook * pNotebook,wxWindowID iTaskWindowID,int iTaskWindowFlags,wxWindowID iListWindowID,int iListWindowFlags)70 CBOINCBaseView::CBOINCBaseView(wxNotebook* pNotebook, wxWindowID iTaskWindowID, int iTaskWindowFlags, wxWindowID iListWindowID, int iListWindowFlags) :
71     wxPanel(pNotebook, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL)
72 {
73     m_bProcessingTaskRenderEvent = false;
74     m_bProcessingListRenderEvent = false;
75 
76     m_bForceUpdateSelection = true;
77     m_bIgnoreUIEvents = false;
78 
79     m_iPreviousSelectionCount = 0;
80     m_lPreviousFirstSelection = -1;
81 
82     //
83     // Setup View
84     //
85     m_pTaskPane = NULL;
86     m_pListPane = NULL;
87 
88     SetName(GetViewName());
89     SetAutoLayout(TRUE);
90 
91     wxFlexGridSizer* itemFlexGridSizer = new wxFlexGridSizer(2, 0, 0);
92     wxASSERT(itemFlexGridSizer);
93 
94     itemFlexGridSizer->AddGrowableRow(0);
95     itemFlexGridSizer->AddGrowableCol(1);
96 
97     m_pTaskPane = new CBOINCTaskCtrl(this, iTaskWindowID, iTaskWindowFlags);
98     wxASSERT(m_pTaskPane);
99 
100     m_pListPane = new CBOINCListCtrl(this, iListWindowID, iListWindowFlags);
101     wxASSERT(m_pListPane);
102 
103     itemFlexGridSizer->Add(m_pTaskPane, 1, wxGROW|wxALL, 1);
104     itemFlexGridSizer->Add(m_pListPane, 1, wxGROW|wxALL, 1);
105 
106     SetSizer(itemFlexGridSizer);
107 
108     UpdateSelection();
109 
110     m_iProgressColumn = -1;
111     m_iSortColumnID = -1;
112     m_bReverseSort = false;
113 
114     m_SortArrows = new wxImageList(16, 16, true);
115     m_SortArrows->Add( wxIcon( sortascending_xpm ) );
116     m_SortArrows->Add( wxIcon( sortdescending_xpm ) );
117     m_pListPane->SetImageList(m_SortArrows, wxIMAGE_LIST_SMALL);
118 
119     m_aStdColNameOrder = NULL;
120     m_iDefaultShownColumns = NULL;
121     m_iNumDefaultShownColumns = 0;
122 }
123 
124 
~CBOINCBaseView()125 CBOINCBaseView::~CBOINCBaseView() {
126     if (m_SortArrows) {
127         delete m_SortArrows;
128     }
129     m_arrSelectedKeys1.Clear();
130     m_arrSelectedKeys2.Clear();
131     m_iSortedIndexes.Clear();
132     if (m_aStdColNameOrder) {
133         delete m_aStdColNameOrder;
134     }
135 }
136 
137 
138 // The name of the view.
139 //   If it has not been defined by the view "Undefined" is returned.
140 //
GetViewName()141 wxString& CBOINCBaseView::GetViewName() {
142     static wxString strViewName(wxT("Undefined"));
143     return strViewName;
144 }
145 
146 
147 // The user friendly name of the view.
148 //   If it has not been defined by the view "Undefined" is returned.
149 //
GetViewDisplayName()150 wxString& CBOINCBaseView::GetViewDisplayName() {
151     static wxString strViewName(wxT("Undefined"));
152     return strViewName;
153 }
154 
155 
156 // The user friendly icon of the view.
157 //   If it has not been defined by the view the BOINC icon is returned.
158 //
GetViewIcon()159 const char** CBOINCBaseView::GetViewIcon() {
160     wxASSERT(boinc_xpm);
161     return boinc_xpm;
162 }
163 
164 
165 // The rate at which the view is refreshed.
166 //   If it has not been defined by the view 1 second is retrned.
167 //
GetViewRefreshRate()168 int CBOINCBaseView::GetViewRefreshRate() {
169     return 1;
170 }
171 
172 
173 // Get bit mask of current view(s).
174 //
GetViewCurrentViewPage()175 int CBOINCBaseView::GetViewCurrentViewPage() {
176     return 0;
177 }
178 
179 
GetKeyValue1(int)180 wxString CBOINCBaseView::GetKeyValue1(int) {
181     return wxEmptyString;
182 }
183 
184 
GetKeyValue2(int)185 wxString CBOINCBaseView::GetKeyValue2(int) {
186     return wxEmptyString;
187 }
188 
189 
FindRowIndexByKeyValues(wxString &,wxString &)190 int CBOINCBaseView::FindRowIndexByKeyValues(wxString&, wxString&) {
191     return -1;
192 }
193 
194 
FireOnSaveState(wxConfigBase * pConfig)195 bool CBOINCBaseView::FireOnSaveState(wxConfigBase* pConfig) {
196     return OnSaveState(pConfig);
197 }
198 
199 
FireOnRestoreState(wxConfigBase * pConfig)200 bool CBOINCBaseView::FireOnRestoreState(wxConfigBase* pConfig) {
201     return OnRestoreState(pConfig);
202 }
203 
204 
GetListRowCount()205 int CBOINCBaseView::GetListRowCount() {
206     wxASSERT(m_pListPane);
207     return m_pListPane->GetItemCount();
208 }
209 
210 
FireOnListRender(wxTimerEvent & event)211 void CBOINCBaseView::FireOnListRender(wxTimerEvent& event) {
212     OnListRender(event);
213 }
214 
215 
FireOnListSelected(wxListEvent & event)216 void CBOINCBaseView::FireOnListSelected(wxListEvent& event) {
217     OnListSelected(event);
218 }
219 
220 
FireOnListDeselected(wxListEvent & event)221 void CBOINCBaseView::FireOnListDeselected(wxListEvent& event) {
222     OnListDeselected(event);
223 }
224 
225 
FireOnListGetItemText(long item,long column) const226 wxString CBOINCBaseView::FireOnListGetItemText(long item, long column) const {
227     return OnListGetItemText(item, column);
228 }
229 
230 
FireOnListGetItemImage(long item) const231 int CBOINCBaseView::FireOnListGetItemImage(long item) const {
232     return OnListGetItemImage(item);
233 }
234 
235 
OnListRender(wxTimerEvent & event)236 void CBOINCBaseView::OnListRender(wxTimerEvent& event) {
237     if (!m_bProcessingListRenderEvent) {
238         m_bProcessingListRenderEvent = true;
239 
240         wxASSERT(m_pListPane);
241 
242         // Remember the key values of currently selected items
243         SaveSelections();
244 
245         int iDocCount = GetDocCount();
246         int iCacheCount = GetCacheCount();
247         if (iDocCount != iCacheCount) {
248             if (0 >= iDocCount) {
249                 EmptyCache();
250                 m_pListPane->DeleteAllItems();
251             } else {
252                 int iIndex = 0;
253                 int iReturnValue = -1;
254                 if (iDocCount > iCacheCount) {
255                     for (iIndex = 0; iIndex < (iDocCount - iCacheCount); iIndex++) {
256                         iReturnValue = AddCacheElement();
257                         wxASSERT(!iReturnValue);
258                     }
259                     wxASSERT(GetDocCount() == GetCacheCount());
260                     m_pListPane->SetItemCount(iDocCount);
261                     m_bNeedSort = true;
262                } else {
263                     // The virtual ListCtrl keeps a separate its list of selected rows;
264                     // make sure it does not reference any rows beyond the new last row.
265                     // We can ClearSelections() because we called SaveSelections() above.
266                     ClearSelections();
267                     m_pListPane->SetItemCount(iDocCount);
268                     for (iIndex = (iCacheCount - 1); iIndex >= iDocCount; --iIndex) {
269                         iReturnValue = RemoveCacheElement();
270                         wxASSERT(!iReturnValue);
271                     }
272                     wxASSERT(GetDocCount() == GetCacheCount());
273 //fprintf(stderr, "CBOINCBaseView::OnListRender(): m_pListPane->RefreshItems(0, %d)\n", iDocCount - 1);
274                     m_pListPane->RefreshItems(0, iDocCount - 1);
275 #ifdef __WXGTK__
276                     // Work around an apparent bug in wxWidgets 3.0
277                     // which drew blank lines at the top and failed
278                     // to draw the bottom items.  This could happen
279                     // if the list was scrolled near the bottom and
280                     // the user selected "Show active tasks."
281                     m_pListPane->EnsureVisible(iDocCount - 1);
282 #endif
283                     m_bNeedSort = true;
284                 }
285             }
286         }
287 
288         if (iDocCount > 0) {
289             SynchronizeCache();
290             if (iDocCount > 1) {
291                 if (_EnsureLastItemVisible() && (iDocCount != iCacheCount)) {
292                     m_pListPane->EnsureVisible(iDocCount - 1);
293                 }
294             }
295         }
296 
297         // Find the previously selected items by their key values and reselect them
298         RestoreSelections();
299 
300         UpdateSelection();
301 
302         m_bProcessingListRenderEvent = false;
303     }
304 
305     event.Skip();
306 }
307 
308 
OnSaveState(wxConfigBase * pConfig)309 bool CBOINCBaseView::OnSaveState(wxConfigBase* pConfig) {
310     bool bReturnValue = true;
311 
312     wxASSERT(pConfig);
313     wxASSERT(m_pTaskPane);
314     wxASSERT(m_pListPane);
315 
316     if (!m_pTaskPane->OnSaveState(pConfig)) {
317         bReturnValue = false;
318     }
319 
320     if (!m_pListPane->OnSaveState(pConfig)) {
321         bReturnValue = false;
322     }
323     return bReturnValue;
324 }
325 
326 
OnRestoreState(wxConfigBase * pConfig)327 bool CBOINCBaseView::OnRestoreState(wxConfigBase* pConfig) {
328     wxASSERT(pConfig);
329     wxASSERT(m_pTaskPane);
330     wxASSERT(m_pListPane);
331 
332     if (!m_pTaskPane->OnRestoreState(pConfig)) {
333         return false;
334     }
335 
336     if (!m_pListPane->OnRestoreState(pConfig)) {
337         return false;
338     }
339     return true;
340 }
341 
342 
343 // We don't use this because multiple selection virtual
344 // wxListCtrl does not generate selection events for
345 // shift-click; see OnCheckSelectionChanged() below.
OnListSelected(wxListEvent & event)346 void CBOINCBaseView::OnListSelected(wxListEvent& event) {
347     wxLogTrace(wxT("Function Start/End"), wxT("CBOINCBaseView::OnListSelected - Function Begin"));
348 
349     if (!m_bIgnoreUIEvents) {
350         m_bForceUpdateSelection = true;
351         UpdateSelection();
352         event.Skip();
353     }
354 
355     wxLogTrace(wxT("Function Start/End"), wxT("CBOINCBaseView::OnListSelected - Function End"));
356 }
357 
358 
359 // We don't use this because multiple selection virtual
360 // wxListCtrl does generates deselection events only for
361 // control-click; see OnCheckSelectionChanged() below.
OnListDeselected(wxListEvent & event)362 void CBOINCBaseView::OnListDeselected(wxListEvent& event) {
363     wxLogTrace(wxT("Function Start/End"), wxT("CBOINCBaseView::OnListDeselected - Function Begin"));
364 
365     if (!m_bIgnoreUIEvents) {
366         m_bForceUpdateSelection = true;
367         UpdateSelection();
368         event.Skip();
369     }
370 
371     wxLogTrace(wxT("Function Start/End"), wxT("CBOINCBaseView::OnListDeselected - Function End"));
372 }
373 
374 
OnCheckSelectionChanged(CCheckSelectionChangedEvent &)375 void CBOINCBaseView::OnCheckSelectionChanged(CCheckSelectionChangedEvent& ) {
376     CheckSelectionChanged();
377 }
378 
379 
OnCacheHint(wxListEvent &)380 void CBOINCBaseView::OnCacheHint(wxListEvent& ) {
381     CheckSelectionChanged();
382 }
383 
384 
385 // Work around features in multiple selection virtual wxListCtrl:
386 //  * It does not send deselection events (except ctrl-click).
387 //  * It does not send selection events if you add to selection
388 //    using Shift_Click.
389 //
390 // We currently handle all selections and deselections here.
391 // On the Mac, this is called due to an event posted by CBOINCListCtrl::OnMouseDown().
392 // On Windows, it is called due to a EVT_LIST_CACHE_HINT from wxListCtrl.
CheckSelectionChanged()393 void CBOINCBaseView::CheckSelectionChanged() {
394     int newSelectionCount = m_pListPane->GetSelectedItemCount();
395     long currentSelection = m_pListPane->GetFirstSelected();
396 
397     if ((newSelectionCount != m_iPreviousSelectionCount) ||
398         (currentSelection != m_lPreviousFirstSelection)
399     ) {
400         if (!m_bIgnoreUIEvents) {
401             m_bForceUpdateSelection = true;
402             UpdateSelection();
403         }
404     }
405 
406     m_iPreviousSelectionCount = newSelectionCount;
407     m_lPreviousFirstSelection = currentSelection;
408 }
409 
410 
OnListGetItemText(long WXUNUSED (item),long WXUNUSED (column)) const411 wxString CBOINCBaseView::OnListGetItemText(long WXUNUSED(item), long WXUNUSED(column)) const {
412     return wxString(wxT("Undefined"));
413 }
414 
415 
OnListGetItemImage(long WXUNUSED (item)) const416 int CBOINCBaseView::OnListGetItemImage(long WXUNUSED(item)) const {
417     return -1;
418 }
419 
420 
GetDocCount()421 int CBOINCBaseView::GetDocCount() {
422     return 0;
423 }
424 
425 
OnDocGetItemImage(long WXUNUSED (item)) const426 wxString CBOINCBaseView::OnDocGetItemImage(long WXUNUSED(item)) const {
427     return wxString(wxT("Undefined"));
428 }
429 
430 
OnDocGetItemAttr(long WXUNUSED (item)) const431 wxString CBOINCBaseView::OnDocGetItemAttr(long WXUNUSED(item)) const {
432     return wxString(wxT("Undefined"));
433 }
434 
435 
AddCacheElement()436 int CBOINCBaseView::AddCacheElement() {
437     return -1;
438 }
439 
440 
EmptyCache()441 int CBOINCBaseView::EmptyCache() {
442     return -1;
443 }
444 
445 
GetCacheCount()446 int CBOINCBaseView::GetCacheCount() {
447     return -1;
448 }
449 
450 
RemoveCacheElement()451 int CBOINCBaseView::RemoveCacheElement() {
452     return -1;
453 }
454 
455 
SynchronizeCache()456 int CBOINCBaseView::SynchronizeCache() {
457     int         iRowIndex        = 0;
458     int         iRowTotal        = 0;
459     int         iColumnIndex     = 0;
460     int         iColumnTotal     = 0;
461     bool        bNeedRefreshData = false;
462 
463     iRowTotal = GetDocCount();
464     iColumnTotal = m_pListPane->GetColumnCount();
465 
466     for (iRowIndex = 0; iRowIndex < iRowTotal; iRowIndex++) {
467         bNeedRefreshData = false;
468 
469         for (iColumnIndex = 0; iColumnIndex < iColumnTotal; iColumnIndex++) {
470             if (SynchronizeCacheItem(iRowIndex, iColumnIndex)) {
471 #ifdef __WXMAC__
472                 bNeedRefreshData = true;
473 #else
474                 // To reduce flicker, refresh only changed columns
475                 m_pListPane->RefreshCell(iRowIndex, iColumnIndex);
476 #endif
477                 if (iColumnIndex >= 0) {
478                     if (m_iColumnIndexToColumnID[iColumnIndex] == m_iSortColumnID) {
479                         m_bNeedSort = true;
480                     }
481                 }
482             }
483         }
484 
485         // Mac is double-buffered to avoid flicker, so this is more efficient
486         if (bNeedRefreshData) {
487             m_pListPane->RefreshItem(iRowIndex);
488         }
489     }
490 
491     if (m_bNeedSort) {
492         sortData();     // Will mark moved items as needing refresh
493         m_bNeedSort = false;
494     }
495 
496     return 0;
497 }
498 
499 
SynchronizeCacheItem(wxInt32 WXUNUSED (iRowIndex),wxInt32 WXUNUSED (iColumnIndex))500 bool CBOINCBaseView::SynchronizeCacheItem(wxInt32 WXUNUSED(iRowIndex), wxInt32 WXUNUSED(iColumnIndex)) {
501     return false;
502 }
503 
504 
OnColClick(wxListEvent & event)505 void CBOINCBaseView::OnColClick(wxListEvent& event) {
506     wxListItem      item;
507     int             newSortColIndex = event.GetColumn();
508     int             oldSortColIndex = -1;
509 
510     if (newSortColIndex < 0) return;  // Clicked past last column
511 
512     if (m_iSortColumnID >= 0) {
513         oldSortColIndex = m_iColumnIDToColumnIndex[m_iSortColumnID];
514     }
515 
516     item.SetMask(wxLIST_MASK_IMAGE);
517     if (newSortColIndex == oldSortColIndex) {
518         m_bReverseSort = !m_bReverseSort;
519         SetSortColumn(newSortColIndex);
520     } else {
521         // Remove sort arrow from old sort column
522 
523         if (oldSortColIndex >= 0) {
524             item.SetImage(-1);
525             m_pListPane->SetColumn(oldSortColIndex, item);
526         }
527         m_iSortColumnID = m_iColumnIndexToColumnID[newSortColIndex];
528         m_bReverseSort = false;
529 
530         SetSortColumn(newSortColIndex);
531     }
532 
533     // Write the change to the registry
534     // Do this here because SetListColumnOrder() can call SetSortColumn()
535     // even when neither m_iSortColumnID nor m_bReverseSort changes
536     wxConfigBase* pConfig = wxConfigBase::Get(false);
537     pConfig->SetPath(wxT("/") + GetViewName());
538     m_pListPane->OnSaveState(pConfig);
539 }
540 
SetSortColumn(int newSortColIndex)541 void CBOINCBaseView::SetSortColumn(int newSortColIndex) {
542     wxListItem      item;
543     int             i, j, m;
544     wxArrayInt      selections;
545 
546     item.SetImage(m_bReverseSort ? 0 : 1);
547     m_pListPane->SetColumn(newSortColIndex, item);
548 
549     Freeze();   // To reduce flicker
550     // Remember which cache elements are selected and deselect them
551     m_bIgnoreUIEvents = true;
552     i = -1;
553     while (1) {
554         i = m_pListPane->GetNextSelected(i);
555         if (i < 0) break;
556         selections.Add(m_iSortedIndexes[i]);
557         m_pListPane->SelectRow(i, false);
558     }
559 
560     sortData();
561 
562     // Reselect previously selected cache elements in the sorted list
563     m = (int)selections.GetCount();
564     for (i=0; i<m; i++) {
565         if (selections[i] >= 0) {
566             j = m_iSortedIndexes.Index(selections[i]);
567             m_pListPane->SelectRow(j, true);
568         }
569     }
570     m_bIgnoreUIEvents = false;
571 
572     Thaw();
573 }
574 
575 
InitSort()576 void CBOINCBaseView::InitSort() {
577     wxListItem      item;
578 
579     if (m_iSortColumnID < 0) return;
580     int newSortColIndex = m_iColumnIDToColumnIndex[m_iSortColumnID];
581     if (newSortColIndex < 0) return;
582 
583     item.SetMask(wxLIST_MASK_IMAGE);
584     item.SetImage(m_bReverseSort ? 0 : 1);
585     m_pListPane->SetColumn(newSortColIndex, item);
586     Freeze();   // To reduce flicker
587     sortData();
588     Thaw();
589 }
590 
591 
sortData()592 void CBOINCBaseView::sortData() {
593     if (m_iSortColumnID < 0) return;
594     if (m_iColumnIDToColumnIndex[m_iSortColumnID] < 0) return;
595 
596     wxArrayInt oldSortedIndexes(m_iSortedIndexes);
597     int i, n = (int)m_iSortedIndexes.GetCount();
598 
599     std::stable_sort(m_iSortedIndexes.begin(), m_iSortedIndexes.end(), m_funcSortCompare);
600 
601     // Refresh rows which have moved
602     for (i=0; i<n; i++) {
603         if (m_iSortedIndexes[i] != oldSortedIndexes[i]) {
604             m_pListPane->RefreshItem(i);
605          }
606     }
607 }
608 
609 
EmptyTasks()610 void CBOINCBaseView::EmptyTasks() {
611     unsigned int i;
612     unsigned int j;
613     for (i=0; i<m_TaskGroups.size(); i++) {
614         for (j=0; j<m_TaskGroups[i]->m_Tasks.size(); j++) {
615             delete m_TaskGroups[i]->m_Tasks[j];
616         }
617         m_TaskGroups[i]->m_Tasks.clear();
618         delete m_TaskGroups[i];
619     }
620     m_TaskGroups.clear();
621 }
622 
623 
ClearSavedSelections()624 void CBOINCBaseView::ClearSavedSelections() {
625     m_arrSelectedKeys1.Clear();
626     m_arrSelectedKeys2.Clear();
627 }
628 
629 
630 // Save the key values of the currently selected rows for later restore
SaveSelections()631 void CBOINCBaseView::SaveSelections() {
632     if (!_IsSelectionManagementNeeded()) {
633         return;
634     }
635 
636     m_arrSelectedKeys1.Clear();
637     m_arrSelectedKeys2.Clear();
638     m_bIgnoreUIEvents = true;
639     int i = -1;
640     while (1) {
641         i = m_pListPane->GetNextSelected(i);
642         if (i < 0) break;
643         m_arrSelectedKeys1.Add(GetKeyValue1(i));
644         m_arrSelectedKeys2.Add(GetKeyValue2(i));
645     }
646     m_bIgnoreUIEvents = false;
647 }
648 
649 // Select all rows with formerly selected data based on key values
650 //
651 // Each RPC may add (insert) or delete items from the list, so
652 // the selected row numbers may now point to different data.
653 // We called SaveSelections() before updating the underlying
654 // data; this routine finds the data corresponding to each
655 // previous selection and makes any adjustments to ensure that
656 // the rows containing the originally selected data are selected.
RestoreSelections()657 void CBOINCBaseView::RestoreSelections() {
658     if (!_IsSelectionManagementNeeded()) {
659         return;
660     }
661 
662     // To minimize flicker, this method selects or deselects only
663     // those rows which actually need their selection status changed.
664     // First, get a list of which rows should be selected
665     int i, j, m = 0, newCount = 0, oldCount = (int)m_arrSelectedKeys1.size();
666     bool found;
667     wxArrayInt arrSelRows;
668     for(i=0; i< oldCount; ++i) {
669 		int index = FindRowIndexByKeyValues(m_arrSelectedKeys1[i], m_arrSelectedKeys2[i]);
670 		if(index >= 0) {
671             arrSelRows.Add(index);
672 		}
673 	}
674     newCount = (int)arrSelRows.GetCount();
675 
676     // Step through the currently selected row numbers and for each one determine
677     // whether it should remain selected.
678     m_bIgnoreUIEvents = true;
679     i = -1;
680     while (1) {
681         found = false;
682         i = m_pListPane->GetNextSelected(i);
683         if (i < 0) break;
684         m = (int)arrSelRows.GetCount();
685         for (j=0; j<m; ++j) {
686             if (arrSelRows[j] == i) {
687                 arrSelRows.RemoveAt(j); // We have handled this one so remove from list
688                 found = true;           // Already selected, so no change needed
689                 break;
690             }
691         }
692         if (!found) {
693             m_pListPane->SelectRow(i, false);  // This row should no longer be selected
694         }
695     }
696 
697     // Now select those rows which were not previously selected but should now be
698     m = (int)arrSelRows.GetCount();
699     for (j=0; j<m; ++j) {
700         m_pListPane->SelectRow(arrSelRows[j], true);
701     }
702     m_bIgnoreUIEvents = false;
703 
704     if (oldCount != newCount) {
705         m_bForceUpdateSelection = true; // OnListRender() will call UpdateSelection()
706     }
707 }
708 
709 
ClearSelections()710 void CBOINCBaseView::ClearSelections() {
711     if (!m_pListPane) return;
712 
713     m_bIgnoreUIEvents = true;
714     int i = -1;
715     while (1) {
716         i = m_pListPane->GetNextSelected(i);
717         if (i < 0) break;
718         m_pListPane->SelectRow(i, false);
719     }
720     m_bIgnoreUIEvents = false;
721 }
722 
723 
PreUpdateSelection()724 void CBOINCBaseView::PreUpdateSelection(){
725 }
726 
727 
UpdateSelection()728 void CBOINCBaseView::UpdateSelection(){
729 }
730 
731 
PostUpdateSelection()732 void CBOINCBaseView::PostUpdateSelection(){
733     wxASSERT(m_pTaskPane);
734     if (m_pTaskPane->UpdateControls()) {
735         // Under wxWidgets 2.9.4, Layout() causes ListCtrl
736         // to repaint, so call only when actually needed.
737         Layout();
738     }
739 }
740 
741 
UpdateWebsiteSelection(long lControlGroup,PROJECT * project)742 void CBOINCBaseView::UpdateWebsiteSelection(long lControlGroup, PROJECT* project){
743     unsigned int        i;
744     CTaskItemGroup*     pGroup = NULL;
745     CTaskItem*          pItem = NULL;
746 
747     wxASSERT(m_pTaskPane);
748     wxASSERT(m_pListPane);
749 
750     if (m_bForceUpdateSelection) {
751 
752         // Update the websites list
753         //
754         if (m_TaskGroups.size() > 1) {
755 
756             // Delete task group, objects, and controls.
757             pGroup = m_TaskGroups[lControlGroup];
758 
759             m_pTaskPane->DeleteTaskGroupAndTasks(pGroup);
760             for (i=0; i<pGroup->m_Tasks.size(); i++) {
761                 delete pGroup->m_Tasks[i];
762             }
763             pGroup->m_Tasks.clear();
764             delete pGroup;
765 
766             pGroup = NULL;
767 
768             m_TaskGroups.erase( m_TaskGroups.begin() + 1 );
769         }
770 
771         // If something is selected create the tasks and controls
772         //
773         if (m_pListPane->GetSelectedItemCount()) {
774             if (project) {
775                 // Create the web sites task group
776                 pGroup = new CTaskItemGroup( _("Project web pages") );
777                 m_TaskGroups.push_back( pGroup );
778 
779                 // Default project url
780                 pItem = new CTaskItem(
781                     wxString("Home page", wxConvUTF8),
782                     wxString(project->project_name.c_str(), wxConvUTF8) + wxT(" web site"),
783                     wxString(project->master_url, wxConvUTF8),
784                     ID_TASK_PROJECT_WEB_PROJDEF_MIN
785                 );
786                 pGroup->m_Tasks.push_back(pItem);
787 
788 
789                 // Project defined urls
790                 for (i=0;(i<project->gui_urls.size())&&(i<=ID_TASK_PROJECT_WEB_PROJDEF_MAX);i++) {
791                     pItem = new CTaskItem(
792                         wxGetTranslation(wxString(project->gui_urls[i].name.c_str(), wxConvUTF8)),
793                         wxGetTranslation(wxString(project->gui_urls[i].description.c_str(), wxConvUTF8)),
794                         wxString(project->gui_urls[i].url.c_str(), wxConvUTF8),
795                         ID_TASK_PROJECT_WEB_PROJDEF_MIN + 1 + i
796                     );
797                     pGroup->m_Tasks.push_back(pItem);
798                 }
799             }
800         }
801 
802         m_pTaskPane->FitInside();
803         m_bForceUpdateSelection = false;
804     }
805 }
806 
807 
808 // Make sure task pane background is properly erased
RefreshTaskPane()809 void CBOINCBaseView::RefreshTaskPane() {
810     if (m_pTaskPane) {
811         m_pTaskPane->Refresh(true);
812     }
813 }
814 
815 
816 #ifdef __WXMAC__
817 // Fix Keyboard navigation on Mac
818 //
819 // NOTE: to select an item in wxListCtrl when none
820 // has yet been selected, press tab and then space.
821 #define SHIFT_MASK (1<<17)
822 
OnKeyPressed(wxKeyEvent & event)823 void CBOINCBaseView::OnKeyPressed(wxKeyEvent &event) {
824     wxWindow        next;
825     CTaskItemGroup* pGroup = NULL;
826     CTaskItem*      pItem = NULL;
827     int             i, j;
828     bool            focusOK = false;
829 
830     if (m_pTaskPane) {
831         int keyCode = event.GetKeyCode();
832         wxUint32 keyFlags = event.GetRawKeyFlags();
833 
834         if (keyCode == WXK_TAB) {
835             wxWindow* focused = wxWindow::FindFocus();
836             if (!m_pTaskPane->IsDescendant(focused)) {
837                 if (keyFlags & SHIFT_MASK) {
838                     for (i=m_TaskGroups.size()-1; i>=0; --i) {
839                         pGroup = m_TaskGroups[i];
840                         for (j=pGroup->m_Tasks.size()-1; j>=0; --j) {
841                             pItem = pGroup->m_Tasks[j];
842                             if (pItem->m_pButton) {
843                                 if (pItem->m_pButton->CanAcceptFocus()) {
844                                     focusOK = true;
845                                     break;
846                                 }
847                             }
848                         }
849                         if (focusOK) break;
850                     }
851                 } else {
852                    for (i=0; i<m_TaskGroups.size(); ++i) {
853                         pGroup = m_TaskGroups[i];
854                         for (j=0; j<pGroup->m_Tasks.size(); ++j) {
855                             pItem = pGroup->m_Tasks[j];
856                             if (pItem->m_pButton) {
857                                 if (pItem->m_pButton->CanAcceptFocus()) {
858                                     focusOK = true;
859                                     break;
860                                 }
861                             }
862                         }
863                         if (focusOK) break;
864                     }
865                 }
866                 if (focusOK) {
867                     pItem->m_pButton->SetFocus();
868                     return;
869                 }
870             }
871             wxNavigationKeyEvent evt;
872             evt.SetDirection((keyFlags & SHIFT_MASK) == 0);
873             evt.SetFromTab(true);
874             m_pTaskPane->GetEventHandler()->AddPendingEvent(evt);
875             return;
876         }
877     }
878     event.Skip();
879 }
880 #endif
881 
882 
_IsSelectionManagementNeeded()883 bool CBOINCBaseView::_IsSelectionManagementNeeded() {
884     return IsSelectionManagementNeeded();
885 }
886 
887 
IsSelectionManagementNeeded()888 bool CBOINCBaseView::IsSelectionManagementNeeded() {
889     return false;
890 }
891 
892 
_EnsureLastItemVisible()893 bool CBOINCBaseView::_EnsureLastItemVisible() {
894     return EnsureLastItemVisible();
895 }
896 
897 
EnsureLastItemVisible()898 bool CBOINCBaseView::EnsureLastItemVisible() {
899     return false;
900 }
901 
902 
GetProgressValue(long)903 double CBOINCBaseView::GetProgressValue(long) {
904     return 0.0;
905 }
906 
907 
GetProgressText(long)908 wxString CBOINCBaseView::GetProgressText( long ) {
909     return wxEmptyString;
910 }
911 
AppendColumn(int)912 void CBOINCBaseView::AppendColumn(int){
913 }
914 
915 
append_to_status(wxString & existing,const wxString & additional)916 void CBOINCBaseView::append_to_status(wxString& existing, const wxString& additional) {
917     if (existing.size() == 0) {
918         existing = additional;
919     } else {
920         existing += wxT(", ") + additional;
921     }
922 }
923 
924 
925 // HTML Entity Conversion:
926 // http://www.webreference.com/html/reference/character/
927 // Completed: The ISO Latin 1 Character Set
928 //
HtmlEntityEncode(wxString strRaw)929 wxString CBOINCBaseView::HtmlEntityEncode(wxString strRaw) {
930 	wxString strEncodedHtml(strRaw);
931 
932 #ifdef __WXMSW__
933     strEncodedHtml.Replace(wxT("&"),  wxT("&amp;"),    true);
934     strEncodedHtml.Replace(wxT("\""), wxT("&quot;"),   true);
935     strEncodedHtml.Replace(wxT("<"),  wxT("&lt;"),     true);
936     strEncodedHtml.Replace(wxT(">"),  wxT("&gt;"),     true);
937     strEncodedHtml.Replace(wxT("‚"),  wxT("&sbquo;"),  true);
938     strEncodedHtml.Replace(wxT("ƒ"),  wxT("&fnof;"),   true);
939     strEncodedHtml.Replace(wxT("„"),  wxT("&bdquo;"),  true);
940     strEncodedHtml.Replace(wxT("…"),  wxT("&hellip;"), true);
941     strEncodedHtml.Replace(wxT("†"),  wxT("&dagger;"), true);
942     strEncodedHtml.Replace(wxT("‡"),  wxT("&Dagger;"), true);
943     strEncodedHtml.Replace(wxT("Š"),  wxT("&Scaron;"), true);
944     strEncodedHtml.Replace(wxT("Œ"),  wxT("&OElig;"),  true);
945     strEncodedHtml.Replace(wxT("‘"),  wxT("&lsquo;"),  true);
946     strEncodedHtml.Replace(wxT("’"),  wxT("&rsquo;"),  true);
947     strEncodedHtml.Replace(wxT("“"),  wxT("&ldquo;"),  true);
948     strEncodedHtml.Replace(wxT("”"),  wxT("&rdquo;"),  true);
949     strEncodedHtml.Replace(wxT("•"),  wxT("&bull;"),   true);
950     strEncodedHtml.Replace(wxT("–"),  wxT("&ndash;"),  true);
951     strEncodedHtml.Replace(wxT("—"),  wxT("&mdash;"),  true);
952     strEncodedHtml.Replace(wxT("˜˜~"),  wxT("&tilde;"),  true);
953     strEncodedHtml.Replace(wxT("™"),  wxT("&trade;"),  true);
954     strEncodedHtml.Replace(wxT("š"),  wxT("&scaron;"), true);
955     strEncodedHtml.Replace(wxT("œ"),  wxT("&oelig;"),  true);
956     strEncodedHtml.Replace(wxT("Ÿ"),  wxT("&Yuml;"),   true);
957     strEncodedHtml.Replace(wxT("¡"),  wxT("&iexcl;"),  true);
958     strEncodedHtml.Replace(wxT("¢"),  wxT("&cent;"),   true);
959     strEncodedHtml.Replace(wxT("£"),  wxT("&pound;"),  true);
960     strEncodedHtml.Replace(wxT("¤"),  wxT("&curren;"), true);
961     strEncodedHtml.Replace(wxT("¥"),  wxT("&yen;"),    true);
962     strEncodedHtml.Replace(wxT("¦"),  wxT("&brvbar;"), true);
963     strEncodedHtml.Replace(wxT("§"),  wxT("&sect;"),   true);
964     strEncodedHtml.Replace(wxT("¨"),  wxT("&uml;"),    true);
965     strEncodedHtml.Replace(wxT("©"),  wxT("&copy;"),   true);
966     strEncodedHtml.Replace(wxT("ª"),  wxT("&ordf;"),   true);
967     strEncodedHtml.Replace(wxT("«"),  wxT("&laquo;"),  true);
968     strEncodedHtml.Replace(wxT("¬"),  wxT("&not;"),    true);
969     strEncodedHtml.Replace(wxT("®"),  wxT("&reg;"),    true);
970     strEncodedHtml.Replace(wxT("¯"),  wxT("&macr;"),   true);
971     strEncodedHtml.Replace(wxT("°"),  wxT("&deg;"),    true);
972     strEncodedHtml.Replace(wxT("±"),  wxT("&plusmn;"), true);
973     strEncodedHtml.Replace(wxT("²"),  wxT("&sup2;"),   true);
974     strEncodedHtml.Replace(wxT("³"),  wxT("&sup3;"),   true);
975     strEncodedHtml.Replace(wxT("´"),  wxT("&acute;"),  true);
976     strEncodedHtml.Replace(wxT("µ"),  wxT("&micro;"),  true);
977     strEncodedHtml.Replace(wxT("¶"),  wxT("&para;"),   true);
978     strEncodedHtml.Replace(wxT("·"),  wxT("&middot;"), true);
979     strEncodedHtml.Replace(wxT("¸"),  wxT("&cedil;"),  true);
980     strEncodedHtml.Replace(wxT("¹"),  wxT("&sup1;"),   true);
981     strEncodedHtml.Replace(wxT("º"),  wxT("&ordm;"),   true);
982     strEncodedHtml.Replace(wxT("»"),  wxT("&raquo;"),  true);
983     strEncodedHtml.Replace(wxT("¼"),  wxT("&frac14;"), true);
984     strEncodedHtml.Replace(wxT("½"),  wxT("&frac12;"), true);
985     strEncodedHtml.Replace(wxT("¾"),  wxT("&frac34;"), true);
986     strEncodedHtml.Replace(wxT("¿"),  wxT("&iquest;"), true);
987     strEncodedHtml.Replace(wxT("À"),  wxT("&Agrave;"), true);
988     strEncodedHtml.Replace(wxT("Á"),  wxT("&Aacute;"), true);
989     strEncodedHtml.Replace(wxT("Â"),  wxT("&Acirc;"),  true);
990     strEncodedHtml.Replace(wxT("Ã"),  wxT("&Atilde;"), true);
991     strEncodedHtml.Replace(wxT("Ä"),  wxT("&Auml;"),   true);
992     strEncodedHtml.Replace(wxT("Å"),  wxT("&Aring;"),  true);
993     strEncodedHtml.Replace(wxT("Æ"),  wxT("&AElig;"),  true);
994     strEncodedHtml.Replace(wxT("Ç"),  wxT("&Ccedil;"), true);
995     strEncodedHtml.Replace(wxT("È"),  wxT("&Egrave;"), true);
996     strEncodedHtml.Replace(wxT("É"),  wxT("&Eacute;"), true);
997     strEncodedHtml.Replace(wxT("Ê"),  wxT("&Ecirc;"),  true);
998     strEncodedHtml.Replace(wxT("Ë"),  wxT("&Euml;"),   true);
999     strEncodedHtml.Replace(wxT("Ì"),  wxT("&Igrave;"), true);
1000     strEncodedHtml.Replace(wxT("Í"),  wxT("&Iacute;"), true);
1001     strEncodedHtml.Replace(wxT("Î"),  wxT("&Icirc;"),  true);
1002     strEncodedHtml.Replace(wxT("Ï"),  wxT("&Iuml;"),   true);
1003     strEncodedHtml.Replace(wxT("Ð"),  wxT("&ETH;"),    true);
1004     strEncodedHtml.Replace(wxT("Ñ"),  wxT("&Ntilde;"), true);
1005     strEncodedHtml.Replace(wxT("Ò"),  wxT("&Ograve;"), true);
1006     strEncodedHtml.Replace(wxT("Ó"),  wxT("&Oacute;"), true);
1007     strEncodedHtml.Replace(wxT("Ô"),  wxT("&Ocirc;"),  true);
1008     strEncodedHtml.Replace(wxT("Õ"),  wxT("&Otilde;"), true);
1009     strEncodedHtml.Replace(wxT("Ö"),  wxT("&Ouml;"),   true);
1010     strEncodedHtml.Replace(wxT("×"),  wxT("&times;"),  true);
1011     strEncodedHtml.Replace(wxT("Ø"),  wxT("&Oslash;"), true);
1012     strEncodedHtml.Replace(wxT("Ù"),  wxT("&Ugrave;"), true);
1013     strEncodedHtml.Replace(wxT("Ú"),  wxT("&Uacute;"), true);
1014     strEncodedHtml.Replace(wxT("Û"),  wxT("&Ucirc;"),  true);
1015     strEncodedHtml.Replace(wxT("Ü"),  wxT("&Uuml;"),   true);
1016     strEncodedHtml.Replace(wxT("Ý"),  wxT("&Yacute;"), true);
1017     strEncodedHtml.Replace(wxT("Þ"),  wxT("&THORN;"),  true);
1018     strEncodedHtml.Replace(wxT("ß"),  wxT("&szlig;"),  true);
1019     strEncodedHtml.Replace(wxT("à"),  wxT("&agrave;"), true);
1020     strEncodedHtml.Replace(wxT("á"),  wxT("&aacute;"), true);
1021     strEncodedHtml.Replace(wxT("â"),  wxT("&acirc;"),  true);
1022     strEncodedHtml.Replace(wxT("ã"),  wxT("&atilde;"), true);
1023     strEncodedHtml.Replace(wxT("ä"),  wxT("&auml;"),   true);
1024     strEncodedHtml.Replace(wxT("å"),  wxT("&aring;"),  true);
1025     strEncodedHtml.Replace(wxT("æ"),  wxT("&aelig;"),  true);
1026     strEncodedHtml.Replace(wxT("ç"),  wxT("&ccedil;"), true);
1027     strEncodedHtml.Replace(wxT("è"),  wxT("&egrave;"), true);
1028     strEncodedHtml.Replace(wxT("é"),  wxT("&eacute;"), true);
1029     strEncodedHtml.Replace(wxT("ê"),  wxT("&ecirc;"),  true);
1030     strEncodedHtml.Replace(wxT("ë"),  wxT("&euml;"),   true);
1031     strEncodedHtml.Replace(wxT("ì"),  wxT("&igrave;"), true);
1032     strEncodedHtml.Replace(wxT("í"),  wxT("&iacute;"), true);
1033     strEncodedHtml.Replace(wxT("î"),  wxT("&icirc;"),  true);
1034     strEncodedHtml.Replace(wxT("ï"),  wxT("&iuml;"),   true);
1035     strEncodedHtml.Replace(wxT("ð"),  wxT("&eth;"),    true);
1036     strEncodedHtml.Replace(wxT("ñ"),  wxT("&ntilde;"), true);
1037     strEncodedHtml.Replace(wxT("ò"),  wxT("&ograve;"), true);
1038     strEncodedHtml.Replace(wxT("ó"),  wxT("&oacute;"), true);
1039     strEncodedHtml.Replace(wxT("ô"),  wxT("&ocirc;"),  true);
1040     strEncodedHtml.Replace(wxT("õ"),  wxT("&otilde;"), true);
1041     strEncodedHtml.Replace(wxT("ö"),  wxT("&ouml;"),   true);
1042     strEncodedHtml.Replace(wxT("÷"),  wxT("&divide;"), true);
1043     strEncodedHtml.Replace(wxT("ø"),  wxT("&oslash;"), true);
1044     strEncodedHtml.Replace(wxT("ù"),  wxT("&ugrave;"), true);
1045     strEncodedHtml.Replace(wxT("ú"),  wxT("&uacute;"), true);
1046     strEncodedHtml.Replace(wxT("û"),  wxT("&ucirc;"),  true);
1047     strEncodedHtml.Replace(wxT("ü"),  wxT("&uuml;"),   true);
1048     strEncodedHtml.Replace(wxT("ý"),  wxT("&yacute;"), true);
1049     strEncodedHtml.Replace(wxT("þ"),  wxT("&thorn;"),  true);
1050     strEncodedHtml.Replace(wxT("ÿ"),  wxT("&yuml;"),   true);
1051 #endif
1052 
1053     return strEncodedHtml;
1054 }
1055 
HtmlEntityDecode(wxString strRaw)1056 wxString CBOINCBaseView::HtmlEntityDecode(wxString strRaw) {
1057 	wxString strDecodedHtml(strRaw);
1058 
1059     if (0 <= strDecodedHtml.Find(wxT("&"))) {
1060 #ifdef __WXMSW__
1061         strDecodedHtml.Replace(wxT("&amp;"),    wxT("&"),  true);
1062         strDecodedHtml.Replace(wxT("&quot;"),   wxT("\""), true);
1063         strDecodedHtml.Replace(wxT("&lt;"),     wxT("<"),  true);
1064         strDecodedHtml.Replace(wxT("&gt;"),     wxT(">"),  true);
1065         strDecodedHtml.Replace(wxT("&sbquo;"),  wxT("‚"),  true);
1066         strDecodedHtml.Replace(wxT("&fnof;"),   wxT("ƒ"),  true);
1067         strDecodedHtml.Replace(wxT("&bdquo;"),  wxT("„"),  true);
1068         strDecodedHtml.Replace(wxT("&hellip;"), wxT("…"),  true);
1069         strDecodedHtml.Replace(wxT("&dagger;"), wxT("†"),  true);
1070         strDecodedHtml.Replace(wxT("&Dagger;"), wxT("‡"),  true);
1071         strDecodedHtml.Replace(wxT("&Scaron;"), wxT("Š"),  true);
1072         strDecodedHtml.Replace(wxT("&OElig;"),  wxT("Œ"),  true);
1073         strDecodedHtml.Replace(wxT("&lsquo;"),  wxT("‘"),  true);
1074         strDecodedHtml.Replace(wxT("&rsquo;"),  wxT("’"),  true);
1075         strDecodedHtml.Replace(wxT("&ldquo;"),  wxT("“"),  true);
1076         strDecodedHtml.Replace(wxT("&rdquo;"),  wxT("”"),  true);
1077         strDecodedHtml.Replace(wxT("&bull;"),   wxT("•"),  true);
1078         strDecodedHtml.Replace(wxT("&ndash;"),  wxT("–"),  true);
1079         strDecodedHtml.Replace(wxT("&mdash;"),  wxT("—"),  true);
1080         strDecodedHtml.Replace(wxT("&tilde;"),  wxT("˜˜~"),  true);
1081         strDecodedHtml.Replace(wxT("&trade;"),  wxT("™"),  true);
1082         strDecodedHtml.Replace(wxT("&scaron;"), wxT("š"),  true);
1083         strDecodedHtml.Replace(wxT("&oelig;"),  wxT("œ"),  true);
1084         strDecodedHtml.Replace(wxT("&Yuml;"),   wxT("Ÿ"),  true);
1085         strDecodedHtml.Replace(wxT("&iexcl;"),  wxT("¡"),  true);
1086         strDecodedHtml.Replace(wxT("&cent;"),   wxT("¢"),  true);
1087         strDecodedHtml.Replace(wxT("&pound;"),  wxT("£"),  true);
1088         strDecodedHtml.Replace(wxT("&curren;"), wxT("¤"),  true);
1089         strDecodedHtml.Replace(wxT("&yen;"),    wxT("¥"),  true);
1090         strDecodedHtml.Replace(wxT("&brvbar;"), wxT("¦"),  true);
1091         strDecodedHtml.Replace(wxT("&sect;"),   wxT("§"),  true);
1092         strDecodedHtml.Replace(wxT("&uml;"),    wxT("¨"),  true);
1093         strDecodedHtml.Replace(wxT("&copy;"),   wxT("©"),  true);
1094         strDecodedHtml.Replace(wxT("&ordf;"),   wxT("ª"),  true);
1095         strDecodedHtml.Replace(wxT("&laquo;"),  wxT("«"),  true);
1096         strDecodedHtml.Replace(wxT("&not;"),    wxT("¬"),  true);
1097         strDecodedHtml.Replace(wxT("&reg;"),    wxT("®"),  true);
1098         strDecodedHtml.Replace(wxT("&macr;"),   wxT("¯"),  true);
1099         strDecodedHtml.Replace(wxT("&deg;"),    wxT("°"),  true);
1100         strDecodedHtml.Replace(wxT("&plusmn;"), wxT("±"),  true);
1101         strDecodedHtml.Replace(wxT("&sup2;"),   wxT("²"),  true);
1102         strDecodedHtml.Replace(wxT("&sup3;"),   wxT("³"),  true);
1103         strDecodedHtml.Replace(wxT("&acute;"),  wxT("´"),  true);
1104         strDecodedHtml.Replace(wxT("&micro;"),  wxT("µ"),  true);
1105         strDecodedHtml.Replace(wxT("&para;"),   wxT("¶"),  true);
1106         strDecodedHtml.Replace(wxT("&middot;"), wxT("·"),  true);
1107         strDecodedHtml.Replace(wxT("&cedil;"),  wxT("¸"),  true);
1108         strDecodedHtml.Replace(wxT("&sup1;"),   wxT("¹"),  true);
1109         strDecodedHtml.Replace(wxT("&ordm;"),   wxT("º"),  true);
1110         strDecodedHtml.Replace(wxT("&raquo;"),  wxT("»"),  true);
1111         strDecodedHtml.Replace(wxT("&frac14;"), wxT("¼"),  true);
1112         strDecodedHtml.Replace(wxT("&frac12;"), wxT("½"),  true);
1113         strDecodedHtml.Replace(wxT("&frac34;"), wxT("¾"),  true);
1114         strDecodedHtml.Replace(wxT("&iquest;"), wxT("¿"),  true);
1115         strDecodedHtml.Replace(wxT("&Agrave;"), wxT("À"),  true);
1116         strDecodedHtml.Replace(wxT("&Aacute;"), wxT("Á"),  true);
1117         strDecodedHtml.Replace(wxT("&Acirc;"),  wxT("Â"),  true);
1118         strDecodedHtml.Replace(wxT("&Atilde;"), wxT("Ã"),  true);
1119         strDecodedHtml.Replace(wxT("&Auml;"),   wxT("Ä"),  true);
1120         strDecodedHtml.Replace(wxT("&Aring;"),  wxT("Å"),  true);
1121         strDecodedHtml.Replace(wxT("&AElig;"),  wxT("Æ"),  true);
1122         strDecodedHtml.Replace(wxT("&Ccedil;"), wxT("Ç"),  true);
1123         strDecodedHtml.Replace(wxT("&Egrave;"), wxT("È"),  true);
1124         strDecodedHtml.Replace(wxT("&Eacute;"), wxT("É"),  true);
1125         strDecodedHtml.Replace(wxT("&Ecirc;"),  wxT("Ê"),  true);
1126         strDecodedHtml.Replace(wxT("&Euml;"),   wxT("Ë"),  true);
1127         strDecodedHtml.Replace(wxT("&Igrave;"), wxT("Ì"),  true);
1128         strDecodedHtml.Replace(wxT("&Iacute;"), wxT("Í"),  true);
1129         strDecodedHtml.Replace(wxT("&Icirc;"),  wxT("Î"),  true);
1130         strDecodedHtml.Replace(wxT("&Iuml;"),   wxT("Ï"),  true);
1131         strDecodedHtml.Replace(wxT("&ETH;"),    wxT("Ð"),  true);
1132         strDecodedHtml.Replace(wxT("&Ntilde;"), wxT("Ñ"),  true);
1133         strDecodedHtml.Replace(wxT("&Ograve;"), wxT("Ò"),  true);
1134         strDecodedHtml.Replace(wxT("&Oacute;"), wxT("Ó"),  true);
1135         strDecodedHtml.Replace(wxT("&Ocirc;"),  wxT("Ô"),  true);
1136         strDecodedHtml.Replace(wxT("&Otilde;"), wxT("Õ"),  true);
1137         strDecodedHtml.Replace(wxT("&Ouml;"),   wxT("Ö"),  true);
1138         strDecodedHtml.Replace(wxT("&times;"),  wxT("×"),  true);
1139         strDecodedHtml.Replace(wxT("&Oslash;"), wxT("Ø"),  true);
1140         strDecodedHtml.Replace(wxT("&Ugrave;"), wxT("Ù"),  true);
1141         strDecodedHtml.Replace(wxT("&Uacute;"), wxT("Ú"),  true);
1142         strDecodedHtml.Replace(wxT("&Ucirc;"),  wxT("Û"),  true);
1143         strDecodedHtml.Replace(wxT("&Uuml;"),   wxT("Ü"),  true);
1144         strDecodedHtml.Replace(wxT("&Yacute;"), wxT("Ý"),  true);
1145         strDecodedHtml.Replace(wxT("&THORN;"),  wxT("Þ"),  true);
1146         strDecodedHtml.Replace(wxT("&szlig;"),  wxT("ß"),  true);
1147         strDecodedHtml.Replace(wxT("&agrave;"), wxT("à"),  true);
1148         strDecodedHtml.Replace(wxT("&aacute;"), wxT("á"),  true);
1149         strDecodedHtml.Replace(wxT("&acirc;"),  wxT("â"),  true);
1150         strDecodedHtml.Replace(wxT("&atilde;"), wxT("ã"),  true);
1151         strDecodedHtml.Replace(wxT("&auml;"),   wxT("ä"),  true);
1152         strDecodedHtml.Replace(wxT("&aring;"),  wxT("å"),  true);
1153         strDecodedHtml.Replace(wxT("&aelig;"),  wxT("æ"),  true);
1154         strDecodedHtml.Replace(wxT("&ccedil;"), wxT("ç"),  true);
1155         strDecodedHtml.Replace(wxT("&egrave;"), wxT("è"),  true);
1156         strDecodedHtml.Replace(wxT("&eacute;"), wxT("é"),  true);
1157         strDecodedHtml.Replace(wxT("&ecirc;"),  wxT("ê"),  true);
1158         strDecodedHtml.Replace(wxT("&euml;"),   wxT("ë"),  true);
1159         strDecodedHtml.Replace(wxT("&igrave;"), wxT("ì"),  true);
1160         strDecodedHtml.Replace(wxT("&iacute;"), wxT("í"),  true);
1161         strDecodedHtml.Replace(wxT("&icirc;"),  wxT("î"),  true);
1162         strDecodedHtml.Replace(wxT("&iuml;"),   wxT("ï"),  true);
1163         strDecodedHtml.Replace(wxT("&eth;"),    wxT("ð"),  true);
1164         strDecodedHtml.Replace(wxT("&ntilde;"), wxT("ñ"),  true);
1165         strDecodedHtml.Replace(wxT("&ograve;"), wxT("ò"),  true);
1166         strDecodedHtml.Replace(wxT("&oacute;"), wxT("ó"),  true);
1167         strDecodedHtml.Replace(wxT("&ocirc;"),  wxT("ô"),  true);
1168         strDecodedHtml.Replace(wxT("&otilde;"), wxT("õ"),  true);
1169         strDecodedHtml.Replace(wxT("&ouml;"),   wxT("ö"),  true);
1170         strDecodedHtml.Replace(wxT("&divide;"), wxT("÷"),  true);
1171         strDecodedHtml.Replace(wxT("&oslash;"), wxT("ø"),  true);
1172         strDecodedHtml.Replace(wxT("&ugrave;"), wxT("ù"),  true);
1173         strDecodedHtml.Replace(wxT("&uacute;"), wxT("ú"),  true);
1174         strDecodedHtml.Replace(wxT("&ucirc;"),  wxT("û"),  true);
1175         strDecodedHtml.Replace(wxT("&uuml;"),   wxT("ü"),  true);
1176         strDecodedHtml.Replace(wxT("&yacute;"), wxT("ý"),  true);
1177         strDecodedHtml.Replace(wxT("&thorn;"),  wxT("þ"),  true);
1178         strDecodedHtml.Replace(wxT("&yuml;"),   wxT("ÿ"),  true);
1179 #endif
1180     }
1181 
1182 	return strDecodedHtml;
1183 }
1184 
1185