1 /////////////////////////////////////////////////////////////////////////////
2 // Name: 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 // ----------------------------------------------------------------------------
21 #pragma implementation "univlistbox.h"
24 #include "wx/wxprec.h"
35 #include "wx/dcclient.h"
36 #include "wx/listbox.h"
37 #include "wx/validate.h"
40 #include "wx/univ/renderer.h"
41 #include "wx/univ/inphand.h"
42 #include "wx/univ/theme.h"
44 // ============================================================================
45 // implementation of wxListBox
46 // ============================================================================
48 IMPLEMENT_DYNAMIC_CLASS(wxListBox
, wxControl
)
50 BEGIN_EVENT_TABLE(wxListBox
, wxListBoxBase
)
51 EVT_SIZE(wxListBox::OnSize
)
53 EVT_IDLE(wxListBox::OnIdle
)
56 // ----------------------------------------------------------------------------
58 // ----------------------------------------------------------------------------
60 void wxListBox::Init()
62 // will be calculated later when needed
69 // no items hence no current item
72 m_currentChanged
= FALSE
;
74 // no need to update anything initially
77 // no scrollbars to show nor update
81 m_showScrollbarY
= FALSE
;
84 bool wxListBox::Create(wxWindow
*parent
,
89 const wxString choices
[],
91 const wxValidator
& validator
,
94 // for compatibility accept both the new and old styles - they mean the
96 if ( style
& wxLB_ALWAYS_SB
)
97 style
|= wxALWAYS_SHOW_SB
;
99 // if we don't have neither multiple nor extended flag, we must have the
100 // single selection listbox
101 if ( !(style
& (wxLB_MULTIPLE
| wxLB_EXTENDED
)) )
102 style
|= wxLB_SINGLE
;
104 #if wxUSE_TWO_WINDOWS
105 style
|= wxVSCROLL
|wxHSCROLL
;
106 if ((style
& wxBORDER_MASK
) == 0)
107 style
|= wxBORDER_SUNKEN
;
110 if ( !wxControl::Create(parent
, id
, pos
, size
, style
,
111 wxDefaultValidator
, name
) )
116 if ( style
& wxLB_SORT
)
117 m_strings
= wxArrayString(TRUE
/* auto sort */);
123 CreateInputHandler(wxINP_HANDLER_LISTBOX
);
128 wxListBox::~wxListBox()
132 // ----------------------------------------------------------------------------
133 // adding/inserting strings
134 // ----------------------------------------------------------------------------
136 int wxListBox::DoAppend(const wxString
& item
)
138 size_t index
= m_strings
.Add(item
);
139 m_itemsClientData
.Insert(NULL
, index
);
141 m_updateScrollbarY
= TRUE
;
143 if ( HasHorzScrollbar() )
145 // has the max width increased?
147 GetTextExtent(item
, &width
, NULL
);
148 if ( width
> m_maxWidth
)
151 m_maxWidthItem
= index
;
152 m_updateScrollbarX
= TRUE
;
156 RefreshFromItemToEnd(index
);
161 void wxListBox::DoInsertItems(const wxArrayString
& items
, int pos
)
163 // the position of the item being added to a sorted listbox can't be
165 wxCHECK_RET( !IsSorted(), _T("can't insert items into sorted listbox") );
167 size_t count
= items
.GetCount();
168 for ( size_t n
= 0; n
< count
; n
++ )
170 m_strings
.Insert(items
[n
], pos
+ n
);
171 m_itemsClientData
.Insert(NULL
, pos
+ n
);
174 // the number of items has changed so we might have to show the scrollbar
175 m_updateScrollbarY
= TRUE
;
177 // the max width also might have changed - just recalculate it instead of
178 // keeping track of it here, this is probably more efficient for a typical
180 RefreshHorzScrollbar();
182 // note that we have to refresh all the items after the ones we inserted,
183 // not just these items
184 RefreshFromItemToEnd(pos
);
187 void wxListBox::DoSetItems(const wxArrayString
& items
, void **clientData
)
191 size_t count
= items
.GetCount();
195 m_strings
.Alloc(count
);
196 m_itemsClientData
.Alloc(count
);
197 for ( size_t n
= 0; n
< count
; n
++ )
199 size_t index
= m_strings
.Add(items
[n
]);
200 m_itemsClientData
.Insert(clientData
? clientData
[n
] : NULL
, index
);
203 m_updateScrollbarY
= TRUE
;
208 void wxListBox::SetString(int n
, const wxString
& s
)
210 if ( HasHorzScrollbar() )
212 // we need to update m_maxWidth as changing the string may cause the
213 // horz scrollbar [dis]appear
216 GetTextExtent(s
, &width
, NULL
);
218 // it might have increased if the new string is long
219 if ( width
> m_maxWidth
)
223 m_updateScrollbarX
= TRUE
;
225 // or also decreased if the old string was the longest one
226 else if ( n
== m_maxWidthItem
)
228 RefreshHorzScrollbar();
231 else // no horz scrollbar
239 // ----------------------------------------------------------------------------
241 // ----------------------------------------------------------------------------
243 void wxListBox::DoClear()
247 if ( HasClientObjectData() )
249 size_t count
= m_itemsClientData
.GetCount();
250 for ( size_t n
= 0; n
< count
; n
++ )
252 delete (wxClientData
*) m_itemsClientData
[n
];
256 m_itemsClientData
.Clear();
257 m_selections
.Clear();
262 void wxListBox::Clear()
266 m_updateScrollbarY
= TRUE
;
268 RefreshHorzScrollbar();
273 void wxListBox::Delete(int n
)
275 wxCHECK_RET( n
< GetCount(), _T("invalid index in wxListBox::Delete") );
277 // do it before removing the index as otherwise the last item will not be
278 // refreshed (as GetCount() will be decremented)
279 RefreshFromItemToEnd(n
);
281 m_strings
.RemoveAt(n
);
283 if ( HasClientObjectData() )
285 delete (wxClientData
*)m_itemsClientData
[n
];
288 m_itemsClientData
.RemoveAt(n
);
290 // when the item disappears we must not keep using its index
291 if ( n
== m_current
)
295 else if ( n
< m_current
)
299 //else: current item may stay
301 // update the selections array: the indices of all seletected items after
302 // the one being deleted must change and the item itselfm ust be removed
303 int index
= wxNOT_FOUND
;
304 size_t count
= m_selections
.GetCount();
305 for ( size_t item
= 0; item
< count
; item
++ )
307 if ( m_selections
[item
] == n
)
309 // remember to delete it later
312 else if ( m_selections
[item
] > n
)
314 // to account for the index shift
315 m_selections
[item
]--;
317 //else: nothing changed for this one
320 if ( index
!= wxNOT_FOUND
)
322 m_selections
.RemoveAt(index
);
325 // the number of items has changed, hence the scrollbar may disappear
326 m_updateScrollbarY
= TRUE
;
328 // finally, if the longest item was deleted the scrollbar may disappear
329 if ( n
== m_maxWidthItem
)
331 RefreshHorzScrollbar();
335 // ----------------------------------------------------------------------------
336 // client data handling
337 // ----------------------------------------------------------------------------
339 void wxListBox::DoSetItemClientData(int n
, void* clientData
)
341 m_itemsClientData
[n
] = clientData
;
344 void *wxListBox::DoGetItemClientData(int n
) const
346 return m_itemsClientData
[n
];
349 void wxListBox::DoSetItemClientObject(int n
, wxClientData
* clientData
)
351 m_itemsClientData
[n
] = clientData
;
354 wxClientData
* wxListBox::DoGetItemClientObject(int n
) const
356 return (wxClientData
*)m_itemsClientData
[n
];
359 // ----------------------------------------------------------------------------
361 // ----------------------------------------------------------------------------
363 void wxListBox::SetSelection(int n
, bool select
)
367 if ( m_selections
.Index(n
) == wxNOT_FOUND
)
369 if ( !HasMultipleSelection() )
371 // selecting an item in a single selection listbox deselects
380 //else: already selected
384 int index
= m_selections
.Index(n
);
385 if ( index
!= wxNOT_FOUND
)
387 m_selections
.RemoveAt(index
);
394 // sanity check: a single selection listbox can't have more than one item
396 wxASSERT_MSG( HasMultipleSelection() || (m_selections
.GetCount() < 2),
397 _T("multiple selected items in single selection lbox?") );
401 // the newly selected item becomes the current one
406 int wxListBox::GetSelection() const
408 wxCHECK_MSG( !HasMultipleSelection(), -1,
409 _T("use wxListBox::GetSelections for ths listbox") );
411 return m_selections
.IsEmpty() ? -1 : m_selections
[0];
414 int wxCMPFUNC_CONV
wxCompareInts(int *n
, int *m
)
419 int wxListBox::GetSelections(wxArrayInt
& selections
) const
421 // always return sorted array to the user
422 selections
= m_selections
;
423 size_t count
= m_selections
.GetCount();
425 // don't call sort on an empty array
428 selections
.Sort(wxCompareInts
);
434 // ----------------------------------------------------------------------------
435 // refresh logic: we use delayed refreshing which allows to avoid multiple
436 // refreshes (and hence flicker) in case when several listbox items are
437 // added/deleted/changed subsequently
438 // ----------------------------------------------------------------------------
440 void wxListBox::RefreshFromItemToEnd(int from
)
442 RefreshItems(from
, GetCount() - from
);
445 void wxListBox::RefreshItems(int from
, int count
)
447 switch ( m_updateCount
)
451 m_updateCount
= count
;
455 // we refresh everything anyhow
459 // add these items to the others which we have to refresh
460 if ( m_updateFrom
< from
)
462 count
+= from
- m_updateFrom
;
463 if ( m_updateCount
< count
)
464 m_updateCount
= count
;
466 else // m_updateFrom >= from
468 int updateLast
= wxMax(m_updateFrom
+ m_updateCount
,
471 m_updateCount
= updateLast
- m_updateFrom
;
476 void wxListBox::RefreshItem(int n
)
478 switch ( m_updateCount
)
481 // refresh this item only
487 // we refresh everything anyhow
491 // add this item to the others which we have to refresh
492 if ( m_updateFrom
< n
)
494 if ( m_updateCount
< n
- m_updateFrom
+ 1 )
495 m_updateCount
= n
- m_updateFrom
+ 1;
497 else // n <= m_updateFrom
499 m_updateCount
+= m_updateFrom
- n
;
505 void wxListBox::RefreshAll()
510 void wxListBox::RefreshHorzScrollbar()
512 m_maxWidth
= 0; // recalculate it
513 m_updateScrollbarX
= TRUE
;
516 void wxListBox::UpdateScrollbars()
518 wxSize size
= GetClientSize();
520 // is our height enough to show all items?
521 int nLines
= GetCount();
522 wxCoord lineHeight
= GetLineHeight();
523 bool showScrollbarY
= nLines
*lineHeight
> size
.y
;
525 // check the width too if required
526 wxCoord charWidth
, maxWidth
;
528 if ( HasHorzScrollbar() )
530 charWidth
= GetCharWidth();
531 maxWidth
= GetMaxWidth();
532 showScrollbarX
= maxWidth
> size
.x
;
534 else // never show it
536 charWidth
= maxWidth
= 0;
537 showScrollbarX
= FALSE
;
540 // what should be the scrollbar range now?
541 int scrollRangeX
= showScrollbarX
542 ? (maxWidth
+ charWidth
- 1) / charWidth
+ 2 // FIXME
544 int scrollRangeY
= showScrollbarY
546 (size
.y
% lineHeight
+ lineHeight
- 1) / lineHeight
549 // reset scrollbars if something changed: either the visibility status
550 // or the range of a scrollbar which is shown
551 if ( (showScrollbarY
!= m_showScrollbarY
) ||
552 (showScrollbarX
!= m_showScrollbarX
) ||
553 (showScrollbarY
&& (scrollRangeY
!= m_scrollRangeY
)) ||
554 (showScrollbarX
&& (scrollRangeX
!= m_scrollRangeX
)) )
557 GetViewStart(&x
, &y
);
558 SetScrollbars(charWidth
, lineHeight
,
559 scrollRangeX
, scrollRangeY
,
562 m_showScrollbarX
= showScrollbarX
;
563 m_showScrollbarY
= showScrollbarY
;
565 m_scrollRangeX
= scrollRangeX
;
566 m_scrollRangeY
= scrollRangeY
;
570 void wxListBox::UpdateItems()
572 // only refresh the items which must be refreshed
573 if ( m_updateCount
== -1 )
576 wxLogTrace(_T("listbox"), _T("Refreshing all"));
582 wxSize size
= GetClientSize();
585 rect
.height
= size
.y
;
586 rect
.y
+= m_updateFrom
*GetLineHeight();
587 rect
.height
= m_updateCount
*GetLineHeight();
589 // we don't need to calculate x position as we always refresh the
591 CalcScrolledPosition(0, rect
.y
, NULL
, &rect
.y
);
593 wxLogTrace(_T("listbox"), _T("Refreshing items %d..%d (%d-%d)"),
594 m_updateFrom
, m_updateFrom
+ m_updateCount
- 1,
595 rect
.GetTop(), rect
.GetBottom());
597 Refresh(TRUE
, &rect
);
601 void wxListBox::OnIdle(wxIdleEvent
& event
)
603 if ( m_updateScrollbarY
|| m_updateScrollbarX
)
608 m_updateScrollbarY
= FALSE
;
611 if ( m_currentChanged
)
613 DoEnsureVisible(m_current
);
615 m_currentChanged
= FALSE
;
628 // ----------------------------------------------------------------------------
630 // ----------------------------------------------------------------------------
632 wxBorder
wxListBox::GetDefaultBorder() const
634 return wxBORDER_SUNKEN
;
637 void wxListBox::DoDraw(wxControlRenderer
*renderer
)
639 // adjust the DC to account for scrolling
640 wxDC
& dc
= renderer
->GetDC();
642 dc
.SetFont(GetFont());
644 // get the update rect
645 wxRect rectUpdate
= GetUpdateClientRect();
648 CalcUnscrolledPosition(0, rectUpdate
.GetTop(), NULL
, &yTop
);
649 CalcUnscrolledPosition(0, rectUpdate
.GetBottom(), NULL
, &yBottom
);
651 // get the items which must be redrawn
652 wxCoord lineHeight
= GetLineHeight();
653 size_t itemFirst
= yTop
/ lineHeight
,
654 itemLast
= (yBottom
+ lineHeight
- 1) / lineHeight
,
655 itemMax
= m_strings
.GetCount();
657 if ( itemFirst
>= itemMax
)
660 if ( itemLast
> itemMax
)
664 wxLogTrace(_T("listbox"), _T("Repainting items %d..%d"),
665 itemFirst
, itemLast
);
667 DoDrawRange(renderer
, itemFirst
, itemLast
);
670 void wxListBox::DoDrawRange(wxControlRenderer
*renderer
,
671 int itemFirst
, int itemLast
)
673 renderer
->DrawItems(this, itemFirst
, itemLast
);
676 // ----------------------------------------------------------------------------
678 // ----------------------------------------------------------------------------
680 bool wxListBox::SetFont(const wxFont
& font
)
682 if ( !wxControl::SetFont(font
) )
692 void wxListBox::CalcItemsPerPage()
694 m_lineHeight
= GetRenderer()->GetListboxItemHeight(GetCharHeight());
695 m_itemsPerPage
= GetClientSize().y
/ m_lineHeight
;
698 int wxListBox::GetItemsPerPage() const
700 if ( !m_itemsPerPage
)
702 wxConstCast(this, wxListBox
)->CalcItemsPerPage();
705 return m_itemsPerPage
;
708 wxCoord
wxListBox::GetLineHeight() const
712 wxConstCast(this, wxListBox
)->CalcItemsPerPage();
718 wxCoord
wxListBox::GetMaxWidth() const
720 if ( m_maxWidth
== 0 )
722 wxListBox
*self
= wxConstCast(this, wxListBox
);
724 size_t count
= m_strings
.GetCount();
725 for ( size_t n
= 0; n
< count
; n
++ )
727 GetTextExtent(m_strings
[n
], &width
, NULL
);
728 if ( width
> m_maxWidth
)
730 self
->m_maxWidth
= width
;
731 self
->m_maxWidthItem
= n
;
739 void wxListBox::OnSize(wxSizeEvent
& event
)
741 // recalculate the number of items per page
744 // the scrollbars might [dis]appear
746 m_updateScrollbarY
= TRUE
;
751 void wxListBox::DoSetFirstItem(int n
)
756 void wxListBox::DoSetSize(int x
, int y
,
757 int width
, int height
,
760 if ( GetWindowStyle() & wxLB_INT_HEIGHT
)
762 // we must round up the height to an entire number of rows
764 // the client area must contain an int number of rows, so take borders
766 wxRect rectBorders
= GetRenderer()->GetBorderDimensions(GetBorder());
767 wxCoord hBorders
= rectBorders
.y
+ rectBorders
.height
;
769 wxCoord hLine
= GetLineHeight();
770 height
= ((height
- hBorders
+ hLine
- 1) / hLine
)*hLine
+ hBorders
;
773 wxListBoxBase::DoSetSize(x
, y
, width
, height
);
776 wxSize
wxListBox::DoGetBestClientSize() const
781 size_t count
= m_strings
.GetCount();
782 for ( size_t n
= 0; n
< count
; n
++ )
785 GetTextExtent(m_strings
[n
], &w
, &h
);
793 // if the listbox is empty, still give it some non zero (even if
794 // arbitrary) size - otherwise, leave small margin around the strings
798 width
+= 3*GetCharWidth();
801 height
= GetCharHeight();
803 // we need the height of the entire listbox, not just of one line
804 height
*= wxMax(count
, 7);
806 return wxSize(width
, height
);
809 // ----------------------------------------------------------------------------
811 // ----------------------------------------------------------------------------
813 bool wxListBox::SendEvent(wxEventType type
, int item
)
815 wxCommandEvent
event(type
, m_windowId
);
816 event
.SetEventObject(this);
818 // use the current item by default
824 // client data and string parameters only make sense if we have an item
827 if ( HasClientObjectData() )
828 event
.SetClientObject(GetClientObject(item
));
829 else if ( HasClientUntypedData() )
830 event
.SetClientData(GetClientData(item
));
832 event
.SetString(GetString(item
));
835 event
.m_commandInt
= item
;
837 return GetEventHandler()->ProcessEvent(event
);
840 void wxListBox::SetCurrentItem(int n
)
842 if ( n
!= m_current
)
844 if ( m_current
!= -1 )
845 RefreshItem(m_current
);
849 if ( m_current
!= -1 )
851 m_currentChanged
= TRUE
;
853 RefreshItem(m_current
);
856 //else: nothing to do
859 bool wxListBox::FindItem(const wxString
& prefix
, bool strictlyAfter
)
861 int count
= GetCount();
864 // empty listbox, we can't find anything in it
868 // start either from the current item or from the next one if strictlyAfter
873 // the following line will set first correctly to 0 if there is no
874 // selection (m_current == -1)
875 first
= m_current
== count
- 1 ? 0 : m_current
+ 1;
877 else // start with the current
879 first
= m_current
== -1 ? 0 : m_current
;
882 int last
= first
== 0 ? count
- 1 : first
- 1;
884 // if this is not true we'd never exit from the loop below!
885 wxASSERT_MSG( first
< count
&& last
< count
, _T("logic error") );
887 // precompute it outside the loop
888 size_t len
= prefix
.length();
890 // loop over all items in the listbox
891 for ( int item
= first
; item
!= last
; item
< count
- 1 ? item
++ : item
= 0 )
893 if ( wxStrnicmp(m_strings
[item
], prefix
, len
) == 0 )
895 SetCurrentItem(item
);
897 if ( !(GetWindowStyle() & wxLB_MULTIPLE
) )
900 SelectAndNotify(item
);
902 if ( GetWindowStyle() & wxLB_EXTENDED
)
903 AnchorSelection(item
);
914 void wxListBox::EnsureVisible(int n
)
916 if ( m_updateScrollbarY
)
921 m_updateScrollbarY
= FALSE
;
927 void wxListBox::DoEnsureVisible(int n
)
929 if ( !m_showScrollbarY
)
931 // nothing to do - everything is shown anyhow
936 GetViewStart(0, &first
);
939 // we need to scroll upwards, so make the current item appear on top
940 // of the shown range
945 int last
= first
+ GetClientSize().y
/ GetLineHeight() - 1;
948 // scroll down: the current item appears at the bottom of the
950 Scroll(0, n
- (last
- first
));
955 void wxListBox::ChangeCurrent(int diff
)
957 int current
= m_current
== -1 ? 0 : m_current
;
961 int last
= GetCount() - 1;
964 else if ( current
> last
)
967 SetCurrentItem(current
);
970 void wxListBox::ExtendSelection(int itemTo
)
972 // if we don't have the explicit values for selection start/end, make them
974 if ( m_selAnchor
== -1 )
975 m_selAnchor
= m_current
;
980 // swap the start/end of selection range if necessary
981 int itemFrom
= m_selAnchor
;
982 if ( itemFrom
> itemTo
)
984 int itemTmp
= itemFrom
;
989 // the selection should now include all items in the range between the
990 // anchor and the specified item and only them
993 for ( n
= 0; n
< itemFrom
; n
++ )
998 for ( ; n
<= itemTo
; n
++ )
1003 int count
= GetCount();
1004 for ( ; n
< count
; n
++ )
1010 void wxListBox::Select(bool sel
, int item
)
1014 // go to this item first
1015 SetCurrentItem(item
);
1018 // the current item is the one we want to change: either it was just
1019 // changed above to be the same as item or item == -1 in which we case we
1020 // are supposed to use the current one anyhow
1021 if ( m_current
!= -1 )
1024 SetSelection(m_current
, sel
);
1028 void wxListBox::SelectAndNotify(int item
)
1032 SendEvent(wxEVT_COMMAND_LISTBOX_SELECTED
);
1035 void wxListBox::Activate(int item
)
1038 SetCurrentItem(item
);
1042 if ( !(GetWindowStyle() & wxLB_MULTIPLE
) )
1051 SendEvent(wxEVT_COMMAND_LISTBOX_DOUBLECLICKED
);
1055 // ----------------------------------------------------------------------------
1057 // ----------------------------------------------------------------------------
1060 The numArg here is the listbox item index while the strArg is used
1061 differently for the different actions:
1063 a) for wxACTION_LISTBOX_FIND it has the natural meaning: this is the string
1066 b) for wxACTION_LISTBOX_SELECT and wxACTION_LISTBOX_EXTENDSEL it is used
1067 to decide if the listbox should send the notification event (it is empty)
1068 or not (it is not): this allows us to reuse the same action for when the
1069 user is dragging the mouse when it has been released although in the
1070 first case no notification is sent while in the second it is sent.
1072 bool wxListBox::PerformAction(const wxControlAction
& action
,
1074 const wxString
& strArg
)
1076 int item
= (int)numArg
;
1078 if ( action
== wxACTION_LISTBOX_SETFOCUS
)
1080 SetCurrentItem(item
);
1082 else if ( action
== wxACTION_LISTBOX_ACTIVATE
)
1086 else if ( action
== wxACTION_LISTBOX_TOGGLE
)
1091 if ( IsSelected(item
) )
1094 SelectAndNotify(item
);
1096 else if ( action
== wxACTION_LISTBOX_SELECT
)
1100 if ( strArg
.empty() )
1101 SelectAndNotify(item
);
1105 else if ( action
== wxACTION_LISTBOX_SELECTADD
)
1107 else if ( action
== wxACTION_LISTBOX_UNSELECT
)
1108 Select(FALSE
, item
);
1109 else if ( action
== wxACTION_LISTBOX_MOVEDOWN
)
1111 else if ( action
== wxACTION_LISTBOX_MOVEUP
)
1113 else if ( action
== wxACTION_LISTBOX_PAGEDOWN
)
1114 ChangeCurrent(GetItemsPerPage());
1115 else if ( action
== wxACTION_LISTBOX_PAGEUP
)
1116 ChangeCurrent(-GetItemsPerPage());
1117 else if ( action
== wxACTION_LISTBOX_START
)
1119 else if ( action
== wxACTION_LISTBOX_END
)
1120 SetCurrentItem(GetCount() - 1);
1121 else if ( action
== wxACTION_LISTBOX_UNSELECTALL
)
1123 else if ( action
== wxACTION_LISTBOX_EXTENDSEL
)
1124 ExtendSelection(item
);
1125 else if ( action
== wxACTION_LISTBOX_FIND
)
1126 FindNextItem(strArg
);
1127 else if ( action
== wxACTION_LISTBOX_ANCHOR
)
1128 AnchorSelection(item
== -1 ? m_current
: item
);
1129 else if ( action
== wxACTION_LISTBOX_SELECTALL
||
1130 action
== wxACTION_LISTBOX_SELTOGGLE
)
1131 wxFAIL_MSG(_T("unimplemented yet"));
1133 return wxControl::PerformAction(action
, numArg
, strArg
);
1138 // ============================================================================
1139 // implementation of wxStdListboxInputHandler
1140 // ============================================================================
1142 wxStdListboxInputHandler::wxStdListboxInputHandler(wxInputHandler
*handler
,
1143 bool toggleOnPressAlways
)
1144 : wxStdInputHandler(handler
)
1147 m_toggleOnPressAlways
= toggleOnPressAlways
;
1148 m_actionMouse
= wxACTION_NONE
;
1149 m_trackMouseOutside
= TRUE
;
1152 int wxStdListboxInputHandler::HitTest(const wxListBox
*lbox
,
1153 const wxMouseEvent
& event
)
1155 int item
= HitTestUnsafe(lbox
, event
);
1157 return FixItemIndex(lbox
, item
);
1160 int wxStdListboxInputHandler::HitTestUnsafe(const wxListBox
*lbox
,
1161 const wxMouseEvent
& event
)
1163 wxPoint pt
= event
.GetPosition();
1164 pt
-= lbox
->GetClientAreaOrigin();
1166 lbox
->CalcUnscrolledPosition(0, pt
.y
, NULL
, &y
);
1167 return y
/ lbox
->GetLineHeight();
1170 int wxStdListboxInputHandler::FixItemIndex(const wxListBox
*lbox
,
1175 // mouse is above the first item
1178 else if ( item
>= lbox
->GetCount() )
1180 // mouse is below the last item
1181 item
= lbox
->GetCount() - 1;
1187 bool wxStdListboxInputHandler::IsValidIndex(const wxListBox
*lbox
, int item
)
1189 return item
>= 0 && item
< lbox
->GetCount();
1193 wxStdListboxInputHandler::SetupCapture(wxListBox
*lbox
,
1194 const wxMouseEvent
& event
,
1197 // we currently only allow selecting with the left mouse button, if we
1198 // do need to allow using other buttons too we might use the code
1201 m_btnCapture
= event
.LeftDown()
1210 wxControlAction action
;
1211 if ( lbox
->HasMultipleSelection() )
1213 if ( lbox
->GetWindowStyle() & wxLB_MULTIPLE
)
1215 if ( m_toggleOnPressAlways
)
1217 // toggle the item right now
1218 action
= wxACTION_LISTBOX_TOGGLE
;
1222 m_actionMouse
= wxACTION_LISTBOX_SETFOCUS
;
1224 else // wxLB_EXTENDED listbox
1226 // simple click in an extended sel listbox clears the old
1227 // selection and adds the clicked item to it then, ctrl-click
1228 // toggles an item to it and shift-click adds a range between
1229 // the old selection anchor and the clicked item
1230 if ( event
.ControlDown() )
1232 lbox
->PerformAction(wxACTION_LISTBOX_ANCHOR
, item
);
1234 action
= wxACTION_LISTBOX_TOGGLE
;
1236 else if ( event
.ShiftDown() )
1238 action
= wxACTION_LISTBOX_EXTENDSEL
;
1240 else // simple click
1242 lbox
->PerformAction(wxACTION_LISTBOX_ANCHOR
, item
);
1244 action
= wxACTION_LISTBOX_SELECT
;
1247 m_actionMouse
= wxACTION_LISTBOX_EXTENDSEL
;
1250 else // single selection
1253 action
= wxACTION_LISTBOX_SELECT
;
1256 // by default we always do track it
1257 m_trackMouseOutside
= TRUE
;
1262 bool wxStdListboxInputHandler::HandleKey(wxInputConsumer
*consumer
,
1263 const wxKeyEvent
& event
,
1266 // we're only interested in the key press events
1267 if ( pressed
&& !event
.AltDown() )
1269 bool isMoveCmd
= TRUE
;
1270 int style
= consumer
->GetInputWindow()->GetWindowStyle();
1272 wxControlAction action
;
1275 int keycode
= event
.GetKeyCode();
1280 action
= wxACTION_LISTBOX_MOVEUP
;
1284 action
= wxACTION_LISTBOX_MOVEDOWN
;
1290 action
= wxACTION_LISTBOX_PAGEUP
;
1296 action
= wxACTION_LISTBOX_PAGEDOWN
;
1300 action
= wxACTION_LISTBOX_START
;
1304 action
= wxACTION_LISTBOX_END
;
1309 if ( style
& wxLB_MULTIPLE
)
1311 action
= wxACTION_LISTBOX_TOGGLE
;
1317 action
= wxACTION_LISTBOX_ACTIVATE
;
1322 if ( (keycode
< 255) && wxIsalnum(keycode
) )
1324 action
= wxACTION_LISTBOX_FIND
;
1325 strArg
= (wxChar
)keycode
;
1331 consumer
->PerformAction(action
, -1, strArg
);
1335 if ( style
& wxLB_SINGLE
)
1337 // the current item is always the one selected
1338 consumer
->PerformAction(wxACTION_LISTBOX_SELECT
);
1340 else if ( style
& wxLB_EXTENDED
)
1342 if ( event
.ShiftDown() )
1343 consumer
->PerformAction(wxACTION_LISTBOX_EXTENDSEL
);
1346 // select the item and make it the new selection anchor
1347 consumer
->PerformAction(wxACTION_LISTBOX_SELECT
);
1348 consumer
->PerformAction(wxACTION_LISTBOX_ANCHOR
);
1351 //else: nothing to do for multiple selection listboxes
1358 return wxStdInputHandler::HandleKey(consumer
, event
, pressed
);
1361 bool wxStdListboxInputHandler::HandleMouse(wxInputConsumer
*consumer
,
1362 const wxMouseEvent
& event
)
1364 wxListBox
*lbox
= wxStaticCast(consumer
->GetInputWindow(), wxListBox
);
1365 int item
= HitTest(lbox
, event
);
1366 wxControlAction action
;
1368 // when the left mouse button is pressed, capture the mouse and track the
1369 // item under mouse (if the mouse leaves the window, we will still be
1370 // getting the mouse move messages generated by wxScrollWindow)
1371 if ( event
.LeftDown() )
1373 // capture the mouse to track the selected item
1374 lbox
->CaptureMouse();
1376 action
= SetupCapture(lbox
, event
, item
);
1378 else if ( m_btnCapture
&& event
.ButtonUp(m_btnCapture
) )
1380 // when the left mouse button is released, release the mouse too
1381 wxWindow
*winCapture
= wxWindow::GetCapture();
1384 winCapture
->ReleaseMouse();
1387 action
= m_actionMouse
;
1389 //else: the mouse wasn't presed over the listbox, only released here
1391 else if ( event
.LeftDClick() )
1393 action
= wxACTION_LISTBOX_ACTIVATE
;
1398 lbox
->PerformAction(action
, item
);
1403 return wxStdInputHandler::HandleMouse(consumer
, event
);
1406 bool wxStdListboxInputHandler::HandleMouseMove(wxInputConsumer
*consumer
,
1407 const wxMouseEvent
& event
)
1409 wxWindow
*winCapture
= wxWindow::GetCapture();
1410 if ( winCapture
&& (event
.GetEventObject() == winCapture
) )
1412 wxListBox
*lbox
= wxStaticCast(consumer
->GetInputWindow(), wxListBox
);
1414 if ( !m_btnCapture
|| !m_trackMouseOutside
)
1416 // someone captured the mouse for us (we always set m_btnCapture
1417 // when we do it ourselves): in this case we only react to
1418 // the mouse messages when they happen inside the listbox
1419 if ( lbox
->HitTest(event
.GetPosition()) != wxHT_WINDOW_INSIDE
)
1423 int item
= HitTest(lbox
, event
);
1424 if ( !m_btnCapture
)
1426 // now that we have the mouse inside the listbox, do capture it
1427 // normally - but ensure that we will still ignore the outside
1429 SetupCapture(lbox
, event
, item
);
1431 m_trackMouseOutside
= FALSE
;
1434 if ( IsValidIndex(lbox
, item
) )
1436 // pass something into strArg to tell the listbox that it shouldn't
1437 // send the notification message: see PerformAction() above
1438 lbox
->PerformAction(m_actionMouse
, item
, _T("no"));
1440 // else: don't pass invalid index to the listbox
1442 else // we don't have capture any more
1446 // if we lost capture unexpectedly (someone else took the capture
1447 // from us), return to a consistent state
1452 return wxStdInputHandler::HandleMouseMove(consumer
, event
);
1455 #endif // wxUSE_LISTBOX