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