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