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