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

« back to all changes in this revision

Viewing changes to src/generic/vlbox.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:        generic/vlbox.cpp
 
3
// Purpose:     implementation of wxVListBox
 
4
// Author:      Vadim Zeitlin
 
5
// Modified by:
 
6
// Created:     31.05.03
 
7
// RCS-ID:      $Id: vlbox.cpp,v 1.22.2.2 2006/04/14 15:24:13 VZ Exp $
 
8
// Copyright:   (c) 2003 Vadim Zeitlin <vadim@wxwindows.org>
 
9
// License:     wxWindows license
 
10
///////////////////////////////////////////////////////////////////////////////
 
11
 
 
12
// ============================================================================
 
13
// declarations
 
14
// ============================================================================
 
15
 
 
16
// ----------------------------------------------------------------------------
 
17
// headers
 
18
// ----------------------------------------------------------------------------
 
19
 
 
20
// For compilers that support precompilation, includes "wx.h".
 
21
#include "wx/wxprec.h"
 
22
 
 
23
#ifdef __BORLANDC__
 
24
    #pragma hdrstop
 
25
#endif
 
26
 
 
27
#if wxUSE_LISTBOX
 
28
 
 
29
#ifndef WX_PRECOMP
 
30
    #include "wx/settings.h"
 
31
    #include "wx/dcclient.h"
 
32
#endif //WX_PRECOMP
 
33
 
 
34
#include "wx/vlbox.h"
 
35
#include "wx/dcbuffer.h"
 
36
#include "wx/selstore.h"
 
37
#include "wx/bitmap.h"
 
38
 
 
39
// ----------------------------------------------------------------------------
 
40
// event tables
 
41
// ----------------------------------------------------------------------------
 
42
 
 
43
BEGIN_EVENT_TABLE(wxVListBox, wxVScrolledWindow)
 
44
    EVT_PAINT(wxVListBox::OnPaint)
 
45
 
 
46
    EVT_KEY_DOWN(wxVListBox::OnKeyDown)
 
47
    EVT_LEFT_DOWN(wxVListBox::OnLeftDown)
 
48
    EVT_LEFT_DCLICK(wxVListBox::OnLeftDClick)
 
49
END_EVENT_TABLE()
 
50
 
 
51
// ============================================================================
 
52
// implementation
 
53
// ============================================================================
 
54
 
 
55
IMPLEMENT_ABSTRACT_CLASS(wxVListBox, wxVScrolledWindow)
 
56
 
 
57
// ----------------------------------------------------------------------------
 
58
// wxVListBox creation
 
59
// ----------------------------------------------------------------------------
 
60
 
 
61
// due to ABI compatibility reasons, we need to declare double-buffer
 
62
// outside the class
 
63
static wxBitmap* gs_doubleBuffer = NULL;
 
64
 
 
65
 
 
66
void wxVListBox::Init()
 
67
{
 
68
    m_current =
 
69
    m_anchor = wxNOT_FOUND;
 
70
    m_selStore = NULL;
 
71
}
 
72
 
 
73
bool wxVListBox::Create(wxWindow *parent,
 
74
                        wxWindowID id,
 
75
                        const wxPoint& pos,
 
76
                        const wxSize& size,
 
77
                        long style,
 
78
                        const wxString& name)
 
79
{
 
80
    style |= wxWANTS_CHARS | wxFULL_REPAINT_ON_RESIZE;
 
81
    if ( !wxVScrolledWindow::Create(parent, id, pos, size, style, name) )
 
82
        return false;
 
83
 
 
84
    if ( style & wxLB_MULTIPLE )
 
85
        m_selStore = new wxSelectionStore;
 
86
 
 
87
    // make sure the native widget has the right colour since we do
 
88
    // transparent drawing by default
 
89
    SetBackgroundColour(GetBackgroundColour());
 
90
    m_colBgSel = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT);
 
91
 
 
92
    // flicker-free drawing requires this
 
93
    SetBackgroundStyle(wxBG_STYLE_CUSTOM);
 
94
 
 
95
    return true;
 
96
}
 
97
 
 
98
wxVListBox::~wxVListBox()
 
99
{
 
100
    delete m_selStore;
 
101
 
 
102
    delete gs_doubleBuffer;
 
103
    gs_doubleBuffer = NULL;
 
104
}
 
105
 
 
106
void wxVListBox::SetItemCount(size_t count)
 
107
{
 
108
    if ( m_selStore )
 
109
    {
 
110
        // tell the selection store that our number of items has changed
 
111
        m_selStore->SetItemCount(count);
 
112
    }
 
113
 
 
114
    SetLineCount(count);
 
115
}
 
116
 
 
117
// ----------------------------------------------------------------------------
 
118
// selection handling
 
119
// ----------------------------------------------------------------------------
 
120
 
 
121
bool wxVListBox::IsSelected(size_t line) const
 
122
{
 
123
    return m_selStore ? m_selStore->IsSelected(line) : (int)line == m_current;
 
124
}
 
125
 
 
126
bool wxVListBox::Select(size_t item, bool select)
 
127
{
 
128
    wxCHECK_MSG( m_selStore, false,
 
129
                 _T("Select() may only be used with multiselection listbox") );
 
130
 
 
131
    wxCHECK_MSG( item < GetItemCount(), false,
 
132
                 _T("Select(): invalid item index") );
 
133
 
 
134
    bool changed = m_selStore->SelectItem(item, select);
 
135
    if ( changed )
 
136
    {
 
137
        // selection really changed
 
138
        RefreshLine(item);
 
139
    }
 
140
 
 
141
    DoSetCurrent(item);
 
142
 
 
143
    return changed;
 
144
}
 
145
 
 
146
bool wxVListBox::SelectRange(size_t from, size_t to)
 
147
{
 
148
    wxCHECK_MSG( m_selStore, false,
 
149
                 _T("SelectRange() may only be used with multiselection listbox") );
 
150
 
 
151
    // make sure items are in correct order
 
152
    if ( from > to )
 
153
    {
 
154
        size_t tmp = from;
 
155
        from = to;
 
156
        to = tmp;
 
157
    }
 
158
 
 
159
    wxCHECK_MSG( to < GetItemCount(), false,
 
160
                    _T("SelectRange(): invalid item index") );
 
161
 
 
162
    wxArrayInt changed;
 
163
    if ( !m_selStore->SelectRange(from, to, true, &changed) )
 
164
    {
 
165
        // too many items have changed, we didn't record them in changed array
 
166
        // so we have no choice but to refresh everything between from and to
 
167
        RefreshLines(from, to);
 
168
    }
 
169
    else // we've got the indices of the changed items
 
170
    {
 
171
        const size_t count = changed.GetCount();
 
172
        if ( !count )
 
173
        {
 
174
            // nothing changed
 
175
            return false;
 
176
        }
 
177
 
 
178
        // refresh just the lines which have really changed
 
179
        for ( size_t n = 0; n < count; n++ )
 
180
        {
 
181
            RefreshLine(changed[n]);
 
182
        }
 
183
    }
 
184
 
 
185
    // something changed
 
186
    return true;
 
187
}
 
188
 
 
189
bool wxVListBox::DoSelectAll(bool select)
 
190
{
 
191
    wxCHECK_MSG( m_selStore, false,
 
192
                 _T("SelectAll may only be used with multiselection listbox") );
 
193
 
 
194
    size_t count = GetItemCount();
 
195
    if ( count )
 
196
    {
 
197
        wxArrayInt changed;
 
198
        if ( !m_selStore->SelectRange(0, count - 1, select) ||
 
199
                !changed.IsEmpty() )
 
200
        {
 
201
            Refresh();
 
202
 
 
203
            // something changed
 
204
            return true;
 
205
        }
 
206
    }
 
207
 
 
208
    return false;
 
209
}
 
210
 
 
211
bool wxVListBox::DoSetCurrent(int current)
 
212
{
 
213
    wxASSERT_MSG( current == wxNOT_FOUND ||
 
214
                    (current >= 0 && (size_t)current < GetItemCount()),
 
215
                  _T("wxVListBox::DoSetCurrent(): invalid item index") );
 
216
 
 
217
    if ( current == m_current )
 
218
    {
 
219
        // nothing to do
 
220
        return false;
 
221
    }
 
222
 
 
223
    if ( m_current != wxNOT_FOUND )
 
224
        RefreshLine(m_current);
 
225
 
 
226
    m_current = current;
 
227
 
 
228
    if ( m_current != wxNOT_FOUND )
 
229
    {
 
230
        // if the line is not visible at all, we scroll it into view but we
 
231
        // don't need to refresh it -- it will be redrawn anyhow
 
232
        if ( !IsVisible(m_current) )
 
233
        {
 
234
            ScrollToLine(m_current);
 
235
        }
 
236
        else // line is at least partly visible
 
237
        {
 
238
            // it is, indeed, only partly visible, so scroll it into view to
 
239
            // make it entirely visible
 
240
            while ( (size_t)m_current == GetLastVisibleLine() &&
 
241
                    ScrollToLine(GetVisibleBegin()+1) );
 
242
 
 
243
            // but in any case refresh it as even if it was only partly visible
 
244
            // before we need to redraw it entirely as its background changed
 
245
            RefreshLine(m_current);
 
246
        }
 
247
    }
 
248
 
 
249
    return true;
 
250
}
 
251
 
 
252
void wxVListBox::SendSelectedEvent()
 
253
{
 
254
    wxASSERT_MSG( m_current != wxNOT_FOUND,
 
255
                    _T("SendSelectedEvent() shouldn't be called") );
 
256
 
 
257
    wxCommandEvent event(wxEVT_COMMAND_LISTBOX_SELECTED, GetId());
 
258
    event.SetEventObject(this);
 
259
    event.SetInt(m_current);
 
260
 
 
261
    (void)GetEventHandler()->ProcessEvent(event);
 
262
}
 
263
 
 
264
void wxVListBox::SetSelection(int selection)
 
265
{
 
266
    wxCHECK_RET( selection == wxNOT_FOUND ||
 
267
                  (selection >= 0 && (size_t)selection < GetItemCount()),
 
268
                  _T("wxVListBox::SetSelection(): invalid item index") );
 
269
 
 
270
    if ( HasMultipleSelection() )
 
271
    {
 
272
        Select(selection);
 
273
        m_anchor = selection;
 
274
    }
 
275
 
 
276
    DoSetCurrent(selection);
 
277
}
 
278
 
 
279
size_t wxVListBox::GetSelectedCount() const
 
280
{
 
281
    return m_selStore ? m_selStore->GetSelectedCount()
 
282
                      : m_current == wxNOT_FOUND ? 0 : 1;
 
283
}
 
284
 
 
285
int wxVListBox::GetFirstSelected(unsigned long& cookie) const
 
286
{
 
287
    cookie = 0;
 
288
 
 
289
    return GetNextSelected(cookie);
 
290
}
 
291
 
 
292
int wxVListBox::GetNextSelected(unsigned long& cookie) const
 
293
{
 
294
    wxCHECK_MSG( m_selStore, wxNOT_FOUND,
 
295
                  _T("GetFirst/NextSelected() may only be used with multiselection listboxes") );
 
296
 
 
297
    while ( cookie < GetItemCount() )
 
298
    {
 
299
        if ( IsSelected(cookie++) )
 
300
            return cookie - 1;
 
301
    }
 
302
 
 
303
    return wxNOT_FOUND;
 
304
}
 
305
 
 
306
// ----------------------------------------------------------------------------
 
307
// wxVListBox appearance parameters
 
308
// ----------------------------------------------------------------------------
 
309
 
 
310
void wxVListBox::SetMargins(const wxPoint& pt)
 
311
{
 
312
    if ( pt != m_ptMargins )
 
313
    {
 
314
        m_ptMargins = pt;
 
315
 
 
316
        Refresh();
 
317
    }
 
318
}
 
319
 
 
320
void wxVListBox::SetSelectionBackground(const wxColour& col)
 
321
{
 
322
    m_colBgSel = col;
 
323
}
 
324
 
 
325
// ----------------------------------------------------------------------------
 
326
// wxVListBox painting
 
327
// ----------------------------------------------------------------------------
 
328
 
 
329
wxCoord wxVListBox::OnGetLineHeight(size_t line) const
 
330
{
 
331
    return OnMeasureItem(line) + 2*m_ptMargins.y;
 
332
}
 
333
 
 
334
void wxVListBox::OnDrawSeparator(wxDC& WXUNUSED(dc),
 
335
                                 wxRect& WXUNUSED(rect),
 
336
                                 size_t WXUNUSED(n)) const
 
337
{
 
338
}
 
339
 
 
340
void wxVListBox::OnDrawBackground(wxDC& dc, const wxRect& rect, size_t n) const
 
341
{
 
342
    // we need to render selected and current items differently
 
343
    const bool isSelected = IsSelected(n),
 
344
               isCurrent = IsCurrent(n);
 
345
    if ( isSelected || isCurrent )
 
346
    {
 
347
        if ( isSelected )
 
348
        {
 
349
            dc.SetBrush(wxBrush(m_colBgSel, wxSOLID));
 
350
        }
 
351
        else // !selected
 
352
        {
 
353
            dc.SetBrush(*wxTRANSPARENT_BRUSH);
 
354
        }
 
355
 
 
356
        dc.SetPen(*(isCurrent ? wxBLACK_PEN : wxTRANSPARENT_PEN));
 
357
 
 
358
        dc.DrawRectangle(rect);
 
359
    }
 
360
    //else: do nothing for the normal items
 
361
}
 
362
 
 
363
void wxVListBox::OnPaint(wxPaintEvent& WXUNUSED(event))
 
364
{
 
365
    // If size is larger, recalculate double buffer bitmap
 
366
    wxSize clientSize = GetClientSize();
 
367
 
 
368
    if ( !gs_doubleBuffer ||
 
369
         clientSize.x > gs_doubleBuffer->GetWidth() ||
 
370
         clientSize.y > gs_doubleBuffer->GetHeight() )
 
371
    {
 
372
        delete gs_doubleBuffer;
 
373
        gs_doubleBuffer = new wxBitmap(clientSize.x+25,clientSize.y+25);
 
374
    }
 
375
 
 
376
    wxBufferedPaintDC dc(this,*gs_doubleBuffer);
 
377
 
 
378
    // the update rectangle
 
379
    wxRect rectUpdate = GetUpdateClientRect();
 
380
 
 
381
    // fill it with background colour
 
382
    dc.SetBackground(GetBackgroundColour());
 
383
    dc.Clear();
 
384
 
 
385
    // the bounding rectangle of the current line
 
386
    wxRect rectLine;
 
387
    rectLine.width = clientSize.x;
 
388
 
 
389
    // iterate over all visible lines
 
390
    const size_t lineMax = GetLastVisibleLine();
 
391
    for ( size_t line = GetFirstVisibleLine(); line <= lineMax; line++ )
 
392
    {
 
393
        const wxCoord hLine = OnGetLineHeight(line);
 
394
 
 
395
        rectLine.height = hLine;
 
396
 
 
397
        // and draw the ones which intersect the update rect
 
398
        if ( rectLine.Intersects(rectUpdate) )
 
399
        {
 
400
            // don't allow drawing outside of the lines rectangle
 
401
            wxDCClipper clip(dc, rectLine);
 
402
 
 
403
            wxRect rect = rectLine;
 
404
            OnDrawBackground(dc, rect, line);
 
405
 
 
406
            OnDrawSeparator(dc, rect, line);
 
407
 
 
408
            rect.Deflate(m_ptMargins.x, m_ptMargins.y);
 
409
            OnDrawItem(dc, rect, line);
 
410
        }
 
411
        else // no intersection
 
412
        {
 
413
            if ( rectLine.GetTop() > rectUpdate.GetBottom() )
 
414
            {
 
415
                // we are already below the update rect, no need to continue
 
416
                // further
 
417
                break;
 
418
            }
 
419
            //else: the next line may intersect the update rect
 
420
        }
 
421
 
 
422
        rectLine.y += hLine;
 
423
    }
 
424
}
 
425
 
 
426
// ============================================================================
 
427
// wxVListBox keyboard/mouse handling
 
428
// ============================================================================
 
429
 
 
430
void wxVListBox::DoHandleItemClick(int item, int flags)
 
431
{
 
432
    // has anything worth telling the client code about happened?
 
433
    bool notify = false;
 
434
 
 
435
    if ( HasMultipleSelection() )
 
436
    {
 
437
        // select the iteem clicked?
 
438
        bool select = true;
 
439
 
 
440
        // NB: the keyboard interface we implement here corresponds to
 
441
        //     wxLB_EXTENDED rather than wxLB_MULTIPLE but this one makes more
 
442
        //     sense IMHO
 
443
        if ( flags & ItemClick_Shift )
 
444
        {
 
445
            if ( m_current != wxNOT_FOUND )
 
446
            {
 
447
                if ( m_anchor == wxNOT_FOUND )
 
448
                    m_anchor = m_current;
 
449
 
 
450
                select = false;
 
451
 
 
452
                // only the range from the selection anchor to new m_current
 
453
                // must be selected
 
454
                if ( DeselectAll() )
 
455
                    notify = true;
 
456
 
 
457
                if ( SelectRange(m_anchor, item) )
 
458
                    notify = true;
 
459
            }
 
460
            //else: treat it as ordinary click/keypress
 
461
        }
 
462
        else // Shift not pressed
 
463
        {
 
464
            m_anchor = item;
 
465
 
 
466
            if ( flags & ItemClick_Ctrl )
 
467
            {
 
468
                select = false;
 
469
 
 
470
                if ( !(flags & ItemClick_Kbd) )
 
471
                {
 
472
                    Toggle(item);
 
473
 
 
474
                    // the status of the item has definitely changed
 
475
                    notify = true;
 
476
                }
 
477
                //else: Ctrl-arrow pressed, don't change selection
 
478
            }
 
479
            //else: behave as in single selection case
 
480
        }
 
481
 
 
482
        if ( select )
 
483
        {
 
484
            // make the clicked item the only selection
 
485
            if ( DeselectAll() )
 
486
                notify = true;
 
487
 
 
488
            if ( Select(item) )
 
489
                notify = true;
 
490
        }
 
491
    }
 
492
 
 
493
    // in any case the item should become the current one
 
494
    if ( DoSetCurrent(item) )
 
495
    {
 
496
        if ( !HasMultipleSelection() )
 
497
        {
 
498
            // this has also changed the selection for single selection case
 
499
            notify = true;
 
500
        }
 
501
    }
 
502
 
 
503
    if ( notify )
 
504
    {
 
505
        // notify the user about the selection change
 
506
        SendSelectedEvent();
 
507
    }
 
508
    //else: nothing changed at all
 
509
}
 
510
 
 
511
// ----------------------------------------------------------------------------
 
512
// keyboard handling
 
513
// ----------------------------------------------------------------------------
 
514
 
 
515
void wxVListBox::OnKeyDown(wxKeyEvent& event)
 
516
{
 
517
    // flags for DoHandleItemClick()
 
518
    int flags = ItemClick_Kbd;
 
519
 
 
520
    int current;
 
521
    switch ( event.GetKeyCode() )
 
522
    {
 
523
        case WXK_HOME:
 
524
            current = 0;
 
525
            break;
 
526
 
 
527
        case WXK_END:
 
528
            current = GetLineCount() - 1;
 
529
            break;
 
530
 
 
531
        case WXK_DOWN:
 
532
            if ( m_current == (int)GetLineCount() - 1 )
 
533
                return;
 
534
 
 
535
            current = m_current + 1;
 
536
            break;
 
537
 
 
538
        case WXK_UP:
 
539
            if ( m_current == wxNOT_FOUND )
 
540
                current = GetLineCount() - 1;
 
541
            else if ( m_current != 0 )
 
542
                current = m_current - 1;
 
543
            else // m_current == 0
 
544
                return;
 
545
            break;
 
546
 
 
547
        case WXK_PAGEDOWN:
 
548
        case WXK_NEXT:
 
549
            PageDown();
 
550
            current = GetFirstVisibleLine();
 
551
            break;
 
552
 
 
553
        case WXK_PAGEUP:
 
554
        case WXK_PRIOR:
 
555
            if ( m_current == (int)GetFirstVisibleLine() )
 
556
            {
 
557
                PageUp();
 
558
            }
 
559
 
 
560
            current = GetFirstVisibleLine();
 
561
            break;
 
562
 
 
563
        case WXK_SPACE:
 
564
            // hack: pressing space should work like a mouse click rather than
 
565
            // like a keyboard arrow press, so trick DoHandleItemClick() in
 
566
            // thinking we were clicked
 
567
            flags &= ~ItemClick_Kbd;
 
568
            current = m_current;
 
569
            break;
 
570
 
 
571
#ifdef __WXMSW__
 
572
        case WXK_TAB:
 
573
            // Since we are using wxWANTS_CHARS we need to send navigation
 
574
            // events for the tabs on MSW
 
575
            {
 
576
                wxNavigationKeyEvent ne;
 
577
                ne.SetDirection(!event.ShiftDown());
 
578
                ne.SetCurrentFocus(this);
 
579
                ne.SetEventObject(this);
 
580
                GetParent()->GetEventHandler()->ProcessEvent(ne);
 
581
            }
 
582
            // fall through to default
 
583
#endif
 
584
        default:
 
585
            event.Skip();
 
586
            current = 0; // just to silent the stupid compiler warnings
 
587
            wxUnusedVar(current);
 
588
            return;
 
589
    }
 
590
 
 
591
    if ( event.ShiftDown() )
 
592
       flags |= ItemClick_Shift;
 
593
    if ( event.ControlDown() )
 
594
        flags |= ItemClick_Ctrl;
 
595
 
 
596
    DoHandleItemClick(current, flags);
 
597
}
 
598
 
 
599
// ----------------------------------------------------------------------------
 
600
// wxVListBox mouse handling
 
601
// ----------------------------------------------------------------------------
 
602
 
 
603
void wxVListBox::OnLeftDown(wxMouseEvent& event)
 
604
{
 
605
    SetFocus();
 
606
 
 
607
    int item = HitTest(event.GetPosition());
 
608
 
 
609
    if ( item != wxNOT_FOUND )
 
610
    {
 
611
        int flags = 0;
 
612
        if ( event.ShiftDown() )
 
613
           flags |= ItemClick_Shift;
 
614
 
 
615
        // under Mac Apple-click is used in the same way as Ctrl-click
 
616
        // elsewhere
 
617
#ifdef __WXMAC__
 
618
        if ( event.MetaDown() )
 
619
#else
 
620
        if ( event.ControlDown() )
 
621
#endif
 
622
            flags |= ItemClick_Ctrl;
 
623
 
 
624
        DoHandleItemClick(item, flags);
 
625
    }
 
626
}
 
627
 
 
628
void wxVListBox::OnLeftDClick(wxMouseEvent& eventMouse)
 
629
{
 
630
    int item = HitTest(eventMouse.GetPosition());
 
631
    if ( item != wxNOT_FOUND )
 
632
    {
 
633
 
 
634
        // if item double-clicked was not yet selected, then treat
 
635
        // this event as a left-click instead
 
636
        if ( item == m_current )
 
637
        {
 
638
            wxCommandEvent event(wxEVT_COMMAND_LISTBOX_DOUBLECLICKED, GetId());
 
639
            event.SetEventObject(this);
 
640
            event.SetInt(item);
 
641
 
 
642
            (void)GetEventHandler()->ProcessEvent(event);
 
643
        }
 
644
        else
 
645
        {
 
646
            OnLeftDown(eventMouse);
 
647
        }
 
648
    
 
649
    }
 
650
}
 
651
 
 
652
 
 
653
// ----------------------------------------------------------------------------
 
654
// use the same default attributes as wxListBox
 
655
// ----------------------------------------------------------------------------
 
656
 
 
657
#include "wx/listbox.h"
 
658
 
 
659
//static
 
660
wxVisualAttributes
 
661
wxVListBox::GetClassDefaultAttributes(wxWindowVariant variant)
 
662
{
 
663
    return wxListBox::GetClassDefaultAttributes(variant);
 
664
}
 
665
 
 
666
#endif