1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: generic/vlbox.cpp
3 // Purpose: implementation of wxVListBox
4 // Author: Vadim Zeitlin
8 // Copyright: (c) 2003 Vadim Zeitlin <vadim@wxwindows.org>
9 // License: wxWindows license
10 ///////////////////////////////////////////////////////////////////////////////
12 // ============================================================================
14 // ============================================================================
16 // ----------------------------------------------------------------------------
18 // ----------------------------------------------------------------------------
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
30 #include "wx/settings.h"
31 #include "wx/dcclient.h"
35 #include "wx/selstore.h"
37 // ----------------------------------------------------------------------------
39 // ----------------------------------------------------------------------------
41 BEGIN_EVENT_TABLE(wxVListBox
, wxVScrolledWindow
)
42 EVT_PAINT(wxVListBox::OnPaint
)
44 EVT_KEY_DOWN(wxVListBox::OnKeyDown
)
45 EVT_LEFT_DOWN(wxVListBox::OnLeftDown
)
46 EVT_LEFT_DCLICK(wxVListBox::OnLeftDClick
)
49 // ============================================================================
51 // ============================================================================
53 IMPLEMENT_ABSTRACT_CLASS(wxVListBox
, wxVScrolledWindow
)
55 // ----------------------------------------------------------------------------
56 // wxVListBox creation
57 // ----------------------------------------------------------------------------
59 void wxVListBox::Init()
62 m_anchor
= wxNOT_FOUND
;
66 bool wxVListBox::Create(wxWindow
*parent
,
73 style
|= wxWANTS_CHARS
| wxFULL_REPAINT_ON_RESIZE
;
74 if ( !wxVScrolledWindow::Create(parent
, id
, pos
, size
, style
, name
) )
77 if ( style
& wxLB_MULTIPLE
)
78 m_selStore
= new wxSelectionStore
;
80 // make sure the native widget has the right colour since we do
81 // transparent drawing by default
82 SetBackgroundColour(GetBackgroundColour());
83 m_colBgSel
= wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
);
88 wxVListBox::~wxVListBox()
93 void wxVListBox::SetItemCount(size_t count
)
97 // tell the selection store that our number of items has changed
98 m_selStore
->SetItemCount(count
);
104 // ----------------------------------------------------------------------------
105 // selection handling
106 // ----------------------------------------------------------------------------
108 bool wxVListBox::IsSelected(size_t line
) const
110 return m_selStore
? m_selStore
->IsSelected(line
) : (int)line
== m_current
;
113 bool wxVListBox::Select(size_t item
, bool select
)
115 wxCHECK_MSG( m_selStore
, false,
116 _T("Select() may only be used with multiselection listbox") );
118 wxCHECK_MSG( item
< GetItemCount(), false,
119 _T("Select(): invalid item index") );
121 bool changed
= m_selStore
->SelectItem(item
, select
);
124 // selection really changed
133 bool wxVListBox::SelectRange(size_t from
, size_t to
)
135 wxCHECK_MSG( m_selStore
, false,
136 _T("SelectRange() may only be used with multiselection listbox") );
138 // make sure items are in correct order
146 wxCHECK_MSG( to
< GetItemCount(), false,
147 _T("SelectRange(): invalid item index") );
150 if ( !m_selStore
->SelectRange(from
, to
, true, &changed
) )
152 // too many items have changed, we didn't record them in changed array
153 // so we have no choice but to refresh everything between from and to
154 RefreshLines(from
, to
);
156 else // we've got the indices of the changed items
158 const size_t count
= changed
.GetCount();
165 // refresh just the lines which have really changed
166 for ( size_t n
= 0; n
< count
; n
++ )
168 RefreshLine(changed
[n
]);
176 bool wxVListBox::DoSelectAll(bool select
)
178 wxCHECK_MSG( m_selStore
, false,
179 _T("SelectAll may only be used with multiselection listbox") );
181 size_t count
= GetItemCount();
185 if ( !m_selStore
->SelectRange(0, count
- 1, select
) ||
198 bool wxVListBox::DoSetCurrent(int current
)
200 wxASSERT_MSG( current
== wxNOT_FOUND
||
201 (current
>= 0 && (size_t)current
< GetItemCount()),
202 _T("wxVListBox::DoSetCurrent(): invalid item index") );
204 if ( current
== m_current
)
210 if ( m_current
!= wxNOT_FOUND
)
211 RefreshLine(m_current
);
215 if ( m_current
!= wxNOT_FOUND
)
217 // if the line is not visible at all, we scroll it into view but we
218 // don't need to refresh it -- it will be redrawn anyhow
219 if ( !IsVisible(m_current
) )
221 ScrollToLine(m_current
);
223 else // line is at least partly visible
225 // it is, indeed, only partly visible, so scroll it into view to
226 // make it entirely visible
227 if ( (size_t)m_current
== GetLastVisibleLine() )
229 ScrollToLine(m_current
);
232 // but in any case refresh it as even if it was only partly visible
233 // before we need to redraw it entirely as its background changed
234 RefreshLine(m_current
);
241 void wxVListBox::SendSelectedEvent()
243 wxASSERT_MSG( m_current
!= wxNOT_FOUND
,
244 _T("SendSelectedEvent() shouldn't be called") );
246 wxCommandEvent
event(wxEVT_COMMAND_LISTBOX_SELECTED
, GetId());
247 event
.SetEventObject(this);
248 event
.SetInt(m_current
);
250 (void)GetEventHandler()->ProcessEvent(event
);
253 void wxVListBox::SetSelection(int selection
)
255 wxCHECK_RET( selection
== wxNOT_FOUND
||
256 (selection
>= 0 && (size_t)selection
< GetItemCount()),
257 _T("wxVListBox::SetSelection(): invalid item index") );
259 if ( HasMultipleSelection() )
262 m_anchor
= selection
;
265 DoSetCurrent(selection
);
268 size_t wxVListBox::GetSelectedCount() const
270 return m_selStore
? m_selStore
->GetSelectedCount()
271 : m_current
== wxNOT_FOUND
? 0 : 1;
274 int wxVListBox::GetFirstSelected(unsigned long& cookie
) const
278 return GetNextSelected(cookie
);
281 int wxVListBox::GetNextSelected(unsigned long& cookie
) const
283 wxCHECK_MSG( m_selStore
, wxNOT_FOUND
,
284 _T("GetFirst/NextSelected() may only be used with multiselection listboxes") );
286 while ( cookie
< GetItemCount() )
288 if ( IsSelected(cookie
++) )
295 // ----------------------------------------------------------------------------
296 // wxVListBox appearance parameters
297 // ----------------------------------------------------------------------------
299 void wxVListBox::SetMargins(const wxPoint
& pt
)
301 if ( pt
!= m_ptMargins
)
309 void wxVListBox::SetSelectionBackground(const wxColour
& col
)
314 // ----------------------------------------------------------------------------
315 // wxVListBox painting
316 // ----------------------------------------------------------------------------
318 wxCoord
wxVListBox::OnGetLineHeight(size_t line
) const
320 return OnMeasureItem(line
) + 2*m_ptMargins
.y
;
323 void wxVListBox::OnDrawSeparator(wxDC
& WXUNUSED(dc
),
324 wxRect
& WXUNUSED(rect
),
325 size_t WXUNUSED(n
)) const
329 void wxVListBox::OnDrawBackground(wxDC
& dc
, const wxRect
& rect
, size_t n
) const
331 // we need to render selected and current items differently
332 const bool isSelected
= IsSelected(n
),
333 isCurrent
= IsCurrent(n
);
334 if ( isSelected
|| isCurrent
)
338 dc
.SetBrush(wxBrush(m_colBgSel
, wxSOLID
));
342 dc
.SetBrush(*wxTRANSPARENT_BRUSH
);
345 dc
.SetPen(*(isCurrent
? wxBLACK_PEN
: wxTRANSPARENT_PEN
));
347 dc
.DrawRectangle(rect
);
349 //else: do nothing for the normal items
352 void wxVListBox::OnPaint(wxPaintEvent
& WXUNUSED(event
))
356 // the update rectangle
357 wxRect rectUpdate
= GetUpdateClientRect();
359 // the bounding rectangle of the current line
361 rectLine
.width
= GetClientSize().x
;
363 // iterate over all visible lines
364 const size_t lineMax
= GetLastVisibleLine();
365 for ( size_t line
= GetFirstVisibleLine(); line
<= lineMax
; line
++ )
367 const wxCoord hLine
= OnGetLineHeight(line
);
369 rectLine
.height
= hLine
;
371 // and draw the ones which intersect the update rect
372 if ( rectLine
.Intersects(rectUpdate
) )
374 // don't allow drawing outside of the lines rectangle
375 wxDCClipper
clip(dc
, rectLine
);
377 wxRect rect
= rectLine
;
378 OnDrawBackground(dc
, rect
, line
);
380 OnDrawSeparator(dc
, rect
, line
);
382 rect
.Deflate(m_ptMargins
.x
, m_ptMargins
.y
);
383 OnDrawItem(dc
, rect
, line
);
385 else // no intersection
387 if ( rectLine
.GetTop() > rectUpdate
.GetBottom() )
389 // we are already below the update rect, no need to continue
393 //else: the next line may intersect the update rect
400 // ============================================================================
401 // wxVListBox keyboard/mouse handling
402 // ============================================================================
404 void wxVListBox::DoHandleItemClick(int item
, int flags
)
406 // has anything worth telling the client code about happened?
409 if ( HasMultipleSelection() )
411 // select the iteem clicked?
414 // NB: the keyboard interface we implement here corresponds to
415 // wxLB_EXTENDED rather than wxLB_MULTIPLE but this one makes more
417 if ( flags
& ItemClick_Shift
)
419 if ( m_current
!= wxNOT_FOUND
)
421 if ( m_anchor
== wxNOT_FOUND
)
422 m_anchor
= m_current
;
426 // only the range from the selection anchor to new m_current
431 if ( SelectRange(m_anchor
, item
) )
434 //else: treat it as ordinary click/keypress
436 else // Shift not pressed
440 if ( flags
& ItemClick_Ctrl
)
444 if ( !(flags
& ItemClick_Kbd
) )
448 // the status of the item has definitely changed
451 //else: Ctrl-arrow pressed, don't change selection
453 //else: behave as in single selection case
458 // make the clicked item the only selection
467 // in any case the item should become the current one
468 if ( DoSetCurrent(item
) )
470 if ( !HasMultipleSelection() )
472 // this has also changed the selection for single selection case
479 // notify the user about the selection change
482 //else: nothing changed at all
485 // ----------------------------------------------------------------------------
487 // ----------------------------------------------------------------------------
489 void wxVListBox::OnKeyDown(wxKeyEvent
& event
)
491 // flags for DoHandleItemClick()
492 int flags
= ItemClick_Kbd
;
495 switch ( event
.GetKeyCode() )
502 current
= GetLineCount() - 1;
506 if ( m_current
== (int)GetLineCount() - 1 )
509 current
= m_current
+ 1;
513 if ( m_current
== wxNOT_FOUND
)
514 current
= GetLineCount() - 1;
515 else if ( m_current
!= 0 )
516 current
= m_current
- 1;
517 else // m_current == 0
524 current
= GetFirstVisibleLine();
529 if ( m_current
== (int)GetFirstVisibleLine() )
534 current
= GetFirstVisibleLine();
538 // hack: pressing space should work like a mouse click rather than
539 // like a keyboard arrow press, so trick DoHandleItemClick() in
540 // thinking we were clicked
541 flags
&= ~ItemClick_Kbd
;
547 // Since we are using wxWANTS_CHARS we need to send navigation
548 // events for the tabs on MSW
550 wxNavigationKeyEvent ne
;
551 ne
.SetDirection(!event
.ShiftDown());
552 ne
.SetCurrentFocus(this);
553 ne
.SetEventObject(this);
554 GetParent()->GetEventHandler()->ProcessEvent(ne
);
556 // fall through to default
560 current
= 0; // just to silent the stupid compiler warnings
561 wxUnusedVar(current
);
565 if ( event
.ShiftDown() )
566 flags
|= ItemClick_Shift
;
567 if ( event
.ControlDown() )
568 flags
|= ItemClick_Ctrl
;
570 DoHandleItemClick(current
, flags
);
573 // ----------------------------------------------------------------------------
574 // wxVListBox mouse handling
575 // ----------------------------------------------------------------------------
577 void wxVListBox::OnLeftDown(wxMouseEvent
& event
)
581 int item
= HitTest(event
.GetPosition());
583 if ( item
!= wxNOT_FOUND
)
586 if ( event
.ShiftDown() )
587 flags
|= ItemClick_Shift
;
589 // under Mac Apple-click is used in the same way as Ctrl-click
592 if ( event
.MetaDown() )
594 if ( event
.ControlDown() )
596 flags
|= ItemClick_Ctrl
;
598 DoHandleItemClick(item
, flags
);
602 void wxVListBox::OnLeftDClick(wxMouseEvent
& eventMouse
)
604 int item
= HitTest(eventMouse
.GetPosition());
605 if ( item
!= wxNOT_FOUND
)
607 wxCommandEvent
event(wxEVT_COMMAND_LISTBOX_DOUBLECLICKED
, GetId());
608 event
.SetEventObject(this);
611 (void)GetEventHandler()->ProcessEvent(event
);
616 // ----------------------------------------------------------------------------
617 // use the same default attributes as wxListBox
618 // ----------------------------------------------------------------------------
620 #include "wx/listbox.h"
624 wxVListBox::GetClassDefaultAttributes(wxWindowVariant variant
)
626 return wxListBox::GetClassDefaultAttributes(variant
);