]> git.saurik.com Git - wxWidgets.git/blob - src/generic/listctrl.cpp
implemented EVT_LIST_CACHE_HINT support: send this message from OnPaint() now
[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 // tell the caller cache to cache the data
2486 wxListEvent evCache(wxEVT_COMMAND_LIST_CACHE_HINT, GetParent()->GetId());
2487 evCache.SetEventObject( GetParent() );
2488 evCache.m_oldItemIndex = visibleFrom;
2489 evCache.m_itemIndex = visibleTo;
2490 GetParent()->GetEventHandler()->ProcessEvent( evCache );
2491
2492 for ( size_t line = visibleFrom; line <= visibleTo; line++ )
2493 {
2494 rectLine = GetLineRect(line);
2495
2496 if ( !IsExposed(rectLine.x - xOrig, rectLine.y - yOrig,
2497 rectLine.width, rectLine.height) )
2498 {
2499 // don't redraw unaffected lines to avoid flicker
2500 continue;
2501 }
2502
2503 GetLine(line)->DrawInReportMode( &dc,
2504 rectLine,
2505 GetLineHighlightRect(line),
2506 IsHighlighted(line) );
2507 }
2508
2509 if ( HasFlag(wxLC_HRULES) )
2510 {
2511 wxPen pen(GetRuleColour(), 1, wxSOLID);
2512 wxSize clientSize = GetClientSize();
2513
2514 for ( size_t i = visibleFrom; i <= visibleTo; i++ )
2515 {
2516 dc.SetPen(pen);
2517 dc.SetBrush( *wxTRANSPARENT_BRUSH );
2518 dc.DrawLine(0 - dev_x, i*lineHeight,
2519 clientSize.x - dev_x, i*lineHeight);
2520 }
2521
2522 // Draw last horizontal rule
2523 if ( visibleTo > visibleFrom )
2524 {
2525 dc.SetPen(pen);
2526 dc.SetBrush( *wxTRANSPARENT_BRUSH );
2527 dc.DrawLine(0 - dev_x, m_lineTo*lineHeight,
2528 clientSize.x - dev_x , m_lineTo*lineHeight );
2529 }
2530 }
2531
2532 // Draw vertical rules if required
2533 if ( HasFlag(wxLC_VRULES) && !IsEmpty() )
2534 {
2535 wxPen pen(GetRuleColour(), 1, wxSOLID);
2536
2537 int col = 0;
2538 wxRect firstItemRect;
2539 wxRect lastItemRect;
2540 GetItemRect(0, firstItemRect);
2541 GetItemRect(GetItemCount() - 1, lastItemRect);
2542 int x = firstItemRect.GetX();
2543 dc.SetPen(pen);
2544 dc.SetBrush(* wxTRANSPARENT_BRUSH);
2545 for (col = 0; col < GetColumnCount(); col++)
2546 {
2547 int colWidth = GetColumnWidth(col);
2548 x += colWidth;
2549 dc.DrawLine(x - dev_x, firstItemRect.GetY() - 1 - dev_y,
2550 x - dev_x, lastItemRect.GetBottom() + 1 - dev_y);
2551 }
2552 }
2553 }
2554 else // !report
2555 {
2556 size_t count = GetItemCount();
2557 for ( size_t i = 0; i < count; i++ )
2558 {
2559 GetLine(i)->Draw( &dc );
2560 }
2561 }
2562
2563 if ( HasCurrent() && m_hasFocus )
2564 {
2565 #ifdef __WXMAC__
2566 // no rect outline, we already have the background color
2567 #else
2568 dc.SetPen( *wxBLACK_PEN );
2569 dc.SetBrush( *wxTRANSPARENT_BRUSH );
2570 dc.DrawRectangle( GetLineHighlightRect(m_current) );
2571 #endif
2572 }
2573
2574 dc.EndDrawing();
2575 }
2576
2577 void wxListMainWindow::HighlightAll( bool on )
2578 {
2579 if ( IsSingleSel() )
2580 {
2581 wxASSERT_MSG( !on, _T("can't do this in a single sel control") );
2582
2583 // we just have one item to turn off
2584 if ( HasCurrent() && IsHighlighted(m_current) )
2585 {
2586 HighlightLine(m_current, FALSE);
2587 RefreshLine(m_current);
2588 }
2589 }
2590 else // multi sel
2591 {
2592 HighlightLines(0, GetItemCount() - 1, on);
2593 }
2594 }
2595
2596 void wxListMainWindow::SendNotify( size_t line,
2597 wxEventType command,
2598 wxPoint point )
2599 {
2600 wxListEvent le( command, GetParent()->GetId() );
2601 le.SetEventObject( GetParent() );
2602 le.m_itemIndex = line;
2603
2604 // set only for events which have position
2605 if ( point != wxDefaultPosition )
2606 le.m_pointDrag = point;
2607
2608 GetLine(line)->GetItem( 0, le.m_item );
2609 GetParent()->GetEventHandler()->ProcessEvent( le );
2610 }
2611
2612 void wxListMainWindow::OnFocusLine( size_t WXUNUSED(line) )
2613 {
2614 // SendNotify( line, wxEVT_COMMAND_LIST_ITEM_FOCUSSED );
2615 }
2616
2617 void wxListMainWindow::OnUnfocusLine( size_t WXUNUSED(line) )
2618 {
2619 // SendNotify( line, wxEVT_COMMAND_LIST_ITEM_UNFOCUSSED );
2620 }
2621
2622 void wxListMainWindow::EditLabel( long item )
2623 {
2624 wxCHECK_RET( (item >= 0) && ((size_t)item < GetItemCount()),
2625 wxT("wrong index in wxListCtrl::EditLabel()") );
2626
2627 m_currentEdit = (size_t)item;
2628
2629 wxListEvent le( wxEVT_COMMAND_LIST_BEGIN_LABEL_EDIT, GetParent()->GetId() );
2630 le.SetEventObject( GetParent() );
2631 le.m_itemIndex = item;
2632 wxListLineData *data = GetLine(m_currentEdit);
2633 wxCHECK_RET( data, _T("invalid index in EditLabel()") );
2634 data->GetItem( 0, le.m_item );
2635 GetParent()->GetEventHandler()->ProcessEvent( le );
2636
2637 if (!le.IsAllowed())
2638 return;
2639
2640 // We have to call this here because the label in question might just have
2641 // been added and no screen update taken place.
2642 if (m_dirty)
2643 wxSafeYield();
2644
2645 wxClientDC dc(this);
2646 PrepareDC( dc );
2647
2648 wxString s = data->GetText(0);
2649 wxRect rectLabel = GetLineLabelRect(m_currentEdit);
2650
2651 rectLabel.x = dc.LogicalToDeviceX( rectLabel.x );
2652 rectLabel.y = dc.LogicalToDeviceY( rectLabel.y );
2653
2654 wxListTextCtrl *text = new wxListTextCtrl
2655 (
2656 this, -1,
2657 &m_renameAccept,
2658 &m_renameRes,
2659 this,
2660 s,
2661 wxPoint(rectLabel.x-4,rectLabel.y-4),
2662 wxSize(rectLabel.width+11,rectLabel.height+8)
2663 );
2664 text->SetFocus();
2665 }
2666
2667 void wxListMainWindow::OnRenameTimer()
2668 {
2669 wxCHECK_RET( HasCurrent(), wxT("unexpected rename timer") );
2670
2671 EditLabel( m_current );
2672 }
2673
2674 void wxListMainWindow::OnRenameAccept()
2675 {
2676 wxListEvent le( wxEVT_COMMAND_LIST_END_LABEL_EDIT, GetParent()->GetId() );
2677 le.SetEventObject( GetParent() );
2678 le.m_itemIndex = m_currentEdit;
2679
2680 wxListLineData *data = GetLine(m_currentEdit);
2681 wxCHECK_RET( data, _T("invalid index in OnRenameAccept()") );
2682
2683 data->GetItem( 0, le.m_item );
2684 le.m_item.m_text = m_renameRes;
2685 GetParent()->GetEventHandler()->ProcessEvent( le );
2686
2687 if (!le.IsAllowed()) return;
2688
2689 wxListItem info;
2690 info.m_mask = wxLIST_MASK_TEXT;
2691 info.m_itemId = le.m_itemIndex;
2692 info.m_text = m_renameRes;
2693 info.SetTextColour(le.m_item.GetTextColour());
2694 SetItem( info );
2695 }
2696
2697 void wxListMainWindow::OnMouse( wxMouseEvent &event )
2698 {
2699 event.SetEventObject( GetParent() );
2700 if ( GetParent()->GetEventHandler()->ProcessEvent( event) )
2701 return;
2702
2703 if ( !HasCurrent() || IsEmpty() )
2704 return;
2705
2706 if (m_dirty)
2707 return;
2708
2709 if ( !(event.Dragging() || event.ButtonDown() || event.LeftUp() ||
2710 event.ButtonDClick()) )
2711 return;
2712
2713 int x = event.GetX();
2714 int y = event.GetY();
2715 CalcUnscrolledPosition( x, y, &x, &y );
2716
2717 // where did we hit it (if we did)?
2718 long hitResult = 0;
2719
2720 size_t count = GetItemCount(),
2721 current;
2722
2723 if ( HasFlag(wxLC_REPORT) )
2724 {
2725 current = y / GetLineHeight();
2726 if ( current < count )
2727 hitResult = HitTestLine(current, x, y);
2728 }
2729 else // !report
2730 {
2731 // TODO: optimize it too! this is less simple than for report view but
2732 // enumerating all items is still not a way to do it!!
2733 for ( current = 0; current < count; current++ )
2734 {
2735 hitResult = HitTestLine(current, x, y);
2736 if ( hitResult )
2737 break;
2738 }
2739 }
2740
2741 if (event.Dragging())
2742 {
2743 if (m_dragCount == 0)
2744 m_dragStart = wxPoint(x,y);
2745
2746 m_dragCount++;
2747
2748 if (m_dragCount != 3)
2749 return;
2750
2751 int command = event.RightIsDown() ? wxEVT_COMMAND_LIST_BEGIN_RDRAG
2752 : wxEVT_COMMAND_LIST_BEGIN_DRAG;
2753
2754 wxListEvent le( command, GetParent()->GetId() );
2755 le.SetEventObject( GetParent() );
2756 le.m_pointDrag = m_dragStart;
2757 GetParent()->GetEventHandler()->ProcessEvent( le );
2758
2759 return;
2760 }
2761 else
2762 {
2763 m_dragCount = 0;
2764 }
2765
2766 if ( !hitResult )
2767 {
2768 // outside of any item
2769 return;
2770 }
2771
2772 bool forceClick = FALSE;
2773 if (event.ButtonDClick())
2774 {
2775 m_renameTimer->Stop();
2776 m_lastOnSame = FALSE;
2777
2778 if ( current == m_lineBeforeLastClicked )
2779 {
2780 SendNotify( current, wxEVT_COMMAND_LIST_ITEM_ACTIVATED );
2781
2782 return;
2783 }
2784 else
2785 {
2786 // the first click was on another item, so don't interpret this as
2787 // a double click, but as a simple click instead
2788 forceClick = TRUE;
2789 }
2790 }
2791
2792 if (event.LeftUp() && m_lastOnSame)
2793 {
2794 if ((current == m_current) &&
2795 (hitResult == wxLIST_HITTEST_ONITEMLABEL) &&
2796 HasFlag(wxLC_EDIT_LABELS) )
2797 {
2798 m_renameTimer->Start( 100, TRUE );
2799 }
2800 m_lastOnSame = FALSE;
2801 }
2802 else if (event.RightDown())
2803 {
2804 SendNotify( current, wxEVT_COMMAND_LIST_ITEM_RIGHT_CLICK,
2805 event.GetPosition() );
2806 }
2807 else if (event.MiddleDown())
2808 {
2809 SendNotify( current, wxEVT_COMMAND_LIST_ITEM_MIDDLE_CLICK );
2810 }
2811 else if ( event.LeftDown() || forceClick )
2812 {
2813 m_lineBeforeLastClicked = m_lineLastClicked;
2814 m_lineLastClicked = current;
2815
2816 size_t oldCurrent = m_current;
2817
2818 if ( IsSingleSel() || !(event.ControlDown() || event.ShiftDown()) )
2819 {
2820 HighlightAll( FALSE );
2821 m_current = current;
2822
2823 ReverseHighlight(m_current);
2824 }
2825 else // multi sel & either ctrl or shift is down
2826 {
2827 if (event.ControlDown())
2828 {
2829 m_current = current;
2830
2831 ReverseHighlight(m_current);
2832 }
2833 else if (event.ShiftDown())
2834 {
2835 m_current = current;
2836
2837 size_t lineFrom = oldCurrent,
2838 lineTo = current;
2839
2840 if ( lineTo < lineFrom )
2841 {
2842 lineTo = lineFrom;
2843 lineFrom = m_current;
2844 }
2845
2846 HighlightLines(lineFrom, lineTo);
2847 }
2848 else // !ctrl, !shift
2849 {
2850 // test in the enclosing if should make it impossible
2851 wxFAIL_MSG( _T("how did we get here?") );
2852 }
2853 }
2854
2855 if (m_current != oldCurrent)
2856 {
2857 RefreshLine( oldCurrent );
2858 OnUnfocusLine( oldCurrent );
2859 OnFocusLine( m_current );
2860 }
2861
2862 // forceClick is only set if the previous click was on another item
2863 m_lastOnSame = !forceClick && (m_current == oldCurrent);
2864 }
2865 }
2866
2867 void wxListMainWindow::MoveToFocus()
2868 {
2869 if ( !HasCurrent() )
2870 return;
2871
2872 wxRect rect = GetLineRect(m_current);
2873
2874 int client_w, client_h;
2875 GetClientSize( &client_w, &client_h );
2876
2877 int view_x = m_xScroll*GetScrollPos( wxHORIZONTAL );
2878 int view_y = m_yScroll*GetScrollPos( wxVERTICAL );
2879
2880 if ( HasFlag(wxLC_REPORT) )
2881 {
2882 // the next we need the range of lines shown it might be different, so
2883 // recalculate it
2884 ResetVisibleLinesRange();
2885
2886 if (rect.y < view_y )
2887 Scroll( -1, rect.y/m_yScroll );
2888 if (rect.y+rect.height+5 > view_y+client_h)
2889 Scroll( -1, (rect.y+rect.height-client_h+SCROLL_UNIT_Y)/m_yScroll );
2890 }
2891 else // !report
2892 {
2893 if (rect.x-view_x < 5)
2894 Scroll( (rect.x-5)/m_xScroll, -1 );
2895 if (rect.x+rect.width-5 > view_x+client_w)
2896 Scroll( (rect.x+rect.width-client_w+SCROLL_UNIT_X)/m_xScroll, -1 );
2897 }
2898 }
2899
2900 // ----------------------------------------------------------------------------
2901 // keyboard handling
2902 // ----------------------------------------------------------------------------
2903
2904 void wxListMainWindow::OnArrowChar(size_t newCurrent, const wxKeyEvent& event)
2905 {
2906 wxCHECK_RET( newCurrent < (size_t)GetItemCount(),
2907 _T("invalid item index in OnArrowChar()") );
2908
2909 size_t oldCurrent = m_current;
2910
2911 // in single selection we just ignore Shift as we can't select several
2912 // items anyhow
2913 if ( event.ShiftDown() && !IsSingleSel() )
2914 {
2915 m_current = newCurrent;
2916
2917 // select all the items between the old and the new one
2918 if ( oldCurrent > newCurrent )
2919 {
2920 newCurrent = oldCurrent;
2921 oldCurrent = m_current;
2922 }
2923
2924 HighlightLines(oldCurrent, newCurrent);
2925 }
2926 else // !shift
2927 {
2928 // all previously selected items are unselected unless ctrl is held
2929 if ( !event.ControlDown() )
2930 HighlightAll(FALSE);
2931
2932 m_current = newCurrent;
2933
2934 HighlightLine( oldCurrent, FALSE );
2935 RefreshLine( oldCurrent );
2936
2937 if ( !event.ControlDown() )
2938 {
2939 HighlightLine( m_current, TRUE );
2940 }
2941 }
2942
2943 OnUnfocusLine( oldCurrent );
2944 OnFocusLine( m_current );
2945 RefreshLine( m_current );
2946
2947 MoveToFocus();
2948 }
2949
2950 void wxListMainWindow::OnKeyDown( wxKeyEvent &event )
2951 {
2952 wxWindow *parent = GetParent();
2953
2954 /* we propagate the key event up */
2955 wxKeyEvent ke( wxEVT_KEY_DOWN );
2956 ke.m_shiftDown = event.m_shiftDown;
2957 ke.m_controlDown = event.m_controlDown;
2958 ke.m_altDown = event.m_altDown;
2959 ke.m_metaDown = event.m_metaDown;
2960 ke.m_keyCode = event.m_keyCode;
2961 ke.m_x = event.m_x;
2962 ke.m_y = event.m_y;
2963 ke.SetEventObject( parent );
2964 if (parent->GetEventHandler()->ProcessEvent( ke )) return;
2965
2966 event.Skip();
2967 }
2968
2969 void wxListMainWindow::OnChar( wxKeyEvent &event )
2970 {
2971 wxWindow *parent = GetParent();
2972
2973 /* we send a list_key event up */
2974 if ( HasCurrent() )
2975 {
2976 wxListEvent le( wxEVT_COMMAND_LIST_KEY_DOWN, GetParent()->GetId() );
2977 le.m_itemIndex = m_current;
2978 GetLine(m_current)->GetItem( 0, le.m_item );
2979 le.m_code = (int)event.KeyCode();
2980 le.SetEventObject( parent );
2981 parent->GetEventHandler()->ProcessEvent( le );
2982 }
2983
2984 /* we propagate the char event up */
2985 wxKeyEvent ke( wxEVT_CHAR );
2986 ke.m_shiftDown = event.m_shiftDown;
2987 ke.m_controlDown = event.m_controlDown;
2988 ke.m_altDown = event.m_altDown;
2989 ke.m_metaDown = event.m_metaDown;
2990 ke.m_keyCode = event.m_keyCode;
2991 ke.m_x = event.m_x;
2992 ke.m_y = event.m_y;
2993 ke.SetEventObject( parent );
2994 if (parent->GetEventHandler()->ProcessEvent( ke )) return;
2995
2996 if (event.KeyCode() == WXK_TAB)
2997 {
2998 wxNavigationKeyEvent nevent;
2999 nevent.SetWindowChange( event.ControlDown() );
3000 nevent.SetDirection( !event.ShiftDown() );
3001 nevent.SetEventObject( GetParent()->GetParent() );
3002 nevent.SetCurrentFocus( m_parent );
3003 if (GetParent()->GetParent()->GetEventHandler()->ProcessEvent( nevent )) return;
3004 }
3005
3006 /* no item -> nothing to do */
3007 if (!HasCurrent())
3008 {
3009 event.Skip();
3010 return;
3011 }
3012
3013 switch (event.KeyCode())
3014 {
3015 case WXK_UP:
3016 if ( m_current > 0 )
3017 OnArrowChar( m_current - 1, event );
3018 break;
3019
3020 case WXK_DOWN:
3021 if ( m_current < (size_t)GetItemCount() - 1 )
3022 OnArrowChar( m_current + 1, event );
3023 break;
3024
3025 case WXK_END:
3026 if (!IsEmpty())
3027 OnArrowChar( GetItemCount() - 1, event );
3028 break;
3029
3030 case WXK_HOME:
3031 if (!IsEmpty())
3032 OnArrowChar( 0, event );
3033 break;
3034
3035 case WXK_PRIOR:
3036 {
3037 int steps = 0;
3038 if ( HasFlag(wxLC_REPORT) )
3039 {
3040 steps = m_linesPerPage - 1;
3041 }
3042 else
3043 {
3044 steps = m_current % m_linesPerPage;
3045 }
3046
3047 int index = m_current - steps;
3048 if (index < 0)
3049 index = 0;
3050
3051 OnArrowChar( index, event );
3052 }
3053 break;
3054
3055 case WXK_NEXT:
3056 {
3057 int steps = 0;
3058 if ( HasFlag(wxLC_REPORT) )
3059 {
3060 steps = m_linesPerPage - 1;
3061 }
3062 else
3063 {
3064 steps = m_linesPerPage - (m_current % m_linesPerPage) - 1;
3065 }
3066
3067 size_t index = m_current + steps;
3068 size_t count = GetItemCount();
3069 if ( index >= count )
3070 index = count - 1;
3071
3072 OnArrowChar( index, event );
3073 }
3074 break;
3075
3076 case WXK_LEFT:
3077 if ( !HasFlag(wxLC_REPORT) )
3078 {
3079 int index = m_current - m_linesPerPage;
3080 if (index < 0)
3081 index = 0;
3082
3083 OnArrowChar( index, event );
3084 }
3085 break;
3086
3087 case WXK_RIGHT:
3088 if ( !HasFlag(wxLC_REPORT) )
3089 {
3090 size_t index = m_current + m_linesPerPage;
3091
3092 size_t count = GetItemCount();
3093 if ( index >= count )
3094 index = count - 1;
3095
3096 OnArrowChar( index, event );
3097 }
3098 break;
3099
3100 case WXK_SPACE:
3101 if ( IsSingleSel() )
3102 {
3103 wxListEvent le( wxEVT_COMMAND_LIST_ITEM_ACTIVATED,
3104 GetParent()->GetId() );
3105 le.SetEventObject( GetParent() );
3106 le.m_itemIndex = m_current;
3107 GetLine(m_current)->GetItem( 0, le.m_item );
3108 GetParent()->GetEventHandler()->ProcessEvent( le );
3109 }
3110 else
3111 {
3112 ReverseHighlight(m_current);
3113 }
3114 break;
3115
3116 case WXK_RETURN:
3117 case WXK_EXECUTE:
3118 {
3119 wxListEvent le( wxEVT_COMMAND_LIST_ITEM_ACTIVATED,
3120 GetParent()->GetId() );
3121 le.SetEventObject( GetParent() );
3122 le.m_itemIndex = m_current;
3123 GetLine(m_current)->GetItem( 0, le.m_item );
3124 GetParent()->GetEventHandler()->ProcessEvent( le );
3125 }
3126 break;
3127
3128 default:
3129 event.Skip();
3130 }
3131 }
3132
3133 // ----------------------------------------------------------------------------
3134 // focus handling
3135 // ----------------------------------------------------------------------------
3136
3137 #ifdef __WXGTK__
3138 extern wxWindow *g_focusWindow;
3139 #endif
3140
3141 void wxListMainWindow::OnSetFocus( wxFocusEvent &WXUNUSED(event) )
3142 {
3143 m_hasFocus = TRUE;
3144
3145 if ( HasCurrent() )
3146 RefreshLine( m_current );
3147
3148 if (!GetParent())
3149 return;
3150
3151 #ifdef __WXGTK__
3152 g_focusWindow = GetParent();
3153 #endif
3154
3155 wxFocusEvent event( wxEVT_SET_FOCUS, GetParent()->GetId() );
3156 event.SetEventObject( GetParent() );
3157 GetParent()->GetEventHandler()->ProcessEvent( event );
3158 }
3159
3160 void wxListMainWindow::OnKillFocus( wxFocusEvent &WXUNUSED(event) )
3161 {
3162 m_hasFocus = FALSE;
3163
3164 if ( HasCurrent() )
3165 RefreshLine( m_current );
3166 }
3167
3168 void wxListMainWindow::DrawImage( int index, wxDC *dc, int x, int y )
3169 {
3170 if ( HasFlag(wxLC_ICON) && (m_normal_image_list))
3171 {
3172 m_normal_image_list->Draw( index, *dc, x, y, wxIMAGELIST_DRAW_TRANSPARENT );
3173 }
3174 else if ( HasFlag(wxLC_SMALL_ICON) && (m_small_image_list))
3175 {
3176 m_small_image_list->Draw( index, *dc, x, y, wxIMAGELIST_DRAW_TRANSPARENT );
3177 }
3178 else if ( HasFlag(wxLC_LIST) && (m_small_image_list))
3179 {
3180 m_small_image_list->Draw( index, *dc, x, y, wxIMAGELIST_DRAW_TRANSPARENT );
3181 }
3182 else if ( HasFlag(wxLC_REPORT) && (m_small_image_list))
3183 {
3184 m_small_image_list->Draw( index, *dc, x, y, wxIMAGELIST_DRAW_TRANSPARENT );
3185 }
3186 }
3187
3188 void wxListMainWindow::GetImageSize( int index, int &width, int &height ) const
3189 {
3190 if ( HasFlag(wxLC_ICON) && m_normal_image_list )
3191 {
3192 m_normal_image_list->GetSize( index, width, height );
3193 }
3194 else if ( HasFlag(wxLC_SMALL_ICON) && m_small_image_list )
3195 {
3196 m_small_image_list->GetSize( index, width, height );
3197 }
3198 else if ( HasFlag(wxLC_LIST) && m_small_image_list )
3199 {
3200 m_small_image_list->GetSize( index, width, height );
3201 }
3202 else if ( HasFlag(wxLC_REPORT) && m_small_image_list )
3203 {
3204 m_small_image_list->GetSize( index, width, height );
3205 }
3206 else
3207 {
3208 width =
3209 height = 0;
3210 }
3211 }
3212
3213 int wxListMainWindow::GetTextLength( const wxString &s ) const
3214 {
3215 wxClientDC dc( wxConstCast(this, wxListMainWindow) );
3216 dc.SetFont( GetFont() );
3217
3218 wxCoord lw;
3219 dc.GetTextExtent( s, &lw, NULL );
3220
3221 return lw + AUTOSIZE_COL_MARGIN;
3222 }
3223
3224 void wxListMainWindow::SetImageList( wxImageList *imageList, int which )
3225 {
3226 m_dirty = TRUE;
3227
3228 // calc the spacing from the icon size
3229 int width = 0,
3230 height = 0;
3231 if ((imageList) && (imageList->GetImageCount()) )
3232 {
3233 imageList->GetSize(0, width, height);
3234 }
3235
3236 if (which == wxIMAGE_LIST_NORMAL)
3237 {
3238 m_normal_image_list = imageList;
3239 m_normal_spacing = width + 8;
3240 }
3241
3242 if (which == wxIMAGE_LIST_SMALL)
3243 {
3244 m_small_image_list = imageList;
3245 m_small_spacing = width + 14;
3246 }
3247 }
3248
3249 void wxListMainWindow::SetItemSpacing( int spacing, bool isSmall )
3250 {
3251 m_dirty = TRUE;
3252 if (isSmall)
3253 {
3254 m_small_spacing = spacing;
3255 }
3256 else
3257 {
3258 m_normal_spacing = spacing;
3259 }
3260 }
3261
3262 int wxListMainWindow::GetItemSpacing( bool isSmall )
3263 {
3264 return isSmall ? m_small_spacing : m_normal_spacing;
3265 }
3266
3267 // ----------------------------------------------------------------------------
3268 // columns
3269 // ----------------------------------------------------------------------------
3270
3271 void wxListMainWindow::SetColumn( int col, wxListItem &item )
3272 {
3273 wxListHeaderDataList::Node *node = m_columns.Item( col );
3274
3275 wxCHECK_RET( node, _T("invalid column index in SetColumn") );
3276
3277 if ( item.m_width == wxLIST_AUTOSIZE_USEHEADER )
3278 item.m_width = GetTextLength( item.m_text );
3279
3280 wxListHeaderData *column = node->GetData();
3281 column->SetItem( item );
3282
3283 wxListHeaderWindow *headerWin = GetListCtrl()->m_headerWin;
3284 if ( headerWin )
3285 headerWin->m_dirty = TRUE;
3286
3287 m_dirty = TRUE;
3288
3289 // invalidate it as it has to be recalculated
3290 m_headerWidth = 0;
3291 }
3292
3293 void wxListMainWindow::SetColumnWidth( int col, int width )
3294 {
3295 wxCHECK_RET( col >= 0 && col < GetColumnCount(),
3296 _T("invalid column index") );
3297
3298 wxCHECK_RET( HasFlag(wxLC_REPORT),
3299 _T("SetColumnWidth() can only be called in report mode.") );
3300
3301 m_dirty = TRUE;
3302
3303 wxListHeaderDataList::Node *node = m_columns.Item( col );
3304 wxCHECK_RET( node, _T("no column?") );
3305
3306 wxListHeaderData *column = node->GetData();
3307
3308 size_t count = GetItemCount();
3309
3310 if (width == wxLIST_AUTOSIZE_USEHEADER)
3311 {
3312 width = GetTextLength(column->GetText());
3313 }
3314 else if ( width == wxLIST_AUTOSIZE )
3315 {
3316 if ( IsVirtual() )
3317 {
3318 // TODO: determine the max width somehow...
3319 width = WIDTH_COL_DEFAULT;
3320 }
3321 else // !virtual
3322 {
3323 wxClientDC dc(this);
3324 dc.SetFont( GetFont() );
3325
3326 int max = AUTOSIZE_COL_MARGIN;
3327
3328 for ( size_t i = 0; i < count; i++ )
3329 {
3330 wxListLineData *line = GetLine(i);
3331 wxListItemDataList::Node *n = line->m_items.Item( col );
3332
3333 wxCHECK_RET( n, _T("no subitem?") );
3334
3335 wxListItemData *item = n->GetData();
3336 int current = 0;
3337
3338 if (item->HasImage())
3339 {
3340 int ix, iy;
3341 GetImageSize( item->GetImage(), ix, iy );
3342 current += ix + 5;
3343 }
3344
3345 if (item->HasText())
3346 {
3347 wxCoord w;
3348 dc.GetTextExtent( item->GetText(), &w, NULL );
3349 current += w;
3350 }
3351
3352 if (current > max)
3353 max = current;
3354 }
3355
3356 width = max + AUTOSIZE_COL_MARGIN;
3357 }
3358 }
3359
3360 column->SetWidth( width );
3361
3362 // invalidate it as it has to be recalculated
3363 m_headerWidth = 0;
3364 }
3365
3366 int wxListMainWindow::GetHeaderWidth() const
3367 {
3368 if ( !m_headerWidth )
3369 {
3370 wxListMainWindow *self = wxConstCast(this, wxListMainWindow);
3371
3372 size_t count = GetColumnCount();
3373 for ( size_t col = 0; col < count; col++ )
3374 {
3375 self->m_headerWidth += GetColumnWidth(col);
3376 }
3377 }
3378
3379 return m_headerWidth;
3380 }
3381
3382 void wxListMainWindow::GetColumn( int col, wxListItem &item ) const
3383 {
3384 wxListHeaderDataList::Node *node = m_columns.Item( col );
3385 wxCHECK_RET( node, _T("invalid column index in GetColumn") );
3386
3387 wxListHeaderData *column = node->GetData();
3388 column->GetItem( item );
3389 }
3390
3391 int wxListMainWindow::GetColumnWidth( int col ) const
3392 {
3393 wxListHeaderDataList::Node *node = m_columns.Item( col );
3394 wxCHECK_MSG( node, 0, _T("invalid column index") );
3395
3396 wxListHeaderData *column = node->GetData();
3397 return column->GetWidth();
3398 }
3399
3400 // ----------------------------------------------------------------------------
3401 // item state
3402 // ----------------------------------------------------------------------------
3403
3404 void wxListMainWindow::SetItem( wxListItem &item )
3405 {
3406 long id = item.m_itemId;
3407 wxCHECK_RET( id >= 0 && (size_t)id < GetItemCount(),
3408 _T("invalid item index in SetItem") );
3409
3410 if ( !IsVirtual() )
3411 {
3412 wxListLineData *line = GetLine((size_t)id);
3413 line->SetItem( item.m_col, item );
3414 }
3415
3416 if ( InReportView() )
3417 {
3418 // just refresh the line to show the new value of the text/image
3419 RefreshLine((size_t)id);
3420 }
3421 else // !report
3422 {
3423 // refresh everything (resulting in horrible flicker - FIXME!)
3424 m_dirty = TRUE;
3425 }
3426 }
3427
3428 void wxListMainWindow::SetItemState( long litem, long state, long stateMask )
3429 {
3430 wxCHECK_RET( litem >= 0 && (size_t)litem < GetItemCount(),
3431 _T("invalid list ctrl item index in SetItem") );
3432
3433 size_t oldCurrent = m_current;
3434 size_t item = (size_t)litem; // sdafe because of the check above
3435
3436 if ( stateMask & wxLIST_STATE_FOCUSED )
3437 {
3438 if ( state & wxLIST_STATE_FOCUSED )
3439 {
3440 // don't do anything if this item is already focused
3441 if ( item != m_current )
3442 {
3443 OnUnfocusLine( m_current );
3444 m_current = item;
3445 OnFocusLine( m_current );
3446
3447 if ( IsSingleSel() && (oldCurrent != (size_t)-1) )
3448 {
3449 HighlightLine(oldCurrent, FALSE);
3450 RefreshLine(oldCurrent);
3451 }
3452
3453 RefreshLine( m_current );
3454 }
3455 }
3456 else // unfocus
3457 {
3458 // don't do anything if this item is not focused
3459 if ( item == m_current )
3460 {
3461 OnUnfocusLine( m_current );
3462 m_current = (size_t)-1;
3463 }
3464 }
3465 }
3466
3467 if ( stateMask & wxLIST_STATE_SELECTED )
3468 {
3469 bool on = (state & wxLIST_STATE_SELECTED) != 0;
3470
3471 if ( IsSingleSel() )
3472 {
3473 if ( on )
3474 {
3475 // selecting the item also makes it the focused one in the
3476 // single sel mode
3477 if ( m_current != item )
3478 {
3479 OnUnfocusLine( m_current );
3480 m_current = item;
3481 OnFocusLine( m_current );
3482
3483 if ( oldCurrent != (size_t)-1 )
3484 {
3485 HighlightLine( oldCurrent, FALSE );
3486 RefreshLine( oldCurrent );
3487 }
3488 }
3489 }
3490 else // off
3491 {
3492 // only the current item may be selected anyhow
3493 if ( item != m_current )
3494 return;
3495 }
3496 }
3497
3498 if ( HighlightLine(item, on) )
3499 {
3500 RefreshLine(item);
3501 }
3502 }
3503 }
3504
3505 int wxListMainWindow::GetItemState( long item, long stateMask )
3506 {
3507 wxCHECK_MSG( item >= 0 && (size_t)item < GetItemCount(), 0,
3508 _T("invalid list ctrl item index in GetItemState()") );
3509
3510 int ret = wxLIST_STATE_DONTCARE;
3511
3512 if ( stateMask & wxLIST_STATE_FOCUSED )
3513 {
3514 if ( (size_t)item == m_current )
3515 ret |= wxLIST_STATE_FOCUSED;
3516 }
3517
3518 if ( stateMask & wxLIST_STATE_SELECTED )
3519 {
3520 if ( IsHighlighted(item) )
3521 ret |= wxLIST_STATE_SELECTED;
3522 }
3523
3524 return ret;
3525 }
3526
3527 void wxListMainWindow::GetItem( wxListItem &item )
3528 {
3529 wxCHECK_RET( item.m_itemId >= 0 && (size_t)item.m_itemId < GetItemCount(),
3530 _T("invalid item index in GetItem") );
3531
3532 wxListLineData *line = GetLine((size_t)item.m_itemId);
3533 line->GetItem( item.m_col, item );
3534 }
3535
3536 // ----------------------------------------------------------------------------
3537 // item count
3538 // ----------------------------------------------------------------------------
3539
3540 size_t wxListMainWindow::GetItemCount() const
3541 {
3542 return IsVirtual() ? m_countVirt : m_lines.GetCount();
3543 }
3544
3545 void wxListMainWindow::SetItemCount(long count)
3546 {
3547 m_selStore.SetItemCount(count);
3548 m_countVirt = count;
3549
3550 ResetVisibleLinesRange();
3551
3552 Refresh();
3553 }
3554
3555 int wxListMainWindow::GetSelectedItemCount()
3556 {
3557 // deal with the quick case first
3558 if ( IsSingleSel() )
3559 {
3560 return HasCurrent() ? IsHighlighted(m_current) : FALSE;
3561 }
3562
3563 // virtual controls remmebers all its selections itself
3564 if ( IsVirtual() )
3565 return m_selStore.GetSelectedCount();
3566
3567 // TODO: we probably should maintain the number of items selected even for
3568 // non virtual controls as enumerating all lines is really slow...
3569 size_t countSel = 0;
3570 size_t count = GetItemCount();
3571 for ( size_t line = 0; line < count; line++ )
3572 {
3573 if ( GetLine(line)->IsHighlighted() )
3574 countSel++;
3575 }
3576
3577 return countSel;
3578 }
3579
3580 // ----------------------------------------------------------------------------
3581 // item position/size
3582 // ----------------------------------------------------------------------------
3583
3584 void wxListMainWindow::GetItemRect( long index, wxRect &rect )
3585 {
3586 wxCHECK_RET( index >= 0 && (size_t)index < GetItemCount(),
3587 _T("invalid index in GetItemRect") );
3588
3589 rect = GetLineRect((size_t)index);
3590
3591 CalcScrolledPosition(rect.x, rect.y, &rect.x, &rect.y);
3592 }
3593
3594 bool wxListMainWindow::GetItemPosition(long item, wxPoint& pos)
3595 {
3596 wxRect rect;
3597 GetItemRect(item, rect);
3598
3599 pos.x = rect.x;
3600 pos.y = rect.y;
3601
3602 return TRUE;
3603 }
3604
3605 // ----------------------------------------------------------------------------
3606 // geometry calculation
3607 // ----------------------------------------------------------------------------
3608
3609 void wxListMainWindow::RecalculatePositions()
3610 {
3611 if ( IsEmpty() )
3612 return;
3613
3614 wxClientDC dc( this );
3615 dc.SetFont( GetFont() );
3616
3617 int iconSpacing;
3618 if ( HasFlag(wxLC_ICON) )
3619 iconSpacing = m_normal_spacing;
3620 else if ( HasFlag(wxLC_SMALL_ICON) )
3621 iconSpacing = m_small_spacing;
3622 else
3623 iconSpacing = 0;
3624
3625 int clientWidth,
3626 clientHeight;
3627 GetClientSize( &clientWidth, &clientHeight );
3628
3629 if ( HasFlag(wxLC_REPORT) )
3630 {
3631 // all lines have the same height
3632 int lineHeight = GetLineHeight();
3633
3634 // scroll one line per step
3635 m_yScroll = lineHeight;
3636
3637 size_t lineCount = GetItemCount();
3638 int entireHeight = lineCount*lineHeight + LINE_SPACING;
3639
3640 m_linesPerPage = clientHeight / lineHeight;
3641
3642 ResetVisibleLinesRange();
3643
3644 SetScrollbars( m_xScroll, m_yScroll,
3645 (GetHeaderWidth() + m_xScroll - 1)/m_xScroll,
3646 (entireHeight + m_yScroll - 1)/m_yScroll,
3647 GetScrollPos(wxHORIZONTAL),
3648 GetScrollPos(wxVERTICAL),
3649 TRUE );
3650 }
3651 else // !report
3652 {
3653 // at first we try without any scrollbar. if the items don't
3654 // fit into the window, we recalculate after subtracting an
3655 // approximated 15 pt for the horizontal scrollbar
3656
3657 clientHeight -= 4; // sunken frame
3658
3659 int entireWidth = 0;
3660
3661 for (int tries = 0; tries < 2; tries++)
3662 {
3663 entireWidth = 0;
3664 int x = 2;
3665 int y = 2;
3666 int maxWidth = 0;
3667 m_linesPerPage = 0;
3668 int currentlyVisibleLines = 0;
3669
3670 size_t count = GetItemCount();
3671 for (size_t i = 0; i < count; i++)
3672 {
3673 currentlyVisibleLines++;
3674 wxListLineData *line = GetLine(i);
3675 line->CalculateSize( &dc, iconSpacing );
3676 line->SetPosition( x, y, clientWidth, iconSpacing );
3677
3678 wxSize sizeLine = GetLineSize(i);
3679
3680 if ( maxWidth < sizeLine.x )
3681 maxWidth = sizeLine.x;
3682
3683 y += sizeLine.y;
3684 if (currentlyVisibleLines > m_linesPerPage)
3685 m_linesPerPage = currentlyVisibleLines;
3686
3687 // assume that the size of the next one is the same... (FIXME)
3688 if ( y + sizeLine.y - 6 >= clientHeight )
3689 {
3690 currentlyVisibleLines = 0;
3691 y = 2;
3692 x += maxWidth+6;
3693 entireWidth += maxWidth+6;
3694 maxWidth = 0;
3695 }
3696 if ( i == count - 1 )
3697 entireWidth += maxWidth;
3698 if ((tries == 0) && (entireWidth > clientWidth))
3699 {
3700 clientHeight -= 15; // scrollbar height
3701 m_linesPerPage = 0;
3702 currentlyVisibleLines = 0;
3703 break;
3704 }
3705 if ( i == count - 1 )
3706 tries = 1; // everything fits, no second try required
3707 }
3708 }
3709
3710 int scroll_pos = GetScrollPos( wxHORIZONTAL );
3711 SetScrollbars( m_xScroll, m_yScroll, (entireWidth+SCROLL_UNIT_X) / m_xScroll, 0, scroll_pos, 0, TRUE );
3712 }
3713
3714 // FIXME: why should we call it from here?
3715 UpdateCurrent();
3716
3717 RefreshAll();
3718 }
3719
3720 void wxListMainWindow::RefreshAll()
3721 {
3722 m_dirty = FALSE;
3723 Refresh();
3724
3725 wxListHeaderWindow *headerWin = GetListCtrl()->m_headerWin;
3726 if ( headerWin )
3727 {
3728 headerWin->m_dirty = FALSE;
3729 headerWin->Refresh();
3730 }
3731 }
3732
3733 void wxListMainWindow::UpdateCurrent()
3734 {
3735 if ( !HasCurrent() && !IsEmpty() )
3736 {
3737 m_current = 0;
3738 }
3739
3740 if ( m_current != (size_t)-1 )
3741 {
3742 OnFocusLine( m_current );
3743 }
3744 }
3745
3746 long wxListMainWindow::GetNextItem( long item,
3747 int WXUNUSED(geometry),
3748 int state )
3749 {
3750 long ret = item,
3751 max = GetItemCount();
3752 wxCHECK_MSG( (ret == -1) || (ret < max), -1,
3753 _T("invalid listctrl index in GetNextItem()") );
3754
3755 // notice that we start with the next item (or the first one if item == -1)
3756 // and this is intentional to allow writing a simple loop to iterate over
3757 // all selected items
3758 ret++;
3759 if ( ret == max )
3760 {
3761 // this is not an error because the index was ok initially, just no
3762 // such item
3763 return -1;
3764 }
3765
3766 if ( !state )
3767 {
3768 // any will do
3769 return (size_t)ret;
3770 }
3771
3772 size_t count = GetItemCount();
3773 for ( size_t line = (size_t)ret; line < count; line++ )
3774 {
3775 if ( (state & wxLIST_STATE_FOCUSED) && (line == m_current) )
3776 return line;
3777
3778 if ( (state & wxLIST_STATE_SELECTED) && IsHighlighted(line) )
3779 return line;
3780 }
3781
3782 return -1;
3783 }
3784
3785 // ----------------------------------------------------------------------------
3786 // deleting stuff
3787 // ----------------------------------------------------------------------------
3788
3789 void wxListMainWindow::DeleteItem( long lindex )
3790 {
3791 size_t count = GetItemCount();
3792
3793 wxCHECK_RET( (lindex >= 0) && ((size_t)lindex < count),
3794 _T("invalid item index in DeleteItem") );
3795
3796 size_t index = (size_t)lindex;
3797
3798 m_dirty = TRUE;
3799
3800 // select the next item when the selected one is deleted
3801 if ( m_current == index )
3802 {
3803 // the last valid index after deleting the item will be count-2
3804 if ( m_current == count - 1 )
3805 {
3806 m_current--;
3807 }
3808 }
3809
3810 SendNotify( index, wxEVT_COMMAND_LIST_DELETE_ITEM );
3811
3812 if ( InReportView() )
3813 {
3814 ResetVisibleLinesRange();
3815 }
3816
3817 if ( IsVirtual() )
3818 {
3819 m_countVirt--;
3820
3821 m_selStore.OnItemDelete(index);
3822 }
3823 else
3824 {
3825 m_lines.RemoveAt( index );
3826 }
3827
3828 m_dirty = TRUE;
3829 RefreshAfter(index);
3830 }
3831
3832 void wxListMainWindow::DeleteColumn( int col )
3833 {
3834 wxListHeaderDataList::Node *node = m_columns.Item( col );
3835
3836 wxCHECK_RET( node, wxT("invalid column index in DeleteColumn()") );
3837
3838 m_dirty = TRUE;
3839 m_columns.DeleteNode( node );
3840 }
3841
3842 void wxListMainWindow::DeleteAllItems()
3843 {
3844 if ( IsEmpty() )
3845 {
3846 // nothing to do - in particular, don't send the event
3847 return;
3848 }
3849
3850 ResetCurrent();
3851
3852 // to make the deletion of all items faster, we don't send the
3853 // notifications for each item deletion in this case but only one event
3854 // for all of them: this is compatible with wxMSW and documented in
3855 // DeleteAllItems() description
3856
3857 wxListEvent event( wxEVT_COMMAND_LIST_DELETE_ALL_ITEMS, GetParent()->GetId() );
3858 event.SetEventObject( GetParent() );
3859 GetParent()->GetEventHandler()->ProcessEvent( event );
3860
3861 if ( IsVirtual() )
3862 {
3863 m_countVirt = 0;
3864
3865 m_selStore.Clear();
3866 }
3867
3868 if ( InReportView() )
3869 {
3870 ResetVisibleLinesRange();
3871 }
3872
3873 m_lines.Clear();
3874
3875 // NB: don't just set m_dirty to TRUE here as RecalculatePositions()
3876 // doesn't do anything if the control is empty and so we won't be
3877 // refreshed
3878 Refresh();
3879 }
3880
3881 void wxListMainWindow::DeleteEverything()
3882 {
3883 DeleteAllItems();
3884
3885 m_columns.Clear();
3886 }
3887
3888 // ----------------------------------------------------------------------------
3889 // scanning for an item
3890 // ----------------------------------------------------------------------------
3891
3892 void wxListMainWindow::EnsureVisible( long index )
3893 {
3894 wxCHECK_RET( index >= 0 && (size_t)index < GetItemCount(),
3895 _T("invalid index in EnsureVisible") );
3896
3897 // We have to call this here because the label in question might just have
3898 // been added and no screen update taken place.
3899 if (m_dirty)
3900 wxSafeYield();
3901
3902 size_t oldCurrent = m_current;
3903 m_current = (size_t)index;
3904 MoveToFocus();
3905 m_current = oldCurrent;
3906 }
3907
3908 long wxListMainWindow::FindItem(long start, const wxString& str, bool WXUNUSED(partial) )
3909 {
3910 long pos = start;
3911 wxString tmp = str;
3912 if (pos < 0)
3913 pos = 0;
3914
3915 size_t count = GetItemCount();
3916 for ( size_t i = (size_t)pos; i < count; i++ )
3917 {
3918 wxListLineData *line = GetLine(i);
3919 if ( line->GetText(0) == tmp )
3920 return i;
3921 }
3922
3923 return wxNOT_FOUND;
3924 }
3925
3926 long wxListMainWindow::FindItem(long start, long data)
3927 {
3928 long pos = start;
3929 if (pos < 0)
3930 pos = 0;
3931
3932 size_t count = GetItemCount();
3933 for (size_t i = (size_t)pos; i < count; i++)
3934 {
3935 wxListLineData *line = GetLine(i);
3936 wxListItem item;
3937 line->GetItem( 0, item );
3938 if (item.m_data == data)
3939 return i;
3940 }
3941
3942 return wxNOT_FOUND;
3943 }
3944
3945 long wxListMainWindow::HitTest( int x, int y, int &flags )
3946 {
3947 CalcUnscrolledPosition( x, y, &x, &y );
3948
3949 if ( HasFlag(wxLC_REPORT) )
3950 {
3951 size_t current = y / GetLineHeight();
3952 flags = HitTestLine(current, x, y);
3953 if ( flags )
3954 return current;
3955 }
3956 else // !report
3957 {
3958 // TODO: optimize it too! this is less simple than for report view but
3959 // enumerating all items is still not a way to do it!!
3960 size_t count = GetItemCount();
3961 for ( size_t current = 0; current < count; current++ )
3962 {
3963 flags = HitTestLine(current, x, y);
3964 if ( flags )
3965 return current;
3966 }
3967 }
3968
3969 return wxNOT_FOUND;
3970 }
3971
3972 // ----------------------------------------------------------------------------
3973 // adding stuff
3974 // ----------------------------------------------------------------------------
3975
3976 void wxListMainWindow::InsertItem( wxListItem &item )
3977 {
3978 wxASSERT_MSG( !IsVirtual(), _T("can't be used with virtual control") );
3979
3980 size_t count = GetItemCount();
3981 wxCHECK_RET( item.m_itemId >= 0 && (size_t)item.m_itemId <= count,
3982 _T("invalid item index") );
3983
3984 size_t id = item.m_itemId;
3985
3986 m_dirty = TRUE;
3987
3988 int mode = 0;
3989 if ( HasFlag(wxLC_REPORT) )
3990 mode = wxLC_REPORT;
3991 else if ( HasFlag(wxLC_LIST) )
3992 mode = wxLC_LIST;
3993 else if ( HasFlag(wxLC_ICON) )
3994 mode = wxLC_ICON;
3995 else if ( HasFlag(wxLC_SMALL_ICON) )
3996 mode = wxLC_ICON; // no typo
3997 else
3998 {
3999 wxFAIL_MSG( _T("unknown mode") );
4000 }
4001
4002 wxListLineData *line = new wxListLineData(this);
4003
4004 line->SetItem( 0, item );
4005
4006 m_lines.Insert( line, id );
4007
4008 m_dirty = TRUE;
4009 RefreshLines(id, GetItemCount() - 1);
4010 }
4011
4012 void wxListMainWindow::InsertColumn( long col, wxListItem &item )
4013 {
4014 m_dirty = TRUE;
4015 if ( HasFlag(wxLC_REPORT) )
4016 {
4017 if (item.m_width == wxLIST_AUTOSIZE_USEHEADER)
4018 item.m_width = GetTextLength( item.m_text );
4019 wxListHeaderData *column = new wxListHeaderData( item );
4020 if ((col >= 0) && (col < (int)m_columns.GetCount()))
4021 {
4022 wxListHeaderDataList::Node *node = m_columns.Item( col );
4023 m_columns.Insert( node, column );
4024 }
4025 else
4026 {
4027 m_columns.Append( column );
4028 }
4029 }
4030 }
4031
4032 // ----------------------------------------------------------------------------
4033 // sorting
4034 // ----------------------------------------------------------------------------
4035
4036 wxListCtrlCompare list_ctrl_compare_func_2;
4037 long list_ctrl_compare_data;
4038
4039 int LINKAGEMODE list_ctrl_compare_func_1( wxListLineData **arg1, wxListLineData **arg2 )
4040 {
4041 wxListLineData *line1 = *arg1;
4042 wxListLineData *line2 = *arg2;
4043 wxListItem item;
4044 line1->GetItem( 0, item );
4045 long data1 = item.m_data;
4046 line2->GetItem( 0, item );
4047 long data2 = item.m_data;
4048 return list_ctrl_compare_func_2( data1, data2, list_ctrl_compare_data );
4049 }
4050
4051 void wxListMainWindow::SortItems( wxListCtrlCompare fn, long data )
4052 {
4053 list_ctrl_compare_func_2 = fn;
4054 list_ctrl_compare_data = data;
4055 m_lines.Sort( list_ctrl_compare_func_1 );
4056 m_dirty = TRUE;
4057 }
4058
4059 // ----------------------------------------------------------------------------
4060 // scrolling
4061 // ----------------------------------------------------------------------------
4062
4063 void wxListMainWindow::OnScroll(wxScrollWinEvent& event)
4064 {
4065 // update our idea of which lines are shown when we redraw the window the
4066 // next time
4067 ResetVisibleLinesRange();
4068
4069 // FIXME
4070 #if defined(__WXGTK__) && !defined(__WXUNIVERSAL__)
4071 wxScrolledWindow::OnScroll(event);
4072 #else
4073 HandleOnScroll( event );
4074 #endif
4075
4076 if ( event.GetOrientation() == wxHORIZONTAL && HasHeader() )
4077 {
4078 wxListCtrl* lc = GetListCtrl();
4079 wxCHECK_RET( lc, _T("no listctrl window?") );
4080
4081 lc->m_headerWin->Refresh() ;
4082 #ifdef __WXMAC__
4083 lc->m_headerWin->MacUpdateImmediately() ;
4084 #endif
4085 }
4086 }
4087
4088 int wxListMainWindow::GetCountPerPage() const
4089 {
4090 if ( !m_linesPerPage )
4091 {
4092 wxConstCast(this, wxListMainWindow)->
4093 m_linesPerPage = GetClientSize().y / GetLineHeight();
4094 }
4095
4096 return m_linesPerPage;
4097 }
4098
4099 void wxListMainWindow::GetVisibleLinesRange(size_t *from, size_t *to)
4100 {
4101 wxASSERT_MSG( HasFlag(wxLC_REPORT), _T("this is for report mode only") );
4102
4103 if ( m_lineFrom == (size_t)-1 )
4104 {
4105 size_t count = GetItemCount();
4106 if ( count )
4107 {
4108 m_lineFrom = GetScrollPos(wxVERTICAL);
4109
4110 // this may happen if SetScrollbars() hadn't been called yet
4111 if ( m_lineFrom >= count )
4112 m_lineFrom = count - 1;
4113
4114 // we redraw one extra line but this is needed to make the redrawing
4115 // logic work when there is a fractional number of lines on screen
4116 m_lineTo = m_lineFrom + m_linesPerPage;
4117 if ( m_lineTo >= count )
4118 m_lineTo = count - 1;
4119 }
4120 else // empty control
4121 {
4122 m_lineFrom = 0;
4123 m_lineTo = (size_t)-1;
4124 }
4125 }
4126
4127 wxASSERT_MSG( IsEmpty() ||
4128 (m_lineFrom <= m_lineTo && m_lineTo < GetItemCount()),
4129 _T("GetVisibleLinesRange() returns incorrect result") );
4130
4131 if ( from )
4132 *from = m_lineFrom;
4133 if ( to )
4134 *to = m_lineTo;
4135 }
4136
4137 // -------------------------------------------------------------------------------------
4138 // wxListItem
4139 // -------------------------------------------------------------------------------------
4140
4141 IMPLEMENT_DYNAMIC_CLASS(wxListItem, wxObject)
4142
4143 wxListItem::wxListItem()
4144 {
4145 m_mask = 0;
4146 m_itemId = 0;
4147 m_col = 0;
4148 m_state = 0;
4149 m_stateMask = 0;
4150 m_image = 0;
4151 m_data = 0;
4152 m_format = wxLIST_FORMAT_CENTRE;
4153 m_width = 0;
4154
4155 m_attr = NULL;
4156 }
4157
4158 void wxListItem::Clear()
4159 {
4160 m_mask = 0;
4161 m_itemId = 0;
4162 m_col = 0;
4163 m_state = 0;
4164 m_stateMask = 0;
4165 m_image = 0;
4166 m_data = 0;
4167 m_format = wxLIST_FORMAT_CENTRE;
4168 m_width = 0;
4169 m_text = _T("");
4170
4171 ClearAttributes();
4172 }
4173
4174 void wxListItem::ClearAttributes()
4175 {
4176 if (m_attr)
4177 {
4178 delete m_attr;
4179 m_attr = NULL;
4180 }
4181 }
4182
4183 // -------------------------------------------------------------------------------------
4184 // wxListEvent
4185 // -------------------------------------------------------------------------------------
4186
4187 IMPLEMENT_DYNAMIC_CLASS(wxListEvent, wxNotifyEvent)
4188
4189 wxListEvent::wxListEvent( wxEventType commandType, int id )
4190 : wxNotifyEvent( commandType, id )
4191 {
4192 m_code = 0;
4193 m_itemIndex = 0;
4194 m_oldItemIndex = 0;
4195 m_col = 0;
4196 m_cancelled = FALSE;
4197 m_pointDrag.x = 0;
4198 m_pointDrag.y = 0;
4199 }
4200
4201 void wxListEvent::CopyObject(wxObject& object_dest) const
4202 {
4203 wxListEvent *obj = (wxListEvent *)&object_dest;
4204
4205 wxNotifyEvent::CopyObject(object_dest);
4206
4207 obj->m_code = m_code;
4208 obj->m_itemIndex = m_itemIndex;
4209 obj->m_oldItemIndex = m_oldItemIndex;
4210 obj->m_col = m_col;
4211 obj->m_cancelled = m_cancelled;
4212 obj->m_pointDrag = m_pointDrag;
4213 obj->m_item.m_mask = m_item.m_mask;
4214 obj->m_item.m_itemId = m_item.m_itemId;
4215 obj->m_item.m_col = m_item.m_col;
4216 obj->m_item.m_state = m_item.m_state;
4217 obj->m_item.m_stateMask = m_item.m_stateMask;
4218 obj->m_item.m_text = m_item.m_text;
4219 obj->m_item.m_image = m_item.m_image;
4220 obj->m_item.m_data = m_item.m_data;
4221 obj->m_item.m_format = m_item.m_format;
4222 obj->m_item.m_width = m_item.m_width;
4223
4224 if ( m_item.HasAttributes() )
4225 {
4226 obj->m_item.SetTextColour(m_item.GetTextColour());
4227 }
4228 }
4229
4230 // -------------------------------------------------------------------------------------
4231 // wxListCtrl
4232 // -------------------------------------------------------------------------------------
4233
4234 IMPLEMENT_DYNAMIC_CLASS(wxListCtrl, wxControl)
4235
4236 BEGIN_EVENT_TABLE(wxListCtrl,wxControl)
4237 EVT_SIZE(wxListCtrl::OnSize)
4238 EVT_IDLE(wxListCtrl::OnIdle)
4239 END_EVENT_TABLE()
4240
4241 wxListCtrl::wxListCtrl()
4242 {
4243 m_imageListNormal = (wxImageList *) NULL;
4244 m_imageListSmall = (wxImageList *) NULL;
4245 m_imageListState = (wxImageList *) NULL;
4246
4247 m_ownsImageListNormal =
4248 m_ownsImageListSmall =
4249 m_ownsImageListState = FALSE;
4250
4251 m_mainWin = (wxListMainWindow*) NULL;
4252 m_headerWin = (wxListHeaderWindow*) NULL;
4253 }
4254
4255 wxListCtrl::~wxListCtrl()
4256 {
4257 if ( m_mainWin )
4258 m_mainWin->ResetCurrent();
4259
4260 if (m_ownsImageListNormal)
4261 delete m_imageListNormal;
4262 if (m_ownsImageListSmall)
4263 delete m_imageListSmall;
4264 if (m_ownsImageListState)
4265 delete m_imageListState;
4266 }
4267
4268 void wxListCtrl::CreateHeaderWindow()
4269 {
4270 m_headerWin = new wxListHeaderWindow
4271 (
4272 this, -1, m_mainWin,
4273 wxPoint(0, 0),
4274 wxSize(GetClientSize().x, HEADER_HEIGHT),
4275 wxTAB_TRAVERSAL
4276 );
4277 }
4278
4279 bool wxListCtrl::Create(wxWindow *parent,
4280 wxWindowID id,
4281 const wxPoint &pos,
4282 const wxSize &size,
4283 long style,
4284 const wxValidator &validator,
4285 const wxString &name)
4286 {
4287 m_imageListNormal =
4288 m_imageListSmall =
4289 m_imageListState = (wxImageList *) NULL;
4290 m_ownsImageListNormal =
4291 m_ownsImageListSmall =
4292 m_ownsImageListState = FALSE;
4293
4294 m_mainWin = (wxListMainWindow*) NULL;
4295 m_headerWin = (wxListHeaderWindow*) NULL;
4296
4297 if ( !(style & wxLC_MASK_TYPE) )
4298 {
4299 style = style | wxLC_LIST;
4300 }
4301
4302 if ( !wxControl::Create( parent, id, pos, size, style, validator, name ) )
4303 return FALSE;
4304
4305 // don't create the inner window with the border
4306 style &= ~wxSUNKEN_BORDER;
4307
4308 m_mainWin = new wxListMainWindow( this, -1, wxPoint(0,0), size, style );
4309
4310 if ( HasFlag(wxLC_REPORT) )
4311 {
4312 CreateHeaderWindow();
4313
4314 if ( HasFlag(wxLC_NO_HEADER) )
4315 {
4316 // VZ: why do we create it at all then?
4317 m_headerWin->Show( FALSE );
4318 }
4319 }
4320
4321 return TRUE;
4322 }
4323
4324 void wxListCtrl::SetSingleStyle( long style, bool add )
4325 {
4326 wxASSERT_MSG( !(style & wxLC_VIRTUAL),
4327 _T("wxLC_VIRTUAL can't be [un]set") );
4328
4329 long flag = GetWindowStyle();
4330
4331 if (add)
4332 {
4333 if (style & wxLC_MASK_TYPE)
4334 flag &= ~(wxLC_MASK_TYPE | wxLC_VIRTUAL);
4335 if (style & wxLC_MASK_ALIGN)
4336 flag &= ~wxLC_MASK_ALIGN;
4337 if (style & wxLC_MASK_SORT)
4338 flag &= ~wxLC_MASK_SORT;
4339 }
4340
4341 if (add)
4342 {
4343 flag |= style;
4344 }
4345 else
4346 {
4347 flag &= ~style;
4348 }
4349
4350 SetWindowStyleFlag( flag );
4351 }
4352
4353 void wxListCtrl::SetWindowStyleFlag( long flag )
4354 {
4355 if (m_mainWin)
4356 {
4357 m_mainWin->DeleteEverything();
4358
4359 int width = 0;
4360 int height = 0;
4361 GetClientSize( &width, &height );
4362
4363 if (flag & wxLC_REPORT)
4364 {
4365 if (!HasFlag(wxLC_REPORT))
4366 {
4367 if (!m_headerWin)
4368 {
4369 CreateHeaderWindow();
4370
4371 if (HasFlag(wxLC_NO_HEADER))
4372 m_headerWin->Show( FALSE );
4373 }
4374 else
4375 {
4376 if (flag & wxLC_NO_HEADER)
4377 m_headerWin->Show( FALSE );
4378 else
4379 m_headerWin->Show( TRUE );
4380 }
4381 }
4382 }
4383 else // !report
4384 {
4385 if ( m_mainWin->HasHeader() )
4386 {
4387 m_headerWin->Show( FALSE );
4388 }
4389 }
4390 }
4391
4392 wxWindow::SetWindowStyleFlag( flag );
4393 }
4394
4395 bool wxListCtrl::GetColumn(int col, wxListItem &item) const
4396 {
4397 m_mainWin->GetColumn( col, item );
4398 return TRUE;
4399 }
4400
4401 bool wxListCtrl::SetColumn( int col, wxListItem& item )
4402 {
4403 m_mainWin->SetColumn( col, item );
4404 return TRUE;
4405 }
4406
4407 int wxListCtrl::GetColumnWidth( int col ) const
4408 {
4409 return m_mainWin->GetColumnWidth( col );
4410 }
4411
4412 bool wxListCtrl::SetColumnWidth( int col, int width )
4413 {
4414 m_mainWin->SetColumnWidth( col, width );
4415 return TRUE;
4416 }
4417
4418 int wxListCtrl::GetCountPerPage() const
4419 {
4420 return m_mainWin->GetCountPerPage(); // different from Windows ?
4421 }
4422
4423 bool wxListCtrl::GetItem( wxListItem &info ) const
4424 {
4425 m_mainWin->GetItem( info );
4426 return TRUE;
4427 }
4428
4429 bool wxListCtrl::SetItem( wxListItem &info )
4430 {
4431 m_mainWin->SetItem( info );
4432 return TRUE;
4433 }
4434
4435 long wxListCtrl::SetItem( long index, int col, const wxString& label, int imageId )
4436 {
4437 wxListItem info;
4438 info.m_text = label;
4439 info.m_mask = wxLIST_MASK_TEXT;
4440 info.m_itemId = index;
4441 info.m_col = col;
4442 if ( imageId > -1 )
4443 {
4444 info.m_image = imageId;
4445 info.m_mask |= wxLIST_MASK_IMAGE;
4446 };
4447 m_mainWin->SetItem(info);
4448 return TRUE;
4449 }
4450
4451 int wxListCtrl::GetItemState( long item, long stateMask ) const
4452 {
4453 return m_mainWin->GetItemState( item, stateMask );
4454 }
4455
4456 bool wxListCtrl::SetItemState( long item, long state, long stateMask )
4457 {
4458 m_mainWin->SetItemState( item, state, stateMask );
4459 return TRUE;
4460 }
4461
4462 bool wxListCtrl::SetItemImage( long item, int image, int WXUNUSED(selImage) )
4463 {
4464 wxListItem info;
4465 info.m_image = image;
4466 info.m_mask = wxLIST_MASK_IMAGE;
4467 info.m_itemId = item;
4468 m_mainWin->SetItem( info );
4469 return TRUE;
4470 }
4471
4472 wxString wxListCtrl::GetItemText( long item ) const
4473 {
4474 wxListItem info;
4475 info.m_itemId = item;
4476 m_mainWin->GetItem( info );
4477 return info.m_text;
4478 }
4479
4480 void wxListCtrl::SetItemText( long item, const wxString &str )
4481 {
4482 wxListItem info;
4483 info.m_mask = wxLIST_MASK_TEXT;
4484 info.m_itemId = item;
4485 info.m_text = str;
4486 m_mainWin->SetItem( info );
4487 }
4488
4489 long wxListCtrl::GetItemData( long item ) const
4490 {
4491 wxListItem info;
4492 info.m_itemId = item;
4493 m_mainWin->GetItem( info );
4494 return info.m_data;
4495 }
4496
4497 bool wxListCtrl::SetItemData( long item, long data )
4498 {
4499 wxListItem info;
4500 info.m_mask = wxLIST_MASK_DATA;
4501 info.m_itemId = item;
4502 info.m_data = data;
4503 m_mainWin->SetItem( info );
4504 return TRUE;
4505 }
4506
4507 bool wxListCtrl::GetItemRect( long item, wxRect &rect, int WXUNUSED(code) ) const
4508 {
4509 m_mainWin->GetItemRect( item, rect );
4510 return TRUE;
4511 }
4512
4513 bool wxListCtrl::GetItemPosition( long item, wxPoint& pos ) const
4514 {
4515 m_mainWin->GetItemPosition( item, pos );
4516 return TRUE;
4517 }
4518
4519 bool wxListCtrl::SetItemPosition( long WXUNUSED(item), const wxPoint& WXUNUSED(pos) )
4520 {
4521 return 0;
4522 }
4523
4524 int wxListCtrl::GetItemCount() const
4525 {
4526 return m_mainWin->GetItemCount();
4527 }
4528
4529 int wxListCtrl::GetColumnCount() const
4530 {
4531 return m_mainWin->GetColumnCount();
4532 }
4533
4534 void wxListCtrl::SetItemSpacing( int spacing, bool isSmall )
4535 {
4536 m_mainWin->SetItemSpacing( spacing, isSmall );
4537 }
4538
4539 int wxListCtrl::GetItemSpacing( bool isSmall ) const
4540 {
4541 return m_mainWin->GetItemSpacing( isSmall );
4542 }
4543
4544 int wxListCtrl::GetSelectedItemCount() const
4545 {
4546 return m_mainWin->GetSelectedItemCount();
4547 }
4548
4549 wxColour wxListCtrl::GetTextColour() const
4550 {
4551 return GetForegroundColour();
4552 }
4553
4554 void wxListCtrl::SetTextColour(const wxColour& col)
4555 {
4556 SetForegroundColour(col);
4557 }
4558
4559 long wxListCtrl::GetTopItem() const
4560 {
4561 return 0;
4562 }
4563
4564 long wxListCtrl::GetNextItem( long item, int geom, int state ) const
4565 {
4566 return m_mainWin->GetNextItem( item, geom, state );
4567 }
4568
4569 wxImageList *wxListCtrl::GetImageList(int which) const
4570 {
4571 if (which == wxIMAGE_LIST_NORMAL)
4572 {
4573 return m_imageListNormal;
4574 }
4575 else if (which == wxIMAGE_LIST_SMALL)
4576 {
4577 return m_imageListSmall;
4578 }
4579 else if (which == wxIMAGE_LIST_STATE)
4580 {
4581 return m_imageListState;
4582 }
4583 return (wxImageList *) NULL;
4584 }
4585
4586 void wxListCtrl::SetImageList( wxImageList *imageList, int which )
4587 {
4588 if ( which == wxIMAGE_LIST_NORMAL )
4589 {
4590 if (m_ownsImageListNormal) delete m_imageListNormal;
4591 m_imageListNormal = imageList;
4592 m_ownsImageListNormal = FALSE;
4593 }
4594 else if ( which == wxIMAGE_LIST_SMALL )
4595 {
4596 if (m_ownsImageListSmall) delete m_imageListSmall;
4597 m_imageListSmall = imageList;
4598 m_ownsImageListSmall = FALSE;
4599 }
4600 else if ( which == wxIMAGE_LIST_STATE )
4601 {
4602 if (m_ownsImageListState) delete m_imageListState;
4603 m_imageListState = imageList;
4604 m_ownsImageListState = FALSE;
4605 }
4606
4607 m_mainWin->SetImageList( imageList, which );
4608 }
4609
4610 void wxListCtrl::AssignImageList(wxImageList *imageList, int which)
4611 {
4612 SetImageList(imageList, which);
4613 if ( which == wxIMAGE_LIST_NORMAL )
4614 m_ownsImageListNormal = TRUE;
4615 else if ( which == wxIMAGE_LIST_SMALL )
4616 m_ownsImageListSmall = TRUE;
4617 else if ( which == wxIMAGE_LIST_STATE )
4618 m_ownsImageListState = TRUE;
4619 }
4620
4621 bool wxListCtrl::Arrange( int WXUNUSED(flag) )
4622 {
4623 return 0;
4624 }
4625
4626 bool wxListCtrl::DeleteItem( long item )
4627 {
4628 m_mainWin->DeleteItem( item );
4629 return TRUE;
4630 }
4631
4632 bool wxListCtrl::DeleteAllItems()
4633 {
4634 m_mainWin->DeleteAllItems();
4635 return TRUE;
4636 }
4637
4638 bool wxListCtrl::DeleteAllColumns()
4639 {
4640 size_t count = m_mainWin->m_columns.GetCount();
4641 for ( size_t n = 0; n < count; n++ )
4642 DeleteColumn(n);
4643
4644 return TRUE;
4645 }
4646
4647 void wxListCtrl::ClearAll()
4648 {
4649 m_mainWin->DeleteEverything();
4650 }
4651
4652 bool wxListCtrl::DeleteColumn( int col )
4653 {
4654 m_mainWin->DeleteColumn( col );
4655 return TRUE;
4656 }
4657
4658 void wxListCtrl::Edit( long item )
4659 {
4660 m_mainWin->EditLabel( item );
4661 }
4662
4663 bool wxListCtrl::EnsureVisible( long item )
4664 {
4665 m_mainWin->EnsureVisible( item );
4666 return TRUE;
4667 }
4668
4669 long wxListCtrl::FindItem( long start, const wxString& str, bool partial )
4670 {
4671 return m_mainWin->FindItem( start, str, partial );
4672 }
4673
4674 long wxListCtrl::FindItem( long start, long data )
4675 {
4676 return m_mainWin->FindItem( start, data );
4677 }
4678
4679 long wxListCtrl::FindItem( long WXUNUSED(start), const wxPoint& WXUNUSED(pt),
4680 int WXUNUSED(direction))
4681 {
4682 return 0;
4683 }
4684
4685 long wxListCtrl::HitTest( const wxPoint &point, int &flags )
4686 {
4687 return m_mainWin->HitTest( (int)point.x, (int)point.y, flags );
4688 }
4689
4690 long wxListCtrl::InsertItem( wxListItem& info )
4691 {
4692 m_mainWin->InsertItem( info );
4693 return info.m_itemId;
4694 }
4695
4696 long wxListCtrl::InsertItem( long index, const wxString &label )
4697 {
4698 wxListItem info;
4699 info.m_text = label;
4700 info.m_mask = wxLIST_MASK_TEXT;
4701 info.m_itemId = index;
4702 return InsertItem( info );
4703 }
4704
4705 long wxListCtrl::InsertItem( long index, int imageIndex )
4706 {
4707 wxListItem info;
4708 info.m_mask = wxLIST_MASK_IMAGE;
4709 info.m_image = imageIndex;
4710 info.m_itemId = index;
4711 return InsertItem( info );
4712 }
4713
4714 long wxListCtrl::InsertItem( long index, const wxString &label, int imageIndex )
4715 {
4716 wxListItem info;
4717 info.m_text = label;
4718 info.m_image = imageIndex;
4719 info.m_mask = wxLIST_MASK_TEXT | wxLIST_MASK_IMAGE;
4720 info.m_itemId = index;
4721 return InsertItem( info );
4722 }
4723
4724 long wxListCtrl::InsertColumn( long col, wxListItem &item )
4725 {
4726 wxASSERT( m_headerWin );
4727 m_mainWin->InsertColumn( col, item );
4728 m_headerWin->Refresh();
4729
4730 return 0;
4731 }
4732
4733 long wxListCtrl::InsertColumn( long col, const wxString &heading,
4734 int format, int width )
4735 {
4736 wxListItem item;
4737 item.m_mask = wxLIST_MASK_TEXT | wxLIST_MASK_FORMAT;
4738 item.m_text = heading;
4739 if (width >= -2)
4740 {
4741 item.m_mask |= wxLIST_MASK_WIDTH;
4742 item.m_width = width;
4743 }
4744 item.m_format = format;
4745
4746 return InsertColumn( col, item );
4747 }
4748
4749 bool wxListCtrl::ScrollList( int WXUNUSED(dx), int WXUNUSED(dy) )
4750 {
4751 return 0;
4752 }
4753
4754 // Sort items.
4755 // fn is a function which takes 3 long arguments: item1, item2, data.
4756 // item1 is the long data associated with a first item (NOT the index).
4757 // item2 is the long data associated with a second item (NOT the index).
4758 // data is the same value as passed to SortItems.
4759 // The return value is a negative number if the first item should precede the second
4760 // item, a positive number of the second item should precede the first,
4761 // or zero if the two items are equivalent.
4762 // data is arbitrary data to be passed to the sort function.
4763
4764 bool wxListCtrl::SortItems( wxListCtrlCompare fn, long data )
4765 {
4766 m_mainWin->SortItems( fn, data );
4767 return TRUE;
4768 }
4769
4770 // ----------------------------------------------------------------------------
4771 // event handlers
4772 // ----------------------------------------------------------------------------
4773
4774 void wxListCtrl::OnSize(wxSizeEvent& event)
4775 {
4776 if ( !m_mainWin )
4777 return;
4778
4779 int cw, ch;
4780 GetClientSize( &cw, &ch );
4781
4782 if ( m_mainWin->HasHeader() )
4783 {
4784 m_headerWin->SetSize( 0, 0, cw, HEADER_HEIGHT );
4785 m_mainWin->SetSize( 0, HEADER_HEIGHT + 1, cw, ch - HEADER_HEIGHT - 1 );
4786 }
4787 else // no header window
4788 {
4789 m_mainWin->SetSize( 0, 0, cw, ch );
4790 }
4791
4792 m_mainWin->RecalculatePositions();
4793 }
4794
4795 void wxListCtrl::OnIdle( wxIdleEvent & event )
4796 {
4797 event.Skip();
4798
4799 // do it only if needed
4800 if ( !m_mainWin->m_dirty )
4801 return;
4802
4803 m_mainWin->RecalculatePositions();
4804 }
4805
4806 // ----------------------------------------------------------------------------
4807 // font/colours
4808 // ----------------------------------------------------------------------------
4809
4810 bool wxListCtrl::SetBackgroundColour( const wxColour &colour )
4811 {
4812 if (m_mainWin)
4813 {
4814 m_mainWin->SetBackgroundColour( colour );
4815 m_mainWin->m_dirty = TRUE;
4816 }
4817
4818 return TRUE;
4819 }
4820
4821 bool wxListCtrl::SetForegroundColour( const wxColour &colour )
4822 {
4823 if ( !wxWindow::SetForegroundColour( colour ) )
4824 return FALSE;
4825
4826 if (m_mainWin)
4827 {
4828 m_mainWin->SetForegroundColour( colour );
4829 m_mainWin->m_dirty = TRUE;
4830 }
4831
4832 if (m_headerWin)
4833 {
4834 m_headerWin->SetForegroundColour( colour );
4835 }
4836
4837 return TRUE;
4838 }
4839
4840 bool wxListCtrl::SetFont( const wxFont &font )
4841 {
4842 if ( !wxWindow::SetFont( font ) )
4843 return FALSE;
4844
4845 if (m_mainWin)
4846 {
4847 m_mainWin->SetFont( font );
4848 m_mainWin->m_dirty = TRUE;
4849 }
4850
4851 if (m_headerWin)
4852 {
4853 m_headerWin->SetFont( font );
4854 }
4855
4856 return TRUE;
4857 }
4858
4859 // ----------------------------------------------------------------------------
4860 // methods forwarded to m_mainWin
4861 // ----------------------------------------------------------------------------
4862
4863 #if wxUSE_DRAG_AND_DROP
4864
4865 void wxListCtrl::SetDropTarget( wxDropTarget *dropTarget )
4866 {
4867 m_mainWin->SetDropTarget( dropTarget );
4868 }
4869
4870 wxDropTarget *wxListCtrl::GetDropTarget() const
4871 {
4872 return m_mainWin->GetDropTarget();
4873 }
4874
4875 #endif // wxUSE_DRAG_AND_DROP
4876
4877 bool wxListCtrl::SetCursor( const wxCursor &cursor )
4878 {
4879 return m_mainWin ? m_mainWin->wxWindow::SetCursor(cursor) : FALSE;
4880 }
4881
4882 wxColour wxListCtrl::GetBackgroundColour() const
4883 {
4884 return m_mainWin ? m_mainWin->GetBackgroundColour() : wxColour();
4885 }
4886
4887 wxColour wxListCtrl::GetForegroundColour() const
4888 {
4889 return m_mainWin ? m_mainWin->GetForegroundColour() : wxColour();
4890 }
4891
4892 bool wxListCtrl::DoPopupMenu( wxMenu *menu, int x, int y )
4893 {
4894 #if wxUSE_MENUS
4895 return m_mainWin->PopupMenu( menu, x, y );
4896 #else
4897 return FALSE;
4898 #endif // wxUSE_MENUS
4899 }
4900
4901 void wxListCtrl::SetFocus()
4902 {
4903 /* The test in window.cpp fails as we are a composite
4904 window, so it checks against "this", but not m_mainWin. */
4905 if ( FindFocus() != this )
4906 m_mainWin->SetFocus();
4907 }
4908
4909 // ----------------------------------------------------------------------------
4910 // virtual list control support
4911 // ----------------------------------------------------------------------------
4912
4913 wxString wxListCtrl::OnGetItemText(long item, long col) const
4914 {
4915 // this is a pure virtual function, in fact - which is not really pure
4916 // because the controls which are not virtual don't need to implement it
4917 wxFAIL_MSG( _T("not supposed to be called") );
4918
4919 return wxEmptyString;
4920 }
4921
4922 int wxListCtrl::OnGetItemImage(long item) const
4923 {
4924 // same as above
4925 wxFAIL_MSG( _T("not supposed to be called") );
4926
4927 return -1;
4928 }
4929
4930 wxListItemAttr *wxListCtrl::OnGetItemAttr(long item) const
4931 {
4932 wxASSERT_MSG( item >= 0 && item < GetItemCount(),
4933 _T("invalid item index in OnGetItemAttr()") );
4934
4935 // no attributes by default
4936 return NULL;
4937 }
4938
4939 void wxListCtrl::SetItemCount(long count)
4940 {
4941 wxASSERT_MSG( IsVirtual(), _T("this is for virtual controls only") );
4942
4943 m_mainWin->SetItemCount(count);
4944 }
4945
4946 void wxListCtrl::RefreshItem(long item)
4947 {
4948 m_mainWin->RefreshLine(item);
4949 }
4950
4951 void wxListCtrl::RefreshItems(long itemFrom, long itemTo)
4952 {
4953 m_mainWin->RefreshLines(itemFrom, itemTo);
4954 }
4955
4956 #endif // wxUSE_LISTCTRL