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