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