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