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