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