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"
28 #include "wx/settings.h"
29 #include "wx/dcclient.h"
33 #include "wx/selstore.h"
35 // ----------------------------------------------------------------------------
37 // ----------------------------------------------------------------------------
39 BEGIN_EVENT_TABLE(wxVListBox
, wxVScrolledWindow
)
40 EVT_PAINT(wxVListBox::OnPaint
)
42 EVT_KEY_DOWN(wxVListBox::OnKeyDown
)
43 EVT_LEFT_DOWN(wxVListBox::OnLeftDown
)
44 EVT_LEFT_DCLICK(wxVListBox::OnLeftDClick
)
47 // ============================================================================
49 // ============================================================================
51 // ----------------------------------------------------------------------------
52 // wxVListBox creation
53 // ----------------------------------------------------------------------------
55 void wxVListBox::Init()
57 m_current
= wxNOT_FOUND
;
61 bool wxVListBox::Create(wxWindow
*parent
,
68 if ( !wxVScrolledWindow::Create(parent
, id
, pos
, size
, style
, name
) )
71 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_LISTBOX
));
73 if ( style
& wxLB_MULTIPLE
)
74 m_selStore
= new wxSelectionStore
;
79 wxVListBox::~wxVListBox()
84 void wxVListBox::SetItemCount(size_t count
)
88 // tell the selection store that our number of items has changed
89 m_selStore
->SetItemCount(count
);
95 // ----------------------------------------------------------------------------
97 // ----------------------------------------------------------------------------
99 bool wxVListBox::IsSelected(size_t line
) const
101 return m_selStore
? m_selStore
->IsSelected(line
) : (int)line
== m_current
;
104 bool wxVListBox::Select(size_t item
, bool select
)
106 wxCHECK_MSG( m_selStore
, false,
107 _T("Select() may only be used with multiselection listbox") );
109 wxCHECK_MSG( item
< GetItemCount(), false,
110 _T("Select(): invalid item index") );
112 bool changed
= m_selStore
->SelectItem(item
, select
);
115 // selection really changed
124 bool wxVListBox::SelectRange(size_t from
, size_t to
)
126 wxCHECK_MSG( m_selStore
, false,
127 _T("SelectRange() may only be used with multiselection listbox") );
129 // make sure items are in correct order
137 wxCHECK_MSG( to
< GetItemCount(), false,
138 _T("SelectRange(): invalid item index") );
141 if ( !m_selStore
->SelectRange(from
, to
, true, &changed
) )
143 // too many items have changed, we didn't record them in changed array
144 // so we have no choice but to refresh everything between from and to
145 RefreshLines(from
, to
);
147 else // we've got the indices of the changed items
149 const size_t count
= changed
.GetCount();
156 // refresh just the lines which have really changed
157 for ( size_t n
= 0; n
< count
; n
++ )
159 RefreshLine(changed
[n
]);
167 bool wxVListBox::DoSelectAll(bool select
)
169 wxCHECK_MSG( m_selStore
, false,
170 _T("SelectAll may only be used with multiselection listbox") );
172 size_t count
= GetItemCount();
176 if ( !m_selStore
->SelectRange(0, count
- 1, select
) ||
189 bool wxVListBox::DoSetCurrent(int current
)
191 if ( current
== m_current
)
197 if ( m_current
!= wxNOT_FOUND
)
198 RefreshLine(m_current
);
202 if ( m_current
!= wxNOT_FOUND
)
204 // if the line is not visible at all, we scroll it into view but we
205 // don't need to refresh it -- it will be redrawn anyhow
206 if ( !IsVisible(m_current
) )
208 ScrollToLine(m_current
);
210 else // line is at least partly visible
212 // it is, indeed, only partly visible, so scroll it into view to
213 // make it entirely visible
214 if ( (size_t)m_current
== GetLastVisibleLine() )
216 ScrollToLine(m_current
);
219 // but in any case refresh it as even if it was only partly visible
220 // before we need to redraw it entirely as its background changed
221 RefreshLine(m_current
);
228 void wxVListBox::SendSelectedEvent()
230 wxASSERT_MSG( m_current
!= wxNOT_FOUND
,
231 _T("SendSelectedEvent() shouldn't be called") );
233 wxCommandEvent
event(wxEVT_COMMAND_LISTBOX_SELECTED
, GetId());
234 event
.SetEventObject(this);
235 event
.m_commandInt
= m_current
;
237 (void)GetEventHandler()->ProcessEvent(event
);
240 void wxVListBox::SetSelection(int selection
)
242 wxASSERT_MSG( !HasMultipleSelection(),
243 _T("SetSelection() is invalid with multiselection listbox") );
245 DoSetCurrent(selection
);
248 size_t wxVListBox::GetSelectedCount() const
250 return m_selStore
? m_selStore
->GetSelectedCount()
251 : m_current
== wxNOT_FOUND
? 0 : 1;
254 int wxVListBox::GetFirstSelected(unsigned long& cookie
) const
258 return GetNextSelected(cookie
);
261 int wxVListBox::GetNextSelected(unsigned long& cookie
) const
263 wxCHECK_MSG( m_selStore
, wxNOT_FOUND
,
264 _T("GetFirst/NextSelected() may only be used with multiselection listboxes") );
266 while ( cookie
< GetItemCount() )
268 if ( IsSelected(cookie
++) )
275 // ----------------------------------------------------------------------------
276 // wxVListBox painting
277 // ----------------------------------------------------------------------------
279 void wxVListBox::SetMargins(const wxPoint
& pt
)
281 if ( pt
!= m_ptMargins
)
289 wxCoord
wxVListBox::OnGetLineHeight(size_t line
) const
291 return OnMeasureItem(line
) + 2*m_ptMargins
.y
;
294 void wxVListBox::OnDrawSeparator(wxDC
& WXUNUSED(dc
),
295 wxRect
& WXUNUSED(rect
),
296 size_t WXUNUSED(n
)) const
300 void wxVListBox::OnPaint(wxPaintEvent
& event
)
304 // the update rectangle
305 wxRect rectUpdate
= GetUpdateClientRect();
307 // the bounding rectangle of the current line
309 rectLine
.width
= GetClientSize().x
;
311 // iterate over all visible lines
312 const size_t lineMax
= GetLastVisibleLine();
313 for ( size_t line
= GetFirstVisibleLine(); line
<= lineMax
; line
++ )
315 const wxCoord hLine
= OnGetLineHeight(line
);
317 rectLine
.height
= hLine
;
319 // and draw the ones which intersect the update rect
320 if ( rectLine
.Intersects(rectUpdate
) )
322 // don't allow drawing outside of the lines rectangle
323 wxDCClipper
clip(dc
, rectLine
);
325 // we need to render selected and current items differently
326 const bool isSelected
= IsSelected(line
);
327 if ( isSelected
|| IsCurrent(line
) )
331 wxBrush
brush(wxSystemSettings::
332 GetColour(wxSYS_COLOUR_HIGHLIGHT
),
338 dc
.SetBrush(*wxTRANSPARENT_BRUSH
);
341 dc
.SetPen(*(IsCurrent(line
) ? wxBLACK_PEN
: wxTRANSPARENT_PEN
));
343 dc
.DrawRectangle(rectLine
);
346 wxRect rect
= rectLine
;
347 OnDrawSeparator(dc
, rect
, line
);
349 rect
.Deflate(m_ptMargins
.x
, m_ptMargins
.y
);
350 OnDrawItem(dc
, rect
, line
);
352 else // no intersection
354 if ( rectLine
.GetTop() > rectUpdate
.GetBottom() )
356 // we are already below the update rect, no need to continue
360 //else: the next line may intersect the update rect
367 // ============================================================================
368 // wxVListBox keyboard/mouse handling
369 // ============================================================================
371 void wxVListBox::DoHandleItemClick(int item
, bool shiftDown
, bool ctrlDown
)
373 // has anything worth telling the client code about happened?
376 if ( HasMultipleSelection() )
378 // select the iteem clicked?
381 // NB: the keyboard interface we implement here corresponds to
382 // wxLB_EXTENDED rather than wxLB_MULTIPLE but this one makes more
386 if ( m_current
!= wxNOT_FOUND
)
390 // only the range from old m_current to new m_current must be
395 if ( SelectRange(m_current
, item
) )
398 //else: treat it as ordinary click/keypress
406 // the status of the item has definitely changed
409 //else: behave as in single selection case
413 // make the clicked item the only selection
422 // in any case the item should become the current one
423 if ( DoSetCurrent(item
) )
425 if ( !HasMultipleSelection() )
427 // this has also changed the selection for single selection case
434 // notify the user about the selection change
437 //else: nothing changed at all
440 // ----------------------------------------------------------------------------
442 // ----------------------------------------------------------------------------
444 void wxVListBox::OnKeyDown(wxKeyEvent
& event
)
446 int current
= 0; // just to silent the stupid compiler warnings
447 switch ( event
.GetKeyCode() )
454 current
= GetLineCount() - 1;
458 if ( m_current
== (int)GetLineCount() - 1 )
461 current
= m_current
+ 1;
465 if ( m_current
== wxNOT_FOUND
)
466 current
= GetLineCount() - 1;
467 else if ( m_current
!= 0 )
468 current
= m_current
- 1;
469 else // m_current == 0
476 current
= GetFirstVisibleLine();
481 if ( m_current
== (int)GetFirstVisibleLine() )
486 current
= GetFirstVisibleLine();
494 DoHandleItemClick(current
, event
.ShiftDown(), event
.ControlDown());
497 // ----------------------------------------------------------------------------
498 // wxVListBox mouse handling
499 // ----------------------------------------------------------------------------
501 void wxVListBox::OnLeftDown(wxMouseEvent
& event
)
503 int item
= HitTest(event
.GetPosition());
505 DoHandleItemClick(item
, event
.ShiftDown(),
514 void wxVListBox::OnLeftDClick(wxMouseEvent
& event
)
516 int item
= HitTest(event
.GetPosition());
517 if ( item
!= wxNOT_FOUND
)
519 wxCommandEvent
event(wxEVT_COMMAND_LISTBOX_DOUBLECLICKED
, GetId());
520 event
.SetEventObject(this);
521 event
.m_commandInt
= item
;
523 (void)GetEventHandler()->ProcessEvent(event
);