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