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 ( !wxControl::Create(parent
, id
, pos
, size
, style
| wxVSCROLL
, wxDefaultValidator
, name
) )
109 if ( style
& wxLB_SORT
)
110 m_strings
= wxArrayString(TRUE
/* auto sort */);
116 CreateInputHandler(wxINP_HANDLER_LISTBOX
);
121 wxListBox::~wxListBox()
125 // ----------------------------------------------------------------------------
126 // adding/inserting strings
127 // ----------------------------------------------------------------------------
129 int wxListBox::DoAppend(const wxString
& item
)
131 size_t index
= m_strings
.Add(item
);
132 m_itemsClientData
.Insert(NULL
, index
);
134 m_updateScrollbarY
= TRUE
;
136 if ( HasHorzScrollbar() )
138 // has the max width increased?
140 GetTextExtent(item
, &width
, NULL
);
141 if ( width
> m_maxWidth
)
144 m_maxWidthItem
= index
;
145 m_updateScrollbarX
= TRUE
;
149 RefreshFromItemToEnd(index
);
154 void wxListBox::DoInsertItems(const wxArrayString
& items
, int pos
)
156 // the position of the item being added to a sorted listbox can't be
158 wxCHECK_RET( !IsSorted(), _T("can't insert items into sorted listbox") );
160 size_t count
= items
.GetCount();
161 for ( size_t n
= 0; n
< count
; n
++ )
163 m_strings
.Insert(items
[n
], pos
+ n
);
164 m_itemsClientData
.Insert(NULL
, pos
+ n
);
167 // the number of items has changed so we might have to show the scrollbar
168 m_updateScrollbarY
= TRUE
;
170 // the max width also might have changed - just recalculate it instead of
171 // keeping track of it here, this is probably more efficient for a typical
173 RefreshHorzScrollbar();
175 // note that we have to refresh all the items after the ones we inserted,
176 // not just these items
177 RefreshFromItemToEnd(pos
);
180 void wxListBox::DoSetItems(const wxArrayString
& items
, void **clientData
)
184 size_t count
= items
.GetCount();
188 m_strings
.Alloc(count
);
189 m_itemsClientData
.Alloc(count
);
190 for ( size_t n
= 0; n
< count
; n
++ )
192 size_t index
= m_strings
.Add(items
[n
]);
193 m_itemsClientData
.Insert(clientData
? clientData
[n
] : NULL
, index
);
196 m_updateScrollbarY
= TRUE
;
201 void wxListBox::SetString(int n
, const wxString
& s
)
203 if ( HasHorzScrollbar() )
205 // we need to update m_maxWidth as changing the string may cause the
206 // horz scrollbar [dis]appear
209 GetTextExtent(s
, &width
, NULL
);
211 // it might have increased if the new string is long
212 if ( width
> m_maxWidth
)
216 m_updateScrollbarX
= TRUE
;
218 // or also decreased if the old string was the longest one
219 else if ( n
== m_maxWidthItem
)
221 RefreshHorzScrollbar();
224 else // no horz scrollbar
232 // ----------------------------------------------------------------------------
234 // ----------------------------------------------------------------------------
236 void wxListBox::DoClear()
240 if ( HasClientObjectData() )
242 size_t count
= m_itemsClientData
.GetCount();
243 for ( size_t n
= 0; n
< count
; n
++ )
245 delete (wxClientData
*) m_itemsClientData
[n
];
249 m_itemsClientData
.Clear();
250 m_selections
.Clear();
255 void wxListBox::Clear()
259 m_updateScrollbarY
= TRUE
;
261 RefreshHorzScrollbar();
266 void wxListBox::Delete(int n
)
268 wxCHECK_RET( n
< GetCount(), _T("invalid index in wxListBox::Delete") );
270 // do it before removing the index as otherwise the last item will not be
271 // refreshed (as GetCount() will be decremented)
272 RefreshFromItemToEnd(n
);
274 m_strings
.RemoveAt(n
);
276 if ( HasClientObjectData() )
278 delete (wxClientData
*)m_itemsClientData
[n
];
281 m_itemsClientData
.RemoveAt(n
);
283 // when the item disappears we must not keep using its index
284 if ( n
== m_current
)
288 else if ( n
< m_current
)
292 //else: current item may stay
294 // update the selections array: the indices of all seletected items after
295 // the one being deleted must change and the item itselfm ust be removed
296 int index
= wxNOT_FOUND
;
297 size_t count
= m_selections
.GetCount();
298 for ( size_t item
= 0; item
< count
; item
++ )
300 if ( m_selections
[item
] == n
)
302 // remember to delete it later
305 else if ( m_selections
[item
] > n
)
307 // to account for the index shift
308 m_selections
[item
]--;
310 //else: nothing changed for this one
313 if ( index
!= wxNOT_FOUND
)
315 m_selections
.RemoveAt(index
);
318 // the number of items has changed, hence the scrollbar may disappear
319 m_updateScrollbarY
= TRUE
;
321 // finally, if the longest item was deleted the scrollbar may disappear
322 if ( n
== m_maxWidthItem
)
324 RefreshHorzScrollbar();
328 // ----------------------------------------------------------------------------
329 // client data handling
330 // ----------------------------------------------------------------------------
332 void wxListBox::DoSetItemClientData(int n
, void* clientData
)
334 m_itemsClientData
[n
] = clientData
;
337 void *wxListBox::DoGetItemClientData(int n
) const
339 return m_itemsClientData
[n
];
342 void wxListBox::DoSetItemClientObject(int n
, wxClientData
* clientData
)
344 m_itemsClientData
[n
] = clientData
;
347 wxClientData
* wxListBox::DoGetItemClientObject(int n
) const
349 return (wxClientData
*)m_itemsClientData
[n
];
352 // ----------------------------------------------------------------------------
354 // ----------------------------------------------------------------------------
356 void wxListBox::SetSelection(int n
, bool select
)
360 if ( m_selections
.Index(n
) == wxNOT_FOUND
)
362 if ( !HasMultipleSelection() )
364 // selecting an item in a single selection listbox deselects
373 //else: already selected
377 int index
= m_selections
.Index(n
);
378 if ( index
!= wxNOT_FOUND
)
380 m_selections
.RemoveAt(index
);
387 // sanity check: a single selection listbox can't have more than one item
389 wxASSERT_MSG( HasMultipleSelection() || (m_selections
.GetCount() < 2),
390 _T("multiple selected items in single selection lbox?") );
394 // the newly selected item becomes the current one
399 int wxListBox::GetSelection() const
401 wxCHECK_MSG( !HasMultipleSelection(), -1,
402 _T("use wxListBox::GetSelections for ths listbox") );
404 return m_selections
.IsEmpty() ? -1 : m_selections
[0];
407 int wxCMPFUNC_CONV
wxCompareInts(int *n
, int *m
)
412 int wxListBox::GetSelections(wxArrayInt
& selections
) const
414 // always return sorted array to the user
415 selections
= m_selections
;
416 size_t count
= m_selections
.GetCount();
418 // don't call sort on an empty array
421 selections
.Sort(wxCompareInts
);
427 // ----------------------------------------------------------------------------
428 // refresh logic: we use delayed refreshing which allows to avoid multiple
429 // refreshes (and hence flicker) in case when several listbox items are
430 // added/deleted/changed subsequently
431 // ----------------------------------------------------------------------------
433 void wxListBox::RefreshFromItemToEnd(int from
)
435 RefreshItems(from
, GetCount() - from
);
438 void wxListBox::RefreshItems(int from
, int count
)
440 switch ( m_updateCount
)
444 m_updateCount
= count
;
448 // we refresh everything anyhow
452 // add these items to the others which we have to refresh
453 if ( m_updateFrom
< from
)
455 count
+= from
- m_updateFrom
;
456 if ( m_updateCount
< count
)
457 m_updateCount
= count
;
459 else // m_updateFrom >= from
461 int updateLast
= wxMax(m_updateFrom
+ m_updateCount
,
464 m_updateCount
= updateLast
- m_updateFrom
;
469 void wxListBox::RefreshItem(int n
)
471 switch ( m_updateCount
)
474 // refresh this item only
480 // we refresh everything anyhow
484 // add this item to the others which we have to refresh
485 if ( m_updateFrom
< n
)
487 if ( m_updateCount
< n
- m_updateFrom
+ 1 )
488 m_updateCount
= n
- m_updateFrom
+ 1;
490 else // n <= m_updateFrom
492 m_updateCount
+= m_updateFrom
- n
;
498 void wxListBox::RefreshAll()
503 void wxListBox::RefreshHorzScrollbar()
505 m_maxWidth
= 0; // recalculate it
506 m_updateScrollbarX
= TRUE
;
509 void wxListBox::UpdateScrollbars()
511 wxSize size
= GetClientSize();
513 // is our height enough to show all items?
514 int nLines
= GetCount();
515 wxCoord lineHeight
= GetLineHeight();
516 bool showScrollbarY
= nLines
*lineHeight
> size
.y
;
518 // check the width too if required
519 wxCoord charWidth
, maxWidth
;
521 if ( HasHorzScrollbar() )
523 charWidth
= GetCharWidth();
524 maxWidth
= GetMaxWidth();
525 showScrollbarX
= maxWidth
> size
.x
;
527 else // never show it
529 charWidth
= maxWidth
= 0;
530 showScrollbarX
= FALSE
;
533 // what should be the scrollbar range now?
534 int scrollRangeX
= showScrollbarX
535 ? (maxWidth
+ charWidth
- 1) / charWidth
+ 2 // FIXME
537 int scrollRangeY
= showScrollbarY
539 (size
.y
% lineHeight
+ lineHeight
- 1) / lineHeight
542 // reset scrollbars if something changed: either the visibility status
543 // or the range of a scrollbar which is shown
544 if ( (showScrollbarY
!= m_showScrollbarY
) ||
545 (showScrollbarX
!= m_showScrollbarX
) ||
546 (showScrollbarY
&& (scrollRangeY
!= m_scrollRangeY
)) ||
547 (showScrollbarX
&& (scrollRangeX
!= m_scrollRangeX
)) )
550 GetViewStart(&x
, &y
);
551 SetScrollbars(charWidth
, lineHeight
,
552 scrollRangeX
, scrollRangeY
,
555 m_showScrollbarX
= showScrollbarX
;
556 m_showScrollbarY
= showScrollbarY
;
558 m_scrollRangeX
= scrollRangeX
;
559 m_scrollRangeY
= scrollRangeY
;
563 void wxListBox::UpdateItems()
565 // only refresh the items which must be refreshed
566 if ( m_updateCount
== -1 )
569 wxLogTrace(_T("listbox"), _T("Refreshing all"));
575 wxSize size
= GetClientSize();
578 rect
.height
= size
.y
;
579 rect
.y
+= m_updateFrom
*GetLineHeight();
580 rect
.height
= m_updateCount
*GetLineHeight();
582 // we don't need to calculate x position as we always refresh the
584 CalcScrolledPosition(0, rect
.y
, NULL
, &rect
.y
);
586 wxLogTrace(_T("listbox"), _T("Refreshing items %d..%d (%d-%d)"),
587 m_updateFrom
, m_updateFrom
+ m_updateCount
- 1,
588 rect
.GetTop(), rect
.GetBottom());
590 Refresh(TRUE
, &rect
);
594 void wxListBox::OnIdle(wxIdleEvent
& event
)
596 if ( m_updateScrollbarY
|| m_updateScrollbarX
)
601 m_updateScrollbarY
= FALSE
;
604 if ( m_currentChanged
)
606 DoEnsureVisible(m_current
);
608 m_currentChanged
= FALSE
;
621 // ----------------------------------------------------------------------------
623 // ----------------------------------------------------------------------------
625 wxBorder
wxListBox::GetDefaultBorder() const
627 return wxBORDER_SUNKEN
;
630 void wxListBox::DoDraw(wxControlRenderer
*renderer
)
632 // adjust the DC to account for scrolling
633 wxDC
& dc
= renderer
->GetDC();
635 dc
.SetFont(GetFont());
637 // get the update rect
638 wxRect rectUpdate
= GetUpdateClientRect();
641 CalcUnscrolledPosition(0, rectUpdate
.GetTop(), NULL
, &yTop
);
642 CalcUnscrolledPosition(0, rectUpdate
.GetBottom(), NULL
, &yBottom
);
644 // get the items which must be redrawn
645 wxCoord lineHeight
= GetLineHeight();
646 size_t itemFirst
= yTop
/ lineHeight
,
647 itemLast
= (yBottom
+ lineHeight
- 1) / lineHeight
,
648 itemMax
= m_strings
.GetCount();
650 if ( itemFirst
>= itemMax
)
653 if ( itemLast
> itemMax
)
657 wxLogTrace(_T("listbox"), _T("Repainting items %d..%d"),
658 itemFirst
, itemLast
);
660 DoDrawRange(renderer
, itemFirst
, itemLast
);
663 void wxListBox::DoDrawRange(wxControlRenderer
*renderer
,
664 int itemFirst
, int itemLast
)
666 renderer
->DrawItems(this, itemFirst
, itemLast
);
669 // ----------------------------------------------------------------------------
671 // ----------------------------------------------------------------------------
673 bool wxListBox::SetFont(const wxFont
& font
)
675 if ( !wxControl::SetFont(font
) )
685 void wxListBox::CalcItemsPerPage()
687 m_lineHeight
= GetRenderer()->GetListboxItemHeight(GetCharHeight());
688 m_itemsPerPage
= GetClientSize().y
/ m_lineHeight
;
691 int wxListBox::GetItemsPerPage() const
693 if ( !m_itemsPerPage
)
695 wxConstCast(this, wxListBox
)->CalcItemsPerPage();
698 return m_itemsPerPage
;
701 wxCoord
wxListBox::GetLineHeight() const
705 wxConstCast(this, wxListBox
)->CalcItemsPerPage();
711 wxCoord
wxListBox::GetMaxWidth() const
713 if ( m_maxWidth
== 0 )
715 wxListBox
*self
= wxConstCast(this, wxListBox
);
717 size_t count
= m_strings
.GetCount();
718 for ( size_t n
= 0; n
< count
; n
++ )
720 GetTextExtent(m_strings
[n
], &width
, NULL
);
721 if ( width
> m_maxWidth
)
723 self
->m_maxWidth
= width
;
724 self
->m_maxWidthItem
= n
;
732 void wxListBox::OnSize(wxSizeEvent
& event
)
734 // recalculate the number of items per page
737 // the scrollbars might [dis]appear
739 m_updateScrollbarY
= TRUE
;
744 void wxListBox::DoSetFirstItem(int n
)
749 void wxListBox::DoSetSize(int x
, int y
,
750 int width
, int height
,
753 if ( GetWindowStyle() & wxLB_INT_HEIGHT
)
755 // we must round up the height to an entire number of rows
757 // the client area must contain an int number of rows, so take borders
759 wxRect rectBorders
= GetRenderer()->GetBorderDimensions(GetBorder());
760 wxCoord hBorders
= rectBorders
.y
+ rectBorders
.height
;
762 wxCoord hLine
= GetLineHeight();
763 height
= ((height
- hBorders
+ hLine
- 1) / hLine
)*hLine
+ hBorders
;
766 wxListBoxBase::DoSetSize(x
, y
, width
, height
);
769 wxSize
wxListBox::DoGetBestClientSize() const
774 size_t count
= m_strings
.GetCount();
775 for ( size_t n
= 0; n
< count
; n
++ )
778 GetTextExtent(m_strings
[n
], &w
, &h
);
786 // if the listbox is empty, still give it some non zero (even if
787 // arbitrary) size - otherwise, leave small margin around the strings
791 width
+= 3*GetCharWidth();
794 height
= GetCharHeight();
796 // we need the height of the entire listbox, not just of one line
797 height
*= wxMax(count
, 7);
799 return wxSize(width
, height
);
802 // ----------------------------------------------------------------------------
804 // ----------------------------------------------------------------------------
806 bool wxListBox::SendEvent(wxEventType type
, int item
)
808 wxCommandEvent
event(type
, m_windowId
);
809 event
.SetEventObject(this);
811 // use the current item by default
817 // client data and string parameters only make sense if we have an item
820 if ( HasClientObjectData() )
821 event
.SetClientObject(GetClientObject(item
));
822 else if ( HasClientUntypedData() )
823 event
.SetClientData(GetClientData(item
));
825 event
.SetString(GetString(item
));
828 event
.m_commandInt
= item
;
830 return GetEventHandler()->ProcessEvent(event
);
833 void wxListBox::SetCurrentItem(int n
)
835 if ( n
!= m_current
)
837 if ( m_current
!= -1 )
838 RefreshItem(m_current
);
842 if ( m_current
!= -1 )
844 m_currentChanged
= TRUE
;
846 RefreshItem(m_current
);
849 //else: nothing to do
852 bool wxListBox::FindItem(const wxString
& prefix
, bool strictlyAfter
)
854 int count
= GetCount();
857 // empty listbox, we can't find anything in it
861 // start either from the current item or from the next one if strictlyAfter
866 // the following line will set first correctly to 0 if there is no
867 // selection (m_current == -1)
868 first
= m_current
== count
- 1 ? 0 : m_current
+ 1;
870 else // start with the current
872 first
= m_current
== -1 ? 0 : m_current
;
875 int last
= first
== 0 ? count
- 1 : first
- 1;
877 // if this is not true we'd never exit from the loop below!
878 wxASSERT_MSG( first
< count
&& last
< count
, _T("logic error") );
880 // precompute it outside the loop
881 size_t len
= prefix
.length();
883 // loop over all items in the listbox
884 for ( int item
= first
; item
!= last
; item
< count
- 1 ? item
++ : item
= 0 )
886 if ( wxStrnicmp(m_strings
[item
], prefix
, len
) == 0 )
888 SetCurrentItem(item
);
890 if ( !(GetWindowStyle() & wxLB_MULTIPLE
) )
893 SelectAndNotify(item
);
895 if ( GetWindowStyle() & wxLB_EXTENDED
)
896 AnchorSelection(item
);
907 void wxListBox::EnsureVisible(int n
)
909 if ( m_updateScrollbarY
)
914 m_updateScrollbarY
= FALSE
;
920 void wxListBox::DoEnsureVisible(int n
)
922 if ( !m_showScrollbarY
)
924 // nothing to do - everything is shown anyhow
929 GetViewStart(0, &first
);
932 // we need to scroll upwards, so make the current item appear on top
933 // of the shown range
938 int last
= first
+ GetClientSize().y
/ GetLineHeight() - 1;
941 // scroll down: the current item appears at the bottom of the
943 Scroll(0, n
- (last
- first
));
948 void wxListBox::ChangeCurrent(int diff
)
950 int current
= m_current
== -1 ? 0 : m_current
;
954 int last
= GetCount() - 1;
957 else if ( current
> last
)
960 SetCurrentItem(current
);
963 void wxListBox::ExtendSelection(int itemTo
)
965 // if we don't have the explicit values for selection start/end, make them
967 if ( m_selAnchor
== -1 )
968 m_selAnchor
= m_current
;
973 // swap the start/end of selection range if necessary
974 int itemFrom
= m_selAnchor
;
975 if ( itemFrom
> itemTo
)
977 int itemTmp
= itemFrom
;
982 // the selection should now include all items in the range between the
983 // anchor and the specified item and only them
986 for ( n
= 0; n
< itemFrom
; n
++ )
991 for ( ; n
<= itemTo
; n
++ )
996 int count
= GetCount();
997 for ( ; n
< count
; n
++ )
1003 void wxListBox::Select(bool sel
, int item
)
1007 // go to this item first
1008 SetCurrentItem(item
);
1011 // the current item is the one we want to change: either it was just
1012 // changed above to be the same as item or item == -1 in which we case we
1013 // are supposed to use the current one anyhow
1014 if ( m_current
!= -1 )
1017 SetSelection(m_current
, sel
);
1021 void wxListBox::SelectAndNotify(int item
)
1025 SendEvent(wxEVT_COMMAND_LISTBOX_SELECTED
);
1028 void wxListBox::Activate(int item
)
1031 SetCurrentItem(item
);
1035 if ( !(GetWindowStyle() & wxLB_MULTIPLE
) )
1044 SendEvent(wxEVT_COMMAND_LISTBOX_DOUBLECLICKED
);
1048 // ----------------------------------------------------------------------------
1050 // ----------------------------------------------------------------------------
1053 The numArg here is the listbox item index while the strArg is used
1054 differently for the different actions:
1056 a) for wxACTION_LISTBOX_FIND it has the natural meaning: this is the string
1059 b) for wxACTION_LISTBOX_SELECT and wxACTION_LISTBOX_EXTENDSEL it is used
1060 to decide if the listbox should send the notification event (it is empty)
1061 or not (it is not): this allows us to reuse the same action for when the
1062 user is dragging the mouse when it has been released although in the
1063 first case no notification is sent while in the second it is sent.
1065 bool wxListBox::PerformAction(const wxControlAction
& action
,
1067 const wxString
& strArg
)
1069 int item
= (int)numArg
;
1071 if ( action
== wxACTION_LISTBOX_SETFOCUS
)
1073 SetCurrentItem(item
);
1075 else if ( action
== wxACTION_LISTBOX_ACTIVATE
)
1079 else if ( action
== wxACTION_LISTBOX_TOGGLE
)
1084 if ( IsSelected(item
) )
1087 SelectAndNotify(item
);
1089 else if ( action
== wxACTION_LISTBOX_SELECT
)
1093 if ( strArg
.empty() )
1094 SelectAndNotify(item
);
1098 else if ( action
== wxACTION_LISTBOX_SELECTADD
)
1100 else if ( action
== wxACTION_LISTBOX_UNSELECT
)
1101 Select(FALSE
, item
);
1102 else if ( action
== wxACTION_LISTBOX_MOVEDOWN
)
1104 else if ( action
== wxACTION_LISTBOX_MOVEUP
)
1106 else if ( action
== wxACTION_LISTBOX_PAGEDOWN
)
1107 ChangeCurrent(GetItemsPerPage());
1108 else if ( action
== wxACTION_LISTBOX_PAGEUP
)
1109 ChangeCurrent(-GetItemsPerPage());
1110 else if ( action
== wxACTION_LISTBOX_START
)
1112 else if ( action
== wxACTION_LISTBOX_END
)
1113 SetCurrentItem(GetCount() - 1);
1114 else if ( action
== wxACTION_LISTBOX_UNSELECTALL
)
1116 else if ( action
== wxACTION_LISTBOX_EXTENDSEL
)
1117 ExtendSelection(item
);
1118 else if ( action
== wxACTION_LISTBOX_FIND
)
1119 FindNextItem(strArg
);
1120 else if ( action
== wxACTION_LISTBOX_ANCHOR
)
1121 AnchorSelection(item
== -1 ? m_current
: item
);
1122 else if ( action
== wxACTION_LISTBOX_SELECTALL
||
1123 action
== wxACTION_LISTBOX_SELTOGGLE
)
1124 wxFAIL_MSG(_T("unimplemented yet"));
1126 return wxControl::PerformAction(action
, numArg
, strArg
);
1131 // ============================================================================
1132 // implementation of wxStdListboxInputHandler
1133 // ============================================================================
1135 wxStdListboxInputHandler::wxStdListboxInputHandler(wxInputHandler
*handler
,
1136 bool toggleOnPressAlways
)
1137 : wxStdInputHandler(handler
)
1140 m_toggleOnPressAlways
= toggleOnPressAlways
;
1141 m_actionMouse
= wxACTION_NONE
;
1142 m_trackMouseOutside
= TRUE
;
1145 int wxStdListboxInputHandler::HitTest(const wxListBox
*lbox
,
1146 const wxMouseEvent
& event
)
1148 int item
= HitTestUnsafe(lbox
, event
);
1150 return FixItemIndex(lbox
, item
);
1153 int wxStdListboxInputHandler::HitTestUnsafe(const wxListBox
*lbox
,
1154 const wxMouseEvent
& event
)
1156 wxPoint pt
= event
.GetPosition();
1157 pt
-= lbox
->GetClientAreaOrigin();
1159 lbox
->CalcUnscrolledPosition(0, pt
.y
, NULL
, &y
);
1160 return y
/ lbox
->GetLineHeight();
1163 int wxStdListboxInputHandler::FixItemIndex(const wxListBox
*lbox
,
1168 // mouse is above the first item
1171 else if ( item
>= lbox
->GetCount() )
1173 // mouse is below the last item
1174 item
= lbox
->GetCount() - 1;
1180 bool wxStdListboxInputHandler::IsValidIndex(const wxListBox
*lbox
, int item
)
1182 return item
>= 0 && item
< lbox
->GetCount();
1186 wxStdListboxInputHandler::SetupCapture(wxListBox
*lbox
,
1187 const wxMouseEvent
& event
,
1190 // we currently only allow selecting with the left mouse button, if we
1191 // do need to allow using other buttons too we might use the code
1194 m_btnCapture
= event
.LeftDown()
1203 wxControlAction action
;
1204 if ( lbox
->HasMultipleSelection() )
1206 if ( lbox
->GetWindowStyle() & wxLB_MULTIPLE
)
1208 if ( m_toggleOnPressAlways
)
1210 // toggle the item right now
1211 action
= wxACTION_LISTBOX_TOGGLE
;
1215 m_actionMouse
= wxACTION_LISTBOX_SETFOCUS
;
1217 else // wxLB_EXTENDED listbox
1219 // simple click in an extended sel listbox clears the old
1220 // selection and adds the clicked item to it then, ctrl-click
1221 // toggles an item to it and shift-click adds a range between
1222 // the old selection anchor and the clicked item
1223 if ( event
.ControlDown() )
1225 lbox
->PerformAction(wxACTION_LISTBOX_ANCHOR
, item
);
1227 action
= wxACTION_LISTBOX_TOGGLE
;
1229 else if ( event
.ShiftDown() )
1231 action
= wxACTION_LISTBOX_EXTENDSEL
;
1233 else // simple click
1235 lbox
->PerformAction(wxACTION_LISTBOX_ANCHOR
, item
);
1237 action
= wxACTION_LISTBOX_SELECT
;
1240 m_actionMouse
= wxACTION_LISTBOX_EXTENDSEL
;
1243 else // single selection
1246 action
= wxACTION_LISTBOX_SELECT
;
1249 // by default we always do track it
1250 m_trackMouseOutside
= TRUE
;
1255 bool wxStdListboxInputHandler::HandleKey(wxInputConsumer
*consumer
,
1256 const wxKeyEvent
& event
,
1259 // we're only interested in the key press events
1260 if ( pressed
&& !event
.AltDown() )
1262 bool isMoveCmd
= TRUE
;
1263 int style
= consumer
->GetInputWindow()->GetWindowStyle();
1265 wxControlAction action
;
1268 int keycode
= event
.GetKeyCode();
1273 action
= wxACTION_LISTBOX_MOVEUP
;
1277 action
= wxACTION_LISTBOX_MOVEDOWN
;
1283 action
= wxACTION_LISTBOX_PAGEUP
;
1289 action
= wxACTION_LISTBOX_PAGEDOWN
;
1293 action
= wxACTION_LISTBOX_START
;
1297 action
= wxACTION_LISTBOX_END
;
1302 if ( style
& wxLB_MULTIPLE
)
1304 action
= wxACTION_LISTBOX_TOGGLE
;
1310 action
= wxACTION_LISTBOX_ACTIVATE
;
1315 if ( (keycode
< 255) && wxIsalnum(keycode
) )
1317 action
= wxACTION_LISTBOX_FIND
;
1318 strArg
= (wxChar
)keycode
;
1324 consumer
->PerformAction(action
, -1, strArg
);
1328 if ( style
& wxLB_SINGLE
)
1330 // the current item is always the one selected
1331 consumer
->PerformAction(wxACTION_LISTBOX_SELECT
);
1333 else if ( style
& wxLB_EXTENDED
)
1335 if ( event
.ShiftDown() )
1336 consumer
->PerformAction(wxACTION_LISTBOX_EXTENDSEL
);
1339 // select the item and make it the new selection anchor
1340 consumer
->PerformAction(wxACTION_LISTBOX_SELECT
);
1341 consumer
->PerformAction(wxACTION_LISTBOX_ANCHOR
);
1344 //else: nothing to do for multiple selection listboxes
1351 return wxStdInputHandler::HandleKey(consumer
, event
, pressed
);
1354 bool wxStdListboxInputHandler::HandleMouse(wxInputConsumer
*consumer
,
1355 const wxMouseEvent
& event
)
1357 wxListBox
*lbox
= wxStaticCast(consumer
->GetInputWindow(), wxListBox
);
1358 int item
= HitTest(lbox
, event
);
1359 wxControlAction action
;
1361 // when the left mouse button is pressed, capture the mouse and track the
1362 // item under mouse (if the mouse leaves the window, we will still be
1363 // getting the mouse move messages generated by wxScrollWindow)
1364 if ( event
.LeftDown() )
1366 // capture the mouse to track the selected item
1367 lbox
->CaptureMouse();
1369 action
= SetupCapture(lbox
, event
, item
);
1371 else if ( m_btnCapture
&& event
.ButtonUp(m_btnCapture
) )
1373 // when the left mouse button is released, release the mouse too
1374 wxWindow
*winCapture
= wxWindow::GetCapture();
1377 winCapture
->ReleaseMouse();
1380 action
= m_actionMouse
;
1382 //else: the mouse wasn't presed over the listbox, only released here
1384 else if ( event
.LeftDClick() )
1386 action
= wxACTION_LISTBOX_ACTIVATE
;
1391 lbox
->PerformAction(action
, item
);
1396 return wxStdInputHandler::HandleMouse(consumer
, event
);
1399 bool wxStdListboxInputHandler::HandleMouseMove(wxInputConsumer
*consumer
,
1400 const wxMouseEvent
& event
)
1402 wxWindow
*winCapture
= wxWindow::GetCapture();
1403 if ( winCapture
&& (event
.GetEventObject() == winCapture
) )
1405 wxListBox
*lbox
= wxStaticCast(consumer
->GetInputWindow(), wxListBox
);
1407 if ( !m_btnCapture
|| !m_trackMouseOutside
)
1409 // someone captured the mouse for us (we always set m_btnCapture
1410 // when we do it ourselves): in this case we only react to
1411 // the mouse messages when they happen inside the listbox
1412 if ( lbox
->HitTest(event
.GetPosition()) != wxHT_WINDOW_INSIDE
)
1416 int item
= HitTest(lbox
, event
);
1417 if ( !m_btnCapture
)
1419 // now that we have the mouse inside the listbox, do capture it
1420 // normally - but ensure that we will still ignore the outside
1422 SetupCapture(lbox
, event
, item
);
1424 m_trackMouseOutside
= FALSE
;
1427 if ( IsValidIndex(lbox
, item
) )
1429 // pass something into strArg to tell the listbox that it shouldn't
1430 // send the notification message: see PerformAction() above
1431 lbox
->PerformAction(m_actionMouse
, item
, _T("no"));
1433 // else: don't pass invalid index to the listbox
1435 else // we don't have capture any more
1439 // if we lost capture unexpectedly (someone else took the capture
1440 // from us), return to a consistent state
1445 return wxStdInputHandler::HandleMouseMove(consumer
, event
);
1448 #endif // wxUSE_LISTBOX