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