]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/generic/listctrl.cpp
Replaced /'s with \'s as BCC requires \'s for path names
[wxWidgets.git] / src / generic / listctrl.cpp
... / ...
CommitLineData
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
54DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_BEGIN_DRAG)
55DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_BEGIN_RDRAG)
56DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_BEGIN_LABEL_EDIT)
57DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_END_LABEL_EDIT)
58DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_DELETE_ITEM)
59DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_DELETE_ALL_ITEMS)
60DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_GET_INFO)
61DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_SET_INFO)
62DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_ITEM_SELECTED)
63DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_ITEM_DESELECTED)
64DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_KEY_DOWN)
65DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_INSERT_ITEM)
66DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_COL_CLICK)
67DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_ITEM_RIGHT_CLICK)
68DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_ITEM_MIDDLE_CLICK)
69DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_ITEM_ACTIVATED)
70DEFINE_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!)
77static const int HEADER_HEIGHT = 23;
78
79// the scrollbar units
80static const int SCROLL_UNIT_X = 15;
81static const int SCROLL_UNIT_Y = 15;
82
83// the spacing between the lines (in report mode)
84static const int LINE_SPACING = 0;
85
86// extra margins around the text label
87static const int EXTRA_WIDTH = 3;
88static const int EXTRA_HEIGHT = 4;
89
90// offset for the header window
91static const int HEADER_OFFSET_X = 1;
92static const int HEADER_OFFSET_Y = 1;
93
94// when autosizing the columns, add some slack
95static const int AUTOSIZE_COL_MARGIN = 10;
96
97// default and minimal widths for the header columns
98static const int WIDTH_COL_DEFAULT = 80;
99static const int WIDTH_COL_MIN = 10;
100
101// ============================================================================
102// private classes
103// ============================================================================
104
105// ----------------------------------------------------------------------------
106// wxSelectionStore
107// ----------------------------------------------------------------------------
108
109int CMPFUNC_CONV wxSizeTCmpFn(size_t n1, size_t n2) { return n1 - n2; }
110
111WX_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
122class WXDLLEXPORT wxSelectionStore
123{
124public:
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
164private:
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
186class WXDLLEXPORT wxListItemData
187{
188public:
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
228public:
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
246protected:
247 // common part of all ctors
248 void Init();
249
250 wxString m_text;
251};
252
253//-----------------------------------------------------------------------------
254// wxListHeaderData (internal)
255//-----------------------------------------------------------------------------
256
257class WXDLLEXPORT wxListHeaderData : public wxObject
258{
259protected:
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
269public:
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
290private:
291 DECLARE_DYNAMIC_CLASS(wxListHeaderData);
292};
293
294//-----------------------------------------------------------------------------
295// wxListLineData (internal)
296//-----------------------------------------------------------------------------
297
298WX_DECLARE_LIST(wxListItemData, wxListItemDataList);
299#include "wx/listimpl.cpp"
300WX_DEFINE_LIST(wxListItemDataList);
301
302class WXDLLEXPORT wxListLineData
303{
304public:
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
330public:
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
387private:
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
406WX_DECLARE_EXPORTED_OBJARRAY(wxListLineData, wxListLineDataArray);
407#include "wx/arrimpl.cpp"
408WX_DEFINE_OBJARRAY(wxListLineDataArray);
409
410//-----------------------------------------------------------------------------
411// wxListHeaderWindow (internal)
412//-----------------------------------------------------------------------------
413
414class WXDLLEXPORT wxListHeaderWindow : public wxWindow
415{
416protected:
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
432public:
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
455private:
456 DECLARE_DYNAMIC_CLASS(wxListHeaderWindow)
457 DECLARE_EVENT_TABLE()
458};
459
460//-----------------------------------------------------------------------------
461// wxListRenameTimer (internal)
462//-----------------------------------------------------------------------------
463
464class WXDLLEXPORT wxListRenameTimer: public wxTimer
465{
466private:
467 wxListMainWindow *m_owner;
468
469public:
470 wxListRenameTimer( wxListMainWindow *owner );
471 void Notify();
472};
473
474//-----------------------------------------------------------------------------
475// wxListTextCtrl (internal)
476//-----------------------------------------------------------------------------
477
478class WXDLLEXPORT wxListTextCtrl: public wxTextCtrl
479{
480private:
481 bool *m_accept;
482 wxString *m_res;
483 wxListMainWindow *m_owner;
484 wxString m_startValue;
485
486public:
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
499private:
500 DECLARE_DYNAMIC_CLASS(wxListTextCtrl);
501 DECLARE_EVENT_TABLE()
502};
503
504//-----------------------------------------------------------------------------
505// wxListMainWindow (internal)
506//-----------------------------------------------------------------------------
507
508WX_DECLARE_LIST(wxListHeaderData, wxListHeaderDataList);
509#include "wx/listimpl.cpp"
510WX_DEFINE_LIST(wxListHeaderDataList);
511
512class WXDLLEXPORT wxListMainWindow : public wxScrolledWindow
513{
514public:
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
745protected:
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
797private:
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
834bool 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
843bool 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
872bool 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
982void 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
1009wxListItemData::~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
1021void wxListItemData::Init()
1022{
1023 m_image = -1;
1024 m_data = 0;
1025
1026 m_attr = NULL;
1027}
1028
1029wxListItemData::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
1045void 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
1071void 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
1079void 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
1089bool 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
1096int 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
1103int 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
1110int 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
1117int 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
1124void 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
1145IMPLEMENT_DYNAMIC_CLASS(wxListHeaderData,wxObject);
1146
1147wxListHeaderData::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
1158wxListHeaderData::wxListHeaderData( const wxListItem &item )
1159{
1160 SetItem( item );
1161 m_xpos = 0;
1162 m_ypos = 0;
1163 m_height = 0;
1164}
1165
1166void 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
1176void wxListHeaderData::SetPosition( int x, int y )
1177{
1178 m_xpos = x;
1179 m_ypos = y;
1180}
1181
1182void wxListHeaderData::SetHeight( int h )
1183{
1184 m_height = h;
1185}
1186
1187void 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
1196void wxListHeaderData::SetFormat( int format )
1197{
1198 m_format = format;
1199}
1200
1201bool wxListHeaderData::HasImage() const
1202{
1203 return (m_image != 0);
1204}
1205
1206bool 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
1211void 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
1220int wxListHeaderData::GetImage() const
1221{
1222 return m_image;
1223}
1224
1225int wxListHeaderData::GetWidth() const
1226{
1227 return m_width;
1228}
1229
1230int wxListHeaderData::GetFormat() const
1231{
1232 return m_format;
1233}
1234
1235//-----------------------------------------------------------------------------
1236// wxListLineData
1237//-----------------------------------------------------------------------------
1238
1239inline int wxListLineData::GetMode() const
1240{
1241 return m_owner->GetListCtrl()->GetWindowStyleFlag() & wxLC_MASK_TYPE;
1242}
1243
1244inline bool wxListLineData::InReportView() const
1245{
1246 return m_owner->HasFlag(wxLC_REPORT);
1247}
1248
1249inline bool wxListLineData::IsVirtual() const
1250{
1251 return m_owner->IsVirtual();
1252}
1253
1254wxListLineData::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
1273void 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
1381void 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
1450void wxListLineData::InitItems( int num )
1451{
1452 for (int i = 0; i < num; i++)
1453 m_items.Append( new wxListItemData(m_owner) );
1454}
1455
1456void 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
1465void 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
1475wxString 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
1489void 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
1499void 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
1508int 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
1517wxListItemAttr *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
1526void 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
1535bool 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
1599void 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
1628void 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
1678bool 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
1690void wxListLineData::ReverseHighlight( void )
1691{
1692 Highlight(!IsHighlighted());
1693}
1694
1695//-----------------------------------------------------------------------------
1696// wxListHeaderWindow
1697//-----------------------------------------------------------------------------
1698
1699IMPLEMENT_DYNAMIC_CLASS(wxListHeaderWindow,wxWindow);
1700
1701BEGIN_EVENT_TABLE(wxListHeaderWindow,wxWindow)
1702 EVT_PAINT (wxListHeaderWindow::OnPaint)
1703 EVT_MOUSE_EVENTS (wxListHeaderWindow::OnMouse)
1704 EVT_SET_FOCUS (wxListHeaderWindow::OnSetFocus)
1705END_EVENT_TABLE()
1706
1707wxListHeaderWindow::wxListHeaderWindow( void )
1708{
1709 m_owner = (wxListMainWindow *) NULL;
1710 m_currentCursor = (wxCursor *) NULL;
1711 m_resizeCursor = (wxCursor *) NULL;
1712 m_isDragging = FALSE;
1713}
1714
1715wxListHeaderWindow::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
1730wxListHeaderWindow::~wxListHeaderWindow( void )
1731{
1732 delete m_resizeCursor;
1733}
1734
1735void 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
1792void 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
1804void 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
1858void 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
1884void 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
1992void wxListHeaderWindow::OnSetFocus( wxFocusEvent &WXUNUSED(event) )
1993{
1994 m_owner->SetFocus();
1995}
1996
1997//-----------------------------------------------------------------------------
1998// wxListRenameTimer (internal)
1999//-----------------------------------------------------------------------------
2000
2001wxListRenameTimer::wxListRenameTimer( wxListMainWindow *owner )
2002{
2003 m_owner = owner;
2004}
2005
2006void wxListRenameTimer::Notify()
2007{
2008 m_owner->OnRenameTimer();
2009}
2010
2011//-----------------------------------------------------------------------------
2012// wxListTextCtrl (internal)
2013//-----------------------------------------------------------------------------
2014
2015IMPLEMENT_DYNAMIC_CLASS(wxListTextCtrl,wxTextCtrl);
2016
2017BEGIN_EVENT_TABLE(wxListTextCtrl,wxTextCtrl)
2018 EVT_CHAR (wxListTextCtrl::OnChar)
2019 EVT_KEY_UP (wxListTextCtrl::OnKeyUp)
2020 EVT_KILL_FOCUS (wxListTextCtrl::OnKillFocus)
2021END_EVENT_TABLE()
2022
2023wxListTextCtrl::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
2044void 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
2073void 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
2090void 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
2103IMPLEMENT_DYNAMIC_CLASS(wxListMainWindow,wxScrolledWindow);
2104
2105BEGIN_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)
2113END_EVENT_TABLE()
2114
2115void 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
2147void 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
2161wxListMainWindow::wxListMainWindow()
2162{
2163 Init();
2164
2165 m_highlightBrush = (wxBrush *) NULL;
2166
2167 m_xScroll =
2168 m_yScroll = 0;
2169}
2170
2171wxListMainWindow::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
2192wxListMainWindow::~wxListMainWindow()
2193{
2194 DoDeleteAllItems();
2195
2196 delete m_highlightBrush;
2197
2198 delete m_renameTimer;
2199}
2200
2201void 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
2217wxListLineData *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
2239wxCoord 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
2264wxCoord 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
2271wxRect 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
2285wxRect 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
2299wxRect 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
2315wxRect wxListMainWindow::GetLineHighlightRect(size_t line) const
2316{
2317 return InReportView() ? GetLineRect(line)
2318 : GetLine(line)->m_gi->m_rectHighlight;
2319}
2320
2321long 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
2344bool 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
2359void 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
2393bool 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
2418void 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
2432void 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
2468void 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
2497void 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
2634void 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
2653void 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
2678void wxListMainWindow::OnFocusLine( size_t WXUNUSED(line) )
2679{
2680// SendNotify( line, wxEVT_COMMAND_LIST_ITEM_FOCUSSED );
2681}
2682
2683void wxListMainWindow::OnUnfocusLine( size_t WXUNUSED(line) )
2684{
2685// SendNotify( line, wxEVT_COMMAND_LIST_ITEM_UNFOCUSSED );
2686}
2687
2688void 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
2733void wxListMainWindow::OnRenameTimer()
2734{
2735 wxCHECK_RET( HasCurrent(), wxT("unexpected rename timer") );
2736
2737 EditLabel( m_current );
2738}
2739
2740void 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
2763void 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
2933void 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
2970void 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
3016void 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
3035void 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__
3204extern wxWindow *g_focusWindow;
3205#endif
3206
3207void 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
3226void wxListMainWindow::OnKillFocus( wxFocusEvent &WXUNUSED(event) )
3227{
3228 m_hasFocus = FALSE;
3229
3230 if ( HasCurrent() )
3231 RefreshLine( m_current );
3232}
3233
3234void 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
3254void 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
3279int 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
3290void 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
3315void 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
3328int wxListMainWindow::GetItemSpacing( bool isSmall )
3329{
3330 return isSmall ? m_small_spacing : m_normal_spacing;
3331}
3332
3333// ----------------------------------------------------------------------------
3334// columns
3335// ----------------------------------------------------------------------------
3336
3337void 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
3359void 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
3432int 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
3448void 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
3457int 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
3470void 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
3494void 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; // sdafe because of the check above
3501
3502 if ( stateMask & wxLIST_STATE_FOCUSED )
3503 {
3504 if ( state & wxLIST_STATE_FOCUSED )
3505 {
3506 // don't do anything if this item is already focused
3507 if ( item != m_current )
3508 {
3509 OnUnfocusLine( m_current );
3510 m_current = item;
3511 OnFocusLine( m_current );
3512
3513 if ( IsSingleSel() && (oldCurrent != (size_t)-1) )
3514 {
3515 HighlightLine(oldCurrent, FALSE);
3516 RefreshLine(oldCurrent);
3517 }
3518
3519 RefreshLine( m_current );
3520 }
3521 }
3522 else // unfocus
3523 {
3524 // don't do anything if this item is not focused
3525 if ( item == m_current )
3526 {
3527 OnUnfocusLine( m_current );
3528 m_current = (size_t)-1;
3529 }
3530 }
3531 }
3532
3533 if ( stateMask & wxLIST_STATE_SELECTED )
3534 {
3535 bool on = (state & wxLIST_STATE_SELECTED) != 0;
3536
3537 if ( IsSingleSel() )
3538 {
3539 if ( on )
3540 {
3541 // selecting the item also makes it the focused one in the
3542 // single sel mode
3543 if ( m_current != item )
3544 {
3545 OnUnfocusLine( m_current );
3546 m_current = item;
3547 OnFocusLine( m_current );
3548
3549 if ( oldCurrent != (size_t)-1 )
3550 {
3551 HighlightLine( oldCurrent, FALSE );
3552 RefreshLine( oldCurrent );
3553 }
3554 }
3555 }
3556 else // off
3557 {
3558 // only the current item may be selected anyhow
3559 if ( item != m_current )
3560 return;
3561 }
3562 }
3563
3564 if ( HighlightLine(item, on) )
3565 {
3566 RefreshLine(item);
3567 }
3568 }
3569}
3570
3571int wxListMainWindow::GetItemState( long item, long stateMask )
3572{
3573 wxCHECK_MSG( item >= 0 && (size_t)item < GetItemCount(), 0,
3574 _T("invalid list ctrl item index in GetItemState()") );
3575
3576 int ret = wxLIST_STATE_DONTCARE;
3577
3578 if ( stateMask & wxLIST_STATE_FOCUSED )
3579 {
3580 if ( (size_t)item == m_current )
3581 ret |= wxLIST_STATE_FOCUSED;
3582 }
3583
3584 if ( stateMask & wxLIST_STATE_SELECTED )
3585 {
3586 if ( IsHighlighted(item) )
3587 ret |= wxLIST_STATE_SELECTED;
3588 }
3589
3590 return ret;
3591}
3592
3593void wxListMainWindow::GetItem( wxListItem &item )
3594{
3595 wxCHECK_RET( item.m_itemId >= 0 && (size_t)item.m_itemId < GetItemCount(),
3596 _T("invalid item index in GetItem") );
3597
3598 wxListLineData *line = GetLine((size_t)item.m_itemId);
3599 line->GetItem( item.m_col, item );
3600}
3601
3602// ----------------------------------------------------------------------------
3603// item count
3604// ----------------------------------------------------------------------------
3605
3606size_t wxListMainWindow::GetItemCount() const
3607{
3608 return IsVirtual() ? m_countVirt : m_lines.GetCount();
3609}
3610
3611void wxListMainWindow::SetItemCount(long count)
3612{
3613 m_selStore.SetItemCount(count);
3614 m_countVirt = count;
3615
3616 ResetVisibleLinesRange();
3617
3618 // scrollbars must be reset
3619 m_dirty = TRUE;
3620}
3621
3622int wxListMainWindow::GetSelectedItemCount()
3623{
3624 // deal with the quick case first
3625 if ( IsSingleSel() )
3626 {
3627 return HasCurrent() ? IsHighlighted(m_current) : FALSE;
3628 }
3629
3630 // virtual controls remmebers all its selections itself
3631 if ( IsVirtual() )
3632 return m_selStore.GetSelectedCount();
3633
3634 // TODO: we probably should maintain the number of items selected even for
3635 // non virtual controls as enumerating all lines is really slow...
3636 size_t countSel = 0;
3637 size_t count = GetItemCount();
3638 for ( size_t line = 0; line < count; line++ )
3639 {
3640 if ( GetLine(line)->IsHighlighted() )
3641 countSel++;
3642 }
3643
3644 return countSel;
3645}
3646
3647// ----------------------------------------------------------------------------
3648// item position/size
3649// ----------------------------------------------------------------------------
3650
3651void wxListMainWindow::GetItemRect( long index, wxRect &rect )
3652{
3653 wxCHECK_RET( index >= 0 && (size_t)index < GetItemCount(),
3654 _T("invalid index in GetItemRect") );
3655
3656 rect = GetLineRect((size_t)index);
3657
3658 CalcScrolledPosition(rect.x, rect.y, &rect.x, &rect.y);
3659}
3660
3661bool wxListMainWindow::GetItemPosition(long item, wxPoint& pos)
3662{
3663 wxRect rect;
3664 GetItemRect(item, rect);
3665
3666 pos.x = rect.x;
3667 pos.y = rect.y;
3668
3669 return TRUE;
3670}
3671
3672// ----------------------------------------------------------------------------
3673// geometry calculation
3674// ----------------------------------------------------------------------------
3675
3676void wxListMainWindow::RecalculatePositions(bool noRefresh)
3677{
3678 wxClientDC dc( this );
3679 dc.SetFont( GetFont() );
3680
3681 int iconSpacing;
3682 if ( HasFlag(wxLC_ICON) )
3683 iconSpacing = m_normal_spacing;
3684 else if ( HasFlag(wxLC_SMALL_ICON) )
3685 iconSpacing = m_small_spacing;
3686 else
3687 iconSpacing = 0;
3688
3689 int clientWidth,
3690 clientHeight;
3691 GetClientSize( &clientWidth, &clientHeight );
3692
3693 if ( HasFlag(wxLC_REPORT) )
3694 {
3695 // all lines have the same height
3696 int lineHeight = GetLineHeight();
3697
3698 // scroll one line per step
3699 m_yScroll = lineHeight;
3700
3701 size_t lineCount = GetItemCount();
3702 int entireHeight = lineCount*lineHeight + LINE_SPACING;
3703
3704 m_linesPerPage = clientHeight / lineHeight;
3705
3706 ResetVisibleLinesRange();
3707
3708 SetScrollbars( m_xScroll, m_yScroll,
3709 (GetHeaderWidth() + m_xScroll - 1)/m_xScroll,
3710 (entireHeight + m_yScroll - 1)/m_yScroll,
3711 GetScrollPos(wxHORIZONTAL),
3712 GetScrollPos(wxVERTICAL),
3713 TRUE );
3714 }
3715 else // !report
3716 {
3717 // at first we try without any scrollbar. if the items don't
3718 // fit into the window, we recalculate after subtracting an
3719 // approximated 15 pt for the horizontal scrollbar
3720
3721 clientHeight -= 4; // sunken frame
3722
3723 int entireWidth = 0;
3724
3725 for (int tries = 0; tries < 2; tries++)
3726 {
3727 entireWidth = 0;
3728 int x = 2;
3729 int y = 2;
3730 int maxWidth = 0;
3731 m_linesPerPage = 0;
3732 int currentlyVisibleLines = 0;
3733
3734 size_t count = GetItemCount();
3735 for (size_t i = 0; i < count; i++)
3736 {
3737 currentlyVisibleLines++;
3738 wxListLineData *line = GetLine(i);
3739 line->CalculateSize( &dc, iconSpacing );
3740 line->SetPosition( x, y, clientWidth, iconSpacing );
3741
3742 wxSize sizeLine = GetLineSize(i);
3743
3744 if ( maxWidth < sizeLine.x )
3745 maxWidth = sizeLine.x;
3746
3747 y += sizeLine.y;
3748 if (currentlyVisibleLines > m_linesPerPage)
3749 m_linesPerPage = currentlyVisibleLines;
3750
3751 // assume that the size of the next one is the same... (FIXME)
3752 if ( y + sizeLine.y - 6 >= clientHeight )
3753 {
3754 currentlyVisibleLines = 0;
3755 y = 2;
3756 x += maxWidth+6;
3757 entireWidth += maxWidth+6;
3758 maxWidth = 0;
3759 }
3760 if ( i == count - 1 )
3761 entireWidth += maxWidth;
3762 if ((tries == 0) && (entireWidth > clientWidth))
3763 {
3764 clientHeight -= 15; // scrollbar height
3765 m_linesPerPage = 0;
3766 currentlyVisibleLines = 0;
3767 break;
3768 }
3769 if ( i == count - 1 )
3770 tries = 1; // everything fits, no second try required
3771 }
3772 }
3773
3774 int scroll_pos = GetScrollPos( wxHORIZONTAL );
3775 SetScrollbars( m_xScroll, m_yScroll, (entireWidth+SCROLL_UNIT_X) / m_xScroll, 0, scroll_pos, 0, TRUE );
3776 }
3777
3778 if ( !noRefresh )
3779 {
3780 // FIXME: why should we call it from here?
3781 UpdateCurrent();
3782
3783 RefreshAll();
3784 }
3785}
3786
3787void wxListMainWindow::RefreshAll()
3788{
3789 m_dirty = FALSE;
3790 Refresh();
3791
3792 wxListHeaderWindow *headerWin = GetListCtrl()->m_headerWin;
3793 if ( headerWin )
3794 {
3795 headerWin->m_dirty = FALSE;
3796 headerWin->Refresh();
3797 }
3798}
3799
3800void wxListMainWindow::UpdateCurrent()
3801{
3802 if ( !HasCurrent() && !IsEmpty() )
3803 {
3804 m_current = 0;
3805 }
3806
3807 if ( m_current != (size_t)-1 )
3808 {
3809 OnFocusLine( m_current );
3810 }
3811}
3812
3813long wxListMainWindow::GetNextItem( long item,
3814 int WXUNUSED(geometry),
3815 int state )
3816{
3817 long ret = item,
3818 max = GetItemCount();
3819 wxCHECK_MSG( (ret == -1) || (ret < max), -1,
3820 _T("invalid listctrl index in GetNextItem()") );
3821
3822 // notice that we start with the next item (or the first one if item == -1)
3823 // and this is intentional to allow writing a simple loop to iterate over
3824 // all selected items
3825 ret++;
3826 if ( ret == max )
3827 {
3828 // this is not an error because the index was ok initially, just no
3829 // such item
3830 return -1;
3831 }
3832
3833 if ( !state )
3834 {
3835 // any will do
3836 return (size_t)ret;
3837 }
3838
3839 size_t count = GetItemCount();
3840 for ( size_t line = (size_t)ret; line < count; line++ )
3841 {
3842 if ( (state & wxLIST_STATE_FOCUSED) && (line == m_current) )
3843 return line;
3844
3845 if ( (state & wxLIST_STATE_SELECTED) && IsHighlighted(line) )
3846 return line;
3847 }
3848
3849 return -1;
3850}
3851
3852// ----------------------------------------------------------------------------
3853// deleting stuff
3854// ----------------------------------------------------------------------------
3855
3856void wxListMainWindow::DeleteItem( long lindex )
3857{
3858 size_t count = GetItemCount();
3859
3860 wxCHECK_RET( (lindex >= 0) && ((size_t)lindex < count),
3861 _T("invalid item index in DeleteItem") );
3862
3863 size_t index = (size_t)lindex;
3864
3865 // we don't need to adjust the index for the previous items
3866 if ( HasCurrent() && m_current >= index )
3867 {
3868 // if the current item is being deleted, we want the next one to
3869 // become selected - unless there is no next one - so don't adjust
3870 // m_current in this case
3871 if ( m_current != index || m_current == count - 1 )
3872 {
3873 m_current--;
3874 }
3875 }
3876
3877 if ( InReportView() )
3878 {
3879 ResetVisibleLinesRange();
3880 }
3881
3882 if ( IsVirtual() )
3883 {
3884 m_countVirt--;
3885
3886 m_selStore.OnItemDelete(index);
3887 }
3888 else
3889 {
3890 m_lines.RemoveAt( index );
3891 }
3892
3893 m_dirty = TRUE;
3894
3895 SendNotify( index, wxEVT_COMMAND_LIST_DELETE_ITEM );
3896
3897 RefreshAfter(index);
3898}
3899
3900void wxListMainWindow::DeleteColumn( int col )
3901{
3902 wxListHeaderDataList::Node *node = m_columns.Item( col );
3903
3904 wxCHECK_RET( node, wxT("invalid column index in DeleteColumn()") );
3905
3906 m_dirty = TRUE;
3907 m_columns.DeleteNode( node );
3908}
3909
3910void wxListMainWindow::DoDeleteAllItems()
3911{
3912 if ( IsEmpty() )
3913 {
3914 // nothing to do - in particular, don't send the event
3915 return;
3916 }
3917
3918 ResetCurrent();
3919
3920 // to make the deletion of all items faster, we don't send the
3921 // notifications for each item deletion in this case but only one event
3922 // for all of them: this is compatible with wxMSW and documented in
3923 // DeleteAllItems() description
3924
3925 wxListEvent event( wxEVT_COMMAND_LIST_DELETE_ALL_ITEMS, GetParent()->GetId() );
3926 event.SetEventObject( GetParent() );
3927 GetParent()->GetEventHandler()->ProcessEvent( event );
3928
3929 if ( IsVirtual() )
3930 {
3931 m_countVirt = 0;
3932
3933 m_selStore.Clear();
3934 }
3935
3936 if ( InReportView() )
3937 {
3938 ResetVisibleLinesRange();
3939 }
3940
3941 m_lines.Clear();
3942}
3943
3944void wxListMainWindow::DeleteAllItems()
3945{
3946 DoDeleteAllItems();
3947
3948 RecalculatePositions();
3949}
3950
3951void wxListMainWindow::DeleteEverything()
3952{
3953 DeleteAllItems();
3954
3955 m_columns.Clear();
3956}
3957
3958// ----------------------------------------------------------------------------
3959// scanning for an item
3960// ----------------------------------------------------------------------------
3961
3962void wxListMainWindow::EnsureVisible( long index )
3963{
3964 wxCHECK_RET( index >= 0 && (size_t)index < GetItemCount(),
3965 _T("invalid index in EnsureVisible") );
3966
3967 // We have to call this here because the label in question might just have
3968 // been added and its position is not known yet
3969 if ( m_dirty )
3970 {
3971 m_dirty = FALSE;
3972
3973 RecalculatePositions(TRUE /* no refresh */);
3974 }
3975
3976 MoveToItem((size_t)index);
3977}
3978
3979long wxListMainWindow::FindItem(long start, const wxString& str, bool WXUNUSED(partial) )
3980{
3981 long pos = start;
3982 wxString tmp = str;
3983 if (pos < 0)
3984 pos = 0;
3985
3986 size_t count = GetItemCount();
3987 for ( size_t i = (size_t)pos; i < count; i++ )
3988 {
3989 wxListLineData *line = GetLine(i);
3990 if ( line->GetText(0) == tmp )
3991 return i;
3992 }
3993
3994 return wxNOT_FOUND;
3995}
3996
3997long wxListMainWindow::FindItem(long start, long data)
3998{
3999 long pos = start;
4000 if (pos < 0)
4001 pos = 0;
4002
4003 size_t count = GetItemCount();
4004 for (size_t i = (size_t)pos; i < count; i++)
4005 {
4006 wxListLineData *line = GetLine(i);
4007 wxListItem item;
4008 line->GetItem( 0, item );
4009 if (item.m_data == data)
4010 return i;
4011 }
4012
4013 return wxNOT_FOUND;
4014}
4015
4016long wxListMainWindow::HitTest( int x, int y, int &flags )
4017{
4018 CalcUnscrolledPosition( x, y, &x, &y );
4019
4020 if ( HasFlag(wxLC_REPORT) )
4021 {
4022 size_t current = y / GetLineHeight();
4023 flags = HitTestLine(current, x, y);
4024 if ( flags )
4025 return current;
4026 }
4027 else // !report
4028 {
4029 // TODO: optimize it too! this is less simple than for report view but
4030 // enumerating all items is still not a way to do it!!
4031 size_t count = GetItemCount();
4032 for ( size_t current = 0; current < count; current++ )
4033 {
4034 flags = HitTestLine(current, x, y);
4035 if ( flags )
4036 return current;
4037 }
4038 }
4039
4040 return wxNOT_FOUND;
4041}
4042
4043// ----------------------------------------------------------------------------
4044// adding stuff
4045// ----------------------------------------------------------------------------
4046
4047void wxListMainWindow::InsertItem( wxListItem &item )
4048{
4049 wxASSERT_MSG( !IsVirtual(), _T("can't be used with virtual control") );
4050
4051 size_t count = GetItemCount();
4052 wxCHECK_RET( item.m_itemId >= 0 && (size_t)item.m_itemId <= count,
4053 _T("invalid item index") );
4054
4055 size_t id = item.m_itemId;
4056
4057 m_dirty = TRUE;
4058
4059 int mode = 0;
4060 if ( HasFlag(wxLC_REPORT) )
4061 mode = wxLC_REPORT;
4062 else if ( HasFlag(wxLC_LIST) )
4063 mode = wxLC_LIST;
4064 else if ( HasFlag(wxLC_ICON) )
4065 mode = wxLC_ICON;
4066 else if ( HasFlag(wxLC_SMALL_ICON) )
4067 mode = wxLC_ICON; // no typo
4068 else
4069 {
4070 wxFAIL_MSG( _T("unknown mode") );
4071 }
4072
4073 wxListLineData *line = new wxListLineData(this);
4074
4075 line->SetItem( 0, item );
4076
4077 m_lines.Insert( line, id );
4078
4079 m_dirty = TRUE;
4080 RefreshLines(id, GetItemCount() - 1);
4081}
4082
4083void wxListMainWindow::InsertColumn( long col, wxListItem &item )
4084{
4085 m_dirty = TRUE;
4086 if ( HasFlag(wxLC_REPORT) )
4087 {
4088 if (item.m_width == wxLIST_AUTOSIZE_USEHEADER)
4089 item.m_width = GetTextLength( item.m_text );
4090 wxListHeaderData *column = new wxListHeaderData( item );
4091 if ((col >= 0) && (col < (int)m_columns.GetCount()))
4092 {
4093 wxListHeaderDataList::Node *node = m_columns.Item( col );
4094 m_columns.Insert( node, column );
4095 }
4096 else
4097 {
4098 m_columns.Append( column );
4099 }
4100 }
4101}
4102
4103// ----------------------------------------------------------------------------
4104// sorting
4105// ----------------------------------------------------------------------------
4106
4107wxListCtrlCompare list_ctrl_compare_func_2;
4108long list_ctrl_compare_data;
4109
4110int LINKAGEMODE list_ctrl_compare_func_1( wxListLineData **arg1, wxListLineData **arg2 )
4111{
4112 wxListLineData *line1 = *arg1;
4113 wxListLineData *line2 = *arg2;
4114 wxListItem item;
4115 line1->GetItem( 0, item );
4116 long data1 = item.m_data;
4117 line2->GetItem( 0, item );
4118 long data2 = item.m_data;
4119 return list_ctrl_compare_func_2( data1, data2, list_ctrl_compare_data );
4120}
4121
4122void wxListMainWindow::SortItems( wxListCtrlCompare fn, long data )
4123{
4124 list_ctrl_compare_func_2 = fn;
4125 list_ctrl_compare_data = data;
4126 m_lines.Sort( list_ctrl_compare_func_1 );
4127 m_dirty = TRUE;
4128}
4129
4130// ----------------------------------------------------------------------------
4131// scrolling
4132// ----------------------------------------------------------------------------
4133
4134void wxListMainWindow::OnScroll(wxScrollWinEvent& event)
4135{
4136 // update our idea of which lines are shown when we redraw the window the
4137 // next time
4138 ResetVisibleLinesRange();
4139
4140 // FIXME
4141#if defined(__WXGTK__) && !defined(__WXUNIVERSAL__)
4142 wxScrolledWindow::OnScroll(event);
4143#else
4144 HandleOnScroll( event );
4145#endif
4146
4147 if ( event.GetOrientation() == wxHORIZONTAL && HasHeader() )
4148 {
4149 wxListCtrl* lc = GetListCtrl();
4150 wxCHECK_RET( lc, _T("no listctrl window?") );
4151
4152 lc->m_headerWin->Refresh() ;
4153#ifdef __WXMAC__
4154 lc->m_headerWin->MacUpdateImmediately() ;
4155#endif
4156 }
4157}
4158
4159int wxListMainWindow::GetCountPerPage() const
4160{
4161 if ( !m_linesPerPage )
4162 {
4163 wxConstCast(this, wxListMainWindow)->
4164 m_linesPerPage = GetClientSize().y / GetLineHeight();
4165 }
4166
4167 return m_linesPerPage;
4168}
4169
4170void wxListMainWindow::GetVisibleLinesRange(size_t *from, size_t *to)
4171{
4172 wxASSERT_MSG( HasFlag(wxLC_REPORT), _T("this is for report mode only") );
4173
4174 if ( m_lineFrom == (size_t)-1 )
4175 {
4176 size_t count = GetItemCount();
4177 if ( count )
4178 {
4179 m_lineFrom = GetScrollPos(wxVERTICAL);
4180
4181 // this may happen if SetScrollbars() hadn't been called yet
4182 if ( m_lineFrom >= count )
4183 m_lineFrom = count - 1;
4184
4185 // we redraw one extra line but this is needed to make the redrawing
4186 // logic work when there is a fractional number of lines on screen
4187 m_lineTo = m_lineFrom + m_linesPerPage;
4188 if ( m_lineTo >= count )
4189 m_lineTo = count - 1;
4190 }
4191 else // empty control
4192 {
4193 m_lineFrom = 0;
4194 m_lineTo = (size_t)-1;
4195 }
4196 }
4197
4198 wxASSERT_MSG( IsEmpty() ||
4199 (m_lineFrom <= m_lineTo && m_lineTo < GetItemCount()),
4200 _T("GetVisibleLinesRange() returns incorrect result") );
4201
4202 if ( from )
4203 *from = m_lineFrom;
4204 if ( to )
4205 *to = m_lineTo;
4206}
4207
4208// -------------------------------------------------------------------------------------
4209// wxListItem
4210// -------------------------------------------------------------------------------------
4211
4212IMPLEMENT_DYNAMIC_CLASS(wxListItem, wxObject)
4213
4214wxListItem::wxListItem()
4215{
4216 m_mask = 0;
4217 m_itemId = 0;
4218 m_col = 0;
4219 m_state = 0;
4220 m_stateMask = 0;
4221 m_image = 0;
4222 m_data = 0;
4223 m_format = wxLIST_FORMAT_CENTRE;
4224 m_width = 0;
4225
4226 m_attr = NULL;
4227}
4228
4229void wxListItem::Clear()
4230{
4231 m_mask = 0;
4232 m_itemId = 0;
4233 m_col = 0;
4234 m_state = 0;
4235 m_stateMask = 0;
4236 m_image = 0;
4237 m_data = 0;
4238 m_format = wxLIST_FORMAT_CENTRE;
4239 m_width = 0;
4240 m_text = _T("");
4241
4242 ClearAttributes();
4243}
4244
4245void wxListItem::ClearAttributes()
4246{
4247 if (m_attr)
4248 {
4249 delete m_attr;
4250 m_attr = NULL;
4251 }
4252}
4253
4254// -------------------------------------------------------------------------------------
4255// wxListEvent
4256// -------------------------------------------------------------------------------------
4257
4258IMPLEMENT_DYNAMIC_CLASS(wxListEvent, wxNotifyEvent)
4259
4260wxListEvent::wxListEvent( wxEventType commandType, int id )
4261 : wxNotifyEvent( commandType, id )
4262{
4263 m_code = 0;
4264 m_itemIndex = 0;
4265 m_oldItemIndex = 0;
4266 m_col = 0;
4267 m_cancelled = FALSE;
4268 m_pointDrag.x = 0;
4269 m_pointDrag.y = 0;
4270}
4271
4272void wxListEvent::CopyObject(wxObject& object_dest) const
4273{
4274 wxListEvent *obj = (wxListEvent *)&object_dest;
4275
4276 wxNotifyEvent::CopyObject(object_dest);
4277
4278 obj->m_code = m_code;
4279 obj->m_itemIndex = m_itemIndex;
4280 obj->m_oldItemIndex = m_oldItemIndex;
4281 obj->m_col = m_col;
4282 obj->m_cancelled = m_cancelled;
4283 obj->m_pointDrag = m_pointDrag;
4284 obj->m_item.m_mask = m_item.m_mask;
4285 obj->m_item.m_itemId = m_item.m_itemId;
4286 obj->m_item.m_col = m_item.m_col;
4287 obj->m_item.m_state = m_item.m_state;
4288 obj->m_item.m_stateMask = m_item.m_stateMask;
4289 obj->m_item.m_text = m_item.m_text;
4290 obj->m_item.m_image = m_item.m_image;
4291 obj->m_item.m_data = m_item.m_data;
4292 obj->m_item.m_format = m_item.m_format;
4293 obj->m_item.m_width = m_item.m_width;
4294
4295 if ( m_item.HasAttributes() )
4296 {
4297 obj->m_item.SetTextColour(m_item.GetTextColour());
4298 }
4299}
4300
4301// -------------------------------------------------------------------------------------
4302// wxListCtrl
4303// -------------------------------------------------------------------------------------
4304
4305IMPLEMENT_DYNAMIC_CLASS(wxListCtrl, wxControl)
4306IMPLEMENT_DYNAMIC_CLASS(wxListView, wxListCtrl)
4307
4308BEGIN_EVENT_TABLE(wxListCtrl,wxControl)
4309 EVT_SIZE(wxListCtrl::OnSize)
4310 EVT_IDLE(wxListCtrl::OnIdle)
4311END_EVENT_TABLE()
4312
4313wxListCtrl::wxListCtrl()
4314{
4315 m_imageListNormal = (wxImageList *) NULL;
4316 m_imageListSmall = (wxImageList *) NULL;
4317 m_imageListState = (wxImageList *) NULL;
4318
4319 m_ownsImageListNormal =
4320 m_ownsImageListSmall =
4321 m_ownsImageListState = FALSE;
4322
4323 m_mainWin = (wxListMainWindow*) NULL;
4324 m_headerWin = (wxListHeaderWindow*) NULL;
4325}
4326
4327wxListCtrl::~wxListCtrl()
4328{
4329 if ( m_mainWin )
4330 m_mainWin->ResetCurrent();
4331
4332 if (m_ownsImageListNormal)
4333 delete m_imageListNormal;
4334 if (m_ownsImageListSmall)
4335 delete m_imageListSmall;
4336 if (m_ownsImageListState)
4337 delete m_imageListState;
4338}
4339
4340void wxListCtrl::CreateHeaderWindow()
4341{
4342 m_headerWin = new wxListHeaderWindow
4343 (
4344 this, -1, m_mainWin,
4345 wxPoint(0, 0),
4346 wxSize(GetClientSize().x, HEADER_HEIGHT),
4347 wxTAB_TRAVERSAL
4348 );
4349}
4350
4351bool wxListCtrl::Create(wxWindow *parent,
4352 wxWindowID id,
4353 const wxPoint &pos,
4354 const wxSize &size,
4355 long style,
4356 const wxValidator &validator,
4357 const wxString &name)
4358{
4359 m_imageListNormal =
4360 m_imageListSmall =
4361 m_imageListState = (wxImageList *) NULL;
4362 m_ownsImageListNormal =
4363 m_ownsImageListSmall =
4364 m_ownsImageListState = FALSE;
4365
4366 m_mainWin = (wxListMainWindow*) NULL;
4367 m_headerWin = (wxListHeaderWindow*) NULL;
4368
4369 if ( !(style & wxLC_MASK_TYPE) )
4370 {
4371 style = style | wxLC_LIST;
4372 }
4373
4374 if ( !wxControl::Create( parent, id, pos, size, style, validator, name ) )
4375 return FALSE;
4376
4377 // don't create the inner window with the border
4378 style &= ~wxSUNKEN_BORDER;
4379
4380 m_mainWin = new wxListMainWindow( this, -1, wxPoint(0,0), size, style );
4381
4382 if ( HasFlag(wxLC_REPORT) )
4383 {
4384 CreateHeaderWindow();
4385
4386 if ( HasFlag(wxLC_NO_HEADER) )
4387 {
4388 // VZ: why do we create it at all then?
4389 m_headerWin->Show( FALSE );
4390 }
4391 }
4392
4393 return TRUE;
4394}
4395
4396void wxListCtrl::SetSingleStyle( long style, bool add )
4397{
4398 wxASSERT_MSG( !(style & wxLC_VIRTUAL),
4399 _T("wxLC_VIRTUAL can't be [un]set") );
4400
4401 long flag = GetWindowStyle();
4402
4403 if (add)
4404 {
4405 if (style & wxLC_MASK_TYPE)
4406 flag &= ~(wxLC_MASK_TYPE | wxLC_VIRTUAL);
4407 if (style & wxLC_MASK_ALIGN)
4408 flag &= ~wxLC_MASK_ALIGN;
4409 if (style & wxLC_MASK_SORT)
4410 flag &= ~wxLC_MASK_SORT;
4411 }
4412
4413 if (add)
4414 {
4415 flag |= style;
4416 }
4417 else
4418 {
4419 flag &= ~style;
4420 }
4421
4422 SetWindowStyleFlag( flag );
4423}
4424
4425void wxListCtrl::SetWindowStyleFlag( long flag )
4426{
4427 if (m_mainWin)
4428 {
4429 m_mainWin->DeleteEverything();
4430
4431 // has the header visibility changed?
4432 bool hasHeader = HasFlag(wxLC_REPORT) && !HasFlag(wxLC_NO_HEADER),
4433 willHaveHeader = (flag & wxLC_REPORT) && !(flag & wxLC_NO_HEADER);
4434
4435 if ( hasHeader != willHaveHeader )
4436 {
4437 // toggle it
4438 if ( hasHeader )
4439 {
4440 if ( m_headerWin )
4441 {
4442 // don't delete, just hide, as we can reuse it later
4443 m_headerWin->Show(FALSE);
4444 }
4445 //else: nothing to do
4446 }
4447 else // must show header
4448 {
4449 if (!m_headerWin)
4450 {
4451 CreateHeaderWindow();
4452 }
4453 else // already have it, just show
4454 {
4455 m_headerWin->Show( TRUE );
4456 }
4457 }
4458
4459 ResizeReportView(willHaveHeader);
4460 }
4461 }
4462
4463 wxWindow::SetWindowStyleFlag( flag );
4464}
4465
4466bool wxListCtrl::GetColumn(int col, wxListItem &item) const
4467{
4468 m_mainWin->GetColumn( col, item );
4469 return TRUE;
4470}
4471
4472bool wxListCtrl::SetColumn( int col, wxListItem& item )
4473{
4474 m_mainWin->SetColumn( col, item );
4475 return TRUE;
4476}
4477
4478int wxListCtrl::GetColumnWidth( int col ) const
4479{
4480 return m_mainWin->GetColumnWidth( col );
4481}
4482
4483bool wxListCtrl::SetColumnWidth( int col, int width )
4484{
4485 m_mainWin->SetColumnWidth( col, width );
4486 return TRUE;
4487}
4488
4489int wxListCtrl::GetCountPerPage() const
4490{
4491 return m_mainWin->GetCountPerPage(); // different from Windows ?
4492}
4493
4494bool wxListCtrl::GetItem( wxListItem &info ) const
4495{
4496 m_mainWin->GetItem( info );
4497 return TRUE;
4498}
4499
4500bool wxListCtrl::SetItem( wxListItem &info )
4501{
4502 m_mainWin->SetItem( info );
4503 return TRUE;
4504}
4505
4506long wxListCtrl::SetItem( long index, int col, const wxString& label, int imageId )
4507{
4508 wxListItem info;
4509 info.m_text = label;
4510 info.m_mask = wxLIST_MASK_TEXT;
4511 info.m_itemId = index;
4512 info.m_col = col;
4513 if ( imageId > -1 )
4514 {
4515 info.m_image = imageId;
4516 info.m_mask |= wxLIST_MASK_IMAGE;
4517 };
4518 m_mainWin->SetItem(info);
4519 return TRUE;
4520}
4521
4522int wxListCtrl::GetItemState( long item, long stateMask ) const
4523{
4524 return m_mainWin->GetItemState( item, stateMask );
4525}
4526
4527bool wxListCtrl::SetItemState( long item, long state, long stateMask )
4528{
4529 m_mainWin->SetItemState( item, state, stateMask );
4530 return TRUE;
4531}
4532
4533bool wxListCtrl::SetItemImage( long item, int image, int WXUNUSED(selImage) )
4534{
4535 wxListItem info;
4536 info.m_image = image;
4537 info.m_mask = wxLIST_MASK_IMAGE;
4538 info.m_itemId = item;
4539 m_mainWin->SetItem( info );
4540 return TRUE;
4541}
4542
4543wxString wxListCtrl::GetItemText( long item ) const
4544{
4545 wxListItem info;
4546 info.m_itemId = item;
4547 m_mainWin->GetItem( info );
4548 return info.m_text;
4549}
4550
4551void wxListCtrl::SetItemText( long item, const wxString &str )
4552{
4553 wxListItem info;
4554 info.m_mask = wxLIST_MASK_TEXT;
4555 info.m_itemId = item;
4556 info.m_text = str;
4557 m_mainWin->SetItem( info );
4558}
4559
4560long wxListCtrl::GetItemData( long item ) const
4561{
4562 wxListItem info;
4563 info.m_itemId = item;
4564 m_mainWin->GetItem( info );
4565 return info.m_data;
4566}
4567
4568bool wxListCtrl::SetItemData( long item, long data )
4569{
4570 wxListItem info;
4571 info.m_mask = wxLIST_MASK_DATA;
4572 info.m_itemId = item;
4573 info.m_data = data;
4574 m_mainWin->SetItem( info );
4575 return TRUE;
4576}
4577
4578bool wxListCtrl::GetItemRect( long item, wxRect &rect, int WXUNUSED(code) ) const
4579{
4580 m_mainWin->GetItemRect( item, rect );
4581 return TRUE;
4582}
4583
4584bool wxListCtrl::GetItemPosition( long item, wxPoint& pos ) const
4585{
4586 m_mainWin->GetItemPosition( item, pos );
4587 return TRUE;
4588}
4589
4590bool wxListCtrl::SetItemPosition( long WXUNUSED(item), const wxPoint& WXUNUSED(pos) )
4591{
4592 return 0;
4593}
4594
4595int wxListCtrl::GetItemCount() const
4596{
4597 return m_mainWin->GetItemCount();
4598}
4599
4600int wxListCtrl::GetColumnCount() const
4601{
4602 return m_mainWin->GetColumnCount();
4603}
4604
4605void wxListCtrl::SetItemSpacing( int spacing, bool isSmall )
4606{
4607 m_mainWin->SetItemSpacing( spacing, isSmall );
4608}
4609
4610int wxListCtrl::GetItemSpacing( bool isSmall ) const
4611{
4612 return m_mainWin->GetItemSpacing( isSmall );
4613}
4614
4615int wxListCtrl::GetSelectedItemCount() const
4616{
4617 return m_mainWin->GetSelectedItemCount();
4618}
4619
4620wxColour wxListCtrl::GetTextColour() const
4621{
4622 return GetForegroundColour();
4623}
4624
4625void wxListCtrl::SetTextColour(const wxColour& col)
4626{
4627 SetForegroundColour(col);
4628}
4629
4630long wxListCtrl::GetTopItem() const
4631{
4632 return 0;
4633}
4634
4635long wxListCtrl::GetNextItem( long item, int geom, int state ) const
4636{
4637 return m_mainWin->GetNextItem( item, geom, state );
4638}
4639
4640wxImageList *wxListCtrl::GetImageList(int which) const
4641{
4642 if (which == wxIMAGE_LIST_NORMAL)
4643 {
4644 return m_imageListNormal;
4645 }
4646 else if (which == wxIMAGE_LIST_SMALL)
4647 {
4648 return m_imageListSmall;
4649 }
4650 else if (which == wxIMAGE_LIST_STATE)
4651 {
4652 return m_imageListState;
4653 }
4654 return (wxImageList *) NULL;
4655}
4656
4657void wxListCtrl::SetImageList( wxImageList *imageList, int which )
4658{
4659 if ( which == wxIMAGE_LIST_NORMAL )
4660 {
4661 if (m_ownsImageListNormal) delete m_imageListNormal;
4662 m_imageListNormal = imageList;
4663 m_ownsImageListNormal = FALSE;
4664 }
4665 else if ( which == wxIMAGE_LIST_SMALL )
4666 {
4667 if (m_ownsImageListSmall) delete m_imageListSmall;
4668 m_imageListSmall = imageList;
4669 m_ownsImageListSmall = FALSE;
4670 }
4671 else if ( which == wxIMAGE_LIST_STATE )
4672 {
4673 if (m_ownsImageListState) delete m_imageListState;
4674 m_imageListState = imageList;
4675 m_ownsImageListState = FALSE;
4676 }
4677
4678 m_mainWin->SetImageList( imageList, which );
4679}
4680
4681void wxListCtrl::AssignImageList(wxImageList *imageList, int which)
4682{
4683 SetImageList(imageList, which);
4684 if ( which == wxIMAGE_LIST_NORMAL )
4685 m_ownsImageListNormal = TRUE;
4686 else if ( which == wxIMAGE_LIST_SMALL )
4687 m_ownsImageListSmall = TRUE;
4688 else if ( which == wxIMAGE_LIST_STATE )
4689 m_ownsImageListState = TRUE;
4690}
4691
4692bool wxListCtrl::Arrange( int WXUNUSED(flag) )
4693{
4694 return 0;
4695}
4696
4697bool wxListCtrl::DeleteItem( long item )
4698{
4699 m_mainWin->DeleteItem( item );
4700 return TRUE;
4701}
4702
4703bool wxListCtrl::DeleteAllItems()
4704{
4705 m_mainWin->DeleteAllItems();
4706 return TRUE;
4707}
4708
4709bool wxListCtrl::DeleteAllColumns()
4710{
4711 size_t count = m_mainWin->m_columns.GetCount();
4712 for ( size_t n = 0; n < count; n++ )
4713 DeleteColumn(n);
4714
4715 return TRUE;
4716}
4717
4718void wxListCtrl::ClearAll()
4719{
4720 m_mainWin->DeleteEverything();
4721}
4722
4723bool wxListCtrl::DeleteColumn( int col )
4724{
4725 m_mainWin->DeleteColumn( col );
4726 return TRUE;
4727}
4728
4729void wxListCtrl::Edit( long item )
4730{
4731 m_mainWin->EditLabel( item );
4732}
4733
4734bool wxListCtrl::EnsureVisible( long item )
4735{
4736 m_mainWin->EnsureVisible( item );
4737 return TRUE;
4738}
4739
4740long wxListCtrl::FindItem( long start, const wxString& str, bool partial )
4741{
4742 return m_mainWin->FindItem( start, str, partial );
4743}
4744
4745long wxListCtrl::FindItem( long start, long data )
4746{
4747 return m_mainWin->FindItem( start, data );
4748}
4749
4750long wxListCtrl::FindItem( long WXUNUSED(start), const wxPoint& WXUNUSED(pt),
4751 int WXUNUSED(direction))
4752{
4753 return 0;
4754}
4755
4756long wxListCtrl::HitTest( const wxPoint &point, int &flags )
4757{
4758 return m_mainWin->HitTest( (int)point.x, (int)point.y, flags );
4759}
4760
4761long wxListCtrl::InsertItem( wxListItem& info )
4762{
4763 m_mainWin->InsertItem( info );
4764 return info.m_itemId;
4765}
4766
4767long wxListCtrl::InsertItem( long index, const wxString &label )
4768{
4769 wxListItem info;
4770 info.m_text = label;
4771 info.m_mask = wxLIST_MASK_TEXT;
4772 info.m_itemId = index;
4773 return InsertItem( info );
4774}
4775
4776long wxListCtrl::InsertItem( long index, int imageIndex )
4777{
4778 wxListItem info;
4779 info.m_mask = wxLIST_MASK_IMAGE;
4780 info.m_image = imageIndex;
4781 info.m_itemId = index;
4782 return InsertItem( info );
4783}
4784
4785long wxListCtrl::InsertItem( long index, const wxString &label, int imageIndex )
4786{
4787 wxListItem info;
4788 info.m_text = label;
4789 info.m_image = imageIndex;
4790 info.m_mask = wxLIST_MASK_TEXT | wxLIST_MASK_IMAGE;
4791 info.m_itemId = index;
4792 return InsertItem( info );
4793}
4794
4795long wxListCtrl::InsertColumn( long col, wxListItem &item )
4796{
4797 wxASSERT( m_headerWin );
4798 m_mainWin->InsertColumn( col, item );
4799 m_headerWin->Refresh();
4800
4801 return 0;
4802}
4803
4804long wxListCtrl::InsertColumn( long col, const wxString &heading,
4805 int format, int width )
4806{
4807 wxListItem item;
4808 item.m_mask = wxLIST_MASK_TEXT | wxLIST_MASK_FORMAT;
4809 item.m_text = heading;
4810 if (width >= -2)
4811 {
4812 item.m_mask |= wxLIST_MASK_WIDTH;
4813 item.m_width = width;
4814 }
4815 item.m_format = format;
4816
4817 return InsertColumn( col, item );
4818}
4819
4820bool wxListCtrl::ScrollList( int WXUNUSED(dx), int WXUNUSED(dy) )
4821{
4822 return 0;
4823}
4824
4825// Sort items.
4826// fn is a function which takes 3 long arguments: item1, item2, data.
4827// item1 is the long data associated with a first item (NOT the index).
4828// item2 is the long data associated with a second item (NOT the index).
4829// data is the same value as passed to SortItems.
4830// The return value is a negative number if the first item should precede the second
4831// item, a positive number of the second item should precede the first,
4832// or zero if the two items are equivalent.
4833// data is arbitrary data to be passed to the sort function.
4834
4835bool wxListCtrl::SortItems( wxListCtrlCompare fn, long data )
4836{
4837 m_mainWin->SortItems( fn, data );
4838 return TRUE;
4839}
4840
4841// ----------------------------------------------------------------------------
4842// event handlers
4843// ----------------------------------------------------------------------------
4844
4845void wxListCtrl::OnSize(wxSizeEvent& event)
4846{
4847 if ( !m_mainWin )
4848 return;
4849
4850 ResizeReportView(m_mainWin->HasHeader());
4851
4852 m_mainWin->RecalculatePositions();
4853}
4854
4855void wxListCtrl::ResizeReportView(bool showHeader)
4856{
4857 int cw, ch;
4858 GetClientSize( &cw, &ch );
4859
4860 if ( showHeader )
4861 {
4862 m_headerWin->SetSize( 0, 0, cw, HEADER_HEIGHT );
4863 m_mainWin->SetSize( 0, HEADER_HEIGHT + 1, cw, ch - HEADER_HEIGHT - 1 );
4864 }
4865 else // no header window
4866 {
4867 m_mainWin->SetSize( 0, 0, cw, ch );
4868 }
4869}
4870
4871void wxListCtrl::OnIdle( wxIdleEvent & event )
4872{
4873 event.Skip();
4874
4875 // do it only if needed
4876 if ( !m_mainWin->m_dirty )
4877 return;
4878
4879 m_mainWin->RecalculatePositions();
4880}
4881
4882// ----------------------------------------------------------------------------
4883// font/colours
4884// ----------------------------------------------------------------------------
4885
4886bool wxListCtrl::SetBackgroundColour( const wxColour &colour )
4887{
4888 if (m_mainWin)
4889 {
4890 m_mainWin->SetBackgroundColour( colour );
4891 m_mainWin->m_dirty = TRUE;
4892 }
4893
4894 return TRUE;
4895}
4896
4897bool wxListCtrl::SetForegroundColour( const wxColour &colour )
4898{
4899 if ( !wxWindow::SetForegroundColour( colour ) )
4900 return FALSE;
4901
4902 if (m_mainWin)
4903 {
4904 m_mainWin->SetForegroundColour( colour );
4905 m_mainWin->m_dirty = TRUE;
4906 }
4907
4908 if (m_headerWin)
4909 {
4910 m_headerWin->SetForegroundColour( colour );
4911 }
4912
4913 return TRUE;
4914}
4915
4916bool wxListCtrl::SetFont( const wxFont &font )
4917{
4918 if ( !wxWindow::SetFont( font ) )
4919 return FALSE;
4920
4921 if (m_mainWin)
4922 {
4923 m_mainWin->SetFont( font );
4924 m_mainWin->m_dirty = TRUE;
4925 }
4926
4927 if (m_headerWin)
4928 {
4929 m_headerWin->SetFont( font );
4930 }
4931
4932 return TRUE;
4933}
4934
4935// ----------------------------------------------------------------------------
4936// methods forwarded to m_mainWin
4937// ----------------------------------------------------------------------------
4938
4939#if wxUSE_DRAG_AND_DROP
4940
4941void wxListCtrl::SetDropTarget( wxDropTarget *dropTarget )
4942{
4943 m_mainWin->SetDropTarget( dropTarget );
4944}
4945
4946wxDropTarget *wxListCtrl::GetDropTarget() const
4947{
4948 return m_mainWin->GetDropTarget();
4949}
4950
4951#endif // wxUSE_DRAG_AND_DROP
4952
4953bool wxListCtrl::SetCursor( const wxCursor &cursor )
4954{
4955 return m_mainWin ? m_mainWin->wxWindow::SetCursor(cursor) : FALSE;
4956}
4957
4958wxColour wxListCtrl::GetBackgroundColour() const
4959{
4960 return m_mainWin ? m_mainWin->GetBackgroundColour() : wxColour();
4961}
4962
4963wxColour wxListCtrl::GetForegroundColour() const
4964{
4965 return m_mainWin ? m_mainWin->GetForegroundColour() : wxColour();
4966}
4967
4968bool wxListCtrl::DoPopupMenu( wxMenu *menu, int x, int y )
4969{
4970#if wxUSE_MENUS
4971 return m_mainWin->PopupMenu( menu, x, y );
4972#else
4973 return FALSE;
4974#endif // wxUSE_MENUS
4975}
4976
4977void wxListCtrl::SetFocus()
4978{
4979 /* The test in window.cpp fails as we are a composite
4980 window, so it checks against "this", but not m_mainWin. */
4981 if ( FindFocus() != this )
4982 m_mainWin->SetFocus();
4983}
4984
4985// ----------------------------------------------------------------------------
4986// virtual list control support
4987// ----------------------------------------------------------------------------
4988
4989wxString wxListCtrl::OnGetItemText(long item, long col) const
4990{
4991 // this is a pure virtual function, in fact - which is not really pure
4992 // because the controls which are not virtual don't need to implement it
4993 wxFAIL_MSG( _T("not supposed to be called") );
4994
4995 return wxEmptyString;
4996}
4997
4998int wxListCtrl::OnGetItemImage(long item) const
4999{
5000 // same as above
5001 wxFAIL_MSG( _T("not supposed to be called") );
5002
5003 return -1;
5004}
5005
5006wxListItemAttr *wxListCtrl::OnGetItemAttr(long item) const
5007{
5008 wxASSERT_MSG( item >= 0 && item < GetItemCount(),
5009 _T("invalid item index in OnGetItemAttr()") );
5010
5011 // no attributes by default
5012 return NULL;
5013}
5014
5015void wxListCtrl::SetItemCount(long count)
5016{
5017 wxASSERT_MSG( IsVirtual(), _T("this is for virtual controls only") );
5018
5019 m_mainWin->SetItemCount(count);
5020}
5021
5022void wxListCtrl::RefreshItem(long item)
5023{
5024 m_mainWin->RefreshLine(item);
5025}
5026
5027void wxListCtrl::RefreshItems(long itemFrom, long itemTo)
5028{
5029 m_mainWin->RefreshLines(itemFrom, itemTo);
5030}
5031
5032#endif // wxUSE_LISTCTRL