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