]> git.saurik.com Git - wxWidgets.git/blame - src/univ/listbox.cpp
fix focus problems in picker controls under MSW which were due to not letting the...
[wxWidgets.git] / src / univ / listbox.cpp
CommitLineData
1e6feb95 1/////////////////////////////////////////////////////////////////////////////
8228b893 2// Name: src/univ/listbox.cpp
1e6feb95
VZ
3// Purpose: wxListBox implementation
4// Author: Vadim Zeitlin
5// Modified by:
6// Created: 30.08.00
7// RCS-ID: $Id$
442b35b5 8// Copyright: (c) 2000 SciTech Software, Inc. (www.scitechsoft.com)
65571936 9// Licence: wxWindows licence
1e6feb95
VZ
10/////////////////////////////////////////////////////////////////////////////
11
12// ============================================================================
13// declarations
14// ============================================================================
15
16// ----------------------------------------------------------------------------
17// headers
18// ----------------------------------------------------------------------------
19
1e6feb95
VZ
20#include "wx/wxprec.h"
21
22#ifdef __BORLANDC__
23 #pragma hdrstop
24#endif
25
26#if wxUSE_LISTBOX
27
28#ifndef WX_PRECOMP
29 #include "wx/log.h"
30
31 #include "wx/dcclient.h"
32 #include "wx/listbox.h"
33 #include "wx/validate.h"
34#endif
35
36#include "wx/univ/renderer.h"
37#include "wx/univ/inphand.h"
38#include "wx/univ/theme.h"
39
9467bdb7
VZ
40// ----------------------------------------------------------------------------
41// wxStdListboxInputHandler: handles mouse and kbd in a single or multi
42// selection listbox
43// ----------------------------------------------------------------------------
44
45class WXDLLEXPORT wxStdListboxInputHandler : public wxStdInputHandler
46{
47public:
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);
54
55 // base class methods
56 virtual bool HandleKey(wxInputConsumer *consumer,
57 const wxKeyEvent& event,
58 bool pressed);
59 virtual bool HandleMouse(wxInputConsumer *consumer,
60 const wxMouseEvent& event);
61 virtual bool HandleMouseMove(wxInputConsumer *consumer,
62 const wxMouseEvent& event);
63
64protected:
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);
68
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);
75
76 // init m_btnCapture and m_actionMouse
77 wxControlAction SetupCapture(wxListBox *lbox,
78 const wxMouseEvent& event,
79 int item);
80
81 wxRenderer *m_renderer;
82
83 // the button which initiated the mouse capture (currently 0 or 1)
84 int m_btnCapture;
85
86 // the action to perform when the mouse moves while we capture it
87 wxControlAction m_actionMouse;
88
89 // the ctor parameter toggleOnPressAlways (see comments near it)
90 bool m_toggleOnPressAlways;
91
92 // do we track the mouse outside the window when it is captured?
93 bool m_trackMouseOutside;
94};
95
1e6feb95
VZ
96// ============================================================================
97// implementation of wxListBox
98// ============================================================================
99
b1294ada 100IMPLEMENT_DYNAMIC_CLASS(wxListBox, wxControlWithItems)
1e6feb95
VZ
101
102BEGIN_EVENT_TABLE(wxListBox, wxListBoxBase)
103 EVT_SIZE(wxListBox::OnSize)
1e6feb95
VZ
104END_EVENT_TABLE()
105
106// ----------------------------------------------------------------------------
107// construction
108// ----------------------------------------------------------------------------
109
110void wxListBox::Init()
111{
112 // will be calculated later when needed
113 m_lineHeight = 0;
114 m_itemsPerPage = 0;
115 m_maxWidth = 0;
116 m_scrollRangeY = 0;
117 m_maxWidthItem = -1;
6f34862c 118 m_strings.unsorted = NULL;
1e6feb95
VZ
119
120 // no items hence no current item
121 m_current = -1;
122 m_selAnchor = -1;
a290fa5a 123 m_currentChanged = false;
1e6feb95
VZ
124
125 // no need to update anything initially
126 m_updateCount = 0;
127
128 // no scrollbars to show nor update
129 m_updateScrollbarX =
130 m_showScrollbarX =
131 m_updateScrollbarY =
a290fa5a 132 m_showScrollbarY = false;
1e6feb95
VZ
133}
134
584ad2a3
MB
135wxListBox::wxListBox(wxWindow *parent,
136 wxWindowID id,
137 const wxPoint &pos,
138 const wxSize &size,
139 const wxArrayString& choices,
140 long style,
141 const wxValidator& validator,
142 const wxString &name)
afe13769 143 :wxScrollHelper(this)
584ad2a3
MB
144{
145 Init();
146
147 Create(parent, id, pos, size, choices, style, validator, name);
148}
149
150bool wxListBox::Create(wxWindow *parent,
151 wxWindowID id,
152 const wxPoint &pos,
153 const wxSize &size,
154 const wxArrayString& choices,
155 long style,
156 const wxValidator& validator,
157 const wxString &name)
158{
159 wxCArrayString chs(choices);
160
161 return Create(parent, id, pos, size, chs.GetCount(), chs.GetStrings(),
162 style, validator, name);
163}
164
1e6feb95
VZ
165bool wxListBox::Create(wxWindow *parent,
166 wxWindowID id,
167 const wxPoint &pos,
168 const wxSize &size,
169 int n,
170 const wxString choices[],
171 long style,
172 const wxValidator& validator,
173 const wxString &name)
174{
175 // for compatibility accept both the new and old styles - they mean the
176 // same thing for us
177 if ( style & wxLB_ALWAYS_SB )
178 style |= wxALWAYS_SHOW_SB;
179
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;
184
372c2812
RR
185#if wxUSE_TWO_WINDOWS
186 style |= wxVSCROLL|wxHSCROLL;
187 if ((style & wxBORDER_MASK) == 0)
188 style |= wxBORDER_SUNKEN;
189#endif
190
32b13913 191 if ( !wxControl::Create(parent, id, pos, size, style,
61fef19b 192 validator, name) )
a290fa5a 193 return false;
1e6feb95 194
6f34862c
VZ
195 if ( IsSorted() )
196 m_strings.sorted = new wxSortedArrayString;
197 else
198 m_strings.unsorted = new wxArrayString;
1e6feb95
VZ
199
200 Set(n, choices);
201
170acdc9 202 SetInitialSize(size);
1e6feb95
VZ
203
204 CreateInputHandler(wxINP_HANDLER_LISTBOX);
205
a290fa5a 206 return true;
1e6feb95
VZ
207}
208
209wxListBox::~wxListBox()
210{
d00b880a
VZ
211 // call this just to free the client data -- and avoid leaking memory
212 DoClear();
18dbdd3c 213
6f34862c
VZ
214 if ( IsSorted() )
215 delete m_strings.sorted;
216 else
217 delete m_strings.unsorted;
18dbdd3c 218
6f34862c
VZ
219 m_strings.sorted = NULL;
220}
221
222// ----------------------------------------------------------------------------
223// accessing strings
224// ----------------------------------------------------------------------------
225
226unsigned int wxListBox::GetCount() const
227{
228 return IsSorted() ? m_strings.sorted->size()
229 : m_strings.unsorted->size();
230}
231
232wxString wxListBox::GetString(unsigned int n) const
233{
234 return IsSorted() ? m_strings.sorted->Item(n)
235 : m_strings.unsorted->Item(n);
236}
237
238int wxListBox::FindString(const wxString& s, bool bCase) const
239{
240 return IsSorted() ? m_strings.sorted->Index(s, bCase)
241 : m_strings.unsorted->Index(s, bCase);
1e6feb95
VZ
242}
243
244// ----------------------------------------------------------------------------
245// adding/inserting strings
246// ----------------------------------------------------------------------------
247
a236aa20
VZ
248int wxListBox::DoInsertItems(const wxArrayStringsAdapter& items,
249 unsigned int pos,
250 void **clientData,
251 wxClientDataType type)
e1d6e01c 252{
a236aa20 253 int idx = wxNOT_FOUND;
1e6feb95 254
a236aa20
VZ
255 const unsigned int numItems = items.GetCount();
256 for ( unsigned int i = 0; i < numItems; ++i )
1e6feb95 257 {
a236aa20 258 const wxString& item = items[i];
6f34862c
VZ
259 idx = IsSorted() ? m_strings.sorted->Add(item)
260 : (m_strings.unsorted->Insert(item, pos), pos++);
1e6feb95 261
a236aa20
VZ
262 m_itemsClientData.Insert(NULL, idx);
263 AssignNewItemClientData(idx, clientData, i, type);
1e6feb95 264
a236aa20
VZ
265 // call the wxCheckListBox hook
266 OnItemInserted(idx);
1e6feb95
VZ
267 }
268
269 // the number of items has changed so we might have to show the scrollbar
a290fa5a 270 m_updateScrollbarY = true;
1e6feb95
VZ
271
272 // the max width also might have changed - just recalculate it instead of
273 // keeping track of it here, this is probably more efficient for a typical
274 // use pattern
275 RefreshHorzScrollbar();
276
277 // note that we have to refresh all the items after the ones we inserted,
278 // not just these items
279 RefreshFromItemToEnd(pos);
1e6feb95 280
a236aa20 281 return idx;
1e6feb95
VZ
282}
283
aa61d352 284void wxListBox::SetString(unsigned int n, const wxString& s)
1e6feb95 285{
e1d6e01c
WS
286 wxCHECK_RET( !IsSorted(), _T("can't set string in sorted listbox") );
287
6f34862c
VZ
288 if ( IsSorted() )
289 (*m_strings.sorted)[n] = s;
290 else
291 (*m_strings.unsorted)[n] = s;
e1d6e01c 292
1e6feb95
VZ
293 if ( HasHorzScrollbar() )
294 {
295 // we need to update m_maxWidth as changing the string may cause the
296 // horz scrollbar [dis]appear
297 wxCoord width;
e1d6e01c 298
1e6feb95
VZ
299 GetTextExtent(s, &width, NULL);
300
301 // it might have increased if the new string is long
302 if ( width > m_maxWidth )
303 {
304 m_maxWidth = width;
305 m_maxWidthItem = n;
a290fa5a 306 m_updateScrollbarX = true;
1e6feb95
VZ
307 }
308 // or also decreased if the old string was the longest one
87815d3d 309 else if ( n == (unsigned int)m_maxWidthItem )
1e6feb95
VZ
310 {
311 RefreshHorzScrollbar();
312 }
313 }
1e6feb95
VZ
314
315 RefreshItem(n);
316}
317
318// ----------------------------------------------------------------------------
319// removing strings
320// ----------------------------------------------------------------------------
321
322void wxListBox::DoClear()
323{
6f34862c
VZ
324 if ( IsSorted() )
325 m_strings.sorted->Clear();
326 else
327 m_strings.unsorted->Clear();
1e6feb95 328
1e6feb95
VZ
329 m_itemsClientData.Clear();
330 m_selections.Clear();
331
332 m_current = -1;
1e6feb95 333
a290fa5a 334 m_updateScrollbarY = true;
1e6feb95
VZ
335
336 RefreshHorzScrollbar();
337
338 RefreshAll();
339}
340
a236aa20 341void wxListBox::DoDeleteOneItem(unsigned int n)
1e6feb95 342{
8228b893 343 wxCHECK_RET( IsValid(n),
f7a201b9 344 _T("invalid index in wxListBox::Delete") );
1e6feb95
VZ
345
346 // do it before removing the index as otherwise the last item will not be
347 // refreshed (as GetCount() will be decremented)
348 RefreshFromItemToEnd(n);
349
6f34862c
VZ
350 if ( IsSorted() )
351 m_strings.sorted->RemoveAt(n);
352 else
353 m_strings.unsorted->RemoveAt(n);
1e6feb95 354
1e6feb95
VZ
355 m_itemsClientData.RemoveAt(n);
356
357 // when the item disappears we must not keep using its index
aa61d352 358 if ( (int)n == m_current )
1e6feb95
VZ
359 {
360 m_current = -1;
361 }
87815d3d 362 else if ( (int)n < m_current )
1e6feb95
VZ
363 {
364 m_current--;
365 }
366 //else: current item may stay
367
368 // update the selections array: the indices of all seletected items after
369 // the one being deleted must change and the item itselfm ust be removed
370 int index = wxNOT_FOUND;
aa61d352
VZ
371 unsigned int count = m_selections.GetCount();
372 for ( unsigned int item = 0; item < count; item++ )
1e6feb95 373 {
87815d3d 374 if ( m_selections[item] == (int)n )
1e6feb95
VZ
375 {
376 // remember to delete it later
377 index = item;
378 }
87815d3d 379 else if ( m_selections[item] > (int)n )
1e6feb95
VZ
380 {
381 // to account for the index shift
382 m_selections[item]--;
383 }
384 //else: nothing changed for this one
385 }
386
387 if ( index != wxNOT_FOUND )
388 {
389 m_selections.RemoveAt(index);
390 }
391
392 // the number of items has changed, hence the scrollbar may disappear
a290fa5a 393 m_updateScrollbarY = true;
1e6feb95
VZ
394
395 // finally, if the longest item was deleted the scrollbar may disappear
87815d3d 396 if ( (int)n == m_maxWidthItem )
1e6feb95
VZ
397 {
398 RefreshHorzScrollbar();
399 }
400}
401
402// ----------------------------------------------------------------------------
403// client data handling
404// ----------------------------------------------------------------------------
405
aa61d352 406void wxListBox::DoSetItemClientData(unsigned int n, void* clientData)
1e6feb95
VZ
407{
408 m_itemsClientData[n] = clientData;
409}
410
aa61d352 411void *wxListBox::DoGetItemClientData(unsigned int n) const
1e6feb95
VZ
412{
413 return m_itemsClientData[n];
414}
415
1e6feb95
VZ
416// ----------------------------------------------------------------------------
417// selection
418// ----------------------------------------------------------------------------
419
c6179a84 420void wxListBox::DoSetSelection(int n, bool select)
1e6feb95
VZ
421{
422 if ( select )
423 {
b3894f30
VS
424 if ( n == wxNOT_FOUND )
425 {
426 if ( !HasMultipleSelection() )
427 {
428 // selecting wxNOT_FOUND is documented to deselect all items
429 DeselectAll();
430 return;
431 }
432 }
433 else if ( m_selections.Index(n) == wxNOT_FOUND )
1e6feb95
VZ
434 {
435 if ( !HasMultipleSelection() )
436 {
437 // selecting an item in a single selection listbox deselects
438 // all the others
439 DeselectAll();
440 }
441
442 m_selections.Add(n);
443
444 RefreshItem(n);
445 }
446 //else: already selected
447 }
448 else // unselect
449 {
450 int index = m_selections.Index(n);
451 if ( index != wxNOT_FOUND )
452 {
453 m_selections.RemoveAt(index);
454
455 RefreshItem(n);
456 }
457 //else: not selected
458 }
459
460 // sanity check: a single selection listbox can't have more than one item
461 // selected
462 wxASSERT_MSG( HasMultipleSelection() || (m_selections.GetCount() < 2),
463 _T("multiple selected items in single selection lbox?") );
464
465 if ( select )
466 {
467 // the newly selected item becomes the current one
468 SetCurrentItem(n);
469 }
470}
471
472int wxListBox::GetSelection() const
473{
e95c145c 474 wxCHECK_MSG( !HasMultipleSelection(), wxNOT_FOUND,
1e6feb95
VZ
475 _T("use wxListBox::GetSelections for ths listbox") );
476
e95c145c 477 return m_selections.IsEmpty() ? wxNOT_FOUND : m_selections[0];
1e6feb95
VZ
478}
479
480int wxCMPFUNC_CONV wxCompareInts(int *n, int *m)
481{
482 return *n - *m;
483}
484
485int wxListBox::GetSelections(wxArrayInt& selections) const
486{
487 // always return sorted array to the user
488 selections = m_selections;
aa61d352 489 unsigned int count = m_selections.GetCount();
1e6feb95
VZ
490
491 // don't call sort on an empty array
492 if ( count )
493 {
494 selections.Sort(wxCompareInts);
495 }
496
497 return count;
498}
499
500// ----------------------------------------------------------------------------
501// refresh logic: we use delayed refreshing which allows to avoid multiple
502// refreshes (and hence flicker) in case when several listbox items are
503// added/deleted/changed subsequently
504// ----------------------------------------------------------------------------
505
506void wxListBox::RefreshFromItemToEnd(int from)
507{
508 RefreshItems(from, GetCount() - from);
509}
510
511void wxListBox::RefreshItems(int from, int count)
512{
513 switch ( m_updateCount )
514 {
515 case 0:
516 m_updateFrom = from;
517 m_updateCount = count;
518 break;
519
520 case -1:
521 // we refresh everything anyhow
522 break;
523
524 default:
525 // add these items to the others which we have to refresh
526 if ( m_updateFrom < from )
527 {
528 count += from - m_updateFrom;
529 if ( m_updateCount < count )
530 m_updateCount = count;
531 }
532 else // m_updateFrom >= from
533 {
534 int updateLast = wxMax(m_updateFrom + m_updateCount,
535 from + count);
536 m_updateFrom = from;
537 m_updateCount = updateLast - m_updateFrom;
538 }
539 }
540}
541
542void wxListBox::RefreshItem(int n)
543{
544 switch ( m_updateCount )
545 {
546 case 0:
547 // refresh this item only
548 m_updateFrom = n;
549 m_updateCount = 1;
550 break;
551
552 case -1:
553 // we refresh everything anyhow
554 break;
555
556 default:
557 // add this item to the others which we have to refresh
558 if ( m_updateFrom < n )
559 {
560 if ( m_updateCount < n - m_updateFrom + 1 )
561 m_updateCount = n - m_updateFrom + 1;
562 }
563 else // n <= m_updateFrom
564 {
565 m_updateCount += m_updateFrom - n;
566 m_updateFrom = n;
567 }
568 }
569}
570
571void wxListBox::RefreshAll()
572{
573 m_updateCount = -1;
574}
575
576void wxListBox::RefreshHorzScrollbar()
577{
578 m_maxWidth = 0; // recalculate it
a290fa5a 579 m_updateScrollbarX = true;
1e6feb95
VZ
580}
581
582void wxListBox::UpdateScrollbars()
583{
584 wxSize size = GetClientSize();
585
586 // is our height enough to show all items?
aa61d352 587 unsigned int nLines = GetCount();
1e6feb95 588 wxCoord lineHeight = GetLineHeight();
e95c145c 589 bool showScrollbarY = (int)nLines*lineHeight > size.y;
1e6feb95
VZ
590
591 // check the width too if required
592 wxCoord charWidth, maxWidth;
593 bool showScrollbarX;
594 if ( HasHorzScrollbar() )
595 {
596 charWidth = GetCharWidth();
597 maxWidth = GetMaxWidth();
598 showScrollbarX = maxWidth > size.x;
599 }
600 else // never show it
601 {
602 charWidth = maxWidth = 0;
a290fa5a 603 showScrollbarX = false;
1e6feb95
VZ
604 }
605
606 // what should be the scrollbar range now?
607 int scrollRangeX = showScrollbarX
608 ? (maxWidth + charWidth - 1) / charWidth + 2 // FIXME
609 : 0;
610 int scrollRangeY = showScrollbarY
611 ? nLines +
612 (size.y % lineHeight + lineHeight - 1) / lineHeight
613 : 0;
614
615 // reset scrollbars if something changed: either the visibility status
616 // or the range of a scrollbar which is shown
617 if ( (showScrollbarY != m_showScrollbarY) ||
618 (showScrollbarX != m_showScrollbarX) ||
619 (showScrollbarY && (scrollRangeY != m_scrollRangeY)) ||
620 (showScrollbarX && (scrollRangeX != m_scrollRangeX)) )
621 {
622 int x, y;
623 GetViewStart(&x, &y);
624 SetScrollbars(charWidth, lineHeight,
625 scrollRangeX, scrollRangeY,
626 x, y);
627
628 m_showScrollbarX = showScrollbarX;
629 m_showScrollbarY = showScrollbarY;
630
631 m_scrollRangeX = scrollRangeX;
632 m_scrollRangeY = scrollRangeY;
633 }
634}
635
636void wxListBox::UpdateItems()
637{
638 // only refresh the items which must be refreshed
639 if ( m_updateCount == -1 )
640 {
641 // refresh all
642 wxLogTrace(_T("listbox"), _T("Refreshing all"));
643
644 Refresh();
645 }
646 else
647 {
648 wxSize size = GetClientSize();
649 wxRect rect;
650 rect.width = size.x;
651 rect.height = size.y;
652 rect.y += m_updateFrom*GetLineHeight();
653 rect.height = m_updateCount*GetLineHeight();
654
655 // we don't need to calculate x position as we always refresh the
656 // entire line(s)
657 CalcScrolledPosition(0, rect.y, NULL, &rect.y);
658
659 wxLogTrace(_T("listbox"), _T("Refreshing items %d..%d (%d-%d)"),
660 m_updateFrom, m_updateFrom + m_updateCount - 1,
661 rect.GetTop(), rect.GetBottom());
662
a290fa5a 663 Refresh(true, &rect);
1e6feb95
VZ
664 }
665}
666
e39af974 667void wxListBox::OnInternalIdle()
1e6feb95
VZ
668{
669 if ( m_updateScrollbarY || m_updateScrollbarX )
670 {
671 UpdateScrollbars();
672
673 m_updateScrollbarX =
a290fa5a 674 m_updateScrollbarY = false;
1e6feb95
VZ
675 }
676
677 if ( m_currentChanged )
678 {
679 DoEnsureVisible(m_current);
680
a290fa5a 681 m_currentChanged = false;
1e6feb95
VZ
682 }
683
684 if ( m_updateCount )
685 {
686 UpdateItems();
687
688 m_updateCount = 0;
689 }
6f4767ac 690 wxListBoxBase::OnInternalIdle();
1e6feb95
VZ
691}
692
693// ----------------------------------------------------------------------------
694// drawing
695// ----------------------------------------------------------------------------
696
697wxBorder wxListBox::GetDefaultBorder() const
698{
699 return wxBORDER_SUNKEN;
700}
701
702void wxListBox::DoDraw(wxControlRenderer *renderer)
703{
704 // adjust the DC to account for scrolling
705 wxDC& dc = renderer->GetDC();
706 PrepareDC(dc);
707 dc.SetFont(GetFont());
708
709 // get the update rect
710 wxRect rectUpdate = GetUpdateClientRect();
711
712 int yTop, yBottom;
713 CalcUnscrolledPosition(0, rectUpdate.GetTop(), NULL, &yTop);
714 CalcUnscrolledPosition(0, rectUpdate.GetBottom(), NULL, &yBottom);
715
716 // get the items which must be redrawn
717 wxCoord lineHeight = GetLineHeight();
aa61d352
VZ
718 unsigned int itemFirst = yTop / lineHeight,
719 itemLast = (yBottom + lineHeight - 1) / lineHeight,
6f34862c 720 itemMax = GetCount();
1e6feb95
VZ
721
722 if ( itemFirst >= itemMax )
723 return;
724
725 if ( itemLast > itemMax )
726 itemLast = itemMax;
727
728 // do draw them
729 wxLogTrace(_T("listbox"), _T("Repainting items %d..%d"),
730 itemFirst, itemLast);
731
732 DoDrawRange(renderer, itemFirst, itemLast);
733}
734
735void wxListBox::DoDrawRange(wxControlRenderer *renderer,
736 int itemFirst, int itemLast)
737{
738 renderer->DrawItems(this, itemFirst, itemLast);
739}
740
741// ----------------------------------------------------------------------------
742// size calculations
743// ----------------------------------------------------------------------------
744
745bool wxListBox::SetFont(const wxFont& font)
746{
747 if ( !wxControl::SetFont(font) )
a290fa5a 748 return false;
1e6feb95
VZ
749
750 CalcItemsPerPage();
751
752 RefreshAll();
753
a290fa5a 754 return true;
1e6feb95
VZ
755}
756
757void wxListBox::CalcItemsPerPage()
758{
759 m_lineHeight = GetRenderer()->GetListboxItemHeight(GetCharHeight());
760 m_itemsPerPage = GetClientSize().y / m_lineHeight;
761}
762
763int wxListBox::GetItemsPerPage() const
764{
765 if ( !m_itemsPerPage )
766 {
767 wxConstCast(this, wxListBox)->CalcItemsPerPage();
768 }
769
770 return m_itemsPerPage;
771}
772
773wxCoord wxListBox::GetLineHeight() const
774{
775 if ( !m_lineHeight )
776 {
777 wxConstCast(this, wxListBox)->CalcItemsPerPage();
778 }
779
780 return m_lineHeight;
781}
782
783wxCoord wxListBox::GetMaxWidth() const
784{
785 if ( m_maxWidth == 0 )
786 {
787 wxListBox *self = wxConstCast(this, wxListBox);
788 wxCoord width;
6f34862c 789 unsigned int count = GetCount();
aa61d352 790 for ( unsigned int n = 0; n < count; n++ )
1e6feb95 791 {
e1d6e01c 792 GetTextExtent(this->GetString(n), &width, NULL);
1e6feb95
VZ
793 if ( width > m_maxWidth )
794 {
795 self->m_maxWidth = width;
796 self->m_maxWidthItem = n;
797 }
798 }
799 }
800
801 return m_maxWidth;
802}
803
804void wxListBox::OnSize(wxSizeEvent& event)
805{
806 // recalculate the number of items per page
807 CalcItemsPerPage();
808
809 // the scrollbars might [dis]appear
810 m_updateScrollbarX =
a290fa5a 811 m_updateScrollbarY = true;
1e6feb95
VZ
812
813 event.Skip();
814}
815
816void wxListBox::DoSetFirstItem(int n)
817{
818 SetCurrentItem(n);
819}
820
821void wxListBox::DoSetSize(int x, int y,
822 int width, int height,
823 int sizeFlags)
824{
825 if ( GetWindowStyle() & wxLB_INT_HEIGHT )
826 {
827 // we must round up the height to an entire number of rows
828
829 // the client area must contain an int number of rows, so take borders
830 // into account
831 wxRect rectBorders = GetRenderer()->GetBorderDimensions(GetBorder());
832 wxCoord hBorders = rectBorders.y + rectBorders.height;
833
834 wxCoord hLine = GetLineHeight();
835 height = ((height - hBorders + hLine - 1) / hLine)*hLine + hBorders;
836 }
837
61fef19b 838 wxListBoxBase::DoSetSize(x, y, width, height, sizeFlags);
1e6feb95
VZ
839}
840
841wxSize wxListBox::DoGetBestClientSize() const
842{
843 wxCoord width = 0,
844 height = 0;
845
6f34862c 846 unsigned int count = GetCount();
aa61d352 847 for ( unsigned int n = 0; n < count; n++ )
1e6feb95
VZ
848 {
849 wxCoord w,h;
e1d6e01c 850 GetTextExtent(this->GetString(n), &w, &h);
1e6feb95
VZ
851
852 if ( w > width )
853 width = w;
854 if ( h > height )
855 height = h;
856 }
857
858 // if the listbox is empty, still give it some non zero (even if
859 // arbitrary) size - otherwise, leave small margin around the strings
860 if ( !width )
861 width = 100;
862 else
863 width += 3*GetCharWidth();
864
865 if ( !height )
866 height = GetCharHeight();
867
868 // we need the height of the entire listbox, not just of one line
869 height *= wxMax(count, 7);
870
871 return wxSize(width, height);
872}
873
874// ----------------------------------------------------------------------------
875// listbox actions
876// ----------------------------------------------------------------------------
877
878bool wxListBox::SendEvent(wxEventType type, int item)
879{
1e6feb95
VZ
880 wxCommandEvent event(type, m_windowId);
881 event.SetEventObject(this);
882
883 // use the current item by default
884 if ( item == -1 )
885 {
886 item = m_current;
887 }
888
889 // client data and string parameters only make sense if we have an item
890 if ( item != -1 )
891 {
892 if ( HasClientObjectData() )
893 event.SetClientObject(GetClientObject(item));
894 else if ( HasClientUntypedData() )
895 event.SetClientData(GetClientData(item));
896
897 event.SetString(GetString(item));
898 }
899
687706f5 900 event.SetInt(item);
1e6feb95
VZ
901
902 return GetEventHandler()->ProcessEvent(event);
903}
904
905void wxListBox::SetCurrentItem(int n)
906{
907 if ( n != m_current )
908 {
909 if ( m_current != -1 )
910 RefreshItem(m_current);
911
912 m_current = n;
913
914 if ( m_current != -1 )
915 {
a290fa5a 916 m_currentChanged = true;
1e6feb95
VZ
917
918 RefreshItem(m_current);
919 }
920 }
921 //else: nothing to do
922}
923
924bool wxListBox::FindItem(const wxString& prefix, bool strictlyAfter)
925{
aa61d352 926 unsigned int count = GetCount();
8228b893 927 if ( count==0 )
1e6feb95
VZ
928 {
929 // empty listbox, we can't find anything in it
a290fa5a 930 return false;
1e6feb95
VZ
931 }
932
933 // start either from the current item or from the next one if strictlyAfter
934 // is true
935 int first;
936 if ( strictlyAfter )
937 {
938 // the following line will set first correctly to 0 if there is no
939 // selection (m_current == -1)
e95c145c 940 first = m_current == (int)(count - 1) ? 0 : m_current + 1;
1e6feb95
VZ
941 }
942 else // start with the current
943 {
944 first = m_current == -1 ? 0 : m_current;
945 }
946
947 int last = first == 0 ? count - 1 : first - 1;
948
949 // if this is not true we'd never exit from the loop below!
8228b893 950 wxASSERT_MSG( first < (int)count && last < (int)count, _T("logic error") );
1e6feb95
VZ
951
952 // precompute it outside the loop
953 size_t len = prefix.length();
954
955 // loop over all items in the listbox
e95c145c 956 for ( int item = first; item != (int)last; item < (int)(count - 1) ? item++ : item = 0 )
1e6feb95 957 {
e1d6e01c 958 if ( wxStrnicmp(this->GetString(item).c_str(), prefix, len) == 0 )
1e6feb95
VZ
959 {
960 SetCurrentItem(item);
961
962 if ( !(GetWindowStyle() & wxLB_MULTIPLE) )
963 {
964 DeselectAll(item);
965 SelectAndNotify(item);
966
967 if ( GetWindowStyle() & wxLB_EXTENDED )
968 AnchorSelection(item);
969 }
970
a290fa5a 971 return true;
1e6feb95
VZ
972 }
973 }
974
975 // nothing found
a290fa5a 976 return false;
1e6feb95
VZ
977}
978
979void wxListBox::EnsureVisible(int n)
980{
981 if ( m_updateScrollbarY )
982 {
983 UpdateScrollbars();
984
985 m_updateScrollbarX =
a290fa5a 986 m_updateScrollbarY = false;
1e6feb95
VZ
987 }
988
989 DoEnsureVisible(n);
990}
991
992void wxListBox::DoEnsureVisible(int n)
993{
994 if ( !m_showScrollbarY )
995 {
996 // nothing to do - everything is shown anyhow
997 return;
998 }
999
1000 int first;
1001 GetViewStart(0, &first);
1002 if ( first > n )
1003 {
1004 // we need to scroll upwards, so make the current item appear on top
1005 // of the shown range
1006 Scroll(0, n);
1007 }
1008 else
1009 {
1010 int last = first + GetClientSize().y / GetLineHeight() - 1;
1011 if ( last < n )
1012 {
1013 // scroll down: the current item appears at the bottom of the
1014 // range
1015 Scroll(0, n - (last - first));
1016 }
1017 }
1018}
1019
1020void wxListBox::ChangeCurrent(int diff)
1021{
1022 int current = m_current == -1 ? 0 : m_current;
1023
1024 current += diff;
1025
1026 int last = GetCount() - 1;
1027 if ( current < 0 )
1028 current = 0;
1029 else if ( current > last )
1030 current = last;
1031
1032 SetCurrentItem(current);
1033}
1034
1035void wxListBox::ExtendSelection(int itemTo)
1036{
1037 // if we don't have the explicit values for selection start/end, make them
1038 // up
1039 if ( m_selAnchor == -1 )
1040 m_selAnchor = m_current;
1041
1042 if ( itemTo == -1 )
1043 itemTo = m_current;
1044
1045 // swap the start/end of selection range if necessary
1046 int itemFrom = m_selAnchor;
1047 if ( itemFrom > itemTo )
1048 {
1049 int itemTmp = itemFrom;
1050 itemFrom = itemTo;
1051 itemTo = itemTmp;
1052 }
1053
1054 // the selection should now include all items in the range between the
1055 // anchor and the specified item and only them
1056
1057 int n;
1058 for ( n = 0; n < itemFrom; n++ )
1059 {
1060 Deselect(n);
1061 }
1062
1063 for ( ; n <= itemTo; n++ )
1064 {
1065 SetSelection(n);
1066 }
1067
aa61d352 1068 unsigned int count = GetCount();
8228b893 1069 for ( ; n < (int)count; n++ )
1e6feb95
VZ
1070 {
1071 Deselect(n);
1072 }
1073}
1074
2b5f62a0 1075void wxListBox::DoSelect(int item, bool sel)
1e6feb95
VZ
1076{
1077 if ( item != -1 )
1078 {
1079 // go to this item first
1080 SetCurrentItem(item);
1081 }
1082
1083 // the current item is the one we want to change: either it was just
1084 // changed above to be the same as item or item == -1 in which we case we
1085 // are supposed to use the current one anyhow
1086 if ( m_current != -1 )
1087 {
1088 // [de]select it
1089 SetSelection(m_current, sel);
1090 }
1091}
1092
1093void wxListBox::SelectAndNotify(int item)
1094{
2b5f62a0 1095 DoSelect(item);
1e6feb95
VZ
1096
1097 SendEvent(wxEVT_COMMAND_LISTBOX_SELECTED);
1098}
1099
1100void wxListBox::Activate(int item)
1101{
1102 if ( item != -1 )
1103 SetCurrentItem(item);
1104 else
1105 item = m_current;
1106
1107 if ( !(GetWindowStyle() & wxLB_MULTIPLE) )
1108 {
1109 DeselectAll(item);
1110 }
1111
1112 if ( item != -1 )
1113 {
2b5f62a0 1114 DoSelect(item);
1e6feb95
VZ
1115
1116 SendEvent(wxEVT_COMMAND_LISTBOX_DOUBLECLICKED);
1117 }
1118}
1119
1120// ----------------------------------------------------------------------------
1121// input handling
1122// ----------------------------------------------------------------------------
1123
55f095d4
VZ
1124/*
1125 The numArg here is the listbox item index while the strArg is used
1126 differently for the different actions:
1127
1128 a) for wxACTION_LISTBOX_FIND it has the natural meaning: this is the string
1129 to find
1130
1131 b) for wxACTION_LISTBOX_SELECT and wxACTION_LISTBOX_EXTENDSEL it is used
1132 to decide if the listbox should send the notification event (it is empty)
1133 or not (it is not): this allows us to reuse the same action for when the
1134 user is dragging the mouse when it has been released although in the
1135 first case no notification is sent while in the second it is sent.
1136 */
1e6feb95
VZ
1137bool wxListBox::PerformAction(const wxControlAction& action,
1138 long numArg,
1139 const wxString& strArg)
1140{
1141 int item = (int)numArg;
1142
1143 if ( action == wxACTION_LISTBOX_SETFOCUS )
ae1daed0 1144 {
1e6feb95 1145 SetCurrentItem(item);
ae1daed0 1146 }
1e6feb95 1147 else if ( action == wxACTION_LISTBOX_ACTIVATE )
ae1daed0 1148 {
1e6feb95 1149 Activate(item);
ae1daed0 1150 }
1e6feb95
VZ
1151 else if ( action == wxACTION_LISTBOX_TOGGLE )
1152 {
1153 if ( item == -1 )
1154 item = m_current;
1155
1156 if ( IsSelected(item) )
2b5f62a0 1157 DoUnselect(item);
1e6feb95
VZ
1158 else
1159 SelectAndNotify(item);
1160 }
1161 else if ( action == wxACTION_LISTBOX_SELECT )
1162 {
1163 DeselectAll(item);
55f095d4
VZ
1164
1165 if ( strArg.empty() )
1166 SelectAndNotify(item);
1167 else
2b5f62a0 1168 DoSelect(item);
1e6feb95
VZ
1169 }
1170 else if ( action == wxACTION_LISTBOX_SELECTADD )
2b5f62a0 1171 DoSelect(item);
1e6feb95 1172 else if ( action == wxACTION_LISTBOX_UNSELECT )
2b5f62a0 1173 DoUnselect(item);
1e6feb95
VZ
1174 else if ( action == wxACTION_LISTBOX_MOVEDOWN )
1175 ChangeCurrent(1);
1176 else if ( action == wxACTION_LISTBOX_MOVEUP )
1177 ChangeCurrent(-1);
1178 else if ( action == wxACTION_LISTBOX_PAGEDOWN )
1179 ChangeCurrent(GetItemsPerPage());
1180 else if ( action == wxACTION_LISTBOX_PAGEUP )
1181 ChangeCurrent(-GetItemsPerPage());
1182 else if ( action == wxACTION_LISTBOX_START )
1183 SetCurrentItem(0);
1184 else if ( action == wxACTION_LISTBOX_END )
1185 SetCurrentItem(GetCount() - 1);
1186 else if ( action == wxACTION_LISTBOX_UNSELECTALL )
1187 DeselectAll(item);
1188 else if ( action == wxACTION_LISTBOX_EXTENDSEL )
1189 ExtendSelection(item);
1190 else if ( action == wxACTION_LISTBOX_FIND )
1191 FindNextItem(strArg);
1192 else if ( action == wxACTION_LISTBOX_ANCHOR )
1193 AnchorSelection(item == -1 ? m_current : item);
1194 else if ( action == wxACTION_LISTBOX_SELECTALL ||
1195 action == wxACTION_LISTBOX_SELTOGGLE )
1196 wxFAIL_MSG(_T("unimplemented yet"));
1197 else
1198 return wxControl::PerformAction(action, numArg, strArg);
1199
a290fa5a 1200 return true;
1e6feb95
VZ
1201}
1202
9467bdb7
VZ
1203/* static */
1204wxInputHandler *wxListBox::GetStdInputHandler(wxInputHandler *handlerDef)
1205{
1206 static wxStdListboxInputHandler s_handler(handlerDef);
1207
1208 return &s_handler;
1209}
1210
1e6feb95
VZ
1211// ============================================================================
1212// implementation of wxStdListboxInputHandler
1213// ============================================================================
1214
1215wxStdListboxInputHandler::wxStdListboxInputHandler(wxInputHandler *handler,
1216 bool toggleOnPressAlways)
1217 : wxStdInputHandler(handler)
1218{
1219 m_btnCapture = 0;
1220 m_toggleOnPressAlways = toggleOnPressAlways;
1221 m_actionMouse = wxACTION_NONE;
a290fa5a 1222 m_trackMouseOutside = true;
1e6feb95
VZ
1223}
1224
1225int wxStdListboxInputHandler::HitTest(const wxListBox *lbox,
1226 const wxMouseEvent& event)
1227{
1228 int item = HitTestUnsafe(lbox, event);
1229
1230 return FixItemIndex(lbox, item);
1231}
1232
1233int wxStdListboxInputHandler::HitTestUnsafe(const wxListBox *lbox,
1234 const wxMouseEvent& event)
1235{
1236 wxPoint pt = event.GetPosition();
1237 pt -= lbox->GetClientAreaOrigin();
1238 int y;
1239 lbox->CalcUnscrolledPosition(0, pt.y, NULL, &y);
1240 return y / lbox->GetLineHeight();
1241}
1242
1243int wxStdListboxInputHandler::FixItemIndex(const wxListBox *lbox,
1244 int item)
1245{
1246 if ( item < 0 )
1247 {
1248 // mouse is above the first item
1249 item = 0;
1250 }
aa61d352 1251 else if ( (unsigned int)item >= lbox->GetCount() )
1e6feb95
VZ
1252 {
1253 // mouse is below the last item
1254 item = lbox->GetCount() - 1;
1255 }
1256
1257 return item;
1258}
1259
1260bool wxStdListboxInputHandler::IsValidIndex(const wxListBox *lbox, int item)
1261{
aa61d352 1262 return item >= 0 && (unsigned int)item < lbox->GetCount();
1e6feb95
VZ
1263}
1264
1265wxControlAction
1266wxStdListboxInputHandler::SetupCapture(wxListBox *lbox,
1267 const wxMouseEvent& event,
1268 int item)
1269{
1270 // we currently only allow selecting with the left mouse button, if we
1271 // do need to allow using other buttons too we might use the code
1272 // inside #if 0
1273#if 0
1274 m_btnCapture = event.LeftDown()
1275 ? 1
1276 : event.RightDown()
1277 ? 3
1278 : 2;
1279#else
1280 m_btnCapture = 1;
1281#endif // 0/1
1282
1283 wxControlAction action;
1284 if ( lbox->HasMultipleSelection() )
1285 {
1286 if ( lbox->GetWindowStyle() & wxLB_MULTIPLE )
1287 {
1288 if ( m_toggleOnPressAlways )
1289 {
1290 // toggle the item right now
1291 action = wxACTION_LISTBOX_TOGGLE;
1292 }
1293 //else: later
1294
1295 m_actionMouse = wxACTION_LISTBOX_SETFOCUS;
1296 }
1297 else // wxLB_EXTENDED listbox
1298 {
1299 // simple click in an extended sel listbox clears the old
1300 // selection and adds the clicked item to it then, ctrl-click
1301 // toggles an item to it and shift-click adds a range between
1302 // the old selection anchor and the clicked item
1303 if ( event.ControlDown() )
1304 {
1305 lbox->PerformAction(wxACTION_LISTBOX_ANCHOR, item);
1306
1307 action = wxACTION_LISTBOX_TOGGLE;
1308 }
1309 else if ( event.ShiftDown() )
1310 {
1311 action = wxACTION_LISTBOX_EXTENDSEL;
1312 }
1313 else // simple click
1314 {
1315 lbox->PerformAction(wxACTION_LISTBOX_ANCHOR, item);
1316
1317 action = wxACTION_LISTBOX_SELECT;
1318 }
1319
1320 m_actionMouse = wxACTION_LISTBOX_EXTENDSEL;
1321 }
1322 }
1323 else // single selection
1324 {
1325 m_actionMouse =
1326 action = wxACTION_LISTBOX_SELECT;
1327 }
1328
1329 // by default we always do track it
a290fa5a 1330 m_trackMouseOutside = true;
1e6feb95
VZ
1331
1332 return action;
1333}
1334
23645bfa 1335bool wxStdListboxInputHandler::HandleKey(wxInputConsumer *consumer,
1e6feb95
VZ
1336 const wxKeyEvent& event,
1337 bool pressed)
1338{
1339 // we're only interested in the key press events
1340 if ( pressed && !event.AltDown() )
1341 {
a290fa5a 1342 bool isMoveCmd = true;
23645bfa 1343 int style = consumer->GetInputWindow()->GetWindowStyle();
1e6feb95
VZ
1344
1345 wxControlAction action;
1346 wxString strArg;
1347
1348 int keycode = event.GetKeyCode();
1349 switch ( keycode )
1350 {
1351 // movement
55f095d4
VZ
1352 case WXK_UP:
1353 action = wxACTION_LISTBOX_MOVEUP;
1354 break;
1355
1356 case WXK_DOWN:
1357 action = wxACTION_LISTBOX_MOVEDOWN;
1358 break;
1359
187c183c 1360 case WXK_PAGEUP:
55f095d4
VZ
1361 action = wxACTION_LISTBOX_PAGEUP;
1362 break;
1363
187c183c 1364 case WXK_PAGEDOWN:
55f095d4
VZ
1365 action = wxACTION_LISTBOX_PAGEDOWN;
1366 break;
1367
1368 case WXK_HOME:
1369 action = wxACTION_LISTBOX_START;
1370 break;
1371
1372 case WXK_END:
1373 action = wxACTION_LISTBOX_END;
1374 break;
1e6feb95
VZ
1375
1376 // selection
1377 case WXK_SPACE:
1378 if ( style & wxLB_MULTIPLE )
1379 {
1380 action = wxACTION_LISTBOX_TOGGLE;
a290fa5a 1381 isMoveCmd = false;
1e6feb95
VZ
1382 }
1383 break;
1384
1385 case WXK_RETURN:
1386 action = wxACTION_LISTBOX_ACTIVATE;
a290fa5a 1387 isMoveCmd = false;
1e6feb95
VZ
1388 break;
1389
1390 default:
32b13913 1391 if ( (keycode < 255) && wxIsalnum((wxChar)keycode) )
1e6feb95
VZ
1392 {
1393 action = wxACTION_LISTBOX_FIND;
1394 strArg = (wxChar)keycode;
1395 }
1396 }
1397
a290fa5a 1398 if ( !action.IsEmpty() )
1e6feb95 1399 {
23645bfa 1400 consumer->PerformAction(action, -1, strArg);
1e6feb95
VZ
1401
1402 if ( isMoveCmd )
1403 {
1404 if ( style & wxLB_SINGLE )
1405 {
1406 // the current item is always the one selected
23645bfa 1407 consumer->PerformAction(wxACTION_LISTBOX_SELECT);
1e6feb95
VZ
1408 }
1409 else if ( style & wxLB_EXTENDED )
1410 {
1411 if ( event.ShiftDown() )
23645bfa 1412 consumer->PerformAction(wxACTION_LISTBOX_EXTENDSEL);
1e6feb95
VZ
1413 else
1414 {
1415 // select the item and make it the new selection anchor
23645bfa
VS
1416 consumer->PerformAction(wxACTION_LISTBOX_SELECT);
1417 consumer->PerformAction(wxACTION_LISTBOX_ANCHOR);
1e6feb95
VZ
1418 }
1419 }
1420 //else: nothing to do for multiple selection listboxes
1421 }
1422
a290fa5a 1423 return true;
1e6feb95
VZ
1424 }
1425 }
1426
23645bfa 1427 return wxStdInputHandler::HandleKey(consumer, event, pressed);
1e6feb95
VZ
1428}
1429
23645bfa 1430bool wxStdListboxInputHandler::HandleMouse(wxInputConsumer *consumer,
1e6feb95
VZ
1431 const wxMouseEvent& event)
1432{
23645bfa 1433 wxListBox *lbox = wxStaticCast(consumer->GetInputWindow(), wxListBox);
1e6feb95
VZ
1434 int item = HitTest(lbox, event);
1435 wxControlAction action;
1436
1437 // when the left mouse button is pressed, capture the mouse and track the
1438 // item under mouse (if the mouse leaves the window, we will still be
1439 // getting the mouse move messages generated by wxScrollWindow)
1440 if ( event.LeftDown() )
1441 {
1442 // capture the mouse to track the selected item
1443 lbox->CaptureMouse();
1444
1445 action = SetupCapture(lbox, event, item);
1446 }
1447 else if ( m_btnCapture && event.ButtonUp(m_btnCapture) )
1448 {
1449 // when the left mouse button is released, release the mouse too
1450 wxWindow *winCapture = wxWindow::GetCapture();
1451 if ( winCapture )
1452 {
1453 winCapture->ReleaseMouse();
1454 m_btnCapture = 0;
1455
1e6feb95
VZ
1456 action = m_actionMouse;
1457 }
1458 //else: the mouse wasn't presed over the listbox, only released here
1459 }
1460 else if ( event.LeftDClick() )
1461 {
1462 action = wxACTION_LISTBOX_ACTIVATE;
1463 }
1464
a290fa5a 1465 if ( !action.IsEmpty() )
1e6feb95
VZ
1466 {
1467 lbox->PerformAction(action, item);
1468
a290fa5a 1469 return true;
1e6feb95
VZ
1470 }
1471
23645bfa 1472 return wxStdInputHandler::HandleMouse(consumer, event);
1e6feb95
VZ
1473}
1474
23645bfa 1475bool wxStdListboxInputHandler::HandleMouseMove(wxInputConsumer *consumer,
1e6feb95
VZ
1476 const wxMouseEvent& event)
1477{
1478 wxWindow *winCapture = wxWindow::GetCapture();
1479 if ( winCapture && (event.GetEventObject() == winCapture) )
1480 {
23645bfa 1481 wxListBox *lbox = wxStaticCast(consumer->GetInputWindow(), wxListBox);
1e6feb95
VZ
1482
1483 if ( !m_btnCapture || !m_trackMouseOutside )
1484 {
1485 // someone captured the mouse for us (we always set m_btnCapture
1486 // when we do it ourselves): in this case we only react to
1487 // the mouse messages when they happen inside the listbox
1488 if ( lbox->HitTest(event.GetPosition()) != wxHT_WINDOW_INSIDE )
a290fa5a 1489 return false;
1e6feb95
VZ
1490 }
1491
1492 int item = HitTest(lbox, event);
1493 if ( !m_btnCapture )
1494 {
1495 // now that we have the mouse inside the listbox, do capture it
1496 // normally - but ensure that we will still ignore the outside
1497 // events
1498 SetupCapture(lbox, event, item);
1499
a290fa5a 1500 m_trackMouseOutside = false;
1e6feb95
VZ
1501 }
1502
1503 if ( IsValidIndex(lbox, item) )
1504 {
55f095d4
VZ
1505 // pass something into strArg to tell the listbox that it shouldn't
1506 // send the notification message: see PerformAction() above
1507 lbox->PerformAction(m_actionMouse, item, _T("no"));
1e6feb95
VZ
1508 }
1509 // else: don't pass invalid index to the listbox
1510 }
1511 else // we don't have capture any more
1512 {
1513 if ( m_btnCapture )
1514 {
1515 // if we lost capture unexpectedly (someone else took the capture
1516 // from us), return to a consistent state
1517 m_btnCapture = 0;
1518 }
1519 }
1520
23645bfa 1521 return wxStdInputHandler::HandleMouseMove(consumer, event);
1e6feb95
VZ
1522}
1523
1524#endif // wxUSE_LISTBOX