~ubuntu-dev/wxwidgets2.6/upstream-debian

« back to all changes in this revision

Viewing changes to src/univ/listbox.cpp

  • Committer: Daniel T Chen
  • Date: 2006-06-26 10:15:11 UTC
  • Revision ID: crimsun@ubuntu.com-20060626101511-a4436cec4c6d9b35
ImportĀ DebianĀ 2.6.3.2.1

Show diffs side-by-side

added added

removed removed

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