]> git.saurik.com Git - wxWidgets.git/blob - src/generic/listctrl.cpp
remove always-true tests of unsigned >= 0
[wxWidgets.git] / src / generic / listctrl.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/generic/listctrl.cpp
3 // Purpose: generic implementation of wxListCtrl
4 // Author: Robert Roebling
5 // Vadim Zeitlin (virtual list control support)
6 // Id: $Id$
7 // Copyright: (c) 1998 Robert Roebling
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10
11 // TODO
12 //
13 // 1. we need to implement searching/sorting for virtual controls somehow
14 // 2. when changing selection the lines are refreshed twice
15
16
17 // For compilers that support precompilation, includes "wx.h".
18 #include "wx/wxprec.h"
19
20 #ifdef __BORLANDC__
21 #pragma hdrstop
22 #endif
23
24 #if wxUSE_LISTCTRL
25
26 #include "wx/listctrl.h"
27
28 #ifndef WX_PRECOMP
29 #include "wx/scrolwin.h"
30 #include "wx/timer.h"
31 #include "wx/settings.h"
32 #include "wx/dynarray.h"
33 #include "wx/dcclient.h"
34 #include "wx/dcscreen.h"
35 #include "wx/math.h"
36 #include "wx/settings.h"
37 #include "wx/sizer.h"
38 #endif
39
40 #include "wx/imaglist.h"
41 #include "wx/renderer.h"
42 #include "wx/generic/private/listctrl.h"
43
44 #ifdef __WXMAC__
45 #include "wx/osx/private.h"
46 #endif
47
48 #if defined(__WXMSW__) && !defined(__WXWINCE__) && !defined(__WXUNIVERSAL__)
49 #define "wx/msw/wrapwin.h"
50 #endif
51
52 // NOTE: If using the wxListBox visual attributes works everywhere then this can
53 // be removed, as well as the #else case below.
54 #define _USE_VISATTR 0
55
56
57 // ----------------------------------------------------------------------------
58 // constants
59 // ----------------------------------------------------------------------------
60
61 // // the height of the header window (FIXME: should depend on its font!)
62 // static const int HEADER_HEIGHT = 23;
63
64 static const int SCROLL_UNIT_X = 15;
65
66 // the spacing between the lines (in report mode)
67 static const int LINE_SPACING = 0;
68
69 // extra margins around the text label
70 #ifdef __WXGTK__
71 static const int EXTRA_WIDTH = 6;
72 #else
73 static const int EXTRA_WIDTH = 4;
74 #endif
75
76 #ifdef __WXGTK__
77 static const int EXTRA_HEIGHT = 6;
78 #else
79 static const int EXTRA_HEIGHT = 4;
80 #endif
81
82 // margin between the window and the items
83 static const int EXTRA_BORDER_X = 2;
84 static const int EXTRA_BORDER_Y = 2;
85
86 // offset for the header window
87 static const int HEADER_OFFSET_X = 0;
88 static const int HEADER_OFFSET_Y = 0;
89
90 // margin between rows of icons in [small] icon view
91 static const int MARGIN_BETWEEN_ROWS = 6;
92
93 // when autosizing the columns, add some slack
94 static const int AUTOSIZE_COL_MARGIN = 10;
95
96 // default width for the header columns
97 static const int WIDTH_COL_DEFAULT = 80;
98
99 // the space between the image and the text in the report mode
100 static const int IMAGE_MARGIN_IN_REPORT_MODE = 5;
101
102 // the space between the image and the text in the report mode in header
103 static const int HEADER_IMAGE_MARGIN_IN_REPORT_MODE = 2;
104
105
106
107 // ----------------------------------------------------------------------------
108 // arrays/list implementations
109 // ----------------------------------------------------------------------------
110
111 #include "wx/listimpl.cpp"
112 WX_DEFINE_LIST(wxListItemDataList)
113
114 #include "wx/arrimpl.cpp"
115 WX_DEFINE_OBJARRAY(wxListLineDataArray)
116
117 #include "wx/listimpl.cpp"
118 WX_DEFINE_LIST(wxListHeaderDataList)
119
120
121 // ----------------------------------------------------------------------------
122 // wxListItemData
123 // ----------------------------------------------------------------------------
124
125 wxListItemData::~wxListItemData()
126 {
127 // in the virtual list control the attributes are managed by the main
128 // program, so don't delete them
129 if ( !m_owner->IsVirtual() )
130 delete m_attr;
131
132 delete m_rect;
133 }
134
135 void wxListItemData::Init()
136 {
137 m_image = -1;
138 m_data = 0;
139
140 m_attr = NULL;
141 }
142
143 wxListItemData::wxListItemData(wxListMainWindow *owner)
144 {
145 Init();
146
147 m_owner = owner;
148
149 if ( owner->InReportView() )
150 m_rect = NULL;
151 else
152 m_rect = new wxRect;
153 }
154
155 void wxListItemData::SetItem( const wxListItem &info )
156 {
157 if ( info.m_mask & wxLIST_MASK_TEXT )
158 SetText(info.m_text);
159 if ( info.m_mask & wxLIST_MASK_IMAGE )
160 m_image = info.m_image;
161 if ( info.m_mask & wxLIST_MASK_DATA )
162 m_data = info.m_data;
163
164 if ( info.HasAttributes() )
165 {
166 if ( m_attr )
167 m_attr->AssignFrom(*info.GetAttributes());
168 else
169 m_attr = new wxListItemAttr(*info.GetAttributes());
170 }
171
172 if ( m_rect )
173 {
174 m_rect->x =
175 m_rect->y =
176 m_rect->height = 0;
177 m_rect->width = info.m_width;
178 }
179 }
180
181 void wxListItemData::SetPosition( int x, int y )
182 {
183 wxCHECK_RET( m_rect, wxT("unexpected SetPosition() call") );
184
185 m_rect->x = x;
186 m_rect->y = y;
187 }
188
189 void wxListItemData::SetSize( int width, int height )
190 {
191 wxCHECK_RET( m_rect, wxT("unexpected SetSize() call") );
192
193 if ( width != -1 )
194 m_rect->width = width;
195 if ( height != -1 )
196 m_rect->height = height;
197 }
198
199 bool wxListItemData::IsHit( int x, int y ) const
200 {
201 wxCHECK_MSG( m_rect, false, wxT("can't be called in this mode") );
202
203 return wxRect(GetX(), GetY(), GetWidth(), GetHeight()).Contains(x, y);
204 }
205
206 int wxListItemData::GetX() const
207 {
208 wxCHECK_MSG( m_rect, 0, wxT("can't be called in this mode") );
209
210 return m_rect->x;
211 }
212
213 int wxListItemData::GetY() const
214 {
215 wxCHECK_MSG( m_rect, 0, wxT("can't be called in this mode") );
216
217 return m_rect->y;
218 }
219
220 int wxListItemData::GetWidth() const
221 {
222 wxCHECK_MSG( m_rect, 0, wxT("can't be called in this mode") );
223
224 return m_rect->width;
225 }
226
227 int wxListItemData::GetHeight() const
228 {
229 wxCHECK_MSG( m_rect, 0, wxT("can't be called in this mode") );
230
231 return m_rect->height;
232 }
233
234 void wxListItemData::GetItem( wxListItem &info ) const
235 {
236 long mask = info.m_mask;
237 if ( !mask )
238 // by default, get everything for backwards compatibility
239 mask = -1;
240
241 if ( mask & wxLIST_MASK_TEXT )
242 info.m_text = m_text;
243 if ( mask & wxLIST_MASK_IMAGE )
244 info.m_image = m_image;
245 if ( mask & wxLIST_MASK_DATA )
246 info.m_data = m_data;
247
248 if ( m_attr )
249 {
250 if ( m_attr->HasTextColour() )
251 info.SetTextColour(m_attr->GetTextColour());
252 if ( m_attr->HasBackgroundColour() )
253 info.SetBackgroundColour(m_attr->GetBackgroundColour());
254 if ( m_attr->HasFont() )
255 info.SetFont(m_attr->GetFont());
256 }
257 }
258
259 //-----------------------------------------------------------------------------
260 // wxListHeaderData
261 //-----------------------------------------------------------------------------
262
263 void wxListHeaderData::Init()
264 {
265 m_mask = 0;
266 m_image = -1;
267 m_format = 0;
268 m_width = 0;
269 m_xpos = 0;
270 m_ypos = 0;
271 m_height = 0;
272 m_state = 0;
273 }
274
275 wxListHeaderData::wxListHeaderData()
276 {
277 Init();
278 }
279
280 wxListHeaderData::wxListHeaderData( const wxListItem &item )
281 {
282 Init();
283
284 SetItem( item );
285 }
286
287 void wxListHeaderData::SetItem( const wxListItem &item )
288 {
289 m_mask = item.m_mask;
290
291 if ( m_mask & wxLIST_MASK_TEXT )
292 m_text = item.m_text;
293
294 if ( m_mask & wxLIST_MASK_IMAGE )
295 m_image = item.m_image;
296
297 if ( m_mask & wxLIST_MASK_FORMAT )
298 m_format = item.m_format;
299
300 if ( m_mask & wxLIST_MASK_WIDTH )
301 SetWidth(item.m_width);
302
303 if ( m_mask & wxLIST_MASK_STATE )
304 SetState(item.m_state);
305 }
306
307 void wxListHeaderData::SetPosition( int x, int y )
308 {
309 m_xpos = x;
310 m_ypos = y;
311 }
312
313 void wxListHeaderData::SetHeight( int h )
314 {
315 m_height = h;
316 }
317
318 void wxListHeaderData::SetWidth( int w )
319 {
320 m_width = w < 0 ? WIDTH_COL_DEFAULT : w;
321 }
322
323 void wxListHeaderData::SetState( int flag )
324 {
325 m_state = flag;
326 }
327
328 void wxListHeaderData::SetFormat( int format )
329 {
330 m_format = format;
331 }
332
333 bool wxListHeaderData::HasImage() const
334 {
335 return m_image != -1;
336 }
337
338 bool wxListHeaderData::IsHit( int x, int y ) const
339 {
340 return ((x >= m_xpos) && (x <= m_xpos+m_width) && (y >= m_ypos) && (y <= m_ypos+m_height));
341 }
342
343 void wxListHeaderData::GetItem( wxListItem& item )
344 {
345 item.m_mask = m_mask;
346 item.m_text = m_text;
347 item.m_image = m_image;
348 item.m_format = m_format;
349 item.m_width = m_width;
350 item.m_state = m_state;
351 }
352
353 int wxListHeaderData::GetImage() const
354 {
355 return m_image;
356 }
357
358 int wxListHeaderData::GetWidth() const
359 {
360 return m_width;
361 }
362
363 int wxListHeaderData::GetFormat() const
364 {
365 return m_format;
366 }
367
368 int wxListHeaderData::GetState() const
369 {
370 return m_state;
371 }
372
373 //-----------------------------------------------------------------------------
374 // wxListLineData
375 //-----------------------------------------------------------------------------
376
377 inline int wxListLineData::GetMode() const
378 {
379 return m_owner->GetListCtrl()->GetWindowStyleFlag() & wxLC_MASK_TYPE;
380 }
381
382 inline bool wxListLineData::InReportView() const
383 {
384 return m_owner->HasFlag(wxLC_REPORT);
385 }
386
387 inline bool wxListLineData::IsVirtual() const
388 {
389 return m_owner->IsVirtual();
390 }
391
392 wxListLineData::wxListLineData( wxListMainWindow *owner )
393 {
394 m_owner = owner;
395
396 if ( InReportView() )
397 m_gi = NULL;
398 else // !report
399 m_gi = new GeometryInfo;
400
401 m_highlighted = false;
402
403 InitItems( GetMode() == wxLC_REPORT ? m_owner->GetColumnCount() : 1 );
404 }
405
406 void wxListLineData::CalculateSize( wxDC *dc, int spacing )
407 {
408 wxListItemDataList::compatibility_iterator node = m_items.GetFirst();
409 wxCHECK_RET( node, wxT("no subitems at all??") );
410
411 wxListItemData *item = node->GetData();
412
413 wxString s;
414 wxCoord lw, lh;
415
416 switch ( GetMode() )
417 {
418 case wxLC_ICON:
419 case wxLC_SMALL_ICON:
420 m_gi->m_rectAll.width = spacing;
421
422 s = item->GetText();
423
424 if ( s.empty() )
425 {
426 lh =
427 m_gi->m_rectLabel.width =
428 m_gi->m_rectLabel.height = 0;
429 }
430 else // has label
431 {
432 dc->GetTextExtent( s, &lw, &lh );
433 lw += EXTRA_WIDTH;
434 lh += EXTRA_HEIGHT;
435
436 m_gi->m_rectAll.height = spacing + lh;
437 if (lw > spacing)
438 m_gi->m_rectAll.width = lw;
439
440 m_gi->m_rectLabel.width = lw;
441 m_gi->m_rectLabel.height = lh;
442 }
443
444 if (item->HasImage())
445 {
446 int w, h;
447 m_owner->GetImageSize( item->GetImage(), w, h );
448 m_gi->m_rectIcon.width = w + 8;
449 m_gi->m_rectIcon.height = h + 8;
450
451 if ( m_gi->m_rectIcon.width > m_gi->m_rectAll.width )
452 m_gi->m_rectAll.width = m_gi->m_rectIcon.width;
453 if ( m_gi->m_rectIcon.height + lh > m_gi->m_rectAll.height - 4 )
454 m_gi->m_rectAll.height = m_gi->m_rectIcon.height + lh + 4;
455 }
456
457 if ( item->HasText() )
458 {
459 m_gi->m_rectHighlight.width = m_gi->m_rectLabel.width;
460 m_gi->m_rectHighlight.height = m_gi->m_rectLabel.height;
461 }
462 else // no text, highlight the icon
463 {
464 m_gi->m_rectHighlight.width = m_gi->m_rectIcon.width;
465 m_gi->m_rectHighlight.height = m_gi->m_rectIcon.height;
466 }
467 break;
468
469 case wxLC_LIST:
470 s = item->GetTextForMeasuring();
471
472 dc->GetTextExtent( s, &lw, &lh );
473 lw += EXTRA_WIDTH;
474 lh += EXTRA_HEIGHT;
475
476 m_gi->m_rectLabel.width = lw;
477 m_gi->m_rectLabel.height = lh;
478
479 m_gi->m_rectAll.width = lw;
480 m_gi->m_rectAll.height = lh;
481
482 if (item->HasImage())
483 {
484 int w, h;
485 m_owner->GetImageSize( item->GetImage(), w, h );
486 m_gi->m_rectIcon.width = w;
487 m_gi->m_rectIcon.height = h;
488
489 m_gi->m_rectAll.width += 4 + w;
490 if (h > m_gi->m_rectAll.height)
491 m_gi->m_rectAll.height = h;
492 }
493
494 m_gi->m_rectHighlight.width = m_gi->m_rectAll.width;
495 m_gi->m_rectHighlight.height = m_gi->m_rectAll.height;
496 break;
497
498 case wxLC_REPORT:
499 wxFAIL_MSG( wxT("unexpected call to SetSize") );
500 break;
501
502 default:
503 wxFAIL_MSG( wxT("unknown mode") );
504 break;
505 }
506 }
507
508 void wxListLineData::SetPosition( int x, int y, int spacing )
509 {
510 wxListItemDataList::compatibility_iterator node = m_items.GetFirst();
511 wxCHECK_RET( node, wxT("no subitems at all??") );
512
513 wxListItemData *item = node->GetData();
514
515 switch ( GetMode() )
516 {
517 case wxLC_ICON:
518 case wxLC_SMALL_ICON:
519 m_gi->m_rectAll.x = x;
520 m_gi->m_rectAll.y = y;
521
522 if ( item->HasImage() )
523 {
524 m_gi->m_rectIcon.x = m_gi->m_rectAll.x + 4 +
525 (m_gi->m_rectAll.width - m_gi->m_rectIcon.width) / 2;
526 m_gi->m_rectIcon.y = m_gi->m_rectAll.y + 4;
527 }
528
529 if ( item->HasText() )
530 {
531 if (m_gi->m_rectAll.width > spacing)
532 m_gi->m_rectLabel.x = m_gi->m_rectAll.x + (EXTRA_WIDTH/2);
533 else
534 m_gi->m_rectLabel.x = m_gi->m_rectAll.x + (EXTRA_WIDTH/2) + (spacing / 2) - (m_gi->m_rectLabel.width / 2);
535 m_gi->m_rectLabel.y = m_gi->m_rectAll.y + m_gi->m_rectAll.height + 2 - m_gi->m_rectLabel.height;
536 m_gi->m_rectHighlight.x = m_gi->m_rectLabel.x - 2;
537 m_gi->m_rectHighlight.y = m_gi->m_rectLabel.y - 2;
538 }
539 else // no text, highlight the icon
540 {
541 m_gi->m_rectHighlight.x = m_gi->m_rectIcon.x - 4;
542 m_gi->m_rectHighlight.y = m_gi->m_rectIcon.y - 4;
543 }
544 break;
545
546 case wxLC_LIST:
547 m_gi->m_rectAll.x = x;
548 m_gi->m_rectAll.y = y;
549
550 m_gi->m_rectHighlight.x = m_gi->m_rectAll.x;
551 m_gi->m_rectHighlight.y = m_gi->m_rectAll.y;
552 m_gi->m_rectLabel.y = m_gi->m_rectAll.y + 2;
553
554 if (item->HasImage())
555 {
556 m_gi->m_rectIcon.x = m_gi->m_rectAll.x + 2;
557 m_gi->m_rectIcon.y = m_gi->m_rectAll.y + 2;
558 m_gi->m_rectLabel.x = m_gi->m_rectAll.x + 4 + (EXTRA_WIDTH/2) + m_gi->m_rectIcon.width;
559 }
560 else
561 {
562 m_gi->m_rectLabel.x = m_gi->m_rectAll.x + (EXTRA_WIDTH/2);
563 }
564 break;
565
566 case wxLC_REPORT:
567 wxFAIL_MSG( wxT("unexpected call to SetPosition") );
568 break;
569
570 default:
571 wxFAIL_MSG( wxT("unknown mode") );
572 break;
573 }
574 }
575
576 void wxListLineData::InitItems( int num )
577 {
578 for (int i = 0; i < num; i++)
579 m_items.Append( new wxListItemData(m_owner) );
580 }
581
582 void wxListLineData::SetItem( int index, const wxListItem &info )
583 {
584 wxListItemDataList::compatibility_iterator node = m_items.Item( index );
585 wxCHECK_RET( node, wxT("invalid column index in SetItem") );
586
587 wxListItemData *item = node->GetData();
588 item->SetItem( info );
589 }
590
591 void wxListLineData::GetItem( int index, wxListItem &info )
592 {
593 wxListItemDataList::compatibility_iterator node = m_items.Item( index );
594 if (node)
595 {
596 wxListItemData *item = node->GetData();
597 item->GetItem( info );
598 }
599 }
600
601 wxString wxListLineData::GetText(int index) const
602 {
603 wxString s;
604
605 wxListItemDataList::compatibility_iterator node = m_items.Item( index );
606 if (node)
607 {
608 wxListItemData *item = node->GetData();
609 s = item->GetText();
610 }
611
612 return s;
613 }
614
615 void wxListLineData::SetText( int index, const wxString& s )
616 {
617 wxListItemDataList::compatibility_iterator node = m_items.Item( index );
618 if (node)
619 {
620 wxListItemData *item = node->GetData();
621 item->SetText( s );
622 }
623 }
624
625 void wxListLineData::SetImage( int index, int image )
626 {
627 wxListItemDataList::compatibility_iterator node = m_items.Item( index );
628 wxCHECK_RET( node, wxT("invalid column index in SetImage()") );
629
630 wxListItemData *item = node->GetData();
631 item->SetImage(image);
632 }
633
634 int wxListLineData::GetImage( int index ) const
635 {
636 wxListItemDataList::compatibility_iterator node = m_items.Item( index );
637 wxCHECK_MSG( node, -1, wxT("invalid column index in GetImage()") );
638
639 wxListItemData *item = node->GetData();
640 return item->GetImage();
641 }
642
643 wxListItemAttr *wxListLineData::GetAttr() const
644 {
645 wxListItemDataList::compatibility_iterator node = m_items.GetFirst();
646 wxCHECK_MSG( node, NULL, wxT("invalid column index in GetAttr()") );
647
648 wxListItemData *item = node->GetData();
649 return item->GetAttr();
650 }
651
652 void wxListLineData::SetAttr(wxListItemAttr *attr)
653 {
654 wxListItemDataList::compatibility_iterator node = m_items.GetFirst();
655 wxCHECK_RET( node, wxT("invalid column index in SetAttr()") );
656
657 wxListItemData *item = node->GetData();
658 item->SetAttr(attr);
659 }
660
661 void wxListLineData::ApplyAttributes(wxDC *dc,
662 const wxRect& rectHL,
663 bool highlighted,
664 bool current)
665 {
666 const wxListItemAttr * const attr = GetAttr();
667
668 wxWindow * const listctrl = m_owner->GetParent();
669
670 const bool hasFocus = listctrl->HasFocus()
671 #if defined(__WXMAC__) && !defined(__WXUNIVERSAL__) && wxOSX_USE_CARBON
672 && IsControlActive( (ControlRef)listctrl->GetHandle() )
673 #endif
674 ;
675
676 // fg colour
677
678 // don't use foreground colour for drawing highlighted items - this might
679 // make them completely invisible (and there is no way to do bit
680 // arithmetics on wxColour, unfortunately)
681 wxColour colText;
682 if ( highlighted )
683 {
684 #ifdef __WXMAC__
685 if ( hasFocus )
686 colText = *wxWHITE;
687 else
688 colText = *wxBLACK;
689 #else
690 colText = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT);
691 #endif
692 }
693 else if ( attr && attr->HasTextColour() )
694 colText = attr->GetTextColour();
695 else
696 colText = listctrl->GetForegroundColour();
697
698 dc->SetTextForeground(colText);
699
700 // font
701 wxFont font;
702 if ( attr && attr->HasFont() )
703 font = attr->GetFont();
704 else
705 font = listctrl->GetFont();
706
707 dc->SetFont(font);
708
709 // background
710 if ( highlighted )
711 {
712 // Use the renderer method to ensure that the selected items use the
713 // native look.
714 int flags = wxCONTROL_SELECTED;
715 if ( hasFocus )
716 flags |= wxCONTROL_FOCUSED;
717 if (current)
718 flags |= wxCONTROL_CURRENT;
719 wxRendererNative::Get().
720 DrawItemSelectionRect( m_owner, *dc, rectHL, flags );
721 }
722 else if ( attr && attr->HasBackgroundColour() )
723 {
724 // Draw the background using the items custom background colour.
725 dc->SetBrush(attr->GetBackgroundColour());
726 dc->SetPen(*wxTRANSPARENT_PEN);
727 dc->DrawRectangle(rectHL);
728 }
729
730 // just for debugging to better see where the items are
731 #if 0
732 dc->SetPen(*wxRED_PEN);
733 dc->SetBrush(*wxTRANSPARENT_BRUSH);
734 dc->DrawRectangle( m_gi->m_rectAll );
735 dc->SetPen(*wxGREEN_PEN);
736 dc->DrawRectangle( m_gi->m_rectIcon );
737 #endif
738 }
739
740 void wxListLineData::Draw(wxDC *dc, bool current)
741 {
742 wxListItemDataList::compatibility_iterator node = m_items.GetFirst();
743 wxCHECK_RET( node, wxT("no subitems at all??") );
744
745 ApplyAttributes(dc, m_gi->m_rectHighlight, IsHighlighted(), current);
746
747 wxListItemData *item = node->GetData();
748 if (item->HasImage())
749 {
750 // centre the image inside our rectangle, this looks nicer when items
751 // ae aligned in a row
752 const wxRect& rectIcon = m_gi->m_rectIcon;
753
754 m_owner->DrawImage(item->GetImage(), dc, rectIcon.x, rectIcon.y);
755 }
756
757 if (item->HasText())
758 {
759 const wxRect& rectLabel = m_gi->m_rectLabel;
760
761 wxDCClipper clipper(*dc, rectLabel);
762 dc->DrawText(item->GetText(), rectLabel.x, rectLabel.y);
763 }
764 }
765
766 void wxListLineData::DrawInReportMode( wxDC *dc,
767 const wxRect& rect,
768 const wxRect& rectHL,
769 bool highlighted,
770 bool current )
771 {
772 // TODO: later we should support setting different attributes for
773 // different columns - to do it, just add "col" argument to
774 // GetAttr() and move these lines into the loop below
775
776 ApplyAttributes(dc, rectHL, highlighted, current);
777
778 wxCoord x = rect.x + HEADER_OFFSET_X,
779 yMid = rect.y + rect.height/2;
780 #ifdef __WXGTK__
781 // This probably needs to be done
782 // on all platforms as the icons
783 // otherwise nearly touch the border
784 x += 2;
785 #endif
786
787 size_t col = 0;
788 for ( wxListItemDataList::compatibility_iterator node = m_items.GetFirst();
789 node;
790 node = node->GetNext(), col++ )
791 {
792 wxListItemData *item = node->GetData();
793
794 int width = m_owner->GetColumnWidth(col);
795 int xOld = x;
796 x += width;
797
798 width -= 8;
799 const int wText = width;
800 wxDCClipper clipper(*dc, xOld, rect.y, wText, rect.height);
801
802 if ( item->HasImage() )
803 {
804 int ix, iy;
805 m_owner->GetImageSize( item->GetImage(), ix, iy );
806 m_owner->DrawImage( item->GetImage(), dc, xOld, yMid - iy/2 );
807
808 ix += IMAGE_MARGIN_IN_REPORT_MODE;
809
810 xOld += ix;
811 width -= ix;
812 }
813
814 if ( item->HasText() )
815 DrawTextFormatted(dc, item->GetText(), col, xOld, yMid, width);
816 }
817 }
818
819 void wxListLineData::DrawTextFormatted(wxDC *dc,
820 const wxString& textOrig,
821 int col,
822 int x,
823 int yMid,
824 int width)
825 {
826 // we don't support displaying multiple lines currently (and neither does
827 // wxMSW FWIW) so just merge all the lines
828 wxString text(textOrig);
829 text.Replace(wxT("\n"), wxT(" "));
830
831 wxCoord w, h;
832 dc->GetTextExtent(text, &w, &h);
833
834 const wxCoord y = yMid - (h + 1)/2;
835
836 wxDCClipper clipper(*dc, x, y, width, h);
837
838 // determine if the string can fit inside the current width
839 if (w <= width)
840 {
841 // it can, draw it using the items alignment
842 wxListItem item;
843 m_owner->GetColumn(col, item);
844 switch ( item.GetAlign() )
845 {
846 case wxLIST_FORMAT_LEFT:
847 // nothing to do
848 break;
849
850 case wxLIST_FORMAT_RIGHT:
851 x += width - w;
852 break;
853
854 case wxLIST_FORMAT_CENTER:
855 x += (width - w) / 2;
856 break;
857
858 default:
859 wxFAIL_MSG( wxT("unknown list item format") );
860 break;
861 }
862
863 dc->DrawText(text, x, y);
864 }
865 else // otherwise, truncate and add an ellipsis if possible
866 {
867 // determine the base width
868 wxString ellipsis(wxT("..."));
869 wxCoord base_w;
870 dc->GetTextExtent(ellipsis, &base_w, &h);
871
872 // continue until we have enough space or only one character left
873 wxCoord w_c, h_c;
874 size_t len = text.length();
875 wxString drawntext = text.Left(len);
876 while (len > 1)
877 {
878 dc->GetTextExtent(drawntext.Last(), &w_c, &h_c);
879 drawntext.RemoveLast();
880 len--;
881 w -= w_c;
882 if (w + base_w <= width)
883 break;
884 }
885
886 // if still not enough space, remove ellipsis characters
887 while (ellipsis.length() > 0 && w + base_w > width)
888 {
889 ellipsis = ellipsis.Left(ellipsis.length() - 1);
890 dc->GetTextExtent(ellipsis, &base_w, &h);
891 }
892
893 // now draw the text
894 dc->DrawText(drawntext, x, y);
895 dc->DrawText(ellipsis, x + w, y);
896 }
897 }
898
899 bool wxListLineData::Highlight( bool on )
900 {
901 wxCHECK_MSG( !IsVirtual(), false, wxT("unexpected call to Highlight") );
902
903 if ( on == m_highlighted )
904 return false;
905
906 m_highlighted = on;
907
908 return true;
909 }
910
911 void wxListLineData::ReverseHighlight( void )
912 {
913 Highlight(!IsHighlighted());
914 }
915
916 //-----------------------------------------------------------------------------
917 // wxListHeaderWindow
918 //-----------------------------------------------------------------------------
919
920 BEGIN_EVENT_TABLE(wxListHeaderWindow,wxWindow)
921 EVT_PAINT (wxListHeaderWindow::OnPaint)
922 EVT_MOUSE_EVENTS (wxListHeaderWindow::OnMouse)
923 EVT_SET_FOCUS (wxListHeaderWindow::OnSetFocus)
924 END_EVENT_TABLE()
925
926 void wxListHeaderWindow::Init()
927 {
928 m_currentCursor = NULL;
929 m_isDragging = false;
930 m_dirty = false;
931 m_sendSetColumnWidth = false;
932 }
933
934 wxListHeaderWindow::wxListHeaderWindow()
935 {
936 Init();
937
938 m_owner = NULL;
939 m_resizeCursor = NULL;
940 }
941
942 wxListHeaderWindow::wxListHeaderWindow( wxWindow *win,
943 wxWindowID id,
944 wxListMainWindow *owner,
945 const wxPoint& pos,
946 const wxSize& size,
947 long style,
948 const wxString &name )
949 : wxWindow( win, id, pos, size, style, name )
950 {
951 Init();
952
953 m_owner = owner;
954 m_resizeCursor = new wxCursor( wxCURSOR_SIZEWE );
955
956 #if _USE_VISATTR
957 wxVisualAttributes attr = wxPanel::GetClassDefaultAttributes();
958 SetOwnForegroundColour( attr.colFg );
959 SetOwnBackgroundColour( attr.colBg );
960 if (!m_hasFont)
961 SetOwnFont( attr.font );
962 #else
963 SetOwnForegroundColour( wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
964 SetOwnBackgroundColour( wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE));
965 if (!m_hasFont)
966 SetOwnFont( wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT ));
967 #endif
968 }
969
970 wxListHeaderWindow::~wxListHeaderWindow()
971 {
972 delete m_resizeCursor;
973 }
974
975 #ifdef __WXUNIVERSAL__
976 #include "wx/univ/renderer.h"
977 #include "wx/univ/theme.h"
978 #endif
979
980 // shift the DC origin to match the position of the main window horz
981 // scrollbar: this allows us to always use logical coords
982 void wxListHeaderWindow::AdjustDC(wxDC& dc)
983 {
984 wxGenericListCtrl *parent = m_owner->GetListCtrl();
985
986 int xpix;
987 parent->GetScrollPixelsPerUnit( &xpix, NULL );
988
989 int view_start;
990 parent->GetViewStart( &view_start, NULL );
991
992
993 int org_x = 0;
994 int org_y = 0;
995 dc.GetDeviceOrigin( &org_x, &org_y );
996
997 // account for the horz scrollbar offset
998 #ifdef __WXGTK__
999 if (GetLayoutDirection() == wxLayout_RightToLeft)
1000 {
1001 // Maybe we just have to check for m_signX
1002 // in the DC, but I leave the #ifdef __WXGTK__
1003 // for now
1004 dc.SetDeviceOrigin( org_x + (view_start * xpix), org_y );
1005 }
1006 else
1007 #endif
1008 dc.SetDeviceOrigin( org_x - (view_start * xpix), org_y );
1009 }
1010
1011 void wxListHeaderWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
1012 {
1013 wxGenericListCtrl *parent = m_owner->GetListCtrl();
1014
1015 wxPaintDC dc( this );
1016
1017 AdjustDC( dc );
1018
1019 dc.SetFont( GetFont() );
1020
1021 // width and height of the entire header window
1022 int w, h;
1023 GetClientSize( &w, &h );
1024 parent->CalcUnscrolledPosition(w, 0, &w, NULL);
1025
1026 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
1027 dc.SetTextForeground(GetForegroundColour());
1028
1029 int x = HEADER_OFFSET_X;
1030 int numColumns = m_owner->GetColumnCount();
1031 wxListItem item;
1032 for ( int i = 0; i < numColumns && x < w; i++ )
1033 {
1034 m_owner->GetColumn( i, item );
1035 int wCol = item.m_width;
1036
1037 int cw = wCol;
1038 int ch = h;
1039
1040 int flags = 0;
1041 if (!m_parent->IsEnabled())
1042 flags |= wxCONTROL_DISABLED;
1043
1044 // NB: The code below is not really Mac-specific, but since we are close
1045 // to 2.8 release and I don't have time to test on other platforms, I
1046 // defined this only for wxMac. If this behavior is desired on
1047 // other platforms, please go ahead and revise or remove the #ifdef.
1048 #ifdef __WXMAC__
1049 if ( !m_owner->IsVirtual() && (item.m_mask & wxLIST_MASK_STATE) &&
1050 (item.m_state & wxLIST_STATE_SELECTED) )
1051 flags |= wxCONTROL_SELECTED;
1052 #endif
1053
1054 if (i == 0)
1055 flags |= wxCONTROL_SPECIAL; // mark as first column
1056
1057 wxRendererNative::Get().DrawHeaderButton
1058 (
1059 this,
1060 dc,
1061 wxRect(x, HEADER_OFFSET_Y, cw, ch),
1062 flags
1063 );
1064
1065 // see if we have enough space for the column label
1066
1067 // for this we need the width of the text
1068 wxCoord wLabel;
1069 wxCoord hLabel;
1070 dc.GetTextExtent(item.GetText(), &wLabel, &hLabel);
1071 wLabel += 2 * EXTRA_WIDTH;
1072
1073 // and the width of the icon, if any
1074 int ix = 0, iy = 0; // init them just to suppress the compiler warnings
1075 const int image = item.m_image;
1076 wxImageList *imageList;
1077 if ( image != -1 )
1078 {
1079 imageList = m_owner->GetSmallImageList();
1080 if ( imageList )
1081 {
1082 imageList->GetSize(image, ix, iy);
1083 wLabel += ix + HEADER_IMAGE_MARGIN_IN_REPORT_MODE;
1084 }
1085 }
1086 else
1087 {
1088 imageList = NULL;
1089 }
1090
1091 // ignore alignment if there is not enough space anyhow
1092 int xAligned;
1093 switch ( wLabel < cw ? item.GetAlign() : wxLIST_FORMAT_LEFT )
1094 {
1095 default:
1096 wxFAIL_MSG( wxT("unknown list item format") );
1097 // fall through
1098
1099 case wxLIST_FORMAT_LEFT:
1100 xAligned = x;
1101 break;
1102
1103 case wxLIST_FORMAT_RIGHT:
1104 xAligned = x + cw - wLabel;
1105 break;
1106
1107 case wxLIST_FORMAT_CENTER:
1108 xAligned = x + (cw - wLabel) / 2;
1109 break;
1110 }
1111
1112 // draw the text and image clipping them so that they
1113 // don't overwrite the column boundary
1114 wxDCClipper clipper(dc, x, HEADER_OFFSET_Y, cw, h);
1115
1116 // if we have an image, draw it on the right of the label
1117 if ( imageList )
1118 {
1119 imageList->Draw
1120 (
1121 image,
1122 dc,
1123 xAligned + wLabel - ix - HEADER_IMAGE_MARGIN_IN_REPORT_MODE,
1124 HEADER_OFFSET_Y + (h - iy)/2,
1125 wxIMAGELIST_DRAW_TRANSPARENT
1126 );
1127 }
1128
1129 dc.DrawText( item.GetText(),
1130 xAligned + EXTRA_WIDTH, (h - hLabel) / 2 );
1131
1132 x += wCol;
1133 }
1134
1135 // Fill in what's missing to the right of the columns, otherwise we will
1136 // leave an unpainted area when columns are removed (and it looks better)
1137 if ( x < w )
1138 {
1139 wxRendererNative::Get().DrawHeaderButton
1140 (
1141 this,
1142 dc,
1143 wxRect(x, HEADER_OFFSET_Y, w - x, h),
1144 wxCONTROL_DIRTY // mark as last column
1145 );
1146 }
1147 }
1148
1149 void wxListHeaderWindow::OnInternalIdle()
1150 {
1151 wxWindow::OnInternalIdle();
1152
1153 if (m_sendSetColumnWidth)
1154 {
1155 m_owner->SetColumnWidth( m_colToSend, m_widthToSend );
1156 m_sendSetColumnWidth = false;
1157 }
1158 }
1159
1160 void wxListHeaderWindow::DrawCurrent()
1161 {
1162 #if 1
1163 // m_owner->SetColumnWidth( m_column, m_currentX - m_minX );
1164 m_sendSetColumnWidth = true;
1165 m_colToSend = m_column;
1166 m_widthToSend = m_currentX - m_minX;
1167 #else
1168 int x1 = m_currentX;
1169 int y1 = 0;
1170 m_owner->ClientToScreen( &x1, &y1 );
1171
1172 int x2 = m_currentX;
1173 int y2 = 0;
1174 m_owner->GetClientSize( NULL, &y2 );
1175 m_owner->ClientToScreen( &x2, &y2 );
1176
1177 wxScreenDC dc;
1178 dc.SetLogicalFunction( wxINVERT );
1179 dc.SetPen( wxPen(*wxBLACK, 2) );
1180 dc.SetBrush( *wxTRANSPARENT_BRUSH );
1181
1182 AdjustDC(dc);
1183
1184 dc.DrawLine( x1, y1, x2, y2 );
1185
1186 dc.SetLogicalFunction( wxCOPY );
1187
1188 dc.SetPen( wxNullPen );
1189 dc.SetBrush( wxNullBrush );
1190 #endif
1191 }
1192
1193 void wxListHeaderWindow::OnMouse( wxMouseEvent &event )
1194 {
1195 wxGenericListCtrl *parent = m_owner->GetListCtrl();
1196
1197 // we want to work with logical coords
1198 int x;
1199 parent->CalcUnscrolledPosition(event.GetX(), 0, &x, NULL);
1200 int y = event.GetY();
1201
1202 if (m_isDragging)
1203 {
1204 SendListEvent(wxEVT_COMMAND_LIST_COL_DRAGGING, event.GetPosition());
1205
1206 // we don't draw the line beyond our window, but we allow dragging it
1207 // there
1208 int w = 0;
1209 GetClientSize( &w, NULL );
1210 parent->CalcUnscrolledPosition(w, 0, &w, NULL);
1211 w -= 6;
1212
1213 // erase the line if it was drawn
1214 if ( m_currentX < w )
1215 DrawCurrent();
1216
1217 if (event.ButtonUp())
1218 {
1219 ReleaseMouse();
1220 m_isDragging = false;
1221 m_dirty = true;
1222 m_owner->SetColumnWidth( m_column, m_currentX - m_minX );
1223 SendListEvent(wxEVT_COMMAND_LIST_COL_END_DRAG, event.GetPosition());
1224 }
1225 else
1226 {
1227 if (x > m_minX + 7)
1228 m_currentX = x;
1229 else
1230 m_currentX = m_minX + 7;
1231
1232 // draw in the new location
1233 if ( m_currentX < w )
1234 DrawCurrent();
1235 }
1236 }
1237 else // not dragging
1238 {
1239 m_minX = 0;
1240 bool hit_border = false;
1241
1242 // end of the current column
1243 int xpos = 0;
1244
1245 // find the column where this event occurred
1246 int col,
1247 countCol = m_owner->GetColumnCount();
1248 for (col = 0; col < countCol; col++)
1249 {
1250 xpos += m_owner->GetColumnWidth( col );
1251 m_column = col;
1252
1253 if ( (abs(x-xpos) < 3) && (y < 22) )
1254 {
1255 // near the column border
1256 hit_border = true;
1257 break;
1258 }
1259
1260 if ( x < xpos )
1261 {
1262 // inside the column
1263 break;
1264 }
1265
1266 m_minX = xpos;
1267 }
1268
1269 if ( col == countCol )
1270 m_column = -1;
1271
1272 if (event.LeftDown() || event.RightUp())
1273 {
1274 if (hit_border && event.LeftDown())
1275 {
1276 if ( SendListEvent(wxEVT_COMMAND_LIST_COL_BEGIN_DRAG,
1277 event.GetPosition()) )
1278 {
1279 m_isDragging = true;
1280 m_currentX = x;
1281 CaptureMouse();
1282 DrawCurrent();
1283 }
1284 //else: column resizing was vetoed by the user code
1285 }
1286 else // click on a column
1287 {
1288 // record the selected state of the columns
1289 if (event.LeftDown())
1290 {
1291 for (int i=0; i < m_owner->GetColumnCount(); i++)
1292 {
1293 wxListItem colItem;
1294 m_owner->GetColumn(i, colItem);
1295 long state = colItem.GetState();
1296 if (i == m_column)
1297 colItem.SetState(state | wxLIST_STATE_SELECTED);
1298 else
1299 colItem.SetState(state & ~wxLIST_STATE_SELECTED);
1300 m_owner->SetColumn(i, colItem);
1301 }
1302 }
1303
1304 SendListEvent( event.LeftDown()
1305 ? wxEVT_COMMAND_LIST_COL_CLICK
1306 : wxEVT_COMMAND_LIST_COL_RIGHT_CLICK,
1307 event.GetPosition());
1308 }
1309 }
1310 else if (event.Moving())
1311 {
1312 bool setCursor;
1313 if (hit_border)
1314 {
1315 setCursor = m_currentCursor == wxSTANDARD_CURSOR;
1316 m_currentCursor = m_resizeCursor;
1317 }
1318 else
1319 {
1320 setCursor = m_currentCursor != wxSTANDARD_CURSOR;
1321 m_currentCursor = wxSTANDARD_CURSOR;
1322 }
1323
1324 if ( setCursor )
1325 SetCursor(*m_currentCursor);
1326 }
1327 }
1328 }
1329
1330 void wxListHeaderWindow::OnSetFocus( wxFocusEvent &WXUNUSED(event) )
1331 {
1332 m_owner->SetFocus();
1333 m_owner->Update();
1334 }
1335
1336 bool wxListHeaderWindow::SendListEvent(wxEventType type, const wxPoint& pos)
1337 {
1338 wxWindow *parent = GetParent();
1339 wxListEvent le( type, parent->GetId() );
1340 le.SetEventObject( parent );
1341 le.m_pointDrag = pos;
1342
1343 // the position should be relative to the parent window, not
1344 // this one for compatibility with MSW and common sense: the
1345 // user code doesn't know anything at all about this header
1346 // window, so why should it get positions relative to it?
1347 le.m_pointDrag.y -= GetSize().y;
1348
1349 le.m_col = m_column;
1350 return !parent->GetEventHandler()->ProcessEvent( le ) || le.IsAllowed();
1351 }
1352
1353 //-----------------------------------------------------------------------------
1354 // wxListRenameTimer (internal)
1355 //-----------------------------------------------------------------------------
1356
1357 wxListRenameTimer::wxListRenameTimer( wxListMainWindow *owner )
1358 {
1359 m_owner = owner;
1360 }
1361
1362 void wxListRenameTimer::Notify()
1363 {
1364 m_owner->OnRenameTimer();
1365 }
1366
1367 //-----------------------------------------------------------------------------
1368 // wxListTextCtrlWrapper (internal)
1369 //-----------------------------------------------------------------------------
1370
1371 BEGIN_EVENT_TABLE(wxListTextCtrlWrapper, wxEvtHandler)
1372 EVT_CHAR (wxListTextCtrlWrapper::OnChar)
1373 EVT_KEY_UP (wxListTextCtrlWrapper::OnKeyUp)
1374 EVT_KILL_FOCUS (wxListTextCtrlWrapper::OnKillFocus)
1375 END_EVENT_TABLE()
1376
1377 wxListTextCtrlWrapper::wxListTextCtrlWrapper(wxListMainWindow *owner,
1378 wxTextCtrl *text,
1379 size_t itemEdit)
1380 : m_startValue(owner->GetItemText(itemEdit)),
1381 m_itemEdited(itemEdit)
1382 {
1383 m_owner = owner;
1384 m_text = text;
1385 m_aboutToFinish = false;
1386
1387 wxGenericListCtrl *parent = m_owner->GetListCtrl();
1388
1389 wxRect rectLabel = owner->GetLineLabelRect(itemEdit);
1390
1391 parent->CalcScrolledPosition(rectLabel.x, rectLabel.y,
1392 &rectLabel.x, &rectLabel.y);
1393
1394 m_text->Create(owner, wxID_ANY, m_startValue,
1395 wxPoint(rectLabel.x-4,rectLabel.y-4),
1396 wxSize(rectLabel.width+11,rectLabel.height+8));
1397 m_text->SetFocus();
1398
1399 m_text->PushEventHandler(this);
1400 }
1401
1402 void wxListTextCtrlWrapper::EndEdit(EndReason reason)
1403 {
1404 m_aboutToFinish = true;
1405
1406 switch ( reason )
1407 {
1408 case End_Accept:
1409 // Notify the owner about the changes
1410 AcceptChanges();
1411
1412 // Even if vetoed, close the control (consistent with MSW)
1413 Finish( true );
1414 break;
1415
1416 case End_Discard:
1417 m_owner->OnRenameCancelled(m_itemEdited);
1418
1419 Finish( true );
1420 break;
1421
1422 case End_Destroy:
1423 // Don't generate any notifications for the control being destroyed
1424 // and don't set focus to it neither.
1425 Finish(false);
1426 break;
1427 }
1428 }
1429
1430 void wxListTextCtrlWrapper::Finish( bool setfocus )
1431 {
1432 m_text->RemoveEventHandler(this);
1433 m_owner->ResetTextControl( m_text );
1434
1435 wxPendingDelete.Append( this );
1436
1437 if (setfocus)
1438 m_owner->SetFocus();
1439 }
1440
1441 bool wxListTextCtrlWrapper::AcceptChanges()
1442 {
1443 const wxString value = m_text->GetValue();
1444
1445 // notice that we should always call OnRenameAccept() to generate the "end
1446 // label editing" event, even if the user hasn't really changed anything
1447 if ( !m_owner->OnRenameAccept(m_itemEdited, value) )
1448 {
1449 // vetoed by the user
1450 return false;
1451 }
1452
1453 // accepted, do rename the item (unless nothing changed)
1454 if ( value != m_startValue )
1455 m_owner->SetItemText(m_itemEdited, value);
1456
1457 return true;
1458 }
1459
1460 void wxListTextCtrlWrapper::OnChar( wxKeyEvent &event )
1461 {
1462 switch ( event.m_keyCode )
1463 {
1464 case WXK_RETURN:
1465 EndEdit( End_Accept );
1466 break;
1467
1468 case WXK_ESCAPE:
1469 EndEdit( End_Discard );
1470 break;
1471
1472 default:
1473 event.Skip();
1474 }
1475 }
1476
1477 void wxListTextCtrlWrapper::OnKeyUp( wxKeyEvent &event )
1478 {
1479 if (m_aboutToFinish)
1480 {
1481 // auto-grow the textctrl:
1482 wxSize parentSize = m_owner->GetSize();
1483 wxPoint myPos = m_text->GetPosition();
1484 wxSize mySize = m_text->GetSize();
1485 int sx, sy;
1486 m_text->GetTextExtent(m_text->GetValue() + wxT("MM"), &sx, &sy);
1487 if (myPos.x + sx > parentSize.x)
1488 sx = parentSize.x - myPos.x;
1489 if (mySize.x > sx)
1490 sx = mySize.x;
1491 m_text->SetSize(sx, wxDefaultCoord);
1492 }
1493
1494 event.Skip();
1495 }
1496
1497 void wxListTextCtrlWrapper::OnKillFocus( wxFocusEvent &event )
1498 {
1499 if ( !m_aboutToFinish )
1500 {
1501 if ( !AcceptChanges() )
1502 m_owner->OnRenameCancelled( m_itemEdited );
1503
1504 Finish( false );
1505 }
1506
1507 // We must let the native text control handle focus
1508 event.Skip();
1509 }
1510
1511 //-----------------------------------------------------------------------------
1512 // wxListMainWindow
1513 //-----------------------------------------------------------------------------
1514
1515 BEGIN_EVENT_TABLE(wxListMainWindow, wxWindow)
1516 EVT_PAINT (wxListMainWindow::OnPaint)
1517 EVT_MOUSE_EVENTS (wxListMainWindow::OnMouse)
1518 EVT_CHAR (wxListMainWindow::OnChar)
1519 EVT_KEY_DOWN (wxListMainWindow::OnKeyDown)
1520 EVT_KEY_UP (wxListMainWindow::OnKeyUp)
1521 EVT_SET_FOCUS (wxListMainWindow::OnSetFocus)
1522 EVT_KILL_FOCUS (wxListMainWindow::OnKillFocus)
1523 EVT_SCROLLWIN (wxListMainWindow::OnScroll)
1524 EVT_CHILD_FOCUS (wxListMainWindow::OnChildFocus)
1525 END_EVENT_TABLE()
1526
1527 void wxListMainWindow::Init()
1528 {
1529 m_dirty = true;
1530 m_countVirt = 0;
1531 m_lineFrom =
1532 m_lineTo = (size_t)-1;
1533 m_linesPerPage = 0;
1534
1535 m_headerWidth =
1536 m_lineHeight = 0;
1537
1538 m_small_image_list = NULL;
1539 m_normal_image_list = NULL;
1540
1541 m_small_spacing = 30;
1542 m_normal_spacing = 40;
1543
1544 m_hasFocus = false;
1545 m_dragCount = 0;
1546 m_isCreated = false;
1547
1548 m_lastOnSame = false;
1549 m_renameTimer = new wxListRenameTimer( this );
1550 m_textctrlWrapper = NULL;
1551
1552 m_current =
1553 m_lineLastClicked =
1554 m_lineSelectSingleOnUp =
1555 m_lineBeforeLastClicked = (size_t)-1;
1556 }
1557
1558 wxListMainWindow::wxListMainWindow()
1559 {
1560 Init();
1561
1562 m_highlightBrush =
1563 m_highlightUnfocusedBrush = NULL;
1564 }
1565
1566 wxListMainWindow::wxListMainWindow( wxWindow *parent,
1567 wxWindowID id,
1568 const wxPoint& pos,
1569 const wxSize& size,
1570 long style,
1571 const wxString &name )
1572 : wxWindow( parent, id, pos, size, style, name )
1573 {
1574 Init();
1575
1576 m_highlightBrush = new wxBrush
1577 (
1578 wxSystemSettings::GetColour
1579 (
1580 wxSYS_COLOUR_HIGHLIGHT
1581 ),
1582 wxBRUSHSTYLE_SOLID
1583 );
1584
1585 m_highlightUnfocusedBrush = new wxBrush
1586 (
1587 wxSystemSettings::GetColour
1588 (
1589 wxSYS_COLOUR_BTNSHADOW
1590 ),
1591 wxBRUSHSTYLE_SOLID
1592 );
1593
1594 wxVisualAttributes attr = wxGenericListCtrl::GetClassDefaultAttributes();
1595 SetOwnForegroundColour( attr.colFg );
1596 SetOwnBackgroundColour( attr.colBg );
1597 if (!m_hasFont)
1598 SetOwnFont( attr.font );
1599 }
1600
1601 wxListMainWindow::~wxListMainWindow()
1602 {
1603 if ( m_textctrlWrapper )
1604 m_textctrlWrapper->EndEdit(wxListTextCtrlWrapper::End_Destroy);
1605
1606 DoDeleteAllItems();
1607 WX_CLEAR_LIST(wxListHeaderDataList, m_columns);
1608 WX_CLEAR_ARRAY(m_aColWidths);
1609
1610 delete m_highlightBrush;
1611 delete m_highlightUnfocusedBrush;
1612 delete m_renameTimer;
1613 }
1614
1615 void wxListMainWindow::SetReportView(bool inReportView)
1616 {
1617 const size_t count = m_lines.size();
1618 for ( size_t n = 0; n < count; n++ )
1619 {
1620 m_lines[n].SetReportView(inReportView);
1621 }
1622 }
1623
1624 void wxListMainWindow::CacheLineData(size_t line)
1625 {
1626 wxGenericListCtrl *listctrl = GetListCtrl();
1627
1628 wxListLineData *ld = GetDummyLine();
1629
1630 size_t countCol = GetColumnCount();
1631 for ( size_t col = 0; col < countCol; col++ )
1632 {
1633 ld->SetText(col, listctrl->OnGetItemText(line, col));
1634 ld->SetImage(col, listctrl->OnGetItemColumnImage(line, col));
1635 }
1636
1637 ld->SetAttr(listctrl->OnGetItemAttr(line));
1638 }
1639
1640 wxListLineData *wxListMainWindow::GetDummyLine() const
1641 {
1642 wxASSERT_MSG( !IsEmpty(), wxT("invalid line index") );
1643 wxASSERT_MSG( IsVirtual(), wxT("GetDummyLine() shouldn't be called") );
1644
1645 wxListMainWindow *self = wxConstCast(this, wxListMainWindow);
1646
1647 // we need to recreate the dummy line if the number of columns in the
1648 // control changed as it would have the incorrect number of fields
1649 // otherwise
1650 if ( !m_lines.IsEmpty() &&
1651 m_lines[0].m_items.GetCount() != (size_t)GetColumnCount() )
1652 {
1653 self->m_lines.Clear();
1654 }
1655
1656 if ( m_lines.IsEmpty() )
1657 {
1658 wxListLineData *line = new wxListLineData(self);
1659 self->m_lines.Add(line);
1660
1661 // don't waste extra memory -- there never going to be anything
1662 // else/more in this array
1663 self->m_lines.Shrink();
1664 }
1665
1666 return &m_lines[0];
1667 }
1668
1669 // ----------------------------------------------------------------------------
1670 // line geometry (report mode only)
1671 // ----------------------------------------------------------------------------
1672
1673 wxCoord wxListMainWindow::GetLineHeight() const
1674 {
1675 // we cache the line height as calling GetTextExtent() is slow
1676 if ( !m_lineHeight )
1677 {
1678 wxListMainWindow *self = wxConstCast(this, wxListMainWindow);
1679
1680 wxClientDC dc( self );
1681 dc.SetFont( GetFont() );
1682
1683 wxCoord y;
1684 dc.GetTextExtent(wxT("H"), NULL, &y);
1685
1686 if ( m_small_image_list && m_small_image_list->GetImageCount() )
1687 {
1688 int iw = 0, ih = 0;
1689 m_small_image_list->GetSize(0, iw, ih);
1690 y = wxMax(y, ih);
1691 }
1692
1693 y += EXTRA_HEIGHT;
1694 self->m_lineHeight = y + LINE_SPACING;
1695 }
1696
1697 return m_lineHeight;
1698 }
1699
1700 wxCoord wxListMainWindow::GetLineY(size_t line) const
1701 {
1702 wxASSERT_MSG( InReportView(), wxT("only works in report mode") );
1703
1704 return LINE_SPACING + line * GetLineHeight();
1705 }
1706
1707 wxRect wxListMainWindow::GetLineRect(size_t line) const
1708 {
1709 if ( !InReportView() )
1710 return GetLine(line)->m_gi->m_rectAll;
1711
1712 wxRect rect;
1713 rect.x = HEADER_OFFSET_X;
1714 rect.y = GetLineY(line);
1715 rect.width = GetHeaderWidth();
1716 rect.height = GetLineHeight();
1717
1718 return rect;
1719 }
1720
1721 wxRect wxListMainWindow::GetLineLabelRect(size_t line) const
1722 {
1723 if ( !InReportView() )
1724 return GetLine(line)->m_gi->m_rectLabel;
1725
1726 int image_x = 0;
1727 wxListLineData *data = GetLine(line);
1728 wxListItemDataList::compatibility_iterator node = data->m_items.GetFirst();
1729 if (node)
1730 {
1731 wxListItemData *item = node->GetData();
1732 if ( item->HasImage() )
1733 {
1734 int ix, iy;
1735 GetImageSize( item->GetImage(), ix, iy );
1736 image_x = 3 + ix + IMAGE_MARGIN_IN_REPORT_MODE;
1737 }
1738 }
1739
1740 wxRect rect;
1741 rect.x = image_x + HEADER_OFFSET_X;
1742 rect.y = GetLineY(line);
1743 rect.width = GetColumnWidth(0) - image_x;
1744 rect.height = GetLineHeight();
1745
1746 return rect;
1747 }
1748
1749 wxRect wxListMainWindow::GetLineIconRect(size_t line) const
1750 {
1751 if ( !InReportView() )
1752 return GetLine(line)->m_gi->m_rectIcon;
1753
1754 wxListLineData *ld = GetLine(line);
1755 wxASSERT_MSG( ld->HasImage(), wxT("should have an image") );
1756
1757 wxRect rect;
1758 rect.x = HEADER_OFFSET_X;
1759 rect.y = GetLineY(line);
1760 GetImageSize(ld->GetImage(), rect.width, rect.height);
1761
1762 return rect;
1763 }
1764
1765 wxRect wxListMainWindow::GetLineHighlightRect(size_t line) const
1766 {
1767 return InReportView() ? GetLineRect(line)
1768 : GetLine(line)->m_gi->m_rectHighlight;
1769 }
1770
1771 long wxListMainWindow::HitTestLine(size_t line, int x, int y) const
1772 {
1773 wxASSERT_MSG( line < GetItemCount(), wxT("invalid line in HitTestLine") );
1774
1775 wxListLineData *ld = GetLine(line);
1776
1777 if ( ld->HasImage() && GetLineIconRect(line).Contains(x, y) )
1778 return wxLIST_HITTEST_ONITEMICON;
1779
1780 // VS: Testing for "ld->HasText() || InReportView()" instead of
1781 // "ld->HasText()" is needed to make empty lines in report view
1782 // possible
1783 if ( ld->HasText() || InReportView() )
1784 {
1785 wxRect rect = InReportView() ? GetLineRect(line)
1786 : GetLineLabelRect(line);
1787
1788 if ( rect.Contains(x, y) )
1789 return wxLIST_HITTEST_ONITEMLABEL;
1790 }
1791
1792 return 0;
1793 }
1794
1795 // ----------------------------------------------------------------------------
1796 // highlight (selection) handling
1797 // ----------------------------------------------------------------------------
1798
1799 bool wxListMainWindow::IsHighlighted(size_t line) const
1800 {
1801 if ( IsVirtual() )
1802 {
1803 return m_selStore.IsSelected(line);
1804 }
1805 else // !virtual
1806 {
1807 wxListLineData *ld = GetLine(line);
1808 wxCHECK_MSG( ld, false, wxT("invalid index in IsHighlighted") );
1809
1810 return ld->IsHighlighted();
1811 }
1812 }
1813
1814 void wxListMainWindow::HighlightLines( size_t lineFrom,
1815 size_t lineTo,
1816 bool highlight )
1817 {
1818 if ( IsVirtual() )
1819 {
1820 wxArrayInt linesChanged;
1821 if ( !m_selStore.SelectRange(lineFrom, lineTo, highlight,
1822 &linesChanged) )
1823 {
1824 // meny items changed state, refresh everything
1825 RefreshLines(lineFrom, lineTo);
1826 }
1827 else // only a few items changed state, refresh only them
1828 {
1829 size_t count = linesChanged.GetCount();
1830 for ( size_t n = 0; n < count; n++ )
1831 {
1832 RefreshLine(linesChanged[n]);
1833 }
1834 }
1835 }
1836 else // iterate over all items in non report view
1837 {
1838 for ( size_t line = lineFrom; line <= lineTo; line++ )
1839 {
1840 if ( HighlightLine(line, highlight) )
1841 RefreshLine(line);
1842 }
1843 }
1844 }
1845
1846 bool wxListMainWindow::HighlightLine( size_t line, bool highlight )
1847 {
1848 bool changed;
1849
1850 if ( IsVirtual() )
1851 {
1852 changed = m_selStore.SelectItem(line, highlight);
1853 }
1854 else // !virtual
1855 {
1856 wxListLineData *ld = GetLine(line);
1857 wxCHECK_MSG( ld, false, wxT("invalid index in HighlightLine") );
1858
1859 changed = ld->Highlight(highlight);
1860 }
1861
1862 if ( changed )
1863 {
1864 SendNotify( line, highlight ? wxEVT_COMMAND_LIST_ITEM_SELECTED
1865 : wxEVT_COMMAND_LIST_ITEM_DESELECTED );
1866 }
1867
1868 return changed;
1869 }
1870
1871 void wxListMainWindow::RefreshLine( size_t line )
1872 {
1873 if ( InReportView() )
1874 {
1875 size_t visibleFrom, visibleTo;
1876 GetVisibleLinesRange(&visibleFrom, &visibleTo);
1877
1878 if ( line < visibleFrom || line > visibleTo )
1879 return;
1880 }
1881
1882 wxRect rect = GetLineRect(line);
1883
1884 GetListCtrl()->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
1885 RefreshRect( rect );
1886 }
1887
1888 void wxListMainWindow::RefreshLines( size_t lineFrom, size_t lineTo )
1889 {
1890 // we suppose that they are ordered by caller
1891 wxASSERT_MSG( lineFrom <= lineTo, wxT("indices in disorder") );
1892
1893 wxASSERT_MSG( lineTo < GetItemCount(), wxT("invalid line range") );
1894
1895 if ( InReportView() )
1896 {
1897 size_t visibleFrom, visibleTo;
1898 GetVisibleLinesRange(&visibleFrom, &visibleTo);
1899
1900 if ( lineFrom < visibleFrom )
1901 lineFrom = visibleFrom;
1902 if ( lineTo > visibleTo )
1903 lineTo = visibleTo;
1904
1905 wxRect rect;
1906 rect.x = 0;
1907 rect.y = GetLineY(lineFrom);
1908 rect.width = GetClientSize().x;
1909 rect.height = GetLineY(lineTo) - rect.y + GetLineHeight();
1910
1911 GetListCtrl()->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
1912 RefreshRect( rect );
1913 }
1914 else // !report
1915 {
1916 // TODO: this should be optimized...
1917 for ( size_t line = lineFrom; line <= lineTo; line++ )
1918 {
1919 RefreshLine(line);
1920 }
1921 }
1922 }
1923
1924 void wxListMainWindow::RefreshAfter( size_t lineFrom )
1925 {
1926 if ( InReportView() )
1927 {
1928 size_t visibleFrom, visibleTo;
1929 GetVisibleLinesRange(&visibleFrom, &visibleTo);
1930
1931 if ( lineFrom < visibleFrom )
1932 lineFrom = visibleFrom;
1933 else if ( lineFrom > visibleTo )
1934 return;
1935
1936 wxRect rect;
1937 rect.x = 0;
1938 rect.y = GetLineY(lineFrom);
1939 GetListCtrl()->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
1940
1941 wxSize size = GetClientSize();
1942 rect.width = size.x;
1943
1944 // refresh till the bottom of the window
1945 rect.height = size.y - rect.y;
1946
1947 RefreshRect( rect );
1948 }
1949 else // !report
1950 {
1951 // TODO: how to do it more efficiently?
1952 m_dirty = true;
1953 }
1954 }
1955
1956 void wxListMainWindow::RefreshSelected()
1957 {
1958 if ( IsEmpty() )
1959 return;
1960
1961 size_t from, to;
1962 if ( InReportView() )
1963 {
1964 GetVisibleLinesRange(&from, &to);
1965 }
1966 else // !virtual
1967 {
1968 from = 0;
1969 to = GetItemCount() - 1;
1970 }
1971
1972 if ( HasCurrent() && m_current >= from && m_current <= to )
1973 RefreshLine(m_current);
1974
1975 for ( size_t line = from; line <= to; line++ )
1976 {
1977 // NB: the test works as expected even if m_current == -1
1978 if ( line != m_current && IsHighlighted(line) )
1979 RefreshLine(line);
1980 }
1981 }
1982
1983 void wxListMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
1984 {
1985 // Note: a wxPaintDC must be constructed even if no drawing is
1986 // done (a Windows requirement).
1987 wxPaintDC dc( this );
1988
1989 if ( IsEmpty() )
1990 {
1991 // nothing to draw or not the moment to draw it
1992 return;
1993 }
1994
1995 if ( m_dirty )
1996 RecalculatePositions( false );
1997
1998 GetListCtrl()->PrepareDC( dc );
1999
2000 int dev_x, dev_y;
2001 GetListCtrl()->CalcScrolledPosition( 0, 0, &dev_x, &dev_y );
2002
2003 dc.SetFont( GetFont() );
2004
2005 if ( InReportView() )
2006 {
2007 int lineHeight = GetLineHeight();
2008
2009 size_t visibleFrom, visibleTo;
2010 GetVisibleLinesRange(&visibleFrom, &visibleTo);
2011
2012 wxRect rectLine;
2013 int xOrig = dc.LogicalToDeviceX( 0 );
2014 int yOrig = dc.LogicalToDeviceY( 0 );
2015
2016 // tell the caller cache to cache the data
2017 if ( IsVirtual() )
2018 {
2019 wxListEvent evCache(wxEVT_COMMAND_LIST_CACHE_HINT,
2020 GetParent()->GetId());
2021 evCache.SetEventObject( GetParent() );
2022 evCache.m_oldItemIndex = visibleFrom;
2023 evCache.m_itemIndex = visibleTo;
2024 GetParent()->GetEventHandler()->ProcessEvent( evCache );
2025 }
2026
2027 for ( size_t line = visibleFrom; line <= visibleTo; line++ )
2028 {
2029 rectLine = GetLineRect(line);
2030
2031
2032 if ( !IsExposed(rectLine.x + xOrig, rectLine.y + yOrig,
2033 rectLine.width, rectLine.height) )
2034 {
2035 // don't redraw unaffected lines to avoid flicker
2036 continue;
2037 }
2038
2039 GetLine(line)->DrawInReportMode( &dc,
2040 rectLine,
2041 GetLineHighlightRect(line),
2042 IsHighlighted(line),
2043 line == m_current );
2044 }
2045
2046 if ( HasFlag(wxLC_HRULES) )
2047 {
2048 wxPen pen(GetRuleColour(), 1, wxPENSTYLE_SOLID);
2049 wxSize clientSize = GetClientSize();
2050
2051 size_t i = visibleFrom;
2052 if (i == 0) i = 1; // Don't draw the first one
2053 for ( ; i <= visibleTo; i++ )
2054 {
2055 dc.SetPen(pen);
2056 dc.SetBrush( *wxTRANSPARENT_BRUSH );
2057 dc.DrawLine(0 - dev_x, i * lineHeight,
2058 clientSize.x - dev_x, i * lineHeight);
2059 }
2060
2061 // Draw last horizontal rule
2062 if ( visibleTo == GetItemCount() - 1 )
2063 {
2064 dc.SetPen( pen );
2065 dc.SetBrush( *wxTRANSPARENT_BRUSH );
2066 dc.DrawLine(0 - dev_x, (m_lineTo + 1) * lineHeight,
2067 clientSize.x - dev_x , (m_lineTo + 1) * lineHeight );
2068 }
2069 }
2070
2071 // Draw vertical rules if required
2072 if ( HasFlag(wxLC_VRULES) && !IsEmpty() )
2073 {
2074 wxPen pen(GetRuleColour(), 1, wxPENSTYLE_SOLID);
2075 wxRect firstItemRect, lastItemRect;
2076
2077 GetItemRect(visibleFrom, firstItemRect);
2078 GetItemRect(visibleTo, lastItemRect);
2079 int x = firstItemRect.GetX();
2080 dc.SetPen(pen);
2081 dc.SetBrush(* wxTRANSPARENT_BRUSH);
2082
2083 for (int col = 0; col < GetColumnCount(); col++)
2084 {
2085 int colWidth = GetColumnWidth(col);
2086 x += colWidth;
2087 int x_pos = x - dev_x;
2088 if (col < GetColumnCount()-1) x_pos -= 2;
2089 dc.DrawLine(x_pos, firstItemRect.GetY() - 1 - dev_y,
2090 x_pos, lastItemRect.GetBottom() + 1 - dev_y);
2091 }
2092 }
2093 }
2094 else // !report
2095 {
2096 size_t count = GetItemCount();
2097 for ( size_t i = 0; i < count; i++ )
2098 {
2099 GetLine(i)->Draw( &dc, i == m_current );
2100 }
2101 }
2102
2103 // DrawFocusRect() is unusable under Mac, it draws outside of the highlight
2104 // rectangle somehow and so leaves traces when the item is not selected any
2105 // more, see #12229.
2106 #ifndef __WXMAC__
2107 if ( HasCurrent() )
2108 {
2109 int flags = 0;
2110 if ( IsHighlighted(m_current) )
2111 flags |= wxCONTROL_SELECTED;
2112
2113 wxRendererNative::Get().
2114 DrawFocusRect(this, dc, GetLineHighlightRect(m_current), flags);
2115 }
2116 #endif // !__WXMAC__
2117 }
2118
2119 void wxListMainWindow::HighlightAll( bool on )
2120 {
2121 if ( IsSingleSel() )
2122 {
2123 wxASSERT_MSG( !on, wxT("can't do this in a single selection control") );
2124
2125 // we just have one item to turn off
2126 if ( HasCurrent() && IsHighlighted(m_current) )
2127 {
2128 HighlightLine(m_current, false);
2129 RefreshLine(m_current);
2130 }
2131 }
2132 else // multi selection
2133 {
2134 if ( !IsEmpty() )
2135 HighlightLines(0, GetItemCount() - 1, on);
2136 }
2137 }
2138
2139 void wxListMainWindow::OnChildFocus(wxChildFocusEvent& WXUNUSED(event))
2140 {
2141 // Do nothing here. This prevents the default handler in wxScrolledWindow
2142 // from needlessly scrolling the window when the edit control is
2143 // dismissed. See ticket #9563.
2144 }
2145
2146 void wxListMainWindow::SendNotify( size_t line,
2147 wxEventType command,
2148 const wxPoint& point )
2149 {
2150 wxListEvent le( command, GetParent()->GetId() );
2151 le.SetEventObject( GetParent() );
2152
2153 le.m_itemIndex = line;
2154
2155 // set only for events which have position
2156 if ( point != wxDefaultPosition )
2157 le.m_pointDrag = point;
2158
2159 // don't try to get the line info for virtual list controls: the main
2160 // program has it anyhow and if we did it would result in accessing all
2161 // the lines, even those which are not visible now and this is precisely
2162 // what we're trying to avoid
2163 if ( !IsVirtual() )
2164 {
2165 if ( line != (size_t)-1 )
2166 {
2167 GetLine(line)->GetItem( 0, le.m_item );
2168 }
2169 //else: this happens for wxEVT_COMMAND_LIST_ITEM_FOCUSED event
2170 }
2171 //else: there may be no more such item
2172
2173 GetParent()->GetEventHandler()->ProcessEvent( le );
2174 }
2175
2176 void wxListMainWindow::ChangeCurrent(size_t current)
2177 {
2178 m_current = current;
2179
2180 // as the current item changed, we shouldn't start editing it when the
2181 // "slow click" timer expires as the click happened on another item
2182 if ( m_renameTimer->IsRunning() )
2183 m_renameTimer->Stop();
2184
2185 SendNotify(current, wxEVT_COMMAND_LIST_ITEM_FOCUSED);
2186 }
2187
2188 wxTextCtrl *wxListMainWindow::EditLabel(long item, wxClassInfo* textControlClass)
2189 {
2190 wxCHECK_MSG( (item >= 0) && ((size_t)item < GetItemCount()), NULL,
2191 wxT("wrong index in wxGenericListCtrl::EditLabel()") );
2192
2193 wxASSERT_MSG( textControlClass->IsKindOf(CLASSINFO(wxTextCtrl)),
2194 wxT("EditLabel() needs a text control") );
2195
2196 size_t itemEdit = (size_t)item;
2197
2198 wxListEvent le( wxEVT_COMMAND_LIST_BEGIN_LABEL_EDIT, GetParent()->GetId() );
2199 le.SetEventObject( GetParent() );
2200 le.m_itemIndex = item;
2201 wxListLineData *data = GetLine(itemEdit);
2202 wxCHECK_MSG( data, NULL, wxT("invalid index in EditLabel()") );
2203 data->GetItem( 0, le.m_item );
2204
2205 if ( GetParent()->GetEventHandler()->ProcessEvent( le ) && !le.IsAllowed() )
2206 {
2207 // vetoed by user code
2208 return NULL;
2209 }
2210
2211 // We have to call this here because the label in question might just have
2212 // been added and no screen update taken place.
2213 if ( m_dirty )
2214 {
2215 // TODO: use wxTheApp->SafeYieldFor(NULL, wxEVT_CATEGORY_UI) instead
2216 // so that no pending events may change the item count (see below)
2217 // IMPORTANT: needs to be tested!
2218 wxSafeYield();
2219
2220 // Pending events dispatched by wxSafeYield might have changed the item
2221 // count
2222 if ( (size_t)item >= GetItemCount() )
2223 return NULL;
2224 }
2225
2226 wxTextCtrl * const text = (wxTextCtrl *)textControlClass->CreateObject();
2227 m_textctrlWrapper = new wxListTextCtrlWrapper(this, text, item);
2228 return m_textctrlWrapper->GetText();
2229 }
2230
2231 void wxListMainWindow::OnRenameTimer()
2232 {
2233 wxCHECK_RET( HasCurrent(), wxT("unexpected rename timer") );
2234
2235 EditLabel( m_current );
2236 }
2237
2238 bool wxListMainWindow::OnRenameAccept(size_t itemEdit, const wxString& value)
2239 {
2240 wxListEvent le( wxEVT_COMMAND_LIST_END_LABEL_EDIT, GetParent()->GetId() );
2241 le.SetEventObject( GetParent() );
2242 le.m_itemIndex = itemEdit;
2243
2244 wxListLineData *data = GetLine(itemEdit);
2245
2246 wxCHECK_MSG( data, false, wxT("invalid index in OnRenameAccept()") );
2247
2248 data->GetItem( 0, le.m_item );
2249 le.m_item.m_text = value;
2250 return !GetParent()->GetEventHandler()->ProcessEvent( le ) ||
2251 le.IsAllowed();
2252 }
2253
2254 void wxListMainWindow::OnRenameCancelled(size_t itemEdit)
2255 {
2256 // let owner know that the edit was cancelled
2257 wxListEvent le( wxEVT_COMMAND_LIST_END_LABEL_EDIT, GetParent()->GetId() );
2258
2259 le.SetEditCanceled(true);
2260
2261 le.SetEventObject( GetParent() );
2262 le.m_itemIndex = itemEdit;
2263
2264 wxListLineData *data = GetLine(itemEdit);
2265 wxCHECK_RET( data, wxT("invalid index in OnRenameCancelled()") );
2266
2267 data->GetItem( 0, le.m_item );
2268 GetEventHandler()->ProcessEvent( le );
2269 }
2270
2271 void wxListMainWindow::OnMouse( wxMouseEvent &event )
2272 {
2273 #ifdef __WXMAC__
2274 // On wxMac we can't depend on the EVT_KILL_FOCUS event to properly
2275 // shutdown the edit control when the mouse is clicked elsewhere on the
2276 // listctrl because the order of events is different (or something like
2277 // that), so explicitly end the edit if it is active.
2278 if ( event.LeftDown() && m_textctrlWrapper )
2279 m_textctrlWrapper->EndEdit(wxListTextCtrlWrapper::End_Accept);
2280 #endif // __WXMAC__
2281
2282 if ( event.LeftDown() )
2283 SetFocus();
2284
2285 event.SetEventObject( GetParent() );
2286 if ( GetParent()->GetEventHandler()->ProcessEvent( event) )
2287 return;
2288
2289 if (event.GetEventType() == wxEVT_MOUSEWHEEL)
2290 {
2291 // let the base class handle mouse wheel events.
2292 event.Skip();
2293 return;
2294 }
2295
2296 if ( !HasCurrent() || IsEmpty() )
2297 {
2298 if (event.RightDown())
2299 {
2300 SendNotify( (size_t)-1, wxEVT_COMMAND_LIST_ITEM_RIGHT_CLICK, event.GetPosition() );
2301
2302 wxContextMenuEvent evtCtx(wxEVT_CONTEXT_MENU,
2303 GetParent()->GetId(),
2304 ClientToScreen(event.GetPosition()));
2305 evtCtx.SetEventObject(GetParent());
2306 GetParent()->GetEventHandler()->ProcessEvent(evtCtx);
2307 }
2308 return;
2309 }
2310
2311 if (m_dirty)
2312 return;
2313
2314 if ( !(event.Dragging() || event.ButtonDown() || event.LeftUp() ||
2315 event.ButtonDClick()) )
2316 return;
2317
2318 int x = event.GetX();
2319 int y = event.GetY();
2320 GetListCtrl()->CalcUnscrolledPosition( x, y, &x, &y );
2321
2322 // where did we hit it (if we did)?
2323 long hitResult = 0;
2324
2325 size_t count = GetItemCount(),
2326 current;
2327
2328 if ( InReportView() )
2329 {
2330 current = y / GetLineHeight();
2331 if ( current < count )
2332 hitResult = HitTestLine(current, x, y);
2333 }
2334 else // !report
2335 {
2336 // TODO: optimize it too! this is less simple than for report view but
2337 // enumerating all items is still not a way to do it!!
2338 for ( current = 0; current < count; current++ )
2339 {
2340 hitResult = HitTestLine(current, x, y);
2341 if ( hitResult )
2342 break;
2343 }
2344 }
2345
2346 if (event.Dragging())
2347 {
2348 if (m_dragCount == 0)
2349 {
2350 // we have to report the raw, physical coords as we want to be
2351 // able to call HitTest(event.m_pointDrag) from the user code to
2352 // get the item being dragged
2353 m_dragStart = event.GetPosition();
2354 }
2355
2356 m_dragCount++;
2357
2358 if (m_dragCount != 3)
2359 return;
2360
2361 int command = event.RightIsDown() ? wxEVT_COMMAND_LIST_BEGIN_RDRAG
2362 : wxEVT_COMMAND_LIST_BEGIN_DRAG;
2363
2364 wxListEvent le( command, GetParent()->GetId() );
2365 le.SetEventObject( GetParent() );
2366 le.m_itemIndex = m_lineLastClicked;
2367 le.m_pointDrag = m_dragStart;
2368 GetParent()->GetEventHandler()->ProcessEvent( le );
2369
2370 return;
2371 }
2372 else
2373 {
2374 m_dragCount = 0;
2375 }
2376
2377 if ( !hitResult )
2378 {
2379 // outside of any item
2380 if (event.RightDown())
2381 {
2382 SendNotify( (size_t) -1, wxEVT_COMMAND_LIST_ITEM_RIGHT_CLICK, event.GetPosition() );
2383
2384 wxContextMenuEvent evtCtx(
2385 wxEVT_CONTEXT_MENU,
2386 GetParent()->GetId(),
2387 ClientToScreen(event.GetPosition()));
2388 evtCtx.SetEventObject(GetParent());
2389 GetParent()->GetEventHandler()->ProcessEvent(evtCtx);
2390 }
2391 else
2392 {
2393 // reset the selection and bail out
2394 HighlightAll(false);
2395 }
2396
2397 return;
2398 }
2399
2400 bool forceClick = false;
2401 if (event.ButtonDClick())
2402 {
2403 if ( m_renameTimer->IsRunning() )
2404 m_renameTimer->Stop();
2405
2406 m_lastOnSame = false;
2407
2408 if ( current == m_lineLastClicked )
2409 {
2410 SendNotify( current, wxEVT_COMMAND_LIST_ITEM_ACTIVATED );
2411
2412 return;
2413 }
2414 else
2415 {
2416 // The first click was on another item, so don't interpret this as
2417 // a double click, but as a simple click instead
2418 forceClick = true;
2419 }
2420 }
2421
2422 if (event.LeftUp())
2423 {
2424 if (m_lineSelectSingleOnUp != (size_t)-1)
2425 {
2426 // select single line
2427 HighlightAll( false );
2428 ReverseHighlight(m_lineSelectSingleOnUp);
2429 }
2430
2431 if (m_lastOnSame)
2432 {
2433 if ((current == m_current) &&
2434 (hitResult == wxLIST_HITTEST_ONITEMLABEL) &&
2435 HasFlag(wxLC_EDIT_LABELS) )
2436 {
2437 if ( !InReportView() ||
2438 GetLineLabelRect(current).Contains(x, y) )
2439 {
2440 int dclick = wxSystemSettings::GetMetric(wxSYS_DCLICK_MSEC);
2441 m_renameTimer->Start(dclick > 0 ? dclick : 250, true);
2442 }
2443 }
2444 }
2445
2446 m_lastOnSame = false;
2447 m_lineSelectSingleOnUp = (size_t)-1;
2448 }
2449 else
2450 {
2451 // This is necessary, because after a DnD operation in
2452 // from and to ourself, the up event is swallowed by the
2453 // DnD code. So on next non-up event (which means here and
2454 // now) m_lineSelectSingleOnUp should be reset.
2455 m_lineSelectSingleOnUp = (size_t)-1;
2456 }
2457 if (event.RightDown())
2458 {
2459 m_lineBeforeLastClicked = m_lineLastClicked;
2460 m_lineLastClicked = current;
2461
2462 // If the item is already selected, do not update the selection.
2463 // Multi-selections should not be cleared if a selected item is clicked.
2464 if (!IsHighlighted(current))
2465 {
2466 HighlightAll(false);
2467 ChangeCurrent(current);
2468 ReverseHighlight(m_current);
2469 }
2470
2471 SendNotify( current, wxEVT_COMMAND_LIST_ITEM_RIGHT_CLICK, event.GetPosition() );
2472
2473 // Allow generation of context menu event
2474 event.Skip();
2475 }
2476 else if (event.MiddleDown())
2477 {
2478 SendNotify( current, wxEVT_COMMAND_LIST_ITEM_MIDDLE_CLICK );
2479 }
2480 else if ( event.LeftDown() || forceClick )
2481 {
2482 m_lineBeforeLastClicked = m_lineLastClicked;
2483 m_lineLastClicked = current;
2484
2485 size_t oldCurrent = m_current;
2486 bool oldWasSelected = IsHighlighted(m_current);
2487
2488 bool cmdModifierDown = event.CmdDown();
2489 if ( IsSingleSel() || !(cmdModifierDown || event.ShiftDown()) )
2490 {
2491 if ( IsSingleSel() || !IsHighlighted(current) )
2492 {
2493 HighlightAll( false );
2494
2495 ChangeCurrent(current);
2496
2497 ReverseHighlight(m_current);
2498 }
2499 else // multi sel & current is highlighted & no mod keys
2500 {
2501 m_lineSelectSingleOnUp = current;
2502 ChangeCurrent(current); // change focus
2503 }
2504 }
2505 else // multi sel & either ctrl or shift is down
2506 {
2507 if (cmdModifierDown)
2508 {
2509 ChangeCurrent(current);
2510
2511 ReverseHighlight(m_current);
2512 }
2513 else if (event.ShiftDown())
2514 {
2515 ChangeCurrent(current);
2516
2517 size_t lineFrom = oldCurrent,
2518 lineTo = current;
2519
2520 if ( lineTo < lineFrom )
2521 {
2522 lineTo = lineFrom;
2523 lineFrom = m_current;
2524 }
2525
2526 HighlightLines(lineFrom, lineTo);
2527 }
2528 else // !ctrl, !shift
2529 {
2530 // test in the enclosing if should make it impossible
2531 wxFAIL_MSG( wxT("how did we get here?") );
2532 }
2533 }
2534
2535 if (m_current != oldCurrent)
2536 RefreshLine( oldCurrent );
2537
2538 // forceClick is only set if the previous click was on another item
2539 m_lastOnSame = !forceClick && (m_current == oldCurrent) && oldWasSelected;
2540 }
2541 }
2542
2543 void wxListMainWindow::MoveToItem(size_t item)
2544 {
2545 if ( item == (size_t)-1 )
2546 return;
2547
2548 wxRect rect = GetLineRect(item);
2549
2550 int client_w, client_h;
2551 GetClientSize( &client_w, &client_h );
2552
2553 const int hLine = GetLineHeight();
2554
2555 int view_x = SCROLL_UNIT_X * GetListCtrl()->GetScrollPos( wxHORIZONTAL );
2556 int view_y = hLine * GetListCtrl()->GetScrollPos( wxVERTICAL );
2557
2558 if ( InReportView() )
2559 {
2560 // the next we need the range of lines shown it might be different,
2561 // so recalculate it
2562 ResetVisibleLinesRange();
2563
2564 if (rect.y < view_y)
2565 GetListCtrl()->Scroll( -1, rect.y / hLine );
2566 if (rect.y + rect.height + 5 > view_y + client_h)
2567 GetListCtrl()->Scroll( -1, (rect.y + rect.height - client_h + hLine) / hLine );
2568
2569 #ifdef __WXMAC__
2570 // At least on Mac the visible lines value will get reset inside of
2571 // Scroll *before* it actually scrolls the window because of the
2572 // Update() that happens there, so it will still have the wrong value.
2573 // So let's reset it again and wait for it to be recalculated in the
2574 // next paint event. I would expect this problem to show up in wxGTK
2575 // too but couldn't duplicate it there. Perhaps the order of events
2576 // is different... --Robin
2577 ResetVisibleLinesRange();
2578 #endif
2579 }
2580 else // !report
2581 {
2582 int sx = -1,
2583 sy = -1;
2584
2585 if (rect.x-view_x < 5)
2586 sx = (rect.x - 5) / SCROLL_UNIT_X;
2587 if (rect.x + rect.width - 5 > view_x + client_w)
2588 sx = (rect.x + rect.width - client_w + SCROLL_UNIT_X) / SCROLL_UNIT_X;
2589
2590 if (rect.y-view_y < 5)
2591 sy = (rect.y - 5) / hLine;
2592 if (rect.y + rect.height - 5 > view_y + client_h)
2593 sy = (rect.y + rect.height - client_h + hLine) / hLine;
2594
2595 GetListCtrl()->Scroll(sx, sy);
2596 }
2597 }
2598
2599 bool wxListMainWindow::ScrollList(int WXUNUSED(dx), int dy)
2600 {
2601 if ( !InReportView() )
2602 {
2603 // TODO: this should work in all views but is not implemented now
2604 return false;
2605 }
2606
2607 size_t top, bottom;
2608 GetVisibleLinesRange(&top, &bottom);
2609
2610 if ( bottom == (size_t)-1 )
2611 return 0;
2612
2613 ResetVisibleLinesRange();
2614
2615 int hLine = GetLineHeight();
2616
2617 GetListCtrl()->Scroll(-1, top + dy / hLine);
2618
2619 #ifdef __WXMAC__
2620 // see comment in MoveToItem() for why we do this
2621 ResetVisibleLinesRange();
2622 #endif
2623
2624 return true;
2625 }
2626
2627 // ----------------------------------------------------------------------------
2628 // keyboard handling
2629 // ----------------------------------------------------------------------------
2630
2631 void wxListMainWindow::OnArrowChar(size_t newCurrent, const wxKeyEvent& event)
2632 {
2633 wxCHECK_RET( newCurrent < (size_t)GetItemCount(),
2634 wxT("invalid item index in OnArrowChar()") );
2635
2636 size_t oldCurrent = m_current;
2637
2638 // in single selection we just ignore Shift as we can't select several
2639 // items anyhow
2640 if ( event.ShiftDown() && !IsSingleSel() )
2641 {
2642 ChangeCurrent(newCurrent);
2643
2644 // refresh the old focus to remove it
2645 RefreshLine( oldCurrent );
2646
2647 // select all the items between the old and the new one
2648 if ( oldCurrent > newCurrent )
2649 {
2650 newCurrent = oldCurrent;
2651 oldCurrent = m_current;
2652 }
2653
2654 HighlightLines(oldCurrent, newCurrent);
2655 }
2656 else // !shift
2657 {
2658 // all previously selected items are unselected unless ctrl is held
2659 // in a multiselection control
2660 if ( !event.ControlDown() || IsSingleSel() )
2661 HighlightAll(false);
2662
2663 ChangeCurrent(newCurrent);
2664
2665 // refresh the old focus to remove it
2666 RefreshLine( oldCurrent );
2667
2668 // in single selection mode we must always have a selected item
2669 if ( !event.ControlDown() || IsSingleSel() )
2670 HighlightLine( m_current, true );
2671 }
2672
2673 RefreshLine( m_current );
2674
2675 MoveToFocus();
2676 }
2677
2678 void wxListMainWindow::OnKeyDown( wxKeyEvent &event )
2679 {
2680 wxWindow *parent = GetParent();
2681
2682 // propagate the key event upwards
2683 wxKeyEvent ke(event);
2684 ke.SetEventObject( parent );
2685 if (parent->GetEventHandler()->ProcessEvent( ke ))
2686 return;
2687
2688 // send a list event
2689 wxListEvent le( wxEVT_COMMAND_LIST_KEY_DOWN, parent->GetId() );
2690 le.m_itemIndex = m_current;
2691 if (HasCurrent())
2692 GetLine(m_current)->GetItem( 0, le.m_item );
2693 le.m_code = event.GetKeyCode();
2694 le.SetEventObject( parent );
2695 if (parent->GetEventHandler()->ProcessEvent( le ))
2696 return;
2697
2698 event.Skip();
2699 }
2700
2701 void wxListMainWindow::OnKeyUp( wxKeyEvent &event )
2702 {
2703 wxWindow *parent = GetParent();
2704
2705 // propagate the key event upwards
2706 wxKeyEvent ke(event);
2707 if (parent->GetEventHandler()->ProcessEvent( ke ))
2708 return;
2709
2710 event.Skip();
2711 }
2712
2713 void wxListMainWindow::OnChar( wxKeyEvent &event )
2714 {
2715 wxWindow *parent = GetParent();
2716
2717 // propagate the char event upwards
2718 wxKeyEvent ke(event);
2719 ke.SetEventObject( parent );
2720 if (parent->GetEventHandler()->ProcessEvent( ke ))
2721 return;
2722
2723 if ( HandleAsNavigationKey(event) )
2724 return;
2725
2726 // no item -> nothing to do
2727 if (!HasCurrent())
2728 {
2729 event.Skip();
2730 return;
2731 }
2732
2733 // don't use m_linesPerPage directly as it might not be computed yet
2734 const int pageSize = GetCountPerPage();
2735 wxCHECK_RET( pageSize, wxT("should have non zero page size") );
2736
2737 if (GetLayoutDirection() == wxLayout_RightToLeft)
2738 {
2739 if (event.GetKeyCode() == WXK_RIGHT)
2740 event.m_keyCode = WXK_LEFT;
2741 else if (event.GetKeyCode() == WXK_LEFT)
2742 event.m_keyCode = WXK_RIGHT;
2743 }
2744
2745 switch ( event.GetKeyCode() )
2746 {
2747 case WXK_UP:
2748 if ( m_current > 0 )
2749 OnArrowChar( m_current - 1, event );
2750 break;
2751
2752 case WXK_DOWN:
2753 if ( m_current < (size_t)GetItemCount() - 1 )
2754 OnArrowChar( m_current + 1, event );
2755 break;
2756
2757 case WXK_END:
2758 if (!IsEmpty())
2759 OnArrowChar( GetItemCount() - 1, event );
2760 break;
2761
2762 case WXK_HOME:
2763 if (!IsEmpty())
2764 OnArrowChar( 0, event );
2765 break;
2766
2767 case WXK_PAGEUP:
2768 {
2769 int steps = InReportView() ? pageSize - 1
2770 : m_current % pageSize;
2771
2772 int index = m_current - steps;
2773 if (index < 0)
2774 index = 0;
2775
2776 OnArrowChar( index, event );
2777 }
2778 break;
2779
2780 case WXK_PAGEDOWN:
2781 {
2782 int steps = InReportView()
2783 ? pageSize - 1
2784 : pageSize - (m_current % pageSize) - 1;
2785
2786 size_t index = m_current + steps;
2787 size_t count = GetItemCount();
2788 if ( index >= count )
2789 index = count - 1;
2790
2791 OnArrowChar( index, event );
2792 }
2793 break;
2794
2795 case WXK_LEFT:
2796 if ( !InReportView() )
2797 {
2798 int index = m_current - pageSize;
2799 if (index < 0)
2800 index = 0;
2801
2802 OnArrowChar( index, event );
2803 }
2804 break;
2805
2806 case WXK_RIGHT:
2807 if ( !InReportView() )
2808 {
2809 size_t index = m_current + pageSize;
2810
2811 size_t count = GetItemCount();
2812 if ( index >= count )
2813 index = count - 1;
2814
2815 OnArrowChar( index, event );
2816 }
2817 break;
2818
2819 case WXK_SPACE:
2820 if ( IsSingleSel() )
2821 {
2822 if ( event.ControlDown() )
2823 {
2824 ReverseHighlight(m_current);
2825 }
2826 else // normal space press
2827 {
2828 SendNotify( m_current, wxEVT_COMMAND_LIST_ITEM_ACTIVATED );
2829 }
2830 }
2831 else // multiple selection
2832 {
2833 ReverseHighlight(m_current);
2834 }
2835 break;
2836
2837 case WXK_RETURN:
2838 case WXK_EXECUTE:
2839 SendNotify( m_current, wxEVT_COMMAND_LIST_ITEM_ACTIVATED );
2840 break;
2841
2842 default:
2843 event.Skip();
2844 }
2845 }
2846
2847 // ----------------------------------------------------------------------------
2848 // focus handling
2849 // ----------------------------------------------------------------------------
2850
2851 void wxListMainWindow::OnSetFocus( wxFocusEvent &WXUNUSED(event) )
2852 {
2853 if ( GetParent() )
2854 {
2855 wxFocusEvent event( wxEVT_SET_FOCUS, GetParent()->GetId() );
2856 event.SetEventObject( GetParent() );
2857 if ( GetParent()->GetEventHandler()->ProcessEvent( event) )
2858 return;
2859 }
2860
2861 // wxGTK sends us EVT_SET_FOCUS events even if we had never got
2862 // EVT_KILL_FOCUS before which means that we finish by redrawing the items
2863 // which are already drawn correctly resulting in horrible flicker - avoid
2864 // it
2865 if ( !m_hasFocus )
2866 {
2867 m_hasFocus = true;
2868
2869 RefreshSelected();
2870 }
2871 }
2872
2873 void wxListMainWindow::OnKillFocus( wxFocusEvent &WXUNUSED(event) )
2874 {
2875 if ( GetParent() )
2876 {
2877 wxFocusEvent event( wxEVT_KILL_FOCUS, GetParent()->GetId() );
2878 event.SetEventObject( GetParent() );
2879 if ( GetParent()->GetEventHandler()->ProcessEvent( event) )
2880 return;
2881 }
2882
2883 m_hasFocus = false;
2884 RefreshSelected();
2885 }
2886
2887 void wxListMainWindow::DrawImage( int index, wxDC *dc, int x, int y )
2888 {
2889 if ( HasFlag(wxLC_ICON) && (m_normal_image_list))
2890 {
2891 m_normal_image_list->Draw( index, *dc, x, y, wxIMAGELIST_DRAW_TRANSPARENT );
2892 }
2893 else if ( HasFlag(wxLC_SMALL_ICON) && (m_small_image_list))
2894 {
2895 m_small_image_list->Draw( index, *dc, x, y, wxIMAGELIST_DRAW_TRANSPARENT );
2896 }
2897 else if ( HasFlag(wxLC_LIST) && (m_small_image_list))
2898 {
2899 m_small_image_list->Draw( index, *dc, x, y, wxIMAGELIST_DRAW_TRANSPARENT );
2900 }
2901 else if ( InReportView() && (m_small_image_list))
2902 {
2903 m_small_image_list->Draw( index, *dc, x, y, wxIMAGELIST_DRAW_TRANSPARENT );
2904 }
2905 }
2906
2907 void wxListMainWindow::GetImageSize( int index, int &width, int &height ) const
2908 {
2909 if ( HasFlag(wxLC_ICON) && m_normal_image_list )
2910 {
2911 m_normal_image_list->GetSize( index, width, height );
2912 }
2913 else if ( HasFlag(wxLC_SMALL_ICON) && m_small_image_list )
2914 {
2915 m_small_image_list->GetSize( index, width, height );
2916 }
2917 else if ( HasFlag(wxLC_LIST) && m_small_image_list )
2918 {
2919 m_small_image_list->GetSize( index, width, height );
2920 }
2921 else if ( InReportView() && m_small_image_list )
2922 {
2923 m_small_image_list->GetSize( index, width, height );
2924 }
2925 else
2926 {
2927 width =
2928 height = 0;
2929 }
2930 }
2931
2932 int wxListMainWindow::GetTextLength( const wxString &s ) const
2933 {
2934 wxClientDC dc( wxConstCast(this, wxListMainWindow) );
2935 dc.SetFont( GetFont() );
2936
2937 wxCoord lw;
2938 dc.GetTextExtent( s, &lw, NULL );
2939
2940 return lw + AUTOSIZE_COL_MARGIN;
2941 }
2942
2943 void wxListMainWindow::SetImageList( wxImageList *imageList, int which )
2944 {
2945 m_dirty = true;
2946
2947 // calc the spacing from the icon size
2948 int width = 0, height = 0;
2949
2950 if ((imageList) && (imageList->GetImageCount()) )
2951 imageList->GetSize(0, width, height);
2952
2953 if (which == wxIMAGE_LIST_NORMAL)
2954 {
2955 m_normal_image_list = imageList;
2956 m_normal_spacing = width + 8;
2957 }
2958
2959 if (which == wxIMAGE_LIST_SMALL)
2960 {
2961 m_small_image_list = imageList;
2962 m_small_spacing = width + 14;
2963 m_lineHeight = 0; // ensure that the line height will be recalc'd
2964 }
2965 }
2966
2967 void wxListMainWindow::SetItemSpacing( int spacing, bool isSmall )
2968 {
2969 m_dirty = true;
2970 if (isSmall)
2971 m_small_spacing = spacing;
2972 else
2973 m_normal_spacing = spacing;
2974 }
2975
2976 int wxListMainWindow::GetItemSpacing( bool isSmall )
2977 {
2978 return isSmall ? m_small_spacing : m_normal_spacing;
2979 }
2980
2981 // ----------------------------------------------------------------------------
2982 // columns
2983 // ----------------------------------------------------------------------------
2984
2985 void wxListMainWindow::SetColumn( int col, wxListItem &item )
2986 {
2987 wxListHeaderDataList::compatibility_iterator node = m_columns.Item( col );
2988
2989 wxCHECK_RET( node, wxT("invalid column index in SetColumn") );
2990
2991 if ( item.m_width == wxLIST_AUTOSIZE_USEHEADER )
2992 item.m_width = GetTextLength( item.m_text );
2993
2994 wxListHeaderData *column = node->GetData();
2995 column->SetItem( item );
2996
2997 wxListHeaderWindow *headerWin = GetListCtrl()->m_headerWin;
2998 if ( headerWin )
2999 headerWin->m_dirty = true;
3000
3001 m_dirty = true;
3002
3003 // invalidate it as it has to be recalculated
3004 m_headerWidth = 0;
3005 }
3006
3007 void wxListMainWindow::SetColumnWidth( int col, int width )
3008 {
3009 wxCHECK_RET( col >= 0 && col < GetColumnCount(),
3010 wxT("invalid column index") );
3011
3012 wxCHECK_RET( InReportView(),
3013 wxT("SetColumnWidth() can only be called in report mode.") );
3014
3015 m_dirty = true;
3016
3017 wxListHeaderWindow *headerWin = GetListCtrl()->m_headerWin;
3018 if ( headerWin )
3019 headerWin->m_dirty = true;
3020
3021 wxListHeaderDataList::compatibility_iterator node = m_columns.Item( col );
3022 wxCHECK_RET( node, wxT("no column?") );
3023
3024 wxListHeaderData *column = node->GetData();
3025
3026 size_t count = GetItemCount();
3027
3028 if (width == wxLIST_AUTOSIZE_USEHEADER)
3029 {
3030 width = GetTextLength(column->GetText());
3031 width += 2*EXTRA_WIDTH;
3032
3033 // check for column header's image availability
3034 const int image = column->GetImage();
3035 if ( image != -1 )
3036 {
3037 if ( m_small_image_list )
3038 {
3039 int ix = 0, iy = 0;
3040 m_small_image_list->GetSize(image, ix, iy);
3041 width += ix + HEADER_IMAGE_MARGIN_IN_REPORT_MODE;
3042 }
3043 }
3044 }
3045 else if ( width == wxLIST_AUTOSIZE )
3046 {
3047 if ( IsVirtual() )
3048 {
3049 // TODO: determine the max width somehow...
3050 width = WIDTH_COL_DEFAULT;
3051 }
3052 else // !virtual
3053 {
3054 wxClientDC dc(this);
3055 dc.SetFont( GetFont() );
3056
3057 int max = AUTOSIZE_COL_MARGIN;
3058
3059 // if the cached column width isn't valid then recalculate it
3060 if (m_aColWidths.Item(col)->bNeedsUpdate)
3061 {
3062 for (size_t i = 0; i < count; i++)
3063 {
3064 wxListLineData *line = GetLine( i );
3065 wxListItemDataList::compatibility_iterator n = line->m_items.Item( col );
3066
3067 wxCHECK_RET( n, wxT("no subitem?") );
3068
3069 wxListItemData *itemData = n->GetData();
3070 wxListItem item;
3071
3072 itemData->GetItem(item);
3073 int itemWidth = GetItemWidthWithImage(&item);
3074 if (itemWidth > max)
3075 max = itemWidth;
3076 }
3077
3078 m_aColWidths.Item(col)->bNeedsUpdate = false;
3079 m_aColWidths.Item(col)->nMaxWidth = max;
3080 }
3081
3082 max = m_aColWidths.Item(col)->nMaxWidth;
3083 width = max + AUTOSIZE_COL_MARGIN;
3084 }
3085 }
3086
3087 column->SetWidth( width );
3088
3089 // invalidate it as it has to be recalculated
3090 m_headerWidth = 0;
3091 }
3092
3093 int wxListMainWindow::GetHeaderWidth() const
3094 {
3095 if ( !m_headerWidth )
3096 {
3097 wxListMainWindow *self = wxConstCast(this, wxListMainWindow);
3098
3099 size_t count = GetColumnCount();
3100 for ( size_t col = 0; col < count; col++ )
3101 {
3102 self->m_headerWidth += GetColumnWidth(col);
3103 }
3104 }
3105
3106 return m_headerWidth;
3107 }
3108
3109 void wxListMainWindow::GetColumn( int col, wxListItem &item ) const
3110 {
3111 wxListHeaderDataList::compatibility_iterator node = m_columns.Item( col );
3112 wxCHECK_RET( node, wxT("invalid column index in GetColumn") );
3113
3114 wxListHeaderData *column = node->GetData();
3115 column->GetItem( item );
3116 }
3117
3118 int wxListMainWindow::GetColumnWidth( int col ) const
3119 {
3120 wxListHeaderDataList::compatibility_iterator node = m_columns.Item( col );
3121 wxCHECK_MSG( node, 0, wxT("invalid column index") );
3122
3123 wxListHeaderData *column = node->GetData();
3124 return column->GetWidth();
3125 }
3126
3127 // ----------------------------------------------------------------------------
3128 // item state
3129 // ----------------------------------------------------------------------------
3130
3131 void wxListMainWindow::SetItem( wxListItem &item )
3132 {
3133 long id = item.m_itemId;
3134 wxCHECK_RET( id >= 0 && (size_t)id < GetItemCount(),
3135 wxT("invalid item index in SetItem") );
3136
3137 if ( !IsVirtual() )
3138 {
3139 wxListLineData *line = GetLine((size_t)id);
3140 line->SetItem( item.m_col, item );
3141
3142 // Set item state if user wants
3143 if ( item.m_mask & wxLIST_MASK_STATE )
3144 SetItemState( item.m_itemId, item.m_state, item.m_state );
3145
3146 if (InReportView())
3147 {
3148 // update the Max Width Cache if needed
3149 int width = GetItemWidthWithImage(&item);
3150
3151 if (width > m_aColWidths.Item(item.m_col)->nMaxWidth)
3152 m_aColWidths.Item(item.m_col)->nMaxWidth = width;
3153 }
3154 }
3155
3156 // update the item on screen
3157 wxRect rectItem;
3158 GetItemRect(id, rectItem);
3159 RefreshRect(rectItem);
3160 }
3161
3162 void wxListMainWindow::SetItemStateAll(long state, long stateMask)
3163 {
3164 if ( IsEmpty() )
3165 return;
3166
3167 // first deal with selection
3168 if ( stateMask & wxLIST_STATE_SELECTED )
3169 {
3170 // set/clear select state
3171 if ( IsVirtual() )
3172 {
3173 // optimized version for virtual listctrl.
3174 m_selStore.SelectRange(0, GetItemCount() - 1, state == wxLIST_STATE_SELECTED);
3175 Refresh();
3176 }
3177 else if ( state & wxLIST_STATE_SELECTED )
3178 {
3179 const long count = GetItemCount();
3180 for( long i = 0; i < count; i++ )
3181 {
3182 SetItemState( i, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED );
3183 }
3184
3185 }
3186 else
3187 {
3188 // clear for non virtual (somewhat optimized by using GetNextItem())
3189 long i = -1;
3190 while ( (i = GetNextItem(i, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED)) != -1 )
3191 {
3192 SetItemState( i, 0, wxLIST_STATE_SELECTED );
3193 }
3194 }
3195 }
3196
3197 if ( HasCurrent() && (state == 0) && (stateMask & wxLIST_STATE_FOCUSED) )
3198 {
3199 // unfocus all: only one item can be focussed, so clearing focus for
3200 // all items is simply clearing focus of the focussed item.
3201 SetItemState(m_current, state, stateMask);
3202 }
3203 //(setting focus to all items makes no sense, so it is not handled here.)
3204 }
3205
3206 void wxListMainWindow::SetItemState( long litem, long state, long stateMask )
3207 {
3208 if ( litem == -1 )
3209 {
3210 SetItemStateAll(state, stateMask);
3211 return;
3212 }
3213
3214 wxCHECK_RET( litem >= 0 && (size_t)litem < GetItemCount(),
3215 wxT("invalid list ctrl item index in SetItem") );
3216
3217 size_t oldCurrent = m_current;
3218 size_t item = (size_t)litem; // safe because of the check above
3219
3220 // do we need to change the focus?
3221 if ( stateMask & wxLIST_STATE_FOCUSED )
3222 {
3223 if ( state & wxLIST_STATE_FOCUSED )
3224 {
3225 // don't do anything if this item is already focused
3226 if ( item != m_current )
3227 {
3228 ChangeCurrent(item);
3229
3230 if ( oldCurrent != (size_t)-1 )
3231 {
3232 if ( IsSingleSel() )
3233 {
3234 HighlightLine(oldCurrent, false);
3235 }
3236
3237 RefreshLine(oldCurrent);
3238 }
3239
3240 RefreshLine( m_current );
3241 }
3242 }
3243 else // unfocus
3244 {
3245 // don't do anything if this item is not focused
3246 if ( item == m_current )
3247 {
3248 ResetCurrent();
3249
3250 if ( IsSingleSel() )
3251 {
3252 // we must unselect the old current item as well or we
3253 // might end up with more than one selected item in a
3254 // single selection control
3255 HighlightLine(oldCurrent, false);
3256 }
3257
3258 RefreshLine( oldCurrent );
3259 }
3260 }
3261 }
3262
3263 // do we need to change the selection state?
3264 if ( stateMask & wxLIST_STATE_SELECTED )
3265 {
3266 bool on = (state & wxLIST_STATE_SELECTED) != 0;
3267
3268 if ( IsSingleSel() )
3269 {
3270 if ( on )
3271 {
3272 // selecting the item also makes it the focused one in the
3273 // single sel mode
3274 if ( m_current != item )
3275 {
3276 ChangeCurrent(item);
3277
3278 if ( oldCurrent != (size_t)-1 )
3279 {
3280 HighlightLine( oldCurrent, false );
3281 RefreshLine( oldCurrent );
3282 }
3283 }
3284 }
3285 else // off
3286 {
3287 // only the current item may be selected anyhow
3288 if ( item != m_current )
3289 return;
3290 }
3291 }
3292
3293 if ( HighlightLine(item, on) )
3294 {
3295 RefreshLine(item);
3296 }
3297 }
3298 }
3299
3300 int wxListMainWindow::GetItemState( long item, long stateMask ) const
3301 {
3302 wxCHECK_MSG( item >= 0 && (size_t)item < GetItemCount(), 0,
3303 wxT("invalid list ctrl item index in GetItemState()") );
3304
3305 int ret = wxLIST_STATE_DONTCARE;
3306
3307 if ( stateMask & wxLIST_STATE_FOCUSED )
3308 {
3309 if ( (size_t)item == m_current )
3310 ret |= wxLIST_STATE_FOCUSED;
3311 }
3312
3313 if ( stateMask & wxLIST_STATE_SELECTED )
3314 {
3315 if ( IsHighlighted(item) )
3316 ret |= wxLIST_STATE_SELECTED;
3317 }
3318
3319 return ret;
3320 }
3321
3322 void wxListMainWindow::GetItem( wxListItem &item ) const
3323 {
3324 wxCHECK_RET( item.m_itemId >= 0 && (size_t)item.m_itemId < GetItemCount(),
3325 wxT("invalid item index in GetItem") );
3326
3327 wxListLineData *line = GetLine((size_t)item.m_itemId);
3328 line->GetItem( item.m_col, item );
3329
3330 // Get item state if user wants it
3331 if ( item.m_mask & wxLIST_MASK_STATE )
3332 item.m_state = GetItemState( item.m_itemId, wxLIST_STATE_SELECTED |
3333 wxLIST_STATE_FOCUSED );
3334 }
3335
3336 // ----------------------------------------------------------------------------
3337 // item count
3338 // ----------------------------------------------------------------------------
3339
3340 size_t wxListMainWindow::GetItemCount() const
3341 {
3342 return IsVirtual() ? m_countVirt : m_lines.GetCount();
3343 }
3344
3345 void wxListMainWindow::SetItemCount(long count)
3346 {
3347 m_selStore.SetItemCount(count);
3348 m_countVirt = count;
3349
3350 ResetVisibleLinesRange();
3351
3352 // scrollbars must be reset
3353 m_dirty = true;
3354 }
3355
3356 int wxListMainWindow::GetSelectedItemCount() const
3357 {
3358 // deal with the quick case first
3359 if ( IsSingleSel() )
3360 return HasCurrent() ? IsHighlighted(m_current) : false;
3361
3362 // virtual controls remmebers all its selections itself
3363 if ( IsVirtual() )
3364 return m_selStore.GetSelectedCount();
3365
3366 // TODO: we probably should maintain the number of items selected even for
3367 // non virtual controls as enumerating all lines is really slow...
3368 size_t countSel = 0;
3369 size_t count = GetItemCount();
3370 for ( size_t line = 0; line < count; line++ )
3371 {
3372 if ( GetLine(line)->IsHighlighted() )
3373 countSel++;
3374 }
3375
3376 return countSel;
3377 }
3378
3379 // ----------------------------------------------------------------------------
3380 // item position/size
3381 // ----------------------------------------------------------------------------
3382
3383 wxRect wxListMainWindow::GetViewRect() const
3384 {
3385 wxASSERT_MSG( !HasFlag(wxLC_LIST), "not implemented for list view" );
3386
3387 // we need to find the longest/tallest label
3388 wxCoord xMax = 0, yMax = 0;
3389 const int count = GetItemCount();
3390 if ( count )
3391 {
3392 for ( int i = 0; i < count; i++ )
3393 {
3394 // we need logical, not physical, coordinates here, so use
3395 // GetLineRect() instead of GetItemRect()
3396 wxRect r = GetLineRect(i);
3397
3398 wxCoord x = r.GetRight(),
3399 y = r.GetBottom();
3400
3401 if ( x > xMax )
3402 xMax = x;
3403 if ( y > yMax )
3404 yMax = y;
3405 }
3406 }
3407
3408 // some fudge needed to make it look prettier
3409 xMax += 2 * EXTRA_BORDER_X;
3410 yMax += 2 * EXTRA_BORDER_Y;
3411
3412 // account for the scrollbars if necessary
3413 const wxSize sizeAll = GetClientSize();
3414 if ( xMax > sizeAll.x )
3415 yMax += wxSystemSettings::GetMetric(wxSYS_HSCROLL_Y);
3416 if ( yMax > sizeAll.y )
3417 xMax += wxSystemSettings::GetMetric(wxSYS_VSCROLL_X);
3418
3419 return wxRect(0, 0, xMax, yMax);
3420 }
3421
3422 bool
3423 wxListMainWindow::GetSubItemRect(long item, long subItem, wxRect& rect) const
3424 {
3425 wxCHECK_MSG( subItem == wxLIST_GETSUBITEMRECT_WHOLEITEM || InReportView(),
3426 false,
3427 wxT("GetSubItemRect only meaningful in report view") );
3428 wxCHECK_MSG( item >= 0 && (size_t)item < GetItemCount(), false,
3429 wxT("invalid item in GetSubItemRect") );
3430
3431 // ensure that we're laid out, otherwise we could return nonsense
3432 if ( m_dirty )
3433 {
3434 wxConstCast(this, wxListMainWindow)->
3435 RecalculatePositions(true /* no refresh */);
3436 }
3437
3438 rect = GetLineRect((size_t)item);
3439
3440 // Adjust rect to specified column
3441 if ( subItem != wxLIST_GETSUBITEMRECT_WHOLEITEM )
3442 {
3443 wxCHECK_MSG( subItem >= 0 && subItem < GetColumnCount(), false,
3444 wxT("invalid subItem in GetSubItemRect") );
3445
3446 for (int i = 0; i < subItem; i++)
3447 {
3448 rect.x += GetColumnWidth(i);
3449 }
3450 rect.width = GetColumnWidth(subItem);
3451 }
3452
3453 GetListCtrl()->CalcScrolledPosition(rect.x, rect.y, &rect.x, &rect.y);
3454
3455 return true;
3456 }
3457
3458 bool wxListMainWindow::GetItemPosition(long item, wxPoint& pos) const
3459 {
3460 wxRect rect;
3461 GetItemRect(item, rect);
3462
3463 pos.x = rect.x;
3464 pos.y = rect.y;
3465
3466 return true;
3467 }
3468
3469 // ----------------------------------------------------------------------------
3470 // geometry calculation
3471 // ----------------------------------------------------------------------------
3472
3473 void wxListMainWindow::RecalculatePositions(bool noRefresh)
3474 {
3475 const int lineHeight = GetLineHeight();
3476
3477 wxClientDC dc( this );
3478 dc.SetFont( GetFont() );
3479
3480 const size_t count = GetItemCount();
3481
3482 int iconSpacing;
3483 if ( HasFlag(wxLC_ICON) && m_normal_image_list )
3484 iconSpacing = m_normal_spacing;
3485 else if ( HasFlag(wxLC_SMALL_ICON) && m_small_image_list )
3486 iconSpacing = m_small_spacing;
3487 else
3488 iconSpacing = 0;
3489
3490 // Note that we do not call GetClientSize() here but
3491 // GetSize() and subtract the border size for sunken
3492 // borders manually. This is technically incorrect,
3493 // but we need to know the client area's size WITHOUT
3494 // scrollbars here. Since we don't know if there are
3495 // any scrollbars, we use GetSize() instead. Another
3496 // solution would be to call SetScrollbars() here to
3497 // remove the scrollbars and call GetClientSize() then,
3498 // but this might result in flicker and - worse - will
3499 // reset the scrollbars to 0 which is not good at all
3500 // if you resize a dialog/window, but don't want to
3501 // reset the window scrolling. RR.
3502 // Furthermore, we actually do NOT subtract the border
3503 // width as 2 pixels is just the extra space which we
3504 // need around the actual content in the window. Other-
3505 // wise the text would e.g. touch the upper border. RR.
3506 int clientWidth,
3507 clientHeight;
3508 GetSize( &clientWidth, &clientHeight );
3509
3510 if ( InReportView() )
3511 {
3512 // all lines have the same height and we scroll one line per step
3513 int entireHeight = count * lineHeight + LINE_SPACING;
3514
3515 m_linesPerPage = clientHeight / lineHeight;
3516
3517 ResetVisibleLinesRange();
3518
3519 GetListCtrl()->SetScrollbars( SCROLL_UNIT_X, lineHeight,
3520 GetHeaderWidth() / SCROLL_UNIT_X,
3521 (entireHeight + lineHeight - 1) / lineHeight,
3522 GetListCtrl()->GetScrollPos(wxHORIZONTAL),
3523 GetListCtrl()->GetScrollPos(wxVERTICAL),
3524 true );
3525 }
3526 else // !report
3527 {
3528 // we have 3 different layout strategies: either layout all items
3529 // horizontally/vertically (wxLC_ALIGN_XXX styles explicitly given) or
3530 // to arrange them in top to bottom, left to right (don't ask me why
3531 // not the other way round...) order
3532 if ( HasFlag(wxLC_ALIGN_LEFT | wxLC_ALIGN_TOP) )
3533 {
3534 int x = EXTRA_BORDER_X;
3535 int y = EXTRA_BORDER_Y;
3536
3537 wxCoord widthMax = 0;
3538
3539 size_t i;
3540 for ( i = 0; i < count; i++ )
3541 {
3542 wxListLineData *line = GetLine(i);
3543 line->CalculateSize( &dc, iconSpacing );
3544 line->SetPosition( x, y, iconSpacing );
3545
3546 wxSize sizeLine = GetLineSize(i);
3547
3548 if ( HasFlag(wxLC_ALIGN_TOP) )
3549 {
3550 if ( sizeLine.x > widthMax )
3551 widthMax = sizeLine.x;
3552
3553 y += sizeLine.y;
3554 }
3555 else // wxLC_ALIGN_LEFT
3556 {
3557 x += sizeLine.x + MARGIN_BETWEEN_ROWS;
3558 }
3559 }
3560
3561 if ( HasFlag(wxLC_ALIGN_TOP) )
3562 {
3563 // traverse the items again and tweak their sizes so that they are
3564 // all the same in a row
3565 for ( i = 0; i < count; i++ )
3566 {
3567 wxListLineData *line = GetLine(i);
3568 line->m_gi->ExtendWidth(widthMax);
3569 }
3570 }
3571
3572 GetListCtrl()->SetScrollbars
3573 (
3574 SCROLL_UNIT_X,
3575 lineHeight,
3576 (x + SCROLL_UNIT_X) / SCROLL_UNIT_X,
3577 (y + lineHeight) / lineHeight,
3578 GetListCtrl()->GetScrollPos( wxHORIZONTAL ),
3579 GetListCtrl()->GetScrollPos( wxVERTICAL ),
3580 true
3581 );
3582 }
3583 else // "flowed" arrangement, the most complicated case
3584 {
3585 // at first we try without any scrollbars, if the items don't fit into
3586 // the window, we recalculate after subtracting the space taken by the
3587 // scrollbar
3588
3589 int entireWidth = 0;
3590
3591 for (int tries = 0; tries < 2; tries++)
3592 {
3593 entireWidth = 2 * EXTRA_BORDER_X;
3594
3595 if (tries == 1)
3596 {
3597 // Now we have decided that the items do not fit into the
3598 // client area, so we need a scrollbar
3599 entireWidth += SCROLL_UNIT_X;
3600 }
3601
3602 int x = EXTRA_BORDER_X;
3603 int y = EXTRA_BORDER_Y;
3604 int maxWidthInThisRow = 0;
3605
3606 m_linesPerPage = 0;
3607 int currentlyVisibleLines = 0;
3608
3609 for (size_t i = 0; i < count; i++)
3610 {
3611 currentlyVisibleLines++;
3612 wxListLineData *line = GetLine( i );
3613 line->CalculateSize( &dc, iconSpacing );
3614 line->SetPosition( x, y, iconSpacing );
3615
3616 wxSize sizeLine = GetLineSize( i );
3617
3618 if ( maxWidthInThisRow < sizeLine.x )
3619 maxWidthInThisRow = sizeLine.x;
3620
3621 y += sizeLine.y;
3622 if (currentlyVisibleLines > m_linesPerPage)
3623 m_linesPerPage = currentlyVisibleLines;
3624
3625 if ( y + sizeLine.y >= clientHeight )
3626 {
3627 currentlyVisibleLines = 0;
3628 y = EXTRA_BORDER_Y;
3629 maxWidthInThisRow += MARGIN_BETWEEN_ROWS;
3630 x += maxWidthInThisRow;
3631 entireWidth += maxWidthInThisRow;
3632 maxWidthInThisRow = 0;
3633 }
3634
3635 // We have reached the last item.
3636 if ( i == count - 1 )
3637 entireWidth += maxWidthInThisRow;
3638
3639 if ( (tries == 0) &&
3640 (entireWidth + SCROLL_UNIT_X > clientWidth) )
3641 {
3642 clientHeight -= wxSystemSettings::
3643 GetMetric(wxSYS_HSCROLL_Y);
3644 m_linesPerPage = 0;
3645 break;
3646 }
3647
3648 if ( i == count - 1 )
3649 tries = 1; // Everything fits, no second try required.
3650 }
3651 }
3652
3653 GetListCtrl()->SetScrollbars
3654 (
3655 SCROLL_UNIT_X,
3656 lineHeight,
3657 (entireWidth + SCROLL_UNIT_X) / SCROLL_UNIT_X,
3658 0,
3659 GetListCtrl()->GetScrollPos( wxHORIZONTAL ),
3660 0,
3661 true
3662 );
3663 }
3664 }
3665
3666 if ( !noRefresh )
3667 {
3668 // FIXME: why should we call it from here?
3669 UpdateCurrent();
3670
3671 RefreshAll();
3672 }
3673 }
3674
3675 void wxListMainWindow::RefreshAll()
3676 {
3677 m_dirty = false;
3678 Refresh();
3679
3680 wxListHeaderWindow *headerWin = GetListCtrl()->m_headerWin;
3681 if ( headerWin && headerWin->m_dirty )
3682 {
3683 headerWin->m_dirty = false;
3684 headerWin->Refresh();
3685 }
3686 }
3687
3688 void wxListMainWindow::UpdateCurrent()
3689 {
3690 if ( !HasCurrent() && !IsEmpty() )
3691 ChangeCurrent(0);
3692 }
3693
3694 long wxListMainWindow::GetNextItem( long item,
3695 int WXUNUSED(geometry),
3696 int state ) const
3697 {
3698 long ret = item,
3699 max = GetItemCount();
3700 wxCHECK_MSG( (ret == -1) || (ret < max), -1,
3701 wxT("invalid listctrl index in GetNextItem()") );
3702
3703 // notice that we start with the next item (or the first one if item == -1)
3704 // and this is intentional to allow writing a simple loop to iterate over
3705 // all selected items
3706 ret++;
3707 if ( ret == max )
3708 // this is not an error because the index was OK initially,
3709 // just no such item
3710 return -1;
3711
3712 if ( !state )
3713 // any will do
3714 return (size_t)ret;
3715
3716 size_t count = GetItemCount();
3717 for ( size_t line = (size_t)ret; line < count; line++ )
3718 {
3719 if ( (state & wxLIST_STATE_FOCUSED) && (line == m_current) )
3720 return line;
3721
3722 if ( (state & wxLIST_STATE_SELECTED) && IsHighlighted(line) )
3723 return line;
3724 }
3725
3726 return -1;
3727 }
3728
3729 // ----------------------------------------------------------------------------
3730 // deleting stuff
3731 // ----------------------------------------------------------------------------
3732
3733 void wxListMainWindow::DeleteItem( long lindex )
3734 {
3735 size_t count = GetItemCount();
3736
3737 wxCHECK_RET( (lindex >= 0) && ((size_t)lindex < count),
3738 wxT("invalid item index in DeleteItem") );
3739
3740 size_t index = (size_t)lindex;
3741
3742 // we don't need to adjust the index for the previous items
3743 if ( HasCurrent() && m_current >= index )
3744 {
3745 // if the current item is being deleted, we want the next one to
3746 // become selected - unless there is no next one - so don't adjust
3747 // m_current in this case
3748 if ( m_current != index || m_current == count - 1 )
3749 m_current--;
3750 }
3751
3752 if ( InReportView() )
3753 {
3754 // mark the Column Max Width cache as dirty if the items in the line
3755 // we're deleting contain the Max Column Width
3756 wxListLineData * const line = GetLine(index);
3757 wxListItemDataList::compatibility_iterator n;
3758 wxListItemData *itemData;
3759 wxListItem item;
3760 int itemWidth;
3761
3762 for (size_t i = 0; i < m_columns.GetCount(); i++)
3763 {
3764 n = line->m_items.Item( i );
3765 itemData = n->GetData();
3766 itemData->GetItem(item);
3767
3768 itemWidth = GetItemWidthWithImage(&item);
3769
3770 if (itemWidth >= m_aColWidths.Item(i)->nMaxWidth)
3771 m_aColWidths.Item(i)->bNeedsUpdate = true;
3772 }
3773
3774 ResetVisibleLinesRange();
3775 }
3776
3777 SendNotify( index, wxEVT_COMMAND_LIST_DELETE_ITEM, wxDefaultPosition );
3778
3779 if ( IsVirtual() )
3780 {
3781 m_countVirt--;
3782 m_selStore.OnItemDelete(index);
3783 }
3784 else
3785 {
3786 m_lines.RemoveAt( index );
3787 }
3788
3789 // we need to refresh the (vert) scrollbar as the number of items changed
3790 m_dirty = true;
3791
3792 RefreshAfter(index);
3793 }
3794
3795 void wxListMainWindow::DeleteColumn( int col )
3796 {
3797 wxListHeaderDataList::compatibility_iterator node = m_columns.Item( col );
3798
3799 wxCHECK_RET( node, wxT("invalid column index in DeleteColumn()") );
3800
3801 m_dirty = true;
3802 delete node->GetData();
3803 m_columns.Erase( node );
3804
3805 if ( !IsVirtual() )
3806 {
3807 // update all the items
3808 for ( size_t i = 0; i < m_lines.GetCount(); i++ )
3809 {
3810 wxListLineData * const line = GetLine(i);
3811 wxListItemDataList::compatibility_iterator n = line->m_items.Item( col );
3812 delete n->GetData();
3813 line->m_items.Erase(n);
3814 }
3815 }
3816
3817 if ( InReportView() ) // we only cache max widths when in Report View
3818 {
3819 delete m_aColWidths.Item(col);
3820 m_aColWidths.RemoveAt(col);
3821 }
3822
3823 // invalidate it as it has to be recalculated
3824 m_headerWidth = 0;
3825 }
3826
3827 void wxListMainWindow::DoDeleteAllItems()
3828 {
3829 if ( IsEmpty() )
3830 // nothing to do - in particular, don't send the event
3831 return;
3832
3833 ResetCurrent();
3834
3835 // to make the deletion of all items faster, we don't send the
3836 // notifications for each item deletion in this case but only one event
3837 // for all of them: this is compatible with wxMSW and documented in
3838 // DeleteAllItems() description
3839
3840 wxListEvent event( wxEVT_COMMAND_LIST_DELETE_ALL_ITEMS, GetParent()->GetId() );
3841 event.SetEventObject( GetParent() );
3842 GetParent()->GetEventHandler()->ProcessEvent( event );
3843
3844 if ( IsVirtual() )
3845 {
3846 m_countVirt = 0;
3847 m_selStore.Clear();
3848 }
3849
3850 if ( InReportView() )
3851 {
3852 ResetVisibleLinesRange();
3853 for (size_t i = 0; i < m_aColWidths.GetCount(); i++)
3854 {
3855 m_aColWidths.Item(i)->bNeedsUpdate = true;
3856 }
3857 }
3858
3859 m_lines.Clear();
3860 }
3861
3862 void wxListMainWindow::DeleteAllItems()
3863 {
3864 DoDeleteAllItems();
3865
3866 RecalculatePositions();
3867 }
3868
3869 void wxListMainWindow::DeleteEverything()
3870 {
3871 WX_CLEAR_LIST(wxListHeaderDataList, m_columns);
3872 WX_CLEAR_ARRAY(m_aColWidths);
3873
3874 DeleteAllItems();
3875 }
3876
3877 // ----------------------------------------------------------------------------
3878 // scanning for an item
3879 // ----------------------------------------------------------------------------
3880
3881 void wxListMainWindow::EnsureVisible( long index )
3882 {
3883 wxCHECK_RET( index >= 0 && (size_t)index < GetItemCount(),
3884 wxT("invalid index in EnsureVisible") );
3885
3886 // We have to call this here because the label in question might just have
3887 // been added and its position is not known yet
3888 if ( m_dirty )
3889 RecalculatePositions(true /* no refresh */);
3890
3891 MoveToItem((size_t)index);
3892 }
3893
3894 long wxListMainWindow::FindItem(long start, const wxString& str, bool partial )
3895 {
3896 if (str.empty())
3897 return wxNOT_FOUND;
3898
3899 long pos = start;
3900 wxString str_upper = str.Upper();
3901 if (pos < 0)
3902 pos = 0;
3903
3904 size_t count = GetItemCount();
3905 for ( size_t i = (size_t)pos; i < count; i++ )
3906 {
3907 wxListLineData *line = GetLine(i);
3908 wxString line_upper = line->GetText(0).Upper();
3909 if (!partial)
3910 {
3911 if (line_upper == str_upper )
3912 return i;
3913 }
3914 else
3915 {
3916 if (line_upper.find(str_upper) == 0)
3917 return i;
3918 }
3919 }
3920
3921 return wxNOT_FOUND;
3922 }
3923
3924 long wxListMainWindow::FindItem(long start, wxUIntPtr data)
3925 {
3926 long pos = start;
3927 if (pos < 0)
3928 pos = 0;
3929
3930 size_t count = GetItemCount();
3931 for (size_t i = (size_t)pos; i < count; i++)
3932 {
3933 wxListLineData *line = GetLine(i);
3934 wxListItem item;
3935 line->GetItem( 0, item );
3936 if (item.m_data == data)
3937 return i;
3938 }
3939
3940 return wxNOT_FOUND;
3941 }
3942
3943 long wxListMainWindow::FindItem( const wxPoint& pt )
3944 {
3945 size_t topItem;
3946 GetVisibleLinesRange( &topItem, NULL );
3947
3948 wxPoint p;
3949 GetItemPosition( GetItemCount() - 1, p );
3950 if ( p.y == 0 )
3951 return topItem;
3952
3953 long id = (long)floor( pt.y * double(GetItemCount() - topItem - 1) / p.y + topItem );
3954 if ( id >= 0 && id < (long)GetItemCount() )
3955 return id;
3956
3957 return wxNOT_FOUND;
3958 }
3959
3960 long wxListMainWindow::HitTest( int x, int y, int &flags ) const
3961 {
3962 GetListCtrl()->CalcUnscrolledPosition( x, y, &x, &y );
3963
3964 size_t count = GetItemCount();
3965
3966 if ( InReportView() )
3967 {
3968 size_t current = y / GetLineHeight();
3969 if ( current < count )
3970 {
3971 flags = HitTestLine(current, x, y);
3972 if ( flags )
3973 return current;
3974 }
3975 }
3976 else // !report
3977 {
3978 // TODO: optimize it too! this is less simple than for report view but
3979 // enumerating all items is still not a way to do it!!
3980 for ( size_t current = 0; current < count; current++ )
3981 {
3982 flags = HitTestLine(current, x, y);
3983 if ( flags )
3984 return current;
3985 }
3986 }
3987
3988 return wxNOT_FOUND;
3989 }
3990
3991 // ----------------------------------------------------------------------------
3992 // adding stuff
3993 // ----------------------------------------------------------------------------
3994
3995 void wxListMainWindow::InsertItem( wxListItem &item )
3996 {
3997 wxASSERT_MSG( !IsVirtual(), wxT("can't be used with virtual control") );
3998
3999 int count = GetItemCount();
4000 wxCHECK_RET( item.m_itemId >= 0, wxT("invalid item index") );
4001
4002 if (item.m_itemId > count)
4003 item.m_itemId = count;
4004
4005 size_t id = item.m_itemId;
4006
4007 m_dirty = true;
4008
4009 if ( InReportView() )
4010 {
4011 ResetVisibleLinesRange();
4012
4013 const unsigned col = item.GetColumn();
4014 wxCHECK_RET( col < m_aColWidths.size(), "invalid item column" );
4015
4016 // calculate the width of the item and adjust the max column width
4017 wxColWidthInfo *pWidthInfo = m_aColWidths.Item(col);
4018 int width = GetItemWidthWithImage(&item);
4019 item.SetWidth(width);
4020 if (width > pWidthInfo->nMaxWidth)
4021 pWidthInfo->nMaxWidth = width;
4022 }
4023
4024 wxListLineData *line = new wxListLineData(this);
4025
4026 line->SetItem( item.m_col, item );
4027
4028 m_lines.Insert( line, id );
4029
4030 m_dirty = true;
4031
4032 // If an item is selected at or below the point of insertion, we need to
4033 // increment the member variables because the current row's index has gone
4034 // up by one
4035 if ( HasCurrent() && m_current >= id )
4036 m_current++;
4037
4038 SendNotify(id, wxEVT_COMMAND_LIST_INSERT_ITEM);
4039
4040 RefreshLines(id, GetItemCount() - 1);
4041 }
4042
4043 void wxListMainWindow::InsertColumn( long col, wxListItem &item )
4044 {
4045 m_dirty = true;
4046 if ( InReportView() )
4047 {
4048 if (item.m_width == wxLIST_AUTOSIZE_USEHEADER)
4049 item.m_width = GetTextLength( item.m_text );
4050
4051 wxListHeaderData *column = new wxListHeaderData( item );
4052 wxColWidthInfo *colWidthInfo = new wxColWidthInfo();
4053
4054 bool insert = (col >= 0) && ((size_t)col < m_columns.GetCount());
4055 if ( insert )
4056 {
4057 wxListHeaderDataList::compatibility_iterator
4058 node = m_columns.Item( col );
4059 m_columns.Insert( node, column );
4060 m_aColWidths.Insert( colWidthInfo, col );
4061 }
4062 else
4063 {
4064 m_columns.Append( column );
4065 m_aColWidths.Add( colWidthInfo );
4066 }
4067
4068 if ( !IsVirtual() )
4069 {
4070 // update all the items
4071 for ( size_t i = 0; i < m_lines.GetCount(); i++ )
4072 {
4073 wxListLineData * const line = GetLine(i);
4074 wxListItemData * const data = new wxListItemData(this);
4075 if ( insert )
4076 line->m_items.Insert(col, data);
4077 else
4078 line->m_items.Append(data);
4079 }
4080 }
4081
4082 // invalidate it as it has to be recalculated
4083 m_headerWidth = 0;
4084 }
4085 }
4086
4087 int wxListMainWindow::GetItemWidthWithImage(wxListItem * item)
4088 {
4089 int width = 0;
4090 wxClientDC dc(this);
4091
4092 dc.SetFont( GetFont() );
4093
4094 if (item->GetImage() != -1)
4095 {
4096 int ix, iy;
4097 GetImageSize( item->GetImage(), ix, iy );
4098 width += ix + 5;
4099 }
4100
4101 if (!item->GetText().empty())
4102 {
4103 wxCoord w;
4104 dc.GetTextExtent( item->GetText(), &w, NULL );
4105 width += w;
4106 }
4107
4108 return width;
4109 }
4110
4111 // ----------------------------------------------------------------------------
4112 // sorting
4113 // ----------------------------------------------------------------------------
4114
4115 static wxListCtrlCompare list_ctrl_compare_func_2;
4116 static wxIntPtr list_ctrl_compare_data;
4117
4118 int LINKAGEMODE list_ctrl_compare_func_1( wxListLineData **arg1, wxListLineData **arg2 )
4119 {
4120 wxListLineData *line1 = *arg1;
4121 wxListLineData *line2 = *arg2;
4122 wxListItem item;
4123 line1->GetItem( 0, item );
4124 wxUIntPtr data1 = item.m_data;
4125 line2->GetItem( 0, item );
4126 wxUIntPtr data2 = item.m_data;
4127 return list_ctrl_compare_func_2( data1, data2, list_ctrl_compare_data );
4128 }
4129
4130 void wxListMainWindow::SortItems( wxListCtrlCompare fn, wxIntPtr data )
4131 {
4132 // selections won't make sense any more after sorting the items so reset
4133 // them
4134 HighlightAll(false);
4135 ResetCurrent();
4136
4137 list_ctrl_compare_func_2 = fn;
4138 list_ctrl_compare_data = data;
4139 m_lines.Sort( list_ctrl_compare_func_1 );
4140 m_dirty = true;
4141 }
4142
4143 // ----------------------------------------------------------------------------
4144 // scrolling
4145 // ----------------------------------------------------------------------------
4146
4147 void wxListMainWindow::OnScroll(wxScrollWinEvent& event)
4148 {
4149 // update our idea of which lines are shown when we redraw the window the
4150 // next time
4151 ResetVisibleLinesRange();
4152
4153 if ( event.GetOrientation() == wxHORIZONTAL && HasHeader() )
4154 {
4155 wxGenericListCtrl* lc = GetListCtrl();
4156 wxCHECK_RET( lc, wxT("no listctrl window?") );
4157
4158 if (lc->m_headerWin) // when we use wxLC_NO_HEADER, m_headerWin==NULL
4159 {
4160 lc->m_headerWin->Refresh();
4161 lc->m_headerWin->Update();
4162 }
4163 }
4164 }
4165
4166 int wxListMainWindow::GetCountPerPage() const
4167 {
4168 if ( !m_linesPerPage )
4169 {
4170 wxConstCast(this, wxListMainWindow)->
4171 m_linesPerPage = GetClientSize().y / GetLineHeight();
4172 }
4173
4174 return m_linesPerPage;
4175 }
4176
4177 void wxListMainWindow::GetVisibleLinesRange(size_t *from, size_t *to)
4178 {
4179 wxASSERT_MSG( InReportView(), wxT("this is for report mode only") );
4180
4181 if ( m_lineFrom == (size_t)-1 )
4182 {
4183 size_t count = GetItemCount();
4184 if ( count )
4185 {
4186 m_lineFrom = GetListCtrl()->GetScrollPos(wxVERTICAL);
4187
4188 // this may happen if SetScrollbars() hadn't been called yet
4189 if ( m_lineFrom >= count )
4190 m_lineFrom = count - 1;
4191
4192 // we redraw one extra line but this is needed to make the redrawing
4193 // logic work when there is a fractional number of lines on screen
4194 m_lineTo = m_lineFrom + m_linesPerPage;
4195 if ( m_lineTo >= count )
4196 m_lineTo = count - 1;
4197 }
4198 else // empty control
4199 {
4200 m_lineFrom = 0;
4201 m_lineTo = (size_t)-1;
4202 }
4203 }
4204
4205 wxASSERT_MSG( IsEmpty() ||
4206 (m_lineFrom <= m_lineTo && m_lineTo < GetItemCount()),
4207 wxT("GetVisibleLinesRange() returns incorrect result") );
4208
4209 if ( from )
4210 *from = m_lineFrom;
4211 if ( to )
4212 *to = m_lineTo;
4213 }
4214
4215 // -------------------------------------------------------------------------------------
4216 // wxGenericListCtrl
4217 // -------------------------------------------------------------------------------------
4218
4219 IMPLEMENT_DYNAMIC_CLASS(wxGenericListCtrl, wxControl)
4220
4221 BEGIN_EVENT_TABLE(wxGenericListCtrl,wxControl)
4222 EVT_SIZE(wxGenericListCtrl::OnSize)
4223 EVT_SCROLLWIN(wxGenericListCtrl::OnScroll)
4224 END_EVENT_TABLE()
4225
4226 void wxGenericListCtrl::Init()
4227 {
4228 m_imageListNormal = NULL;
4229 m_imageListSmall = NULL;
4230 m_imageListState = NULL;
4231
4232 m_ownsImageListNormal =
4233 m_ownsImageListSmall =
4234 m_ownsImageListState = false;
4235
4236 m_mainWin = NULL;
4237 m_headerWin = NULL;
4238 }
4239
4240 wxGenericListCtrl::~wxGenericListCtrl()
4241 {
4242 if (m_ownsImageListNormal)
4243 delete m_imageListNormal;
4244 if (m_ownsImageListSmall)
4245 delete m_imageListSmall;
4246 if (m_ownsImageListState)
4247 delete m_imageListState;
4248 }
4249
4250 void wxGenericListCtrl::CreateOrDestroyHeaderWindowAsNeeded()
4251 {
4252 bool needs_header = HasHeader();
4253 bool has_header = (m_headerWin != NULL);
4254
4255 if (needs_header == has_header)
4256 return;
4257
4258 if (needs_header)
4259 {
4260 m_headerWin = new wxListHeaderWindow
4261 (
4262 this, wxID_ANY, m_mainWin,
4263 wxPoint(0,0),
4264 wxSize
4265 (
4266 GetClientSize().x,
4267 wxRendererNative::Get().GetHeaderButtonHeight(this)
4268 ),
4269 wxTAB_TRAVERSAL
4270 );
4271
4272 #if defined( __WXMAC__ )
4273 static wxFont font( wxOSX_SYSTEM_FONT_SMALL );
4274 m_headerWin->SetFont( font );
4275 #endif
4276
4277 GetSizer()->Prepend( m_headerWin, 0, wxGROW );
4278 }
4279 else
4280 {
4281 GetSizer()->Detach( m_headerWin );
4282
4283 wxDELETE(m_headerWin);
4284 }
4285 }
4286
4287 bool wxGenericListCtrl::Create(wxWindow *parent,
4288 wxWindowID id,
4289 const wxPoint &pos,
4290 const wxSize &size,
4291 long style,
4292 const wxValidator &validator,
4293 const wxString &name)
4294 {
4295 Init();
4296
4297 // just like in other ports, an assert will fail if the user doesn't give any type style:
4298 wxASSERT_MSG( (style & wxLC_MASK_TYPE),
4299 wxT("wxListCtrl style should have exactly one mode bit set") );
4300
4301 if ( !wxControl::Create( parent, id, pos, size, style|wxVSCROLL|wxHSCROLL, validator, name ) )
4302 return false;
4303
4304 #ifdef __WXGTK__
4305 style &= ~wxBORDER_MASK;
4306 style |= wxBORDER_THEME;
4307 #endif
4308
4309 m_mainWin = new wxListMainWindow( this, wxID_ANY, wxPoint(0, 0), size, style );
4310
4311 SetTargetWindow( m_mainWin );
4312
4313 // We use the cursor keys for moving the selection, not scrolling, so call
4314 // this method to ensure wxScrollHelperEvtHandler doesn't catch all
4315 // keyboard events forwarded to us from wxListMainWindow.
4316 DisableKeyboardScrolling();
4317
4318 wxBoxSizer *sizer = new wxBoxSizer( wxVERTICAL );
4319 sizer->Add( m_mainWin, 1, wxGROW );
4320 SetSizer( sizer );
4321
4322 CreateOrDestroyHeaderWindowAsNeeded();
4323
4324 SetInitialSize(size);
4325
4326 return true;
4327 }
4328
4329 wxBorder wxGenericListCtrl::GetDefaultBorder() const
4330 {
4331 return wxBORDER_THEME;
4332 }
4333
4334 #if defined(__WXMSW__) && !defined(__WXWINCE__) && !defined(__WXUNIVERSAL__)
4335 WXLRESULT wxGenericListCtrl::MSWWindowProc(WXUINT nMsg,
4336 WXWPARAM wParam,
4337 WXLPARAM lParam)
4338 {
4339 WXLRESULT rc = wxControl::MSWWindowProc(nMsg, wParam, lParam);
4340
4341 // we need to process arrows ourselves for scrolling
4342 if ( nMsg == WM_GETDLGCODE )
4343 {
4344 rc |= DLGC_WANTARROWS;
4345 }
4346
4347 return rc;
4348 }
4349 #endif // __WXMSW__
4350
4351 wxSize wxGenericListCtrl::GetSizeAvailableForScrollTarget(const wxSize& size)
4352 {
4353 wxSize newsize = size;
4354 if (m_headerWin)
4355 newsize.y -= m_headerWin->GetSize().y;
4356
4357 return newsize;
4358 }
4359
4360 void wxGenericListCtrl::OnScroll(wxScrollWinEvent& event)
4361 {
4362 // update our idea of which lines are shown when we redraw
4363 // the window the next time
4364 m_mainWin->ResetVisibleLinesRange();
4365
4366 HandleOnScroll( event );
4367
4368 if ( event.GetOrientation() == wxHORIZONTAL && HasHeader() )
4369 {
4370 m_headerWin->Refresh();
4371 m_headerWin->Update();
4372 }
4373 }
4374
4375 void wxGenericListCtrl::SetSingleStyle( long style, bool add )
4376 {
4377 wxASSERT_MSG( !(style & wxLC_VIRTUAL),
4378 wxT("wxLC_VIRTUAL can't be [un]set") );
4379
4380 long flag = GetWindowStyle();
4381
4382 if (add)
4383 {
4384 if (style & wxLC_MASK_TYPE)
4385 flag &= ~(wxLC_MASK_TYPE | wxLC_VIRTUAL);
4386 if (style & wxLC_MASK_ALIGN)
4387 flag &= ~wxLC_MASK_ALIGN;
4388 if (style & wxLC_MASK_SORT)
4389 flag &= ~wxLC_MASK_SORT;
4390 }
4391
4392 if (add)
4393 flag |= style;
4394 else
4395 flag &= ~style;
4396
4397 // some styles can be set without recreating everything (as happens in
4398 // SetWindowStyleFlag() which calls wxListMainWindow::DeleteEverything())
4399 if ( !(style & ~(wxLC_HRULES | wxLC_VRULES)) )
4400 {
4401 Refresh();
4402 wxWindow::SetWindowStyleFlag(flag);
4403 }
4404 else
4405 {
4406 SetWindowStyleFlag( flag );
4407 }
4408 }
4409
4410 void wxGenericListCtrl::SetWindowStyleFlag( long flag )
4411 {
4412 // we add wxHSCROLL and wxVSCROLL in ctor unconditionally and it never
4413 // makes sense to remove them as we'll always add scrollbars anyhow when
4414 // needed
4415 flag |= wxHSCROLL | wxVSCROLL;
4416
4417 const bool wasInReportView = HasFlag(wxLC_REPORT);
4418
4419 // update the window style first so that the header is created or destroyed
4420 // corresponding to the new style
4421 wxWindow::SetWindowStyleFlag( flag );
4422
4423 if (m_mainWin)
4424 {
4425 const bool inReportView = (flag & wxLC_REPORT) != 0;
4426 if ( inReportView != wasInReportView )
4427 {
4428 // we need to notify the main window about this change as it must
4429 // update its data structures
4430 m_mainWin->SetReportView(inReportView);
4431 }
4432
4433 // m_mainWin->DeleteEverything(); wxMSW doesn't do that
4434
4435 CreateOrDestroyHeaderWindowAsNeeded();
4436
4437 GetSizer()->Layout();
4438 }
4439 }
4440
4441 bool wxGenericListCtrl::GetColumn(int col, wxListItem &item) const
4442 {
4443 m_mainWin->GetColumn( col, item );
4444 return true;
4445 }
4446
4447 bool wxGenericListCtrl::SetColumn( int col, wxListItem& item )
4448 {
4449 m_mainWin->SetColumn( col, item );
4450 return true;
4451 }
4452
4453 int wxGenericListCtrl::GetColumnWidth( int col ) const
4454 {
4455 return m_mainWin->GetColumnWidth( col );
4456 }
4457
4458 bool wxGenericListCtrl::SetColumnWidth( int col, int width )
4459 {
4460 m_mainWin->SetColumnWidth( col, width );
4461 return true;
4462 }
4463
4464 int wxGenericListCtrl::GetCountPerPage() const
4465 {
4466 return m_mainWin->GetCountPerPage(); // different from Windows ?
4467 }
4468
4469 bool wxGenericListCtrl::GetItem( wxListItem &info ) const
4470 {
4471 m_mainWin->GetItem( info );
4472 return true;
4473 }
4474
4475 bool wxGenericListCtrl::SetItem( wxListItem &info )
4476 {
4477 m_mainWin->SetItem( info );
4478 return true;
4479 }
4480
4481 long wxGenericListCtrl::SetItem( long index, int col, const wxString& label, int imageId )
4482 {
4483 wxListItem info;
4484 info.m_text = label;
4485 info.m_mask = wxLIST_MASK_TEXT;
4486 info.m_itemId = index;
4487 info.m_col = col;
4488 if ( imageId > -1 )
4489 {
4490 info.m_image = imageId;
4491 info.m_mask |= wxLIST_MASK_IMAGE;
4492 }
4493
4494 m_mainWin->SetItem(info);
4495 return true;
4496 }
4497
4498 int wxGenericListCtrl::GetItemState( long item, long stateMask ) const
4499 {
4500 return m_mainWin->GetItemState( item, stateMask );
4501 }
4502
4503 bool wxGenericListCtrl::SetItemState( long item, long state, long stateMask )
4504 {
4505 m_mainWin->SetItemState( item, state, stateMask );
4506 return true;
4507 }
4508
4509 bool
4510 wxGenericListCtrl::SetItemImage( long item, int image, int WXUNUSED(selImage) )
4511 {
4512 return SetItemColumnImage(item, 0, image);
4513 }
4514
4515 bool
4516 wxGenericListCtrl::SetItemColumnImage( long item, long column, int image )
4517 {
4518 wxListItem info;
4519 info.m_image = image;
4520 info.m_mask = wxLIST_MASK_IMAGE;
4521 info.m_itemId = item;
4522 info.m_col = column;
4523 m_mainWin->SetItem( info );
4524 return true;
4525 }
4526
4527 wxString wxGenericListCtrl::GetItemText( long item, int col ) const
4528 {
4529 return m_mainWin->GetItemText(item, col);
4530 }
4531
4532 void wxGenericListCtrl::SetItemText( long item, const wxString& str )
4533 {
4534 m_mainWin->SetItemText(item, str);
4535 }
4536
4537 wxUIntPtr wxGenericListCtrl::GetItemData( long item ) const
4538 {
4539 wxListItem info;
4540 info.m_mask = wxLIST_MASK_DATA;
4541 info.m_itemId = item;
4542 m_mainWin->GetItem( info );
4543 return info.m_data;
4544 }
4545
4546 bool wxGenericListCtrl::SetItemPtrData( long item, wxUIntPtr data )
4547 {
4548 wxListItem info;
4549 info.m_mask = wxLIST_MASK_DATA;
4550 info.m_itemId = item;
4551 info.m_data = data;
4552 m_mainWin->SetItem( info );
4553 return true;
4554 }
4555
4556 wxRect wxGenericListCtrl::GetViewRect() const
4557 {
4558 return m_mainWin->GetViewRect();
4559 }
4560
4561 bool wxGenericListCtrl::GetItemRect(long item, wxRect& rect, int code) const
4562 {
4563 return GetSubItemRect(item, wxLIST_GETSUBITEMRECT_WHOLEITEM, rect, code);
4564 }
4565
4566 bool wxGenericListCtrl::GetSubItemRect(long item,
4567 long subItem,
4568 wxRect& rect,
4569 int WXUNUSED(code)) const
4570 {
4571 if ( !m_mainWin->GetSubItemRect( item, subItem, rect ) )
4572 return false;
4573
4574 if ( m_mainWin->HasHeader() )
4575 rect.y += m_headerWin->GetSize().y + 1;
4576
4577 return true;
4578 }
4579
4580 bool wxGenericListCtrl::GetItemPosition( long item, wxPoint& pos ) const
4581 {
4582 m_mainWin->GetItemPosition( item, pos );
4583 return true;
4584 }
4585
4586 bool wxGenericListCtrl::SetItemPosition( long WXUNUSED(item), const wxPoint& WXUNUSED(pos) )
4587 {
4588 return false;
4589 }
4590
4591 int wxGenericListCtrl::GetItemCount() const
4592 {
4593 return m_mainWin->GetItemCount();
4594 }
4595
4596 int wxGenericListCtrl::GetColumnCount() const
4597 {
4598 return m_mainWin->GetColumnCount();
4599 }
4600
4601 void wxGenericListCtrl::SetItemSpacing( int spacing, bool isSmall )
4602 {
4603 m_mainWin->SetItemSpacing( spacing, isSmall );
4604 }
4605
4606 wxSize wxGenericListCtrl::GetItemSpacing() const
4607 {
4608 const int spacing = m_mainWin->GetItemSpacing(HasFlag(wxLC_SMALL_ICON));
4609
4610 return wxSize(spacing, spacing);
4611 }
4612
4613 #if WXWIN_COMPATIBILITY_2_6
4614 int wxGenericListCtrl::GetItemSpacing( bool isSmall ) const
4615 {
4616 return m_mainWin->GetItemSpacing( isSmall );
4617 }
4618 #endif // WXWIN_COMPATIBILITY_2_6
4619
4620 void wxGenericListCtrl::SetItemTextColour( long item, const wxColour &col )
4621 {
4622 wxListItem info;
4623 info.m_itemId = item;
4624 info.SetTextColour( col );
4625 m_mainWin->SetItem( info );
4626 }
4627
4628 wxColour wxGenericListCtrl::GetItemTextColour( long item ) const
4629 {
4630 wxListItem info;
4631 info.m_itemId = item;
4632 m_mainWin->GetItem( info );
4633 return info.GetTextColour();
4634 }
4635
4636 void wxGenericListCtrl::SetItemBackgroundColour( long item, const wxColour &col )
4637 {
4638 wxListItem info;
4639 info.m_itemId = item;
4640 info.SetBackgroundColour( col );
4641 m_mainWin->SetItem( info );
4642 }
4643
4644 wxColour wxGenericListCtrl::GetItemBackgroundColour( long item ) const
4645 {
4646 wxListItem info;
4647 info.m_itemId = item;
4648 m_mainWin->GetItem( info );
4649 return info.GetBackgroundColour();
4650 }
4651
4652 void wxGenericListCtrl::SetItemFont( long item, const wxFont &f )
4653 {
4654 wxListItem info;
4655 info.m_itemId = item;
4656 info.SetFont( f );
4657 m_mainWin->SetItem( info );
4658 }
4659
4660 wxFont wxGenericListCtrl::GetItemFont( long item ) const
4661 {
4662 wxListItem info;
4663 info.m_itemId = item;
4664 m_mainWin->GetItem( info );
4665 return info.GetFont();
4666 }
4667
4668 int wxGenericListCtrl::GetSelectedItemCount() const
4669 {
4670 return m_mainWin->GetSelectedItemCount();
4671 }
4672
4673 wxColour wxGenericListCtrl::GetTextColour() const
4674 {
4675 return GetForegroundColour();
4676 }
4677
4678 void wxGenericListCtrl::SetTextColour(const wxColour& col)
4679 {
4680 SetForegroundColour(col);
4681 }
4682
4683 long wxGenericListCtrl::GetTopItem() const
4684 {
4685 size_t top;
4686 m_mainWin->GetVisibleLinesRange(&top, NULL);
4687 return (long)top;
4688 }
4689
4690 long wxGenericListCtrl::GetNextItem( long item, int geom, int state ) const
4691 {
4692 return m_mainWin->GetNextItem( item, geom, state );
4693 }
4694
4695 wxImageList *wxGenericListCtrl::GetImageList(int which) const
4696 {
4697 if (which == wxIMAGE_LIST_NORMAL)
4698 return m_imageListNormal;
4699 else if (which == wxIMAGE_LIST_SMALL)
4700 return m_imageListSmall;
4701 else if (which == wxIMAGE_LIST_STATE)
4702 return m_imageListState;
4703
4704 return NULL;
4705 }
4706
4707 void wxGenericListCtrl::SetImageList( wxImageList *imageList, int which )
4708 {
4709 if ( which == wxIMAGE_LIST_NORMAL )
4710 {
4711 if (m_ownsImageListNormal)
4712 delete m_imageListNormal;
4713 m_imageListNormal = imageList;
4714 m_ownsImageListNormal = false;
4715 }
4716 else if ( which == wxIMAGE_LIST_SMALL )
4717 {
4718 if (m_ownsImageListSmall)
4719 delete m_imageListSmall;
4720 m_imageListSmall = imageList;
4721 m_ownsImageListSmall = false;
4722 }
4723 else if ( which == wxIMAGE_LIST_STATE )
4724 {
4725 if (m_ownsImageListState)
4726 delete m_imageListState;
4727 m_imageListState = imageList;
4728 m_ownsImageListState = false;
4729 }
4730
4731 m_mainWin->SetImageList( imageList, which );
4732 }
4733
4734 void wxGenericListCtrl::AssignImageList(wxImageList *imageList, int which)
4735 {
4736 SetImageList(imageList, which);
4737 if ( which == wxIMAGE_LIST_NORMAL )
4738 m_ownsImageListNormal = true;
4739 else if ( which == wxIMAGE_LIST_SMALL )
4740 m_ownsImageListSmall = true;
4741 else if ( which == wxIMAGE_LIST_STATE )
4742 m_ownsImageListState = true;
4743 }
4744
4745 bool wxGenericListCtrl::Arrange( int WXUNUSED(flag) )
4746 {
4747 return 0;
4748 }
4749
4750 bool wxGenericListCtrl::DeleteItem( long item )
4751 {
4752 m_mainWin->DeleteItem( item );
4753 return true;
4754 }
4755
4756 bool wxGenericListCtrl::DeleteAllItems()
4757 {
4758 m_mainWin->DeleteAllItems();
4759 return true;
4760 }
4761
4762 bool wxGenericListCtrl::DeleteAllColumns()
4763 {
4764 size_t count = m_mainWin->m_columns.GetCount();
4765 for ( size_t n = 0; n < count; n++ )
4766 DeleteColumn( 0 );
4767 return true;
4768 }
4769
4770 void wxGenericListCtrl::ClearAll()
4771 {
4772 m_mainWin->DeleteEverything();
4773 }
4774
4775 bool wxGenericListCtrl::DeleteColumn( int col )
4776 {
4777 m_mainWin->DeleteColumn( col );
4778
4779 // if we don't have the header any longer, we need to relayout the window
4780 // if ( !GetColumnCount() )
4781
4782 return true;
4783 }
4784
4785 wxTextCtrl *wxGenericListCtrl::EditLabel(long item,
4786 wxClassInfo* textControlClass)
4787 {
4788 return m_mainWin->EditLabel( item, textControlClass );
4789 }
4790
4791 wxTextCtrl *wxGenericListCtrl::GetEditControl() const
4792 {
4793 return m_mainWin->GetEditControl();
4794 }
4795
4796 bool wxGenericListCtrl::EnsureVisible( long item )
4797 {
4798 m_mainWin->EnsureVisible( item );
4799 return true;
4800 }
4801
4802 long wxGenericListCtrl::FindItem( long start, const wxString& str, bool partial )
4803 {
4804 return m_mainWin->FindItem( start, str, partial );
4805 }
4806
4807 long wxGenericListCtrl::FindItem( long start, wxUIntPtr data )
4808 {
4809 return m_mainWin->FindItem( start, data );
4810 }
4811
4812 long wxGenericListCtrl::FindItem( long WXUNUSED(start), const wxPoint& pt,
4813 int WXUNUSED(direction))
4814 {
4815 return m_mainWin->FindItem( pt );
4816 }
4817
4818 // TODO: sub item hit testing
4819 long wxGenericListCtrl::HitTest(const wxPoint& point, int& flags, long *) const
4820 {
4821 return m_mainWin->HitTest( (int)point.x, (int)point.y, flags );
4822 }
4823
4824 long wxGenericListCtrl::InsertItem( wxListItem& info )
4825 {
4826 m_mainWin->InsertItem( info );
4827 return info.m_itemId;
4828 }
4829
4830 long wxGenericListCtrl::InsertItem( long index, const wxString &label )
4831 {
4832 wxListItem info;
4833 info.m_text = label;
4834 info.m_mask = wxLIST_MASK_TEXT;
4835 info.m_itemId = index;
4836 return InsertItem( info );
4837 }
4838
4839 long wxGenericListCtrl::InsertItem( long index, int imageIndex )
4840 {
4841 wxListItem info;
4842 info.m_mask = wxLIST_MASK_IMAGE;
4843 info.m_image = imageIndex;
4844 info.m_itemId = index;
4845 return InsertItem( info );
4846 }
4847
4848 long wxGenericListCtrl::InsertItem( long index, const wxString &label, int imageIndex )
4849 {
4850 wxListItem info;
4851 info.m_text = label;
4852 info.m_image = imageIndex;
4853 info.m_mask = wxLIST_MASK_TEXT | wxLIST_MASK_IMAGE;
4854 info.m_itemId = index;
4855 return InsertItem( info );
4856 }
4857
4858 long wxGenericListCtrl::InsertColumn( long col, wxListItem &item )
4859 {
4860 wxCHECK_MSG( InReportView(), -1, wxT("can't add column in non report mode") );
4861
4862 m_mainWin->InsertColumn( col, item );
4863
4864 // NOTE: if wxLC_NO_HEADER was given, then we are in report view mode but
4865 // still have m_headerWin==NULL
4866 if (m_headerWin)
4867 m_headerWin->Refresh();
4868
4869 return 0;
4870 }
4871
4872 long wxGenericListCtrl::InsertColumn( long col, const wxString &heading,
4873 int format, int width )
4874 {
4875 wxListItem item;
4876 item.m_mask = wxLIST_MASK_TEXT | wxLIST_MASK_FORMAT;
4877 item.m_text = heading;
4878 if (width >= -2)
4879 {
4880 item.m_mask |= wxLIST_MASK_WIDTH;
4881 item.m_width = width;
4882 }
4883
4884 item.m_format = format;
4885
4886 return InsertColumn( col, item );
4887 }
4888
4889 bool wxGenericListCtrl::ScrollList( int dx, int dy )
4890 {
4891 return m_mainWin->ScrollList(dx, dy);
4892 }
4893
4894 // Sort items.
4895 // fn is a function which takes 3 long arguments: item1, item2, data.
4896 // item1 is the long data associated with a first item (NOT the index).
4897 // item2 is the long data associated with a second item (NOT the index).
4898 // data is the same value as passed to SortItems.
4899 // The return value is a negative number if the first item should precede the second
4900 // item, a positive number of the second item should precede the first,
4901 // or zero if the two items are equivalent.
4902 // data is arbitrary data to be passed to the sort function.
4903
4904 bool wxGenericListCtrl::SortItems( wxListCtrlCompare fn, wxIntPtr data )
4905 {
4906 m_mainWin->SortItems( fn, data );
4907 return true;
4908 }
4909
4910 // ----------------------------------------------------------------------------
4911 // event handlers
4912 // ----------------------------------------------------------------------------
4913
4914 void wxGenericListCtrl::OnSize(wxSizeEvent& WXUNUSED(event))
4915 {
4916 if (!m_mainWin) return;
4917
4918 // We need to override OnSize so that our scrolled
4919 // window a) does call Layout() to use sizers for
4920 // positioning the controls but b) does not query
4921 // the sizer for their size and use that for setting
4922 // the scrollable area as set that ourselves by
4923 // calling SetScrollbar() further down.
4924
4925 Layout();
4926
4927 m_mainWin->RecalculatePositions();
4928
4929 AdjustScrollbars();
4930 }
4931
4932 void wxGenericListCtrl::OnInternalIdle()
4933 {
4934 wxWindow::OnInternalIdle();
4935
4936 if (m_mainWin->m_dirty)
4937 m_mainWin->RecalculatePositions();
4938 }
4939
4940 // ----------------------------------------------------------------------------
4941 // font/colours
4942 // ----------------------------------------------------------------------------
4943
4944 bool wxGenericListCtrl::SetBackgroundColour( const wxColour &colour )
4945 {
4946 if (m_mainWin)
4947 {
4948 m_mainWin->SetBackgroundColour( colour );
4949 m_mainWin->m_dirty = true;
4950 }
4951
4952 return true;
4953 }
4954
4955 bool wxGenericListCtrl::SetForegroundColour( const wxColour &colour )
4956 {
4957 if ( !wxWindow::SetForegroundColour( colour ) )
4958 return false;
4959
4960 if (m_mainWin)
4961 {
4962 m_mainWin->SetForegroundColour( colour );
4963 m_mainWin->m_dirty = true;
4964 }
4965
4966 if (m_headerWin)
4967 m_headerWin->SetForegroundColour( colour );
4968
4969 return true;
4970 }
4971
4972 bool wxGenericListCtrl::SetFont( const wxFont &font )
4973 {
4974 if ( !wxWindow::SetFont( font ) )
4975 return false;
4976
4977 if (m_mainWin)
4978 {
4979 m_mainWin->SetFont( font );
4980 m_mainWin->m_dirty = true;
4981 }
4982
4983 if (m_headerWin)
4984 {
4985 m_headerWin->SetFont( font );
4986 // CalculateAndSetHeaderHeight();
4987 }
4988
4989 Refresh();
4990
4991 return true;
4992 }
4993
4994 // static
4995 wxVisualAttributes
4996 wxGenericListCtrl::GetClassDefaultAttributes(wxWindowVariant variant)
4997 {
4998 #if _USE_VISATTR
4999 // Use the same color scheme as wxListBox
5000 return wxListBox::GetClassDefaultAttributes(variant);
5001 #else
5002 wxUnusedVar(variant);
5003 wxVisualAttributes attr;
5004 attr.colFg = wxSystemSettings::GetColour(wxSYS_COLOUR_LISTBOXTEXT);
5005 attr.colBg = wxSystemSettings::GetColour(wxSYS_COLOUR_LISTBOX);
5006 attr.font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
5007 return attr;
5008 #endif
5009 }
5010
5011 // ----------------------------------------------------------------------------
5012 // methods forwarded to m_mainWin
5013 // ----------------------------------------------------------------------------
5014
5015 #if wxUSE_DRAG_AND_DROP
5016
5017 void wxGenericListCtrl::SetDropTarget( wxDropTarget *dropTarget )
5018 {
5019 m_mainWin->SetDropTarget( dropTarget );
5020 }
5021
5022 wxDropTarget *wxGenericListCtrl::GetDropTarget() const
5023 {
5024 return m_mainWin->GetDropTarget();
5025 }
5026
5027 #endif
5028
5029 bool wxGenericListCtrl::SetCursor( const wxCursor &cursor )
5030 {
5031 return m_mainWin ? m_mainWin->wxWindow::SetCursor(cursor) : false;
5032 }
5033
5034 wxColour wxGenericListCtrl::GetBackgroundColour() const
5035 {
5036 return m_mainWin ? m_mainWin->GetBackgroundColour() : wxColour();
5037 }
5038
5039 wxColour wxGenericListCtrl::GetForegroundColour() const
5040 {
5041 return m_mainWin ? m_mainWin->GetForegroundColour() : wxColour();
5042 }
5043
5044 bool wxGenericListCtrl::DoPopupMenu( wxMenu *menu, int x, int y )
5045 {
5046 #if wxUSE_MENUS
5047 return m_mainWin->PopupMenu( menu, x, y );
5048 #else
5049 return false;
5050 #endif
5051 }
5052
5053 void wxGenericListCtrl::DoClientToScreen( int *x, int *y ) const
5054 {
5055 // It's not clear whether this can be called before m_mainWin is created
5056 // but it seems better to be on the safe side and check.
5057 if ( m_mainWin )
5058 m_mainWin->DoClientToScreen(x, y);
5059 else
5060 wxControl::DoClientToScreen(x, y);
5061 }
5062
5063 void wxGenericListCtrl::DoScreenToClient( int *x, int *y ) const
5064 {
5065 // At least in wxGTK/Univ build this method can be called before m_mainWin
5066 // is created so avoid crashes in this case.
5067 if ( m_mainWin )
5068 m_mainWin->DoScreenToClient(x, y);
5069 else
5070 wxControl::DoScreenToClient(x, y);
5071 }
5072
5073 void wxGenericListCtrl::SetFocus()
5074 {
5075 // The test in window.cpp fails as we are a composite
5076 // window, so it checks against "this", but not m_mainWin.
5077 if ( DoFindFocus() != this )
5078 m_mainWin->SetFocus();
5079 }
5080
5081 wxSize wxGenericListCtrl::DoGetBestClientSize() const
5082 {
5083 // Something is better than nothing even if this is completely arbitrary.
5084 wxSize sizeBest(100, 80);
5085
5086 if ( !InReportView() )
5087 {
5088 // Ensure that our minimal width is at least big enough to show all our
5089 // items. This is important for wxListbook to size itself correctly.
5090
5091 // Remember the offset of the first item: this corresponds to the
5092 // margins around the item so we will add it to the minimal size below
5093 // to ensure that we have equal margins on all sides.
5094 wxPoint ofs;
5095
5096 // We can iterate over all items as there shouldn't be too many of them
5097 // in non-report view. If it ever becomes a problem, we could examine
5098 // just the first few items probably, the determination of the best
5099 // size is less important if we will need scrollbars anyhow.
5100 for ( int n = 0; n < GetItemCount(); n++ )
5101 {
5102 const wxRect itemRect = m_mainWin->GetLineRect(n);
5103 if ( !n )
5104 {
5105 // Remember the position of the first item as all the rest are
5106 // offset by at least this number of pixels too.
5107 ofs = itemRect.GetPosition();
5108 }
5109
5110 sizeBest.IncTo(itemRect.GetSize());
5111 }
5112
5113 sizeBest.IncBy(2*ofs);
5114
5115
5116 // If we have the scrollbars we need to account for them too. And to
5117 // make sure the scrollbars status is up to date we need to call this
5118 // function to set them.
5119 m_mainWin->RecalculatePositions(true /* no refresh */);
5120
5121 // Unfortunately we can't use wxWindow::HasScrollbar() here as we need
5122 // to use m_mainWin client/virtual size for determination of whether we
5123 // use scrollbars and not the size of this window itself. Maybe that
5124 // function should be extended to work correctly in the case when our
5125 // scrollbars manage a different window from this one but currently it
5126 // doesn't work.
5127 const wxSize sizeClient = m_mainWin->GetClientSize();
5128 const wxSize sizeVirt = m_mainWin->GetVirtualSize();
5129
5130 if ( sizeVirt.x > sizeClient.x /* HasScrollbar(wxHORIZONTAL) */ )
5131 sizeBest.y += wxSystemSettings::GetMetric(wxSYS_HSCROLL_Y);
5132
5133 if ( sizeVirt.y > sizeClient.y /* HasScrollbar(wxVERTICAL) */ )
5134 sizeBest.x += wxSystemSettings::GetMetric(wxSYS_VSCROLL_X);
5135 }
5136
5137 return sizeBest;
5138 }
5139
5140 // ----------------------------------------------------------------------------
5141 // virtual list control support
5142 // ----------------------------------------------------------------------------
5143
5144 wxString wxGenericListCtrl::OnGetItemText(long WXUNUSED(item), long WXUNUSED(col)) const
5145 {
5146 // this is a pure virtual function, in fact - which is not really pure
5147 // because the controls which are not virtual don't need to implement it
5148 wxFAIL_MSG( wxT("wxGenericListCtrl::OnGetItemText not supposed to be called") );
5149
5150 return wxEmptyString;
5151 }
5152
5153 int wxGenericListCtrl::OnGetItemImage(long WXUNUSED(item)) const
5154 {
5155 wxCHECK_MSG(!GetImageList(wxIMAGE_LIST_SMALL),
5156 -1,
5157 wxT("List control has an image list, OnGetItemImage or OnGetItemColumnImage should be overridden."));
5158 return -1;
5159 }
5160
5161 int wxGenericListCtrl::OnGetItemColumnImage(long item, long column) const
5162 {
5163 if (!column)
5164 return OnGetItemImage(item);
5165
5166 return -1;
5167 }
5168
5169 wxListItemAttr *
5170 wxGenericListCtrl::OnGetItemAttr(long WXUNUSED_UNLESS_DEBUG(item)) const
5171 {
5172 wxASSERT_MSG( item >= 0 && item < GetItemCount(),
5173 wxT("invalid item index in OnGetItemAttr()") );
5174
5175 // no attributes by default
5176 return NULL;
5177 }
5178
5179 void wxGenericListCtrl::SetItemCount(long count)
5180 {
5181 wxASSERT_MSG( IsVirtual(), wxT("this is for virtual controls only") );
5182
5183 m_mainWin->SetItemCount(count);
5184 }
5185
5186 void wxGenericListCtrl::RefreshItem(long item)
5187 {
5188 m_mainWin->RefreshLine(item);
5189 }
5190
5191 void wxGenericListCtrl::RefreshItems(long itemFrom, long itemTo)
5192 {
5193 m_mainWin->RefreshLines(itemFrom, itemTo);
5194 }
5195
5196 // Generic wxListCtrl is more or less a container for two other
5197 // windows which drawings are done upon. These are namely
5198 // 'm_headerWin' and 'm_mainWin'.
5199 // Here we override 'virtual wxWindow::Refresh()' to mimic the
5200 // behaviour wxListCtrl has under wxMSW.
5201 //
5202 void wxGenericListCtrl::Refresh(bool eraseBackground, const wxRect *rect)
5203 {
5204 if (!rect)
5205 {
5206 // The easy case, no rectangle specified.
5207 if (m_headerWin)
5208 m_headerWin->Refresh(eraseBackground);
5209
5210 if (m_mainWin)
5211 m_mainWin->Refresh(eraseBackground);
5212 }
5213 else
5214 {
5215 // Refresh the header window
5216 if (m_headerWin)
5217 {
5218 wxRect rectHeader = m_headerWin->GetRect();
5219 rectHeader.Intersect(*rect);
5220 if (rectHeader.GetWidth() && rectHeader.GetHeight())
5221 {
5222 int x, y;
5223 m_headerWin->GetPosition(&x, &y);
5224 rectHeader.Offset(-x, -y);
5225 m_headerWin->Refresh(eraseBackground, &rectHeader);
5226 }
5227 }
5228
5229 // Refresh the main window
5230 if (m_mainWin)
5231 {
5232 wxRect rectMain = m_mainWin->GetRect();
5233 rectMain.Intersect(*rect);
5234 if (rectMain.GetWidth() && rectMain.GetHeight())
5235 {
5236 int x, y;
5237 m_mainWin->GetPosition(&x, &y);
5238 rectMain.Offset(-x, -y);
5239 m_mainWin->Refresh(eraseBackground, &rectMain);
5240 }
5241 }
5242 }
5243 }
5244
5245 void wxGenericListCtrl::Update()
5246 {
5247 if ( m_mainWin )
5248 {
5249 if ( m_mainWin->m_dirty )
5250 m_mainWin->RecalculatePositions();
5251
5252 m_mainWin->Update();
5253 }
5254
5255 if ( m_headerWin )
5256 m_headerWin->Update();
5257 }
5258
5259 #endif // wxUSE_LISTCTRL