1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/univ/listbox.cpp
3 // Purpose: wxListBox implementation
4 // Author: Vadim Zeitlin
8 // Copyright: (c) 2000 SciTech Software, Inc. (www.scitechsoft.com)
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // ============================================================================
14 // ============================================================================
16 // ----------------------------------------------------------------------------
18 // ----------------------------------------------------------------------------
20 #include "wx/wxprec.h"
31 #include "wx/dcclient.h"
32 #include "wx/listbox.h"
33 #include "wx/validate.h"
36 #include "wx/univ/renderer.h"
37 #include "wx/univ/inphand.h"
38 #include "wx/univ/theme.h"
40 // ----------------------------------------------------------------------------
41 // wxStdListboxInputHandler: handles mouse and kbd in a single or multi
43 // ----------------------------------------------------------------------------
45 class WXDLLEXPORT wxStdListboxInputHandler
: public wxStdInputHandler
48 // if pressing the mouse button in a multiselection listbox should toggle
49 // the item under mouse immediately, then specify true as the second
50 // parameter (this is the standard behaviour, under GTK the item is toggled
51 // only when the mouse is released in the multi selection listbox)
52 wxStdListboxInputHandler(wxInputHandler
*inphand
,
53 bool toggleOnPressAlways
= true);
56 virtual bool HandleKey(wxInputConsumer
*consumer
,
57 const wxKeyEvent
& event
,
59 virtual bool HandleMouse(wxInputConsumer
*consumer
,
60 const wxMouseEvent
& event
);
61 virtual bool HandleMouseMove(wxInputConsumer
*consumer
,
62 const wxMouseEvent
& event
);
65 // return the item under mouse, 0 if the mouse is above the listbox or
66 // GetCount() if it is below it
67 int HitTest(const wxListBox
*listbox
, const wxMouseEvent
& event
);
69 // parts of HitTest(): first finds the pseudo (because not in range) index
70 // of the item and the second one adjusts it if necessary - that is if the
71 // third one returns false
72 int HitTestUnsafe(const wxListBox
*listbox
, const wxMouseEvent
& event
);
73 int FixItemIndex(const wxListBox
*listbox
, int item
);
74 bool IsValidIndex(const wxListBox
*listbox
, int item
);
76 // init m_btnCapture and m_actionMouse
77 wxControlAction
SetupCapture(wxListBox
*lbox
,
78 const wxMouseEvent
& event
,
81 wxRenderer
*m_renderer
;
83 // the button which initiated the mouse capture (currently 0 or 1)
86 // the action to perform when the mouse moves while we capture it
87 wxControlAction m_actionMouse
;
89 // the ctor parameter toggleOnPressAlways (see comments near it)
90 bool m_toggleOnPressAlways
;
92 // do we track the mouse outside the window when it is captured?
93 bool m_trackMouseOutside
;
96 // ============================================================================
97 // implementation of wxListBox
98 // ============================================================================
100 IMPLEMENT_DYNAMIC_CLASS(wxListBox
, wxControl
)
102 BEGIN_EVENT_TABLE(wxListBox
, wxListBoxBase
)
103 EVT_SIZE(wxListBox::OnSize
)
106 // ----------------------------------------------------------------------------
108 // ----------------------------------------------------------------------------
110 void wxListBox::Init()
112 // will be calculated later when needed
120 // no items hence no current item
123 m_currentChanged
= false;
125 // no need to update anything initially
128 // no scrollbars to show nor update
132 m_showScrollbarY
= false;
135 wxListBox::wxListBox(wxWindow
*parent
,
139 const wxArrayString
& choices
,
141 const wxValidator
& validator
,
142 const wxString
&name
)
143 :wxScrollHelper(this)
147 Create(parent
, id
, pos
, size
, choices
, style
, validator
, name
);
150 bool wxListBox::Create(wxWindow
*parent
,
154 const wxArrayString
& choices
,
156 const wxValidator
& validator
,
157 const wxString
&name
)
159 wxCArrayString
chs(choices
);
161 return Create(parent
, id
, pos
, size
, chs
.GetCount(), chs
.GetStrings(),
162 style
, validator
, name
);
165 bool wxListBox::Create(wxWindow
*parent
,
170 const wxString choices
[],
172 const wxValidator
& validator
,
173 const wxString
&name
)
175 // for compatibility accept both the new and old styles - they mean the
177 if ( style
& wxLB_ALWAYS_SB
)
178 style
|= wxALWAYS_SHOW_SB
;
180 // if we don't have neither multiple nor extended flag, we must have the
181 // single selection listbox
182 if ( !(style
& (wxLB_MULTIPLE
| wxLB_EXTENDED
)) )
183 style
|= wxLB_SINGLE
;
185 #if wxUSE_TWO_WINDOWS
186 style
|= wxVSCROLL
|wxHSCROLL
;
187 if ((style
& wxBORDER_MASK
) == 0)
188 style
|= wxBORDER_SUNKEN
;
191 if ( !wxControl::Create(parent
, id
, pos
, size
, style
,
195 m_strings
= new wxArrayString
;
201 CreateInputHandler(wxINP_HANDLER_LISTBOX
);
206 wxListBox::~wxListBox()
208 // call this just to free the client data -- and avoid leaking memory
216 // ----------------------------------------------------------------------------
217 // adding/inserting strings
218 // ----------------------------------------------------------------------------
220 int wxCMPFUNC_CONV
wxListBoxSortNoCase(wxString
* s1
, wxString
* s2
)
222 return s1
->CmpNoCase(*s2
);
225 int wxListBox::DoAppendOnly(const wxString
& item
)
231 m_strings
->Add(item
);
232 m_strings
->Sort(wxListBoxSortNoCase
);
233 index
= m_strings
->Index(item
);
237 index
= m_strings
->GetCount();
238 m_strings
->Add(item
);
244 int wxListBox::DoAppend(const wxString
& item
)
246 size_t index
= DoAppendOnly( item
);
248 m_itemsClientData
.Insert(NULL
, index
);
250 m_updateScrollbarY
= true;
252 if ( HasHorzScrollbar() )
254 // has the max width increased?
256 GetTextExtent(item
, &width
, NULL
);
257 if ( width
> m_maxWidth
)
260 m_maxWidthItem
= index
;
261 m_updateScrollbarX
= true;
265 RefreshFromItemToEnd(index
);
270 void wxListBox::DoInsertItems(const wxArrayString
& items
, unsigned int pos
)
272 // the position of the item being added to a sorted listbox can't be
274 wxCHECK_RET( !IsSorted(), _T("can't insert items into sorted listbox") );
276 unsigned int count
= items
.GetCount();
277 for ( unsigned int n
= 0; n
< count
; n
++ )
279 m_strings
->Insert(items
[n
], pos
+ n
);
280 m_itemsClientData
.Insert(NULL
, pos
+ n
);
283 // the number of items has changed so we might have to show the scrollbar
284 m_updateScrollbarY
= true;
286 // the max width also might have changed - just recalculate it instead of
287 // keeping track of it here, this is probably more efficient for a typical
289 RefreshHorzScrollbar();
291 // note that we have to refresh all the items after the ones we inserted,
292 // not just these items
293 RefreshFromItemToEnd(pos
);
296 void wxListBox::DoSetItems(const wxArrayString
& items
, void **clientData
)
300 unsigned int count
= items
.GetCount();
304 m_strings
->Alloc(count
);
306 m_itemsClientData
.Alloc(count
);
307 for ( unsigned int n
= 0; n
< count
; n
++ )
309 unsigned int index
= DoAppendOnly(items
[n
]);
311 m_itemsClientData
.Insert(clientData
? clientData
[n
] : NULL
, index
);
314 m_updateScrollbarY
= true;
319 void wxListBox::SetString(unsigned int n
, const wxString
& s
)
321 wxCHECK_RET( !IsSorted(), _T("can't set string in sorted listbox") );
325 if ( HasHorzScrollbar() )
327 // we need to update m_maxWidth as changing the string may cause the
328 // horz scrollbar [dis]appear
331 GetTextExtent(s
, &width
, NULL
);
333 // it might have increased if the new string is long
334 if ( width
> m_maxWidth
)
338 m_updateScrollbarX
= true;
340 // or also decreased if the old string was the longest one
341 else if ( n
== (unsigned int)m_maxWidthItem
)
343 RefreshHorzScrollbar();
350 // ----------------------------------------------------------------------------
352 // ----------------------------------------------------------------------------
354 void wxListBox::DoClear()
358 if ( HasClientObjectData() )
360 unsigned int count
= m_itemsClientData
.GetCount();
361 for ( unsigned int n
= 0; n
< count
; n
++ )
363 delete (wxClientData
*) m_itemsClientData
[n
];
367 m_itemsClientData
.Clear();
368 m_selections
.Clear();
373 void wxListBox::Clear()
377 m_updateScrollbarY
= true;
379 RefreshHorzScrollbar();
384 void wxListBox::Delete(unsigned int n
)
386 wxCHECK_RET( IsValid(n
),
387 _T("invalid index in wxListBox::Delete") );
389 // do it before removing the index as otherwise the last item will not be
390 // refreshed (as GetCount() will be decremented)
391 RefreshFromItemToEnd(n
);
393 m_strings
->RemoveAt(n
);
395 if ( HasClientObjectData() )
397 delete (wxClientData
*)m_itemsClientData
[n
];
400 m_itemsClientData
.RemoveAt(n
);
402 // when the item disappears we must not keep using its index
403 if ( (int)n
== m_current
)
407 else if ( (int)n
< m_current
)
411 //else: current item may stay
413 // update the selections array: the indices of all seletected items after
414 // the one being deleted must change and the item itselfm ust be removed
415 int index
= wxNOT_FOUND
;
416 unsigned int count
= m_selections
.GetCount();
417 for ( unsigned int item
= 0; item
< count
; item
++ )
419 if ( m_selections
[item
] == (int)n
)
421 // remember to delete it later
424 else if ( m_selections
[item
] > (int)n
)
426 // to account for the index shift
427 m_selections
[item
]--;
429 //else: nothing changed for this one
432 if ( index
!= wxNOT_FOUND
)
434 m_selections
.RemoveAt(index
);
437 // the number of items has changed, hence the scrollbar may disappear
438 m_updateScrollbarY
= true;
440 // finally, if the longest item was deleted the scrollbar may disappear
441 if ( (int)n
== m_maxWidthItem
)
443 RefreshHorzScrollbar();
447 // ----------------------------------------------------------------------------
448 // client data handling
449 // ----------------------------------------------------------------------------
451 void wxListBox::DoSetItemClientData(unsigned int n
, void* clientData
)
453 m_itemsClientData
[n
] = clientData
;
456 void *wxListBox::DoGetItemClientData(unsigned int n
) const
458 return m_itemsClientData
[n
];
461 void wxListBox::DoSetItemClientObject(unsigned int n
, wxClientData
* clientData
)
463 m_itemsClientData
[n
] = clientData
;
466 wxClientData
* wxListBox::DoGetItemClientObject(unsigned int n
) const
468 return (wxClientData
*)m_itemsClientData
[n
];
471 // ----------------------------------------------------------------------------
473 // ----------------------------------------------------------------------------
475 void wxListBox::DoSetSelection(int n
, bool select
)
479 if ( m_selections
.Index(n
) == wxNOT_FOUND
)
481 if ( !HasMultipleSelection() )
483 // selecting an item in a single selection listbox deselects
492 //else: already selected
496 int index
= m_selections
.Index(n
);
497 if ( index
!= wxNOT_FOUND
)
499 m_selections
.RemoveAt(index
);
506 // sanity check: a single selection listbox can't have more than one item
508 wxASSERT_MSG( HasMultipleSelection() || (m_selections
.GetCount() < 2),
509 _T("multiple selected items in single selection lbox?") );
513 // the newly selected item becomes the current one
518 int wxListBox::GetSelection() const
520 wxCHECK_MSG( !HasMultipleSelection(), wxNOT_FOUND
,
521 _T("use wxListBox::GetSelections for ths listbox") );
523 return m_selections
.IsEmpty() ? wxNOT_FOUND
: m_selections
[0];
526 int wxCMPFUNC_CONV
wxCompareInts(int *n
, int *m
)
531 int wxListBox::GetSelections(wxArrayInt
& selections
) const
533 // always return sorted array to the user
534 selections
= m_selections
;
535 unsigned int count
= m_selections
.GetCount();
537 // don't call sort on an empty array
540 selections
.Sort(wxCompareInts
);
546 // ----------------------------------------------------------------------------
547 // refresh logic: we use delayed refreshing which allows to avoid multiple
548 // refreshes (and hence flicker) in case when several listbox items are
549 // added/deleted/changed subsequently
550 // ----------------------------------------------------------------------------
552 void wxListBox::RefreshFromItemToEnd(int from
)
554 RefreshItems(from
, GetCount() - from
);
557 void wxListBox::RefreshItems(int from
, int count
)
559 switch ( m_updateCount
)
563 m_updateCount
= count
;
567 // we refresh everything anyhow
571 // add these items to the others which we have to refresh
572 if ( m_updateFrom
< from
)
574 count
+= from
- m_updateFrom
;
575 if ( m_updateCount
< count
)
576 m_updateCount
= count
;
578 else // m_updateFrom >= from
580 int updateLast
= wxMax(m_updateFrom
+ m_updateCount
,
583 m_updateCount
= updateLast
- m_updateFrom
;
588 void wxListBox::RefreshItem(int n
)
590 switch ( m_updateCount
)
593 // refresh this item only
599 // we refresh everything anyhow
603 // add this item to the others which we have to refresh
604 if ( m_updateFrom
< n
)
606 if ( m_updateCount
< n
- m_updateFrom
+ 1 )
607 m_updateCount
= n
- m_updateFrom
+ 1;
609 else // n <= m_updateFrom
611 m_updateCount
+= m_updateFrom
- n
;
617 void wxListBox::RefreshAll()
622 void wxListBox::RefreshHorzScrollbar()
624 m_maxWidth
= 0; // recalculate it
625 m_updateScrollbarX
= true;
628 void wxListBox::UpdateScrollbars()
630 wxSize size
= GetClientSize();
632 // is our height enough to show all items?
633 unsigned int nLines
= GetCount();
634 wxCoord lineHeight
= GetLineHeight();
635 bool showScrollbarY
= (int)nLines
*lineHeight
> size
.y
;
637 // check the width too if required
638 wxCoord charWidth
, maxWidth
;
640 if ( HasHorzScrollbar() )
642 charWidth
= GetCharWidth();
643 maxWidth
= GetMaxWidth();
644 showScrollbarX
= maxWidth
> size
.x
;
646 else // never show it
648 charWidth
= maxWidth
= 0;
649 showScrollbarX
= false;
652 // what should be the scrollbar range now?
653 int scrollRangeX
= showScrollbarX
654 ? (maxWidth
+ charWidth
- 1) / charWidth
+ 2 // FIXME
656 int scrollRangeY
= showScrollbarY
658 (size
.y
% lineHeight
+ lineHeight
- 1) / lineHeight
661 // reset scrollbars if something changed: either the visibility status
662 // or the range of a scrollbar which is shown
663 if ( (showScrollbarY
!= m_showScrollbarY
) ||
664 (showScrollbarX
!= m_showScrollbarX
) ||
665 (showScrollbarY
&& (scrollRangeY
!= m_scrollRangeY
)) ||
666 (showScrollbarX
&& (scrollRangeX
!= m_scrollRangeX
)) )
669 GetViewStart(&x
, &y
);
670 SetScrollbars(charWidth
, lineHeight
,
671 scrollRangeX
, scrollRangeY
,
674 m_showScrollbarX
= showScrollbarX
;
675 m_showScrollbarY
= showScrollbarY
;
677 m_scrollRangeX
= scrollRangeX
;
678 m_scrollRangeY
= scrollRangeY
;
682 void wxListBox::UpdateItems()
684 // only refresh the items which must be refreshed
685 if ( m_updateCount
== -1 )
688 wxLogTrace(_T("listbox"), _T("Refreshing all"));
694 wxSize size
= GetClientSize();
697 rect
.height
= size
.y
;
698 rect
.y
+= m_updateFrom
*GetLineHeight();
699 rect
.height
= m_updateCount
*GetLineHeight();
701 // we don't need to calculate x position as we always refresh the
703 CalcScrolledPosition(0, rect
.y
, NULL
, &rect
.y
);
705 wxLogTrace(_T("listbox"), _T("Refreshing items %d..%d (%d-%d)"),
706 m_updateFrom
, m_updateFrom
+ m_updateCount
- 1,
707 rect
.GetTop(), rect
.GetBottom());
709 Refresh(true, &rect
);
713 void wxListBox::OnInternalIdle()
715 if ( m_updateScrollbarY
|| m_updateScrollbarX
)
720 m_updateScrollbarY
= false;
723 if ( m_currentChanged
)
725 DoEnsureVisible(m_current
);
727 m_currentChanged
= false;
736 wxListBoxBase::OnInternalIdle();
739 // ----------------------------------------------------------------------------
741 // ----------------------------------------------------------------------------
743 wxBorder
wxListBox::GetDefaultBorder() const
745 return wxBORDER_SUNKEN
;
748 void wxListBox::DoDraw(wxControlRenderer
*renderer
)
750 // adjust the DC to account for scrolling
751 wxDC
& dc
= renderer
->GetDC();
753 dc
.SetFont(GetFont());
755 // get the update rect
756 wxRect rectUpdate
= GetUpdateClientRect();
759 CalcUnscrolledPosition(0, rectUpdate
.GetTop(), NULL
, &yTop
);
760 CalcUnscrolledPosition(0, rectUpdate
.GetBottom(), NULL
, &yBottom
);
762 // get the items which must be redrawn
763 wxCoord lineHeight
= GetLineHeight();
764 unsigned int itemFirst
= yTop
/ lineHeight
,
765 itemLast
= (yBottom
+ lineHeight
- 1) / lineHeight
,
766 itemMax
= m_strings
->GetCount();
768 if ( itemFirst
>= itemMax
)
771 if ( itemLast
> itemMax
)
775 wxLogTrace(_T("listbox"), _T("Repainting items %d..%d"),
776 itemFirst
, itemLast
);
778 DoDrawRange(renderer
, itemFirst
, itemLast
);
781 void wxListBox::DoDrawRange(wxControlRenderer
*renderer
,
782 int itemFirst
, int itemLast
)
784 renderer
->DrawItems(this, itemFirst
, itemLast
);
787 // ----------------------------------------------------------------------------
789 // ----------------------------------------------------------------------------
791 bool wxListBox::SetFont(const wxFont
& font
)
793 if ( !wxControl::SetFont(font
) )
803 void wxListBox::CalcItemsPerPage()
805 m_lineHeight
= GetRenderer()->GetListboxItemHeight(GetCharHeight());
806 m_itemsPerPage
= GetClientSize().y
/ m_lineHeight
;
809 int wxListBox::GetItemsPerPage() const
811 if ( !m_itemsPerPage
)
813 wxConstCast(this, wxListBox
)->CalcItemsPerPage();
816 return m_itemsPerPage
;
819 wxCoord
wxListBox::GetLineHeight() const
823 wxConstCast(this, wxListBox
)->CalcItemsPerPage();
829 wxCoord
wxListBox::GetMaxWidth() const
831 if ( m_maxWidth
== 0 )
833 wxListBox
*self
= wxConstCast(this, wxListBox
);
835 unsigned int count
= m_strings
->GetCount();
836 for ( unsigned int n
= 0; n
< count
; n
++ )
838 GetTextExtent(this->GetString(n
), &width
, NULL
);
839 if ( width
> m_maxWidth
)
841 self
->m_maxWidth
= width
;
842 self
->m_maxWidthItem
= n
;
850 void wxListBox::OnSize(wxSizeEvent
& event
)
852 // recalculate the number of items per page
855 // the scrollbars might [dis]appear
857 m_updateScrollbarY
= true;
862 void wxListBox::DoSetFirstItem(int n
)
867 void wxListBox::DoSetSize(int x
, int y
,
868 int width
, int height
,
871 if ( GetWindowStyle() & wxLB_INT_HEIGHT
)
873 // we must round up the height to an entire number of rows
875 // the client area must contain an int number of rows, so take borders
877 wxRect rectBorders
= GetRenderer()->GetBorderDimensions(GetBorder());
878 wxCoord hBorders
= rectBorders
.y
+ rectBorders
.height
;
880 wxCoord hLine
= GetLineHeight();
881 height
= ((height
- hBorders
+ hLine
- 1) / hLine
)*hLine
+ hBorders
;
884 wxListBoxBase::DoSetSize(x
, y
, width
, height
, sizeFlags
);
887 wxSize
wxListBox::DoGetBestClientSize() const
892 unsigned int count
= m_strings
->GetCount();
893 for ( unsigned int n
= 0; n
< count
; n
++ )
896 GetTextExtent(this->GetString(n
), &w
, &h
);
904 // if the listbox is empty, still give it some non zero (even if
905 // arbitrary) size - otherwise, leave small margin around the strings
909 width
+= 3*GetCharWidth();
912 height
= GetCharHeight();
914 // we need the height of the entire listbox, not just of one line
915 height
*= wxMax(count
, 7);
917 return wxSize(width
, height
);
920 // ----------------------------------------------------------------------------
922 // ----------------------------------------------------------------------------
924 bool wxListBox::SendEvent(wxEventType type
, int item
)
926 wxCommandEvent
event(type
, m_windowId
);
927 event
.SetEventObject(this);
929 // use the current item by default
935 // client data and string parameters only make sense if we have an item
938 if ( HasClientObjectData() )
939 event
.SetClientObject(GetClientObject(item
));
940 else if ( HasClientUntypedData() )
941 event
.SetClientData(GetClientData(item
));
943 event
.SetString(GetString(item
));
948 return GetEventHandler()->ProcessEvent(event
);
951 void wxListBox::SetCurrentItem(int n
)
953 if ( n
!= m_current
)
955 if ( m_current
!= -1 )
956 RefreshItem(m_current
);
960 if ( m_current
!= -1 )
962 m_currentChanged
= true;
964 RefreshItem(m_current
);
967 //else: nothing to do
970 bool wxListBox::FindItem(const wxString
& prefix
, bool strictlyAfter
)
972 unsigned int count
= GetCount();
975 // empty listbox, we can't find anything in it
979 // start either from the current item or from the next one if strictlyAfter
984 // the following line will set first correctly to 0 if there is no
985 // selection (m_current == -1)
986 first
= m_current
== (int)(count
- 1) ? 0 : m_current
+ 1;
988 else // start with the current
990 first
= m_current
== -1 ? 0 : m_current
;
993 int last
= first
== 0 ? count
- 1 : first
- 1;
995 // if this is not true we'd never exit from the loop below!
996 wxASSERT_MSG( first
< (int)count
&& last
< (int)count
, _T("logic error") );
998 // precompute it outside the loop
999 size_t len
= prefix
.length();
1001 // loop over all items in the listbox
1002 for ( int item
= first
; item
!= (int)last
; item
< (int)(count
- 1) ? item
++ : item
= 0 )
1004 if ( wxStrnicmp(this->GetString(item
).c_str(), prefix
, len
) == 0 )
1006 SetCurrentItem(item
);
1008 if ( !(GetWindowStyle() & wxLB_MULTIPLE
) )
1011 SelectAndNotify(item
);
1013 if ( GetWindowStyle() & wxLB_EXTENDED
)
1014 AnchorSelection(item
);
1025 void wxListBox::EnsureVisible(int n
)
1027 if ( m_updateScrollbarY
)
1031 m_updateScrollbarX
=
1032 m_updateScrollbarY
= false;
1038 void wxListBox::DoEnsureVisible(int n
)
1040 if ( !m_showScrollbarY
)
1042 // nothing to do - everything is shown anyhow
1047 GetViewStart(0, &first
);
1050 // we need to scroll upwards, so make the current item appear on top
1051 // of the shown range
1056 int last
= first
+ GetClientSize().y
/ GetLineHeight() - 1;
1059 // scroll down: the current item appears at the bottom of the
1061 Scroll(0, n
- (last
- first
));
1066 void wxListBox::ChangeCurrent(int diff
)
1068 int current
= m_current
== -1 ? 0 : m_current
;
1072 int last
= GetCount() - 1;
1075 else if ( current
> last
)
1078 SetCurrentItem(current
);
1081 void wxListBox::ExtendSelection(int itemTo
)
1083 // if we don't have the explicit values for selection start/end, make them
1085 if ( m_selAnchor
== -1 )
1086 m_selAnchor
= m_current
;
1091 // swap the start/end of selection range if necessary
1092 int itemFrom
= m_selAnchor
;
1093 if ( itemFrom
> itemTo
)
1095 int itemTmp
= itemFrom
;
1100 // the selection should now include all items in the range between the
1101 // anchor and the specified item and only them
1104 for ( n
= 0; n
< itemFrom
; n
++ )
1109 for ( ; n
<= itemTo
; n
++ )
1114 unsigned int count
= GetCount();
1115 for ( ; n
< (int)count
; n
++ )
1121 void wxListBox::DoSelect(int item
, bool sel
)
1125 // go to this item first
1126 SetCurrentItem(item
);
1129 // the current item is the one we want to change: either it was just
1130 // changed above to be the same as item or item == -1 in which we case we
1131 // are supposed to use the current one anyhow
1132 if ( m_current
!= -1 )
1135 SetSelection(m_current
, sel
);
1139 void wxListBox::SelectAndNotify(int item
)
1143 SendEvent(wxEVT_COMMAND_LISTBOX_SELECTED
);
1146 void wxListBox::Activate(int item
)
1149 SetCurrentItem(item
);
1153 if ( !(GetWindowStyle() & wxLB_MULTIPLE
) )
1162 SendEvent(wxEVT_COMMAND_LISTBOX_DOUBLECLICKED
);
1166 // ----------------------------------------------------------------------------
1168 // ----------------------------------------------------------------------------
1171 The numArg here is the listbox item index while the strArg is used
1172 differently for the different actions:
1174 a) for wxACTION_LISTBOX_FIND it has the natural meaning: this is the string
1177 b) for wxACTION_LISTBOX_SELECT and wxACTION_LISTBOX_EXTENDSEL it is used
1178 to decide if the listbox should send the notification event (it is empty)
1179 or not (it is not): this allows us to reuse the same action for when the
1180 user is dragging the mouse when it has been released although in the
1181 first case no notification is sent while in the second it is sent.
1183 bool wxListBox::PerformAction(const wxControlAction
& action
,
1185 const wxString
& strArg
)
1187 int item
= (int)numArg
;
1189 if ( action
== wxACTION_LISTBOX_SETFOCUS
)
1191 SetCurrentItem(item
);
1193 else if ( action
== wxACTION_LISTBOX_ACTIVATE
)
1197 else if ( action
== wxACTION_LISTBOX_TOGGLE
)
1202 if ( IsSelected(item
) )
1205 SelectAndNotify(item
);
1207 else if ( action
== wxACTION_LISTBOX_SELECT
)
1211 if ( strArg
.empty() )
1212 SelectAndNotify(item
);
1216 else if ( action
== wxACTION_LISTBOX_SELECTADD
)
1218 else if ( action
== wxACTION_LISTBOX_UNSELECT
)
1220 else if ( action
== wxACTION_LISTBOX_MOVEDOWN
)
1222 else if ( action
== wxACTION_LISTBOX_MOVEUP
)
1224 else if ( action
== wxACTION_LISTBOX_PAGEDOWN
)
1225 ChangeCurrent(GetItemsPerPage());
1226 else if ( action
== wxACTION_LISTBOX_PAGEUP
)
1227 ChangeCurrent(-GetItemsPerPage());
1228 else if ( action
== wxACTION_LISTBOX_START
)
1230 else if ( action
== wxACTION_LISTBOX_END
)
1231 SetCurrentItem(GetCount() - 1);
1232 else if ( action
== wxACTION_LISTBOX_UNSELECTALL
)
1234 else if ( action
== wxACTION_LISTBOX_EXTENDSEL
)
1235 ExtendSelection(item
);
1236 else if ( action
== wxACTION_LISTBOX_FIND
)
1237 FindNextItem(strArg
);
1238 else if ( action
== wxACTION_LISTBOX_ANCHOR
)
1239 AnchorSelection(item
== -1 ? m_current
: item
);
1240 else if ( action
== wxACTION_LISTBOX_SELECTALL
||
1241 action
== wxACTION_LISTBOX_SELTOGGLE
)
1242 wxFAIL_MSG(_T("unimplemented yet"));
1244 return wxControl::PerformAction(action
, numArg
, strArg
);
1250 wxInputHandler
*wxListBox::GetStdInputHandler(wxInputHandler
*handlerDef
)
1252 static wxStdListboxInputHandler
s_handler(handlerDef
);
1257 // ============================================================================
1258 // implementation of wxStdListboxInputHandler
1259 // ============================================================================
1261 wxStdListboxInputHandler::wxStdListboxInputHandler(wxInputHandler
*handler
,
1262 bool toggleOnPressAlways
)
1263 : wxStdInputHandler(handler
)
1266 m_toggleOnPressAlways
= toggleOnPressAlways
;
1267 m_actionMouse
= wxACTION_NONE
;
1268 m_trackMouseOutside
= true;
1271 int wxStdListboxInputHandler::HitTest(const wxListBox
*lbox
,
1272 const wxMouseEvent
& event
)
1274 int item
= HitTestUnsafe(lbox
, event
);
1276 return FixItemIndex(lbox
, item
);
1279 int wxStdListboxInputHandler::HitTestUnsafe(const wxListBox
*lbox
,
1280 const wxMouseEvent
& event
)
1282 wxPoint pt
= event
.GetPosition();
1283 pt
-= lbox
->GetClientAreaOrigin();
1285 lbox
->CalcUnscrolledPosition(0, pt
.y
, NULL
, &y
);
1286 return y
/ lbox
->GetLineHeight();
1289 int wxStdListboxInputHandler::FixItemIndex(const wxListBox
*lbox
,
1294 // mouse is above the first item
1297 else if ( (unsigned int)item
>= lbox
->GetCount() )
1299 // mouse is below the last item
1300 item
= lbox
->GetCount() - 1;
1306 bool wxStdListboxInputHandler::IsValidIndex(const wxListBox
*lbox
, int item
)
1308 return item
>= 0 && (unsigned int)item
< lbox
->GetCount();
1312 wxStdListboxInputHandler::SetupCapture(wxListBox
*lbox
,
1313 const wxMouseEvent
& event
,
1316 // we currently only allow selecting with the left mouse button, if we
1317 // do need to allow using other buttons too we might use the code
1320 m_btnCapture
= event
.LeftDown()
1329 wxControlAction action
;
1330 if ( lbox
->HasMultipleSelection() )
1332 if ( lbox
->GetWindowStyle() & wxLB_MULTIPLE
)
1334 if ( m_toggleOnPressAlways
)
1336 // toggle the item right now
1337 action
= wxACTION_LISTBOX_TOGGLE
;
1341 m_actionMouse
= wxACTION_LISTBOX_SETFOCUS
;
1343 else // wxLB_EXTENDED listbox
1345 // simple click in an extended sel listbox clears the old
1346 // selection and adds the clicked item to it then, ctrl-click
1347 // toggles an item to it and shift-click adds a range between
1348 // the old selection anchor and the clicked item
1349 if ( event
.ControlDown() )
1351 lbox
->PerformAction(wxACTION_LISTBOX_ANCHOR
, item
);
1353 action
= wxACTION_LISTBOX_TOGGLE
;
1355 else if ( event
.ShiftDown() )
1357 action
= wxACTION_LISTBOX_EXTENDSEL
;
1359 else // simple click
1361 lbox
->PerformAction(wxACTION_LISTBOX_ANCHOR
, item
);
1363 action
= wxACTION_LISTBOX_SELECT
;
1366 m_actionMouse
= wxACTION_LISTBOX_EXTENDSEL
;
1369 else // single selection
1372 action
= wxACTION_LISTBOX_SELECT
;
1375 // by default we always do track it
1376 m_trackMouseOutside
= true;
1381 bool wxStdListboxInputHandler::HandleKey(wxInputConsumer
*consumer
,
1382 const wxKeyEvent
& event
,
1385 // we're only interested in the key press events
1386 if ( pressed
&& !event
.AltDown() )
1388 bool isMoveCmd
= true;
1389 int style
= consumer
->GetInputWindow()->GetWindowStyle();
1391 wxControlAction action
;
1394 int keycode
= event
.GetKeyCode();
1399 action
= wxACTION_LISTBOX_MOVEUP
;
1403 action
= wxACTION_LISTBOX_MOVEDOWN
;
1407 action
= wxACTION_LISTBOX_PAGEUP
;
1411 action
= wxACTION_LISTBOX_PAGEDOWN
;
1415 action
= wxACTION_LISTBOX_START
;
1419 action
= wxACTION_LISTBOX_END
;
1424 if ( style
& wxLB_MULTIPLE
)
1426 action
= wxACTION_LISTBOX_TOGGLE
;
1432 action
= wxACTION_LISTBOX_ACTIVATE
;
1437 if ( (keycode
< 255) && wxIsalnum((wxChar
)keycode
) )
1439 action
= wxACTION_LISTBOX_FIND
;
1440 strArg
= (wxChar
)keycode
;
1444 if ( !action
.IsEmpty() )
1446 consumer
->PerformAction(action
, -1, strArg
);
1450 if ( style
& wxLB_SINGLE
)
1452 // the current item is always the one selected
1453 consumer
->PerformAction(wxACTION_LISTBOX_SELECT
);
1455 else if ( style
& wxLB_EXTENDED
)
1457 if ( event
.ShiftDown() )
1458 consumer
->PerformAction(wxACTION_LISTBOX_EXTENDSEL
);
1461 // select the item and make it the new selection anchor
1462 consumer
->PerformAction(wxACTION_LISTBOX_SELECT
);
1463 consumer
->PerformAction(wxACTION_LISTBOX_ANCHOR
);
1466 //else: nothing to do for multiple selection listboxes
1473 return wxStdInputHandler::HandleKey(consumer
, event
, pressed
);
1476 bool wxStdListboxInputHandler::HandleMouse(wxInputConsumer
*consumer
,
1477 const wxMouseEvent
& event
)
1479 wxListBox
*lbox
= wxStaticCast(consumer
->GetInputWindow(), wxListBox
);
1480 int item
= HitTest(lbox
, event
);
1481 wxControlAction action
;
1483 // when the left mouse button is pressed, capture the mouse and track the
1484 // item under mouse (if the mouse leaves the window, we will still be
1485 // getting the mouse move messages generated by wxScrollWindow)
1486 if ( event
.LeftDown() )
1488 // capture the mouse to track the selected item
1489 lbox
->CaptureMouse();
1491 action
= SetupCapture(lbox
, event
, item
);
1493 else if ( m_btnCapture
&& event
.ButtonUp(m_btnCapture
) )
1495 // when the left mouse button is released, release the mouse too
1496 wxWindow
*winCapture
= wxWindow::GetCapture();
1499 winCapture
->ReleaseMouse();
1502 action
= m_actionMouse
;
1504 //else: the mouse wasn't presed over the listbox, only released here
1506 else if ( event
.LeftDClick() )
1508 action
= wxACTION_LISTBOX_ACTIVATE
;
1511 if ( !action
.IsEmpty() )
1513 lbox
->PerformAction(action
, item
);
1518 return wxStdInputHandler::HandleMouse(consumer
, event
);
1521 bool wxStdListboxInputHandler::HandleMouseMove(wxInputConsumer
*consumer
,
1522 const wxMouseEvent
& event
)
1524 wxWindow
*winCapture
= wxWindow::GetCapture();
1525 if ( winCapture
&& (event
.GetEventObject() == winCapture
) )
1527 wxListBox
*lbox
= wxStaticCast(consumer
->GetInputWindow(), wxListBox
);
1529 if ( !m_btnCapture
|| !m_trackMouseOutside
)
1531 // someone captured the mouse for us (we always set m_btnCapture
1532 // when we do it ourselves): in this case we only react to
1533 // the mouse messages when they happen inside the listbox
1534 if ( lbox
->HitTest(event
.GetPosition()) != wxHT_WINDOW_INSIDE
)
1538 int item
= HitTest(lbox
, event
);
1539 if ( !m_btnCapture
)
1541 // now that we have the mouse inside the listbox, do capture it
1542 // normally - but ensure that we will still ignore the outside
1544 SetupCapture(lbox
, event
, item
);
1546 m_trackMouseOutside
= false;
1549 if ( IsValidIndex(lbox
, item
) )
1551 // pass something into strArg to tell the listbox that it shouldn't
1552 // send the notification message: see PerformAction() above
1553 lbox
->PerformAction(m_actionMouse
, item
, _T("no"));
1555 // else: don't pass invalid index to the listbox
1557 else // we don't have capture any more
1561 // if we lost capture unexpectedly (someone else took the capture
1562 // from us), return to a consistent state
1567 return wxStdInputHandler::HandleMouseMove(consumer
, event
);
1570 #endif // wxUSE_LISTBOX