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