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