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