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