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