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