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