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