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 "BOINCListCtrl.h"
20 #endif
21
22 #include "stdwx.h"
23 #include "BOINCBaseView.h"
24 #include "BOINCListCtrl.h"
25 #include "Events.h"
26
27
28 #ifndef wxHAS_LISTCTRL_COLUMN_ORDER
29 #define GetColumnOrder(x) x
30 #define GetColumnIndexFromOrder(x) x
31 #endif
32
BEGIN_EVENT_TABLE(MyEvtHandler,wxEvtHandler)33 BEGIN_EVENT_TABLE(MyEvtHandler, wxEvtHandler)
34 EVT_PAINT(MyEvtHandler::OnPaint)
35 END_EVENT_TABLE()
36
37
38 IMPLEMENT_DYNAMIC_CLASS(MyEvtHandler, wxEvtHandler)
39
40 MyEvtHandler::MyEvtHandler() {}
41
MyEvtHandler(CBOINCListCtrl * theListControl)42 MyEvtHandler::MyEvtHandler(CBOINCListCtrl *theListControl) {
43 m_listCtrl = theListControl;
44 #ifdef __WXGTK__
45 m_view_startX = 0;
46 #endif
47 }
48
49
50 DEFINE_EVENT_TYPE(wxEVT_CHECK_SELECTION_CHANGED)
51
52 #if USE_NATIVE_LISTCONTROL
DEFINE_EVENT_TYPE(wxEVT_DRAW_PROGRESSBAR)53 DEFINE_EVENT_TYPE(wxEVT_DRAW_PROGRESSBAR)
54 #endif
55
56 BEGIN_EVENT_TABLE(CBOINCListCtrl, LISTCTRL_BASE)
57
58 #if USE_NATIVE_LISTCONTROL
59 EVT_DRAW_PROGRESSBAR(CBOINCListCtrl::OnDrawProgressBar)
60 #else
61 #ifdef __WXMAC__
62 EVT_SIZE(CBOINCListCtrl::OnSize) // In MacAccessibility.mm
63 #endif
64 #endif
65
66 #if ! USE_LIST_CACHE_HINT
67 EVT_LEFT_DOWN(CBOINCListCtrl::OnMouseDown)
68 #endif
69 END_EVENT_TABLE()
70
71
72 IMPLEMENT_DYNAMIC_CLASS(CBOINCListCtrl, LISTCTRL_BASE)
73
74
75 CBOINCListCtrl::CBOINCListCtrl() {}
76
77
CBOINCListCtrl(CBOINCBaseView * pView,wxWindowID iListWindowID,wxInt32 iListWindowFlags)78 CBOINCListCtrl::CBOINCListCtrl(
79 CBOINCBaseView* pView, wxWindowID iListWindowID, wxInt32 iListWindowFlags
80 ) : LISTCTRL_BASE(
81 pView, iListWindowID, wxDefaultPosition, wxSize(-1, -1), iListWindowFlags
82 ) {
83 m_pParentView = pView;
84
85 // Enable Zebra Striping
86 EnableAlternateRowColours(true);
87
88 #if USE_NATIVE_LISTCONTROL
89 m_bProgressBarEventPending = false;
90 PushEventHandler(new MyEvtHandler(this));
91 #else
92 savedHandler = GetMainWin()->GetEventHandler();
93 GetMainWin()->PushEventHandler(new MyEvtHandler(this));
94 #ifdef __WXMAC__
95 SetupMacAccessibilitySupport();
96 #endif
97 #endif
98 }
99
100
~CBOINCListCtrl()101 CBOINCListCtrl::~CBOINCListCtrl()
102 {
103 #if USE_NATIVE_LISTCONTROL
104 PopEventHandler(true);
105 #else
106 GetMainWin()->PopEventHandler(true);
107 #ifdef __WXMAC__
108 RemoveMacAccessibilitySupport();
109 #endif
110 #endif
111
112 m_iRowsNeedingProgressBars.Clear();
113 }
114
115
OnSaveState(wxConfigBase * pConfig)116 bool CBOINCListCtrl::OnSaveState(wxConfigBase* pConfig) {
117 wxString strBaseConfigLocation = wxEmptyString;
118 wxInt32 iIndex = 0;
119 wxInt32 iStdColumnCount = 0;
120 wxInt32 iActualColumnCount = GetColumnCount();
121 int i, j;
122
123 wxASSERT(pConfig);
124
125 // Retrieve the base location to store configuration information
126 // Should be in the following form: "/Projects/"
127 strBaseConfigLocation = pConfig->GetPath() + wxT("/");
128
129 iStdColumnCount = m_pParentView->m_iStdColWidthOrder.size();
130
131 // Cycle through the columns recording their widths
132 for (iIndex = 0; iIndex < iActualColumnCount; iIndex++) {
133 m_pParentView->m_iStdColWidthOrder[m_pParentView->m_iColumnIndexToColumnID[iIndex]] = GetColumnWidth(iIndex);
134 }
135
136 for (iIndex = 0; iIndex < iStdColumnCount; iIndex++) {
137 pConfig->SetPath(strBaseConfigLocation + m_pParentView->m_aStdColNameOrder->Item(iIndex));
138 pConfig->Write(wxT("Width"), m_pParentView->m_iStdColWidthOrder[iIndex]);
139 }
140
141 // Save sorting column and direction
142 pConfig->SetPath(strBaseConfigLocation);
143 pConfig->Write(wxT("SortColumn"), m_pParentView->m_iSortColumnID);
144 pConfig->Write(wxT("ReverseSortOrder"), m_pParentView->m_bReverseSort);
145
146 // Save Column Order
147 wxString strColumnOrder;
148 wxString strBuffer;
149 wxString strHiddenColumns;
150 wxArrayInt aOrder(iActualColumnCount);
151 CBOINCBaseView* pView = (CBOINCBaseView*)GetParent();
152 wxASSERT(wxDynamicCast(pView, CBOINCBaseView));
153
154 #ifdef wxHAS_LISTCTRL_COLUMN_ORDER
155 aOrder = GetColumnsOrder();
156 #else
157 for (i = 0; i < iActualColumnCount; ++i) {
158 aOrder[i] = i;
159 }
160 #endif
161
162 strColumnOrder.Printf(wxT("%s"), pView->m_aStdColNameOrder->Item(pView->m_iColumnIndexToColumnID[aOrder[0]]));
163
164 for (i = 1; i < iActualColumnCount; ++i)
165 {
166 strBuffer.Printf(wxT(";%s"), pView->m_aStdColNameOrder->Item(pView->m_iColumnIndexToColumnID[aOrder[i]]));
167 strColumnOrder += strBuffer;
168 }
169
170 pConfig->Write(wxT("ColumnOrder"), strColumnOrder);
171
172 strHiddenColumns = wxEmptyString;
173 for (i = 0; i < iStdColumnCount; ++i) {
174 bool found = false;
175 for (j = 0; j < iActualColumnCount; ++j) {
176 if (pView->m_iColumnIndexToColumnID[aOrder[j]] == i) {
177 found = true;
178 break;
179 }
180 }
181 if (found) continue;
182 if (!strHiddenColumns.IsEmpty()) {
183 strHiddenColumns += wxT(";");
184 }
185 strHiddenColumns += pView->m_aStdColNameOrder->Item(i);
186 }
187 pConfig->Write(wxT("HiddenColumns"), strHiddenColumns);
188
189 return true;
190 }
191
192
OnRestoreState(wxConfigBase * pConfig)193 bool CBOINCListCtrl::OnRestoreState(wxConfigBase* pConfig) {
194 wxString strBaseConfigLocation = wxEmptyString;
195 wxInt32 iIndex = 0;
196 wxInt32 iStdColumnCount = 0;
197 wxInt32 iTempValue = 0;
198
199 wxASSERT(pConfig);
200
201 // Retrieve the base location to store configuration information
202 // Should be in the following form: "/Projects/"
203 strBaseConfigLocation = pConfig->GetPath() + wxT("/");
204
205 iStdColumnCount = m_pParentView->m_iStdColWidthOrder.size();
206
207 // Cycle through the possible columns updating column widths
208 for (iIndex = 0; iIndex < iStdColumnCount; iIndex++) {
209 pConfig->SetPath(strBaseConfigLocation + m_pParentView->m_aStdColNameOrder->Item(iIndex));
210
211 pConfig->Read(wxT("Width"), &iTempValue, -1);
212 if (-1 != iTempValue) {
213 m_pParentView->m_iStdColWidthOrder[iIndex] = iTempValue;
214 }
215 }
216
217 // Restore sorting column and direction
218 pConfig->SetPath(strBaseConfigLocation);
219 pConfig->Read(wxT("ReverseSortOrder"), &iTempValue,-1);
220 if (-1 != iTempValue) {
221 m_pParentView->m_bReverseSort = iTempValue != 0 ? true : false;
222 }
223 pConfig->Read(wxT("SortColumn"), &iTempValue,-1);
224 if (-1 != iTempValue) {
225 m_pParentView->m_iSortColumnID = iTempValue;
226 }
227
228 // Restore Column Order
229 wxString strColumnOrder;
230 wxString strHiddenColumns;
231 CBOINCBaseView* pView = (CBOINCBaseView*)GetParent();
232
233 if (pConfig->Read(wxT("ColumnOrder"), &strColumnOrder)) {
234 wxArrayString orderArray;
235 TokenizedStringToArray(strColumnOrder, ";", &orderArray);
236 SetListColumnOrder(orderArray);
237
238 // If the user installed a new vesion of BOINC, new columns may have
239 // been added that didn't exist in the older version. Check for this.
240 //
241 // This will also be triggered if the locale is changed, which will cause
242 // SetListColumnOrder() to be called again so the wxListCtrl will be set
243 // up with the correctly labeled columns.
244 bool foundNewColumns = false;
245
246 if (pConfig->Read(wxT("HiddenColumns"), &strHiddenColumns)) {
247 wxArrayString hiddenArray;
248 TokenizedStringToArray(strHiddenColumns, ";", &hiddenArray);
249 int shownCount = orderArray.size();
250 int hiddenCount = hiddenArray.size();
251 int totalCount = pView->m_aStdColNameOrder->size();
252 for (int i = 0; i < totalCount; ++i) {
253 wxString columnNameToFind = pView->m_aStdColNameOrder->Item(i);
254 bool found = false;
255 for (int j = 0; j < shownCount; ++j) {
256 if (orderArray[j].IsSameAs(columnNameToFind)) {
257 found = true;
258 break;
259 }
260 }
261 if (found) continue;
262
263 for (int j = 0; j < hiddenCount; ++j) {
264 if (hiddenArray[j].IsSameAs(columnNameToFind)) {
265 found = true;
266 break;
267 }
268 }
269 if (found) continue;
270
271 foundNewColumns = true;
272 orderArray.Add(columnNameToFind);
273 }
274 }
275 if (foundNewColumns) {
276 bool wasInStandardOrder = IsColumnOrderStandard();
277 SetListColumnOrder(orderArray);
278 if (wasInStandardOrder) SetStandardColumnOrder();
279 }
280 } else {
281 // No "ColumnOrder" tag in pConfig
282 // Show all columns in default column order
283 wxASSERT(wxDynamicCast(pView, CBOINCBaseView));
284
285 SetDefaultColumnDisplay();
286 }
287
288 if (m_pParentView->m_iSortColumnID != -1) {
289 m_pParentView->InitSort();
290 }
291
292 return true;
293 }
294
295
TokenizedStringToArray(wxString tokenized,char * delimiters,wxArrayString * array)296 void CBOINCListCtrl::TokenizedStringToArray(wxString tokenized, char * delimiters, wxArrayString* array) {
297 wxString name;
298
299 array->Clear();
300 wxStringTokenizer tok(tokenized, delimiters);
301 while (tok.HasMoreTokens())
302 {
303 name = tok.GetNextToken();
304 if (name.IsEmpty()) continue;
305 array->Add(name);
306 }
307 }
308
309
310 // SetListColumnOrder() is called mostly from OnRestoreState(), so we don't
311 // call OnSaveState() from here. CDlgHiddenColumns calls OnSaveState()
312 // when we really need to do that.
313 //
314 // Unfortunately, we have no way of immediately calling OnSaveState() when
315 // the user manually reorders columns because that does not generate a
316 // notification from MS Windows so wxWidgets can't generate an event.
SetListColumnOrder(wxArrayString & orderArray)317 void CBOINCListCtrl::SetListColumnOrder(wxArrayString& orderArray) {
318 int i, stdCount, columnPosition;
319 int colCount = GetColumnCount();
320 int shownColCount = orderArray.GetCount();
321 int columnIndex = 0; // Column number among shown columns before re-ordering
322 int columnID = 0; // ID of column, e.g. COLUMN_PROJECT, COLUMN_STATUS, etc.
323 int sortColumnIndex = -1;
324 wxArrayInt aOrder(shownColCount);
325
326 CBOINCBaseView* pView = (CBOINCBaseView*)GetParent();
327 wxASSERT(wxDynamicCast(pView, CBOINCBaseView));
328
329 pView->m_iColumnIndexToColumnID.Clear();
330 for (i=colCount-1; i>=0; --i) {
331 DeleteColumn(i);
332 }
333
334 stdCount = pView->m_aStdColNameOrder->GetCount();
335
336 pView->m_iColumnIDToColumnIndex.Clear();
337 for (columnID=0; columnID<stdCount; ++columnID) {
338 pView->m_iColumnIDToColumnIndex.Add(-1);
339 }
340
341 for (columnID=0; columnID<stdCount; ++columnID) {
342 for (columnPosition=0; columnPosition<shownColCount; ++columnPosition) {
343 if (orderArray[columnPosition].IsSameAs(pView->m_aStdColNameOrder->Item(columnID))) {
344 aOrder[columnPosition] = columnIndex;
345 pView->AppendColumn(columnID);
346 pView->m_iColumnIndexToColumnID.Add(columnID);
347 pView->m_iColumnIDToColumnIndex[columnID] = columnIndex;
348
349 ++columnIndex;
350 break;
351 }
352 }
353 }
354
355 // Prevent a crash bug if we just changed to a new locale.
356 //
357 // If a column has the same name in both the old and new locale, we guard against
358 // changing the sort column to that column.
359 //
360 // CBOINCListCtrl::OnRestoreState() may have incorrectly added the column names in
361 // the new locale as "new" columns, so check against both shownColCount and stdCount.
362 int limit = wxMin(shownColCount, stdCount);
363 if (columnIndex < limit) {
364 SetStandardColumnOrder();
365 for (columnID=0; columnID<limit; ++columnID) {
366 aOrder[columnID] = columnID;
367 pView->AppendColumn(columnID);
368 pView->m_iColumnIndexToColumnID.Add(columnID);
369 pView->m_iColumnIDToColumnIndex[columnID] = columnID;
370 }
371 }
372
373 // If sort column is now hidden, set the new first column as sort column
374 if (pView->m_iSortColumnID >= 0) {
375 sortColumnIndex = pView->m_iColumnIDToColumnIndex[pView->m_iSortColumnID];
376 if (sortColumnIndex < 0) {
377 pView->m_iSortColumnID = pView->m_iColumnIndexToColumnID[0];
378 pView->m_bReverseSort = false;
379 pView->SetSortColumn(0);
380 } else {
381 // Redraw the sort arrow, etc.
382 pView->SetSortColumn(sortColumnIndex);
383 }
384 }
385
386 #ifdef wxHAS_LISTCTRL_COLUMN_ORDER
387 colCount = GetColumnCount();
388 if ((shownColCount > 0) && (shownColCount <= stdCount) && (colCount == shownColCount)) {
389 SetColumnsOrder(aOrder);
390 }
391 #endif
392 }
393
394
IsColumnOrderStandard()395 bool CBOINCListCtrl::IsColumnOrderStandard() {
396 #ifdef wxHAS_LISTCTRL_COLUMN_ORDER
397 int i;
398 wxArrayInt aOrder = GetColumnsOrder();
399 int orderCount = aOrder.GetCount();
400 for (i=1; i<orderCount; ++i) {
401 if(aOrder[i] < aOrder[i-1]) return false;
402 }
403 #endif
404 return true;
405 }
406
407
SetStandardColumnOrder()408 void CBOINCListCtrl::SetStandardColumnOrder() {
409 int i;
410 int colCount = GetColumnCount();
411 wxArrayInt aOrder(colCount);
412
413 for (i=0; i<colCount; ++i) {
414 aOrder[i] = i;
415 }
416 #ifdef wxHAS_LISTCTRL_COLUMN_ORDER
417 if (colCount) {
418 SetColumnsOrder(aOrder);
419 }
420 #endif
421 }
422
423
SetDefaultColumnDisplay()424 void CBOINCListCtrl::SetDefaultColumnDisplay() {
425 int i;
426 wxArrayString orderArray;
427 CBOINCBaseView* pView = (CBOINCBaseView*)GetParent();
428
429 wxASSERT(wxDynamicCast(pView, CBOINCBaseView));
430
431 orderArray.Clear();
432 for (i=0; i<pView->m_iNumDefaultShownColumns; ++i) {
433 orderArray.Add(pView->m_aStdColNameOrder->Item(pView->m_iDefaultShownColumns[i]));
434 }
435
436 SetListColumnOrder(orderArray);
437 SetStandardColumnOrder();
438 }
439
440
SelectRow(int row,bool setSelected)441 void CBOINCListCtrl::SelectRow(int row, bool setSelected) {
442 SetItemState(row, setSelected ? wxLIST_STATE_SELECTED : 0, wxLIST_STATE_SELECTED);
443 }
444
445
AddPendingProgressBar(int row)446 void CBOINCListCtrl::AddPendingProgressBar(int row) {
447 bool duplicate = false;
448 int n = (int)m_iRowsNeedingProgressBars.GetCount();
449 for (int i=0; i<n; ++i) {
450 if (m_iRowsNeedingProgressBars[i] == row) {
451 duplicate = true;
452 }
453 }
454 if (!duplicate) {
455 m_iRowsNeedingProgressBars.Add(row);
456 }
457 }
458
459
OnGetItemText(long item,long column) const460 wxString CBOINCListCtrl::OnGetItemText(long item, long column) const {
461 wxASSERT(m_pParentView);
462 wxASSERT(wxDynamicCast(m_pParentView, CBOINCBaseView));
463
464 return m_pParentView->FireOnListGetItemText(item, column);
465 }
466
467
OnGetItemImage(long item) const468 int CBOINCListCtrl::OnGetItemImage(long item) const {
469 wxASSERT(m_pParentView);
470 wxASSERT(wxDynamicCast(m_pParentView, CBOINCBaseView));
471
472 return m_pParentView->FireOnListGetItemImage(item);
473 }
474
475
DrawProgressBars()476 void CBOINCListCtrl::DrawProgressBars()
477 {
478 long topItem, numItems, numVisibleItems, row;
479 wxRect r, rr;
480 int w = 0, x = 0, xx, yy, ww;
481 int progressColumn = -1;
482
483 if (m_pParentView->GetProgressColumn() >= 0) {
484 progressColumn = m_pParentView->m_iColumnIDToColumnIndex[m_pParentView->GetProgressColumn()];
485 }
486
487 #if USE_NATIVE_LISTCONTROL
488 wxClientDC dc(this);
489 m_bProgressBarEventPending = false;
490 #else
491 wxClientDC dc(GetMainWin()); // Available only in wxGenericListCtrl
492 #endif
493
494 if (progressColumn < 0) {
495 m_iRowsNeedingProgressBars.Clear();
496 return;
497 }
498
499 int n = (int)m_iRowsNeedingProgressBars.GetCount();
500 if (n <= 0) return;
501
502 wxColour progressColor = wxTheColourDatabase->Find(wxT("LIGHT BLUE"));
503 wxBrush progressBrush(progressColor);
504
505 numItems = GetItemCount();
506 if (numItems) {
507 topItem = GetTopItem(); // Doesn't work properly for Mac Native control in wxMac-2.8.7
508
509 numVisibleItems = GetCountPerPage();
510 ++numVisibleItems;
511
512 if (numItems <= (topItem + numVisibleItems)) numVisibleItems = numItems - topItem;
513
514 x = 0;
515 int progressColumnPosition = GetColumnOrder(progressColumn);
516 for (int i=0; i<progressColumnPosition; i++) {
517 x += GetColumnWidth(GetColumnIndexFromOrder(i));
518 }
519 w = GetColumnWidth(progressColumn);
520
521 #if USE_NATIVE_LISTCONTROL
522 x -= GetScrollPos(wxHORIZONTAL);
523 #else
524 CalcScrolledPosition(x, 0, &x, &yy);
525 #endif
526 wxFont theFont = GetFont();
527 dc.SetFont(theFont);
528
529 for (int i=0; i<n; ++i) {
530 row = m_iRowsNeedingProgressBars[i];
531 if (row < topItem) continue;
532 if (row > (topItem + numVisibleItems -1)) continue;
533
534
535 GetItemRect(row, r);
536 #if ! USE_NATIVE_LISTCONTROL
537 r.y = r.y - GetHeaderHeight() - 1;
538 #endif
539 r.x = x;
540 r.width = w;
541 r.Inflate(-1, -2);
542 rr = r;
543
544 wxString progressString = m_pParentView->GetProgressText(row);
545 dc.GetTextExtent(progressString, &xx, &yy);
546
547 r.y += (r.height - yy - 1) / 2;
548
549 // Adapted from ellipis code in wxRendererGeneric::DrawHeaderButtonContents()
550 if (xx > r.width) {
551 int ellipsisWidth;
552 dc.GetTextExtent( wxT("..."), &ellipsisWidth, NULL);
553 if (ellipsisWidth > r.width) {
554 progressString.Clear();
555 xx = 0;
556 } else {
557 do {
558 progressString.Truncate( progressString.length() - 1 );
559 dc.GetTextExtent( progressString, &xx, &yy);
560 } while (xx + ellipsisWidth > r.width && progressString.length() );
561 progressString.append( wxT("...") );
562 xx += ellipsisWidth;
563 }
564 }
565
566 dc.SetLogicalFunction(wxCOPY);
567 dc.SetBackgroundMode(wxSOLID);
568 dc.SetPen(progressColor);
569 dc.SetBrush(progressBrush);
570 dc.DrawRectangle( rr );
571
572 rr.Inflate(-2, -1);
573 ww = rr.width * m_pParentView->GetProgressValue(row);
574 rr.x += ww;
575 rr.width -= ww;
576
577 #if 0
578 // Show background stripes behind progress bars
579 wxListItemAttr* attr = m_pParentView->FireOnListGetItemAttr(row);
580 wxColour bkgd = attr->GetBackgroundColour();
581 dc.SetPen(bkgd);
582 dc.SetBrush(bkgd);
583 #else
584 dc.SetPen(*wxWHITE_PEN);
585 dc.SetBrush(*wxWHITE_BRUSH);
586 #endif
587 dc.DrawRectangle( rr );
588
589 dc.SetPen(*wxBLACK_PEN);
590 dc.SetBackgroundMode(wxTRANSPARENT);
591 if (xx > (r.width - 7)) {
592 dc.DrawText(progressString, r.x, r.y);
593 } else {
594 dc.DrawText(progressString, r.x + (w - 8 - xx), r.y);
595 }
596 }
597 }
598 m_iRowsNeedingProgressBars.Clear();
599 }
600
601 #if USE_NATIVE_LISTCONTROL
602
OnPaint(wxPaintEvent & event)603 void MyEvtHandler::OnPaint(wxPaintEvent & event)
604 {
605 event.Skip();
606 if (m_listCtrl) {
607 m_listCtrl->PostDrawProgressBarEvent();
608 }
609 }
610
PostDrawProgressBarEvent()611 void CBOINCListCtrl::PostDrawProgressBarEvent() {
612 if (m_bProgressBarEventPending) return;
613
614 CDrawProgressBarEvent newEvent(wxEVT_DRAW_PROGRESSBAR, this);
615 AddPendingEvent(newEvent);
616 m_bProgressBarEventPending = true;
617 }
618
OnDrawProgressBar(CDrawProgressBarEvent & event)619 void CBOINCListCtrl::OnDrawProgressBar(CDrawProgressBarEvent& event) {
620 DrawProgressBars();
621 event.Skip();
622 }
623
624 #else
625
OnPaint(wxPaintEvent & event)626 void MyEvtHandler::OnPaint(wxPaintEvent & event)
627 {
628 if (m_listCtrl) {
629 m_listCtrl->savedHandler->ProcessEvent(event);
630 m_listCtrl->DrawProgressBars();
631 #ifdef __WXGTK__
632 // Work around a wxWidgets 3.0 bug in wxGenericListCtrl (Linux
633 // only) which causes headers to be misaligned after horizontal
634 // scrolling due to wxListHeaderWindow::OnPaint() calling
635 // parent->GetViewStart() before the parent window has been
636 // scrolled to the new position.
637 int view_startX;
638 m_listCtrl->GetViewStart( &view_startX, NULL );
639 if (view_startX != m_view_startX) {
640 m_view_startX = view_startX;
641 ((wxWindow *)m_listCtrl->m_headerWin)->Refresh();
642 ((wxWindow *)m_listCtrl->m_headerWin)->Update();
643 }
644 #endif
645 } else {
646 event.Skip();
647 }
648 }
649
650 #endif
651
652
653 #if ! USE_LIST_CACHE_HINT
654
655 // Work around features in multiple selection virtual wxListCtrl:
656 // * It does not send deselection events (except ctrl-click).
657 // * It does not send selection events if you add to selection
658 // using Shift_Click.
659 //
660 // Post a special event. This will allow this mouse event to
661 // propogate through the chain to complete any selection or
662 // deselection operatiion, then the special event will trigger
663 // CBOINCBaseView::OnCheckSelectionChanged() to respond to the
664 // selection change, if any.
665 //
OnMouseDown(wxMouseEvent & event)666 void CBOINCListCtrl::OnMouseDown(wxMouseEvent& event) {
667 CCheckSelectionChangedEvent newEvent(wxEVT_CHECK_SELECTION_CHANGED, this);
668 m_pParentView->GetEventHandler()->AddPendingEvent(newEvent);
669 event.Skip();
670 }
671
672 #endif
673
674
675 // To reduce flicker, refresh only changed columns (except
676 // on Mac, which is double-buffered to eliminate flicker.)
RefreshCell(int row,int col)677 void CBOINCListCtrl::RefreshCell(int row, int col) {
678 wxRect r;
679
680 GetSubItemRect(row, col, r);
681 RefreshRect(r);
682 }
683