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
;
199 SetInitialSize(size
);
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 ( n
== wxNOT_FOUND
)
481 if ( !HasMultipleSelection() )
483 // selecting wxNOT_FOUND is documented to deselect all items
488 else if ( m_selections
.Index(n
) == wxNOT_FOUND
)
490 if ( !HasMultipleSelection() )
492 // selecting an item in a single selection listbox deselects
501 //else: already selected
505 int index
= m_selections
.Index(n
);
506 if ( index
!= wxNOT_FOUND
)
508 m_selections
.RemoveAt(index
);
515 // sanity check: a single selection listbox can't have more than one item
517 wxASSERT_MSG( HasMultipleSelection() || (m_selections
.GetCount() < 2),
518 _T("multiple selected items in single selection lbox?") );
522 // the newly selected item becomes the current one
527 int wxListBox::GetSelection() const
529 wxCHECK_MSG( !HasMultipleSelection(), wxNOT_FOUND
,
530 _T("use wxListBox::GetSelections for ths listbox") );
532 return m_selections
.IsEmpty() ? wxNOT_FOUND
: m_selections
[0];
535 int wxCMPFUNC_CONV
wxCompareInts(int *n
, int *m
)
540 int wxListBox::GetSelections(wxArrayInt
& selections
) const
542 // always return sorted array to the user
543 selections
= m_selections
;
544 unsigned int count
= m_selections
.GetCount();
546 // don't call sort on an empty array
549 selections
.Sort(wxCompareInts
);
555 // ----------------------------------------------------------------------------
556 // refresh logic: we use delayed refreshing which allows to avoid multiple
557 // refreshes (and hence flicker) in case when several listbox items are
558 // added/deleted/changed subsequently
559 // ----------------------------------------------------------------------------
561 void wxListBox::RefreshFromItemToEnd(int from
)
563 RefreshItems(from
, GetCount() - from
);
566 void wxListBox::RefreshItems(int from
, int count
)
568 switch ( m_updateCount
)
572 m_updateCount
= count
;
576 // we refresh everything anyhow
580 // add these items to the others which we have to refresh
581 if ( m_updateFrom
< from
)
583 count
+= from
- m_updateFrom
;
584 if ( m_updateCount
< count
)
585 m_updateCount
= count
;
587 else // m_updateFrom >= from
589 int updateLast
= wxMax(m_updateFrom
+ m_updateCount
,
592 m_updateCount
= updateLast
- m_updateFrom
;
597 void wxListBox::RefreshItem(int n
)
599 switch ( m_updateCount
)
602 // refresh this item only
608 // we refresh everything anyhow
612 // add this item to the others which we have to refresh
613 if ( m_updateFrom
< n
)
615 if ( m_updateCount
< n
- m_updateFrom
+ 1 )
616 m_updateCount
= n
- m_updateFrom
+ 1;
618 else // n <= m_updateFrom
620 m_updateCount
+= m_updateFrom
- n
;
626 void wxListBox::RefreshAll()
631 void wxListBox::RefreshHorzScrollbar()
633 m_maxWidth
= 0; // recalculate it
634 m_updateScrollbarX
= true;
637 void wxListBox::UpdateScrollbars()
639 wxSize size
= GetClientSize();
641 // is our height enough to show all items?
642 unsigned int nLines
= GetCount();
643 wxCoord lineHeight
= GetLineHeight();
644 bool showScrollbarY
= (int)nLines
*lineHeight
> size
.y
;
646 // check the width too if required
647 wxCoord charWidth
, maxWidth
;
649 if ( HasHorzScrollbar() )
651 charWidth
= GetCharWidth();
652 maxWidth
= GetMaxWidth();
653 showScrollbarX
= maxWidth
> size
.x
;
655 else // never show it
657 charWidth
= maxWidth
= 0;
658 showScrollbarX
= false;
661 // what should be the scrollbar range now?
662 int scrollRangeX
= showScrollbarX
663 ? (maxWidth
+ charWidth
- 1) / charWidth
+ 2 // FIXME
665 int scrollRangeY
= showScrollbarY
667 (size
.y
% lineHeight
+ lineHeight
- 1) / lineHeight
670 // reset scrollbars if something changed: either the visibility status
671 // or the range of a scrollbar which is shown
672 if ( (showScrollbarY
!= m_showScrollbarY
) ||
673 (showScrollbarX
!= m_showScrollbarX
) ||
674 (showScrollbarY
&& (scrollRangeY
!= m_scrollRangeY
)) ||
675 (showScrollbarX
&& (scrollRangeX
!= m_scrollRangeX
)) )
678 GetViewStart(&x
, &y
);
679 SetScrollbars(charWidth
, lineHeight
,
680 scrollRangeX
, scrollRangeY
,
683 m_showScrollbarX
= showScrollbarX
;
684 m_showScrollbarY
= showScrollbarY
;
686 m_scrollRangeX
= scrollRangeX
;
687 m_scrollRangeY
= scrollRangeY
;
691 void wxListBox::UpdateItems()
693 // only refresh the items which must be refreshed
694 if ( m_updateCount
== -1 )
697 wxLogTrace(_T("listbox"), _T("Refreshing all"));
703 wxSize size
= GetClientSize();
706 rect
.height
= size
.y
;
707 rect
.y
+= m_updateFrom
*GetLineHeight();
708 rect
.height
= m_updateCount
*GetLineHeight();
710 // we don't need to calculate x position as we always refresh the
712 CalcScrolledPosition(0, rect
.y
, NULL
, &rect
.y
);
714 wxLogTrace(_T("listbox"), _T("Refreshing items %d..%d (%d-%d)"),
715 m_updateFrom
, m_updateFrom
+ m_updateCount
- 1,
716 rect
.GetTop(), rect
.GetBottom());
718 Refresh(true, &rect
);
722 void wxListBox::OnInternalIdle()
724 if ( m_updateScrollbarY
|| m_updateScrollbarX
)
729 m_updateScrollbarY
= false;
732 if ( m_currentChanged
)
734 DoEnsureVisible(m_current
);
736 m_currentChanged
= false;
745 wxListBoxBase::OnInternalIdle();
748 // ----------------------------------------------------------------------------
750 // ----------------------------------------------------------------------------
752 wxBorder
wxListBox::GetDefaultBorder() const
754 return wxBORDER_SUNKEN
;
757 void wxListBox::DoDraw(wxControlRenderer
*renderer
)
759 // adjust the DC to account for scrolling
760 wxDC
& dc
= renderer
->GetDC();
762 dc
.SetFont(GetFont());
764 // get the update rect
765 wxRect rectUpdate
= GetUpdateClientRect();
768 CalcUnscrolledPosition(0, rectUpdate
.GetTop(), NULL
, &yTop
);
769 CalcUnscrolledPosition(0, rectUpdate
.GetBottom(), NULL
, &yBottom
);
771 // get the items which must be redrawn
772 wxCoord lineHeight
= GetLineHeight();
773 unsigned int itemFirst
= yTop
/ lineHeight
,
774 itemLast
= (yBottom
+ lineHeight
- 1) / lineHeight
,
775 itemMax
= m_strings
->GetCount();
777 if ( itemFirst
>= itemMax
)
780 if ( itemLast
> itemMax
)
784 wxLogTrace(_T("listbox"), _T("Repainting items %d..%d"),
785 itemFirst
, itemLast
);
787 DoDrawRange(renderer
, itemFirst
, itemLast
);
790 void wxListBox::DoDrawRange(wxControlRenderer
*renderer
,
791 int itemFirst
, int itemLast
)
793 renderer
->DrawItems(this, itemFirst
, itemLast
);
796 // ----------------------------------------------------------------------------
798 // ----------------------------------------------------------------------------
800 bool wxListBox::SetFont(const wxFont
& font
)
802 if ( !wxControl::SetFont(font
) )
812 void wxListBox::CalcItemsPerPage()
814 m_lineHeight
= GetRenderer()->GetListboxItemHeight(GetCharHeight());
815 m_itemsPerPage
= GetClientSize().y
/ m_lineHeight
;
818 int wxListBox::GetItemsPerPage() const
820 if ( !m_itemsPerPage
)
822 wxConstCast(this, wxListBox
)->CalcItemsPerPage();
825 return m_itemsPerPage
;
828 wxCoord
wxListBox::GetLineHeight() const
832 wxConstCast(this, wxListBox
)->CalcItemsPerPage();
838 wxCoord
wxListBox::GetMaxWidth() const
840 if ( m_maxWidth
== 0 )
842 wxListBox
*self
= wxConstCast(this, wxListBox
);
844 unsigned int count
= m_strings
->GetCount();
845 for ( unsigned int n
= 0; n
< count
; n
++ )
847 GetTextExtent(this->GetString(n
), &width
, NULL
);
848 if ( width
> m_maxWidth
)
850 self
->m_maxWidth
= width
;
851 self
->m_maxWidthItem
= n
;
859 void wxListBox::OnSize(wxSizeEvent
& event
)
861 // recalculate the number of items per page
864 // the scrollbars might [dis]appear
866 m_updateScrollbarY
= true;
871 void wxListBox::DoSetFirstItem(int n
)
876 void wxListBox::DoSetSize(int x
, int y
,
877 int width
, int height
,
880 if ( GetWindowStyle() & wxLB_INT_HEIGHT
)
882 // we must round up the height to an entire number of rows
884 // the client area must contain an int number of rows, so take borders
886 wxRect rectBorders
= GetRenderer()->GetBorderDimensions(GetBorder());
887 wxCoord hBorders
= rectBorders
.y
+ rectBorders
.height
;
889 wxCoord hLine
= GetLineHeight();
890 height
= ((height
- hBorders
+ hLine
- 1) / hLine
)*hLine
+ hBorders
;
893 wxListBoxBase::DoSetSize(x
, y
, width
, height
, sizeFlags
);
896 wxSize
wxListBox::DoGetBestClientSize() const
901 unsigned int count
= m_strings
->GetCount();
902 for ( unsigned int n
= 0; n
< count
; n
++ )
905 GetTextExtent(this->GetString(n
), &w
, &h
);
913 // if the listbox is empty, still give it some non zero (even if
914 // arbitrary) size - otherwise, leave small margin around the strings
918 width
+= 3*GetCharWidth();
921 height
= GetCharHeight();
923 // we need the height of the entire listbox, not just of one line
924 height
*= wxMax(count
, 7);
926 return wxSize(width
, height
);
929 // ----------------------------------------------------------------------------
931 // ----------------------------------------------------------------------------
933 bool wxListBox::SendEvent(wxEventType type
, int item
)
935 wxCommandEvent
event(type
, m_windowId
);
936 event
.SetEventObject(this);
938 // use the current item by default
944 // client data and string parameters only make sense if we have an item
947 if ( HasClientObjectData() )
948 event
.SetClientObject(GetClientObject(item
));
949 else if ( HasClientUntypedData() )
950 event
.SetClientData(GetClientData(item
));
952 event
.SetString(GetString(item
));
957 return GetEventHandler()->ProcessEvent(event
);
960 void wxListBox::SetCurrentItem(int n
)
962 if ( n
!= m_current
)
964 if ( m_current
!= -1 )
965 RefreshItem(m_current
);
969 if ( m_current
!= -1 )
971 m_currentChanged
= true;
973 RefreshItem(m_current
);
976 //else: nothing to do
979 bool wxListBox::FindItem(const wxString
& prefix
, bool strictlyAfter
)
981 unsigned int count
= GetCount();
984 // empty listbox, we can't find anything in it
988 // start either from the current item or from the next one if strictlyAfter
993 // the following line will set first correctly to 0 if there is no
994 // selection (m_current == -1)
995 first
= m_current
== (int)(count
- 1) ? 0 : m_current
+ 1;
997 else // start with the current
999 first
= m_current
== -1 ? 0 : m_current
;
1002 int last
= first
== 0 ? count
- 1 : first
- 1;
1004 // if this is not true we'd never exit from the loop below!
1005 wxASSERT_MSG( first
< (int)count
&& last
< (int)count
, _T("logic error") );
1007 // precompute it outside the loop
1008 size_t len
= prefix
.length();
1010 // loop over all items in the listbox
1011 for ( int item
= first
; item
!= (int)last
; item
< (int)(count
- 1) ? item
++ : item
= 0 )
1013 if ( wxStrnicmp(this->GetString(item
).c_str(), prefix
, len
) == 0 )
1015 SetCurrentItem(item
);
1017 if ( !(GetWindowStyle() & wxLB_MULTIPLE
) )
1020 SelectAndNotify(item
);
1022 if ( GetWindowStyle() & wxLB_EXTENDED
)
1023 AnchorSelection(item
);
1034 void wxListBox::EnsureVisible(int n
)
1036 if ( m_updateScrollbarY
)
1040 m_updateScrollbarX
=
1041 m_updateScrollbarY
= false;
1047 void wxListBox::DoEnsureVisible(int n
)
1049 if ( !m_showScrollbarY
)
1051 // nothing to do - everything is shown anyhow
1056 GetViewStart(0, &first
);
1059 // we need to scroll upwards, so make the current item appear on top
1060 // of the shown range
1065 int last
= first
+ GetClientSize().y
/ GetLineHeight() - 1;
1068 // scroll down: the current item appears at the bottom of the
1070 Scroll(0, n
- (last
- first
));
1075 void wxListBox::ChangeCurrent(int diff
)
1077 int current
= m_current
== -1 ? 0 : m_current
;
1081 int last
= GetCount() - 1;
1084 else if ( current
> last
)
1087 SetCurrentItem(current
);
1090 void wxListBox::ExtendSelection(int itemTo
)
1092 // if we don't have the explicit values for selection start/end, make them
1094 if ( m_selAnchor
== -1 )
1095 m_selAnchor
= m_current
;
1100 // swap the start/end of selection range if necessary
1101 int itemFrom
= m_selAnchor
;
1102 if ( itemFrom
> itemTo
)
1104 int itemTmp
= itemFrom
;
1109 // the selection should now include all items in the range between the
1110 // anchor and the specified item and only them
1113 for ( n
= 0; n
< itemFrom
; n
++ )
1118 for ( ; n
<= itemTo
; n
++ )
1123 unsigned int count
= GetCount();
1124 for ( ; n
< (int)count
; n
++ )
1130 void wxListBox::DoSelect(int item
, bool sel
)
1134 // go to this item first
1135 SetCurrentItem(item
);
1138 // the current item is the one we want to change: either it was just
1139 // changed above to be the same as item or item == -1 in which we case we
1140 // are supposed to use the current one anyhow
1141 if ( m_current
!= -1 )
1144 SetSelection(m_current
, sel
);
1148 void wxListBox::SelectAndNotify(int item
)
1152 SendEvent(wxEVT_COMMAND_LISTBOX_SELECTED
);
1155 void wxListBox::Activate(int item
)
1158 SetCurrentItem(item
);
1162 if ( !(GetWindowStyle() & wxLB_MULTIPLE
) )
1171 SendEvent(wxEVT_COMMAND_LISTBOX_DOUBLECLICKED
);
1175 // ----------------------------------------------------------------------------
1177 // ----------------------------------------------------------------------------
1180 The numArg here is the listbox item index while the strArg is used
1181 differently for the different actions:
1183 a) for wxACTION_LISTBOX_FIND it has the natural meaning: this is the string
1186 b) for wxACTION_LISTBOX_SELECT and wxACTION_LISTBOX_EXTENDSEL it is used
1187 to decide if the listbox should send the notification event (it is empty)
1188 or not (it is not): this allows us to reuse the same action for when the
1189 user is dragging the mouse when it has been released although in the
1190 first case no notification is sent while in the second it is sent.
1192 bool wxListBox::PerformAction(const wxControlAction
& action
,
1194 const wxString
& strArg
)
1196 int item
= (int)numArg
;
1198 if ( action
== wxACTION_LISTBOX_SETFOCUS
)
1200 SetCurrentItem(item
);
1202 else if ( action
== wxACTION_LISTBOX_ACTIVATE
)
1206 else if ( action
== wxACTION_LISTBOX_TOGGLE
)
1211 if ( IsSelected(item
) )
1214 SelectAndNotify(item
);
1216 else if ( action
== wxACTION_LISTBOX_SELECT
)
1220 if ( strArg
.empty() )
1221 SelectAndNotify(item
);
1225 else if ( action
== wxACTION_LISTBOX_SELECTADD
)
1227 else if ( action
== wxACTION_LISTBOX_UNSELECT
)
1229 else if ( action
== wxACTION_LISTBOX_MOVEDOWN
)
1231 else if ( action
== wxACTION_LISTBOX_MOVEUP
)
1233 else if ( action
== wxACTION_LISTBOX_PAGEDOWN
)
1234 ChangeCurrent(GetItemsPerPage());
1235 else if ( action
== wxACTION_LISTBOX_PAGEUP
)
1236 ChangeCurrent(-GetItemsPerPage());
1237 else if ( action
== wxACTION_LISTBOX_START
)
1239 else if ( action
== wxACTION_LISTBOX_END
)
1240 SetCurrentItem(GetCount() - 1);
1241 else if ( action
== wxACTION_LISTBOX_UNSELECTALL
)
1243 else if ( action
== wxACTION_LISTBOX_EXTENDSEL
)
1244 ExtendSelection(item
);
1245 else if ( action
== wxACTION_LISTBOX_FIND
)
1246 FindNextItem(strArg
);
1247 else if ( action
== wxACTION_LISTBOX_ANCHOR
)
1248 AnchorSelection(item
== -1 ? m_current
: item
);
1249 else if ( action
== wxACTION_LISTBOX_SELECTALL
||
1250 action
== wxACTION_LISTBOX_SELTOGGLE
)
1251 wxFAIL_MSG(_T("unimplemented yet"));
1253 return wxControl::PerformAction(action
, numArg
, strArg
);
1259 wxInputHandler
*wxListBox::GetStdInputHandler(wxInputHandler
*handlerDef
)
1261 static wxStdListboxInputHandler
s_handler(handlerDef
);
1266 // ============================================================================
1267 // implementation of wxStdListboxInputHandler
1268 // ============================================================================
1270 wxStdListboxInputHandler::wxStdListboxInputHandler(wxInputHandler
*handler
,
1271 bool toggleOnPressAlways
)
1272 : wxStdInputHandler(handler
)
1275 m_toggleOnPressAlways
= toggleOnPressAlways
;
1276 m_actionMouse
= wxACTION_NONE
;
1277 m_trackMouseOutside
= true;
1280 int wxStdListboxInputHandler::HitTest(const wxListBox
*lbox
,
1281 const wxMouseEvent
& event
)
1283 int item
= HitTestUnsafe(lbox
, event
);
1285 return FixItemIndex(lbox
, item
);
1288 int wxStdListboxInputHandler::HitTestUnsafe(const wxListBox
*lbox
,
1289 const wxMouseEvent
& event
)
1291 wxPoint pt
= event
.GetPosition();
1292 pt
-= lbox
->GetClientAreaOrigin();
1294 lbox
->CalcUnscrolledPosition(0, pt
.y
, NULL
, &y
);
1295 return y
/ lbox
->GetLineHeight();
1298 int wxStdListboxInputHandler::FixItemIndex(const wxListBox
*lbox
,
1303 // mouse is above the first item
1306 else if ( (unsigned int)item
>= lbox
->GetCount() )
1308 // mouse is below the last item
1309 item
= lbox
->GetCount() - 1;
1315 bool wxStdListboxInputHandler::IsValidIndex(const wxListBox
*lbox
, int item
)
1317 return item
>= 0 && (unsigned int)item
< lbox
->GetCount();
1321 wxStdListboxInputHandler::SetupCapture(wxListBox
*lbox
,
1322 const wxMouseEvent
& event
,
1325 // we currently only allow selecting with the left mouse button, if we
1326 // do need to allow using other buttons too we might use the code
1329 m_btnCapture
= event
.LeftDown()
1338 wxControlAction action
;
1339 if ( lbox
->HasMultipleSelection() )
1341 if ( lbox
->GetWindowStyle() & wxLB_MULTIPLE
)
1343 if ( m_toggleOnPressAlways
)
1345 // toggle the item right now
1346 action
= wxACTION_LISTBOX_TOGGLE
;
1350 m_actionMouse
= wxACTION_LISTBOX_SETFOCUS
;
1352 else // wxLB_EXTENDED listbox
1354 // simple click in an extended sel listbox clears the old
1355 // selection and adds the clicked item to it then, ctrl-click
1356 // toggles an item to it and shift-click adds a range between
1357 // the old selection anchor and the clicked item
1358 if ( event
.ControlDown() )
1360 lbox
->PerformAction(wxACTION_LISTBOX_ANCHOR
, item
);
1362 action
= wxACTION_LISTBOX_TOGGLE
;
1364 else if ( event
.ShiftDown() )
1366 action
= wxACTION_LISTBOX_EXTENDSEL
;
1368 else // simple click
1370 lbox
->PerformAction(wxACTION_LISTBOX_ANCHOR
, item
);
1372 action
= wxACTION_LISTBOX_SELECT
;
1375 m_actionMouse
= wxACTION_LISTBOX_EXTENDSEL
;
1378 else // single selection
1381 action
= wxACTION_LISTBOX_SELECT
;
1384 // by default we always do track it
1385 m_trackMouseOutside
= true;
1390 bool wxStdListboxInputHandler::HandleKey(wxInputConsumer
*consumer
,
1391 const wxKeyEvent
& event
,
1394 // we're only interested in the key press events
1395 if ( pressed
&& !event
.AltDown() )
1397 bool isMoveCmd
= true;
1398 int style
= consumer
->GetInputWindow()->GetWindowStyle();
1400 wxControlAction action
;
1403 int keycode
= event
.GetKeyCode();
1408 action
= wxACTION_LISTBOX_MOVEUP
;
1412 action
= wxACTION_LISTBOX_MOVEDOWN
;
1416 action
= wxACTION_LISTBOX_PAGEUP
;
1420 action
= wxACTION_LISTBOX_PAGEDOWN
;
1424 action
= wxACTION_LISTBOX_START
;
1428 action
= wxACTION_LISTBOX_END
;
1433 if ( style
& wxLB_MULTIPLE
)
1435 action
= wxACTION_LISTBOX_TOGGLE
;
1441 action
= wxACTION_LISTBOX_ACTIVATE
;
1446 if ( (keycode
< 255) && wxIsalnum((wxChar
)keycode
) )
1448 action
= wxACTION_LISTBOX_FIND
;
1449 strArg
= (wxChar
)keycode
;
1453 if ( !action
.IsEmpty() )
1455 consumer
->PerformAction(action
, -1, strArg
);
1459 if ( style
& wxLB_SINGLE
)
1461 // the current item is always the one selected
1462 consumer
->PerformAction(wxACTION_LISTBOX_SELECT
);
1464 else if ( style
& wxLB_EXTENDED
)
1466 if ( event
.ShiftDown() )
1467 consumer
->PerformAction(wxACTION_LISTBOX_EXTENDSEL
);
1470 // select the item and make it the new selection anchor
1471 consumer
->PerformAction(wxACTION_LISTBOX_SELECT
);
1472 consumer
->PerformAction(wxACTION_LISTBOX_ANCHOR
);
1475 //else: nothing to do for multiple selection listboxes
1482 return wxStdInputHandler::HandleKey(consumer
, event
, pressed
);
1485 bool wxStdListboxInputHandler::HandleMouse(wxInputConsumer
*consumer
,
1486 const wxMouseEvent
& event
)
1488 wxListBox
*lbox
= wxStaticCast(consumer
->GetInputWindow(), wxListBox
);
1489 int item
= HitTest(lbox
, event
);
1490 wxControlAction action
;
1492 // when the left mouse button is pressed, capture the mouse and track the
1493 // item under mouse (if the mouse leaves the window, we will still be
1494 // getting the mouse move messages generated by wxScrollWindow)
1495 if ( event
.LeftDown() )
1497 // capture the mouse to track the selected item
1498 lbox
->CaptureMouse();
1500 action
= SetupCapture(lbox
, event
, item
);
1502 else if ( m_btnCapture
&& event
.ButtonUp(m_btnCapture
) )
1504 // when the left mouse button is released, release the mouse too
1505 wxWindow
*winCapture
= wxWindow::GetCapture();
1508 winCapture
->ReleaseMouse();
1511 action
= m_actionMouse
;
1513 //else: the mouse wasn't presed over the listbox, only released here
1515 else if ( event
.LeftDClick() )
1517 action
= wxACTION_LISTBOX_ACTIVATE
;
1520 if ( !action
.IsEmpty() )
1522 lbox
->PerformAction(action
, item
);
1527 return wxStdInputHandler::HandleMouse(consumer
, event
);
1530 bool wxStdListboxInputHandler::HandleMouseMove(wxInputConsumer
*consumer
,
1531 const wxMouseEvent
& event
)
1533 wxWindow
*winCapture
= wxWindow::GetCapture();
1534 if ( winCapture
&& (event
.GetEventObject() == winCapture
) )
1536 wxListBox
*lbox
= wxStaticCast(consumer
->GetInputWindow(), wxListBox
);
1538 if ( !m_btnCapture
|| !m_trackMouseOutside
)
1540 // someone captured the mouse for us (we always set m_btnCapture
1541 // when we do it ourselves): in this case we only react to
1542 // the mouse messages when they happen inside the listbox
1543 if ( lbox
->HitTest(event
.GetPosition()) != wxHT_WINDOW_INSIDE
)
1547 int item
= HitTest(lbox
, event
);
1548 if ( !m_btnCapture
)
1550 // now that we have the mouse inside the listbox, do capture it
1551 // normally - but ensure that we will still ignore the outside
1553 SetupCapture(lbox
, event
, item
);
1555 m_trackMouseOutside
= false;
1558 if ( IsValidIndex(lbox
, item
) )
1560 // pass something into strArg to tell the listbox that it shouldn't
1561 // send the notification message: see PerformAction() above
1562 lbox
->PerformAction(m_actionMouse
, item
, _T("no"));
1564 // else: don't pass invalid index to the listbox
1566 else // we don't have capture any more
1570 // if we lost capture unexpectedly (someone else took the capture
1571 // from us), return to a consistent state
1576 return wxStdInputHandler::HandleMouseMove(consumer
, event
);
1579 #endif // wxUSE_LISTBOX