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