]> git.saurik.com Git - wxWidgets.git/blame - src/generic/vlbox.cpp
regenerated all make/project files to include selstore.cpp/.h
[wxWidgets.git] / src / generic / vlbox.cpp
CommitLineData
e0c6027b
VZ
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$
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#ifndef WX_PRECOMP
28 #include "wx/settings.h"
bb178b29 29 #include "wx/dcclient.h"
e0c6027b
VZ
30#endif //WX_PRECOMP
31
32#include "wx/vlbox.h"
be465555 33#include "wx/selstore.h"
e0c6027b
VZ
34
35// ----------------------------------------------------------------------------
36// event tables
37// ----------------------------------------------------------------------------
38
39BEGIN_EVENT_TABLE(wxVListBox, wxVScrolledWindow)
40 EVT_PAINT(wxVListBox::OnPaint)
41
42 EVT_KEY_DOWN(wxVListBox::OnKeyDown)
43 EVT_LEFT_DOWN(wxVListBox::OnLeftDown)
44 EVT_LEFT_DCLICK(wxVListBox::OnLeftDClick)
45END_EVENT_TABLE()
46
47// ============================================================================
48// implementation
49// ============================================================================
50
51// ----------------------------------------------------------------------------
52// wxVListBox creation
53// ----------------------------------------------------------------------------
54
55void wxVListBox::Init()
56{
be465555
VZ
57 m_current = wxNOT_FOUND;
58 m_selStore = NULL;
e0c6027b
VZ
59}
60
61bool wxVListBox::Create(wxWindow *parent,
62 wxWindowID id,
63 const wxPoint& pos,
64 const wxSize& size,
e0c6027b
VZ
65 long style,
66 const wxString& name)
67{
68 if ( !wxVScrolledWindow::Create(parent, id, pos, size, style, name) )
69 return false;
70
71 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_LISTBOX));
72
be465555
VZ
73 if ( style & wxLB_MULTIPLE )
74 m_selStore = new wxSelectionStore;
e0c6027b
VZ
75
76 return true;
77}
78
be465555
VZ
79wxVListBox::~wxVListBox()
80{
81 delete m_selStore;
82}
83
84void wxVListBox::SetItemCount(size_t count)
85{
86 if ( m_selStore )
87 {
88 // tell the selection store that our number of items has changed
89 m_selStore->SetItemCount(count);
90 }
91
92 SetLineCount(count);
93}
94
e0c6027b
VZ
95// ----------------------------------------------------------------------------
96// selection handling
97// ----------------------------------------------------------------------------
98
be465555
VZ
99bool wxVListBox::IsSelected(size_t line) const
100{
101 return m_selStore ? m_selStore->IsSelected(line) : (int)line == m_current;
102}
103
104bool wxVListBox::Select(size_t item, bool select)
105{
106 wxCHECK_MSG( m_selStore, false,
107 _T("Select() may only be used with multiselection listbox") );
108
109 wxCHECK_MSG( item < GetItemCount(), false,
110 _T("Select(): invalid item index") );
111
112 bool changed = m_selStore->SelectItem(item, select);
113 if ( changed )
114 {
115 // selection really changed
116 RefreshLine(item);
117 }
118
119 DoSetCurrent(item);
120
121 return changed;
122}
123
124bool wxVListBox::SelectRange(size_t from, size_t to)
e0c6027b 125{
be465555
VZ
126 wxCHECK_MSG( m_selStore, false,
127 _T("SelectRange() may only be used with multiselection listbox") );
128
129 // make sure items are in correct order
130 if ( from > to )
131 {
132 size_t tmp = from;
133 from = to;
134 to = tmp;
135 }
136
137 wxCHECK_MSG( to < GetItemCount(), false,
138 _T("SelectRange(): invalid item index") );
139
140 wxArrayInt changed;
141 if ( !m_selStore->SelectRange(from, to, true, &changed) )
142 {
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);
146 }
147 else // we've got the indices of the changed items
148 {
149 const size_t count = changed.GetCount();
150 if ( !count )
151 {
152 // nothing changed
153 return false;
154 }
155
156 // refresh just the lines which have really changed
157 for ( size_t n = 0; n < count; n++ )
158 {
159 RefreshLine(changed[n]);
160 }
161 }
162
163 // something changed
164 return true;
165}
166
167bool wxVListBox::DoSelectAll(bool select)
168{
169 wxCHECK_MSG( m_selStore, false,
170 _T("SelectAll may only be used with multiselection listbox") );
171
172 size_t count = GetItemCount();
173 if ( count )
174 {
175 wxArrayInt changed;
176 if ( !m_selStore->SelectRange(0, count - 1, select) ||
177 !changed.IsEmpty() )
178 {
179 Refresh();
180
181 // something changed
182 return true;
183 }
184 }
185
186 return false;
187}
188
189bool wxVListBox::DoSetCurrent(int current)
190{
6c9210a7
VZ
191 wxASSERT_MSG( current == wxNOT_FOUND ||
192 (current >= 0 && (size_t)current < GetItemCount()),
193 _T("wxVListBox::DoSetCurrent(): invalid item index") );
194
be465555 195 if ( current == m_current )
e0c6027b
VZ
196 {
197 // nothing to do
be465555 198 return false;
e0c6027b
VZ
199 }
200
be465555
VZ
201 if ( m_current != wxNOT_FOUND )
202 RefreshLine(m_current);
e0c6027b 203
be465555 204 m_current = current;
e0c6027b 205
be465555 206 if ( m_current != wxNOT_FOUND )
e0c6027b
VZ
207 {
208 // if the line is not visible at all, we scroll it into view but we
209 // don't need to refresh it -- it will be redrawn anyhow
be465555 210 if ( !IsVisible(m_current) )
e0c6027b 211 {
be465555 212 ScrollToLine(m_current);
e0c6027b
VZ
213 }
214 else // line is at least partly visible
215 {
216 // it is, indeed, only partly visible, so scroll it into view to
217 // make it entirely visible
be465555 218 if ( (size_t)m_current == GetLastVisibleLine() )
e0c6027b 219 {
be465555 220 ScrollToLine(m_current);
e0c6027b
VZ
221 }
222
223 // but in any case refresh it as even if it was only partly visible
224 // before we need to redraw it entirely as its background changed
be465555 225 RefreshLine(m_current);
e0c6027b 226 }
be465555 227 }
e0c6027b 228
be465555
VZ
229 return true;
230}
e0c6027b 231
be465555
VZ
232void wxVListBox::SendSelectedEvent()
233{
234 wxASSERT_MSG( m_current != wxNOT_FOUND,
235 _T("SendSelectedEvent() shouldn't be called") );
236
237 wxCommandEvent event(wxEVT_COMMAND_LISTBOX_SELECTED, GetId());
238 event.SetEventObject(this);
239 event.m_commandInt = m_current;
240
241 (void)GetEventHandler()->ProcessEvent(event);
242}
243
244void wxVListBox::SetSelection(int selection)
245{
6c9210a7
VZ
246 wxCHECK_RET( selection == wxNOT_FOUND ||
247 (selection >= 0 && (size_t)selection < GetItemCount()),
248 _T("wxVListBox::SetSelection(): invalid item index") );
249
be465555
VZ
250 wxASSERT_MSG( !HasMultipleSelection(),
251 _T("SetSelection() is invalid with multiselection listbox") );
252
253 DoSetCurrent(selection);
254}
255
256size_t wxVListBox::GetSelectedCount() const
257{
258 return m_selStore ? m_selStore->GetSelectedCount()
259 : m_current == wxNOT_FOUND ? 0 : 1;
260}
261
262int wxVListBox::GetFirstSelected(unsigned long& cookie) const
263{
264 cookie = 0;
265
266 return GetNextSelected(cookie);
267}
268
269int wxVListBox::GetNextSelected(unsigned long& cookie) const
270{
271 wxCHECK_MSG( m_selStore, wxNOT_FOUND,
272 _T("GetFirst/NextSelected() may only be used with multiselection listboxes") );
273
274 while ( cookie < GetItemCount() )
275 {
276 if ( IsSelected(cookie++) )
277 return cookie - 1;
e0c6027b 278 }
be465555
VZ
279
280 return wxNOT_FOUND;
e0c6027b
VZ
281}
282
283// ----------------------------------------------------------------------------
284// wxVListBox painting
285// ----------------------------------------------------------------------------
286
287void wxVListBox::SetMargins(const wxPoint& pt)
288{
289 if ( pt != m_ptMargins )
290 {
291 m_ptMargins = pt;
292
293 Refresh();
294 }
295}
296
297wxCoord wxVListBox::OnGetLineHeight(size_t line) const
298{
299 return OnMeasureItem(line) + 2*m_ptMargins.y;
300}
301
302void wxVListBox::OnDrawSeparator(wxDC& WXUNUSED(dc),
303 wxRect& WXUNUSED(rect),
304 size_t WXUNUSED(n)) const
305{
306}
307
308void wxVListBox::OnPaint(wxPaintEvent& event)
309{
310 wxPaintDC dc(this);
311
312 // the update rectangle
313 wxRect rectUpdate = GetUpdateClientRect();
314
315 // the bounding rectangle of the current line
316 wxRect rectLine;
317 rectLine.width = GetClientSize().x;
318
319 // iterate over all visible lines
320 const size_t lineMax = GetLastVisibleLine();
321 for ( size_t line = GetFirstVisibleLine(); line <= lineMax; line++ )
322 {
323 const wxCoord hLine = OnGetLineHeight(line);
324
325 rectLine.height = hLine;
326
327 // and draw the ones which intersect the update rect
328 if ( rectLine.Intersects(rectUpdate) )
329 {
330 // don't allow drawing outside of the lines rectangle
331 wxDCClipper clip(dc, rectLine);
332
be465555
VZ
333 // we need to render selected and current items differently
334 const bool isSelected = IsSelected(line);
335 if ( isSelected || IsCurrent(line) )
e0c6027b 336 {
be465555
VZ
337 if ( isSelected )
338 {
339 wxBrush brush(wxSystemSettings::
340 GetColour(wxSYS_COLOUR_HIGHLIGHT),
341 wxSOLID);
342 dc.SetBrush(brush);
343 }
344 else // !selected
345 {
346 dc.SetBrush(*wxTRANSPARENT_BRUSH);
347 }
348
349 dc.SetPen(*(IsCurrent(line) ? wxBLACK_PEN : wxTRANSPARENT_PEN));
350
e0c6027b
VZ
351 dc.DrawRectangle(rectLine);
352 }
353
354 wxRect rect = rectLine;
355 OnDrawSeparator(dc, rect, line);
356
357 rect.Deflate(m_ptMargins.x, m_ptMargins.y);
358 OnDrawItem(dc, rect, line);
359 }
360 else // no intersection
361 {
362 if ( rectLine.GetTop() > rectUpdate.GetBottom() )
363 {
364 // we are already below the update rect, no need to continue
365 // further
366 break;
367 }
368 //else: the next line may intersect the update rect
369 }
370
371 rectLine.y += hLine;
372 }
373}
374
be465555
VZ
375// ============================================================================
376// wxVListBox keyboard/mouse handling
377// ============================================================================
378
379void wxVListBox::DoHandleItemClick(int item, bool shiftDown, bool ctrlDown)
380{
381 // has anything worth telling the client code about happened?
382 bool notify = false;
383
384 if ( HasMultipleSelection() )
385 {
386 // select the iteem clicked?
387 bool select = true;
388
389 // NB: the keyboard interface we implement here corresponds to
390 // wxLB_EXTENDED rather than wxLB_MULTIPLE but this one makes more
391 // sense IMHO
392 if ( shiftDown )
393 {
394 if ( m_current != wxNOT_FOUND )
395 {
396 select = false;
397
398 // only the range from old m_current to new m_current must be
399 // selected
400 if ( DeselectAll() )
401 notify = true;
402
403 if ( SelectRange(m_current, item) )
404 notify = true;
405 }
406 //else: treat it as ordinary click/keypress
407 }
408 else if ( ctrlDown )
409 {
410 select = false;
411
412 Toggle(item);
413
414 // the status of the item has definitely changed
415 notify = true;
416 }
417 //else: behave as in single selection case
418
419 if ( select )
420 {
421 // make the clicked item the only selection
422 if ( DeselectAll() )
423 notify = true;
424
425 if ( Select(item) )
426 notify = true;
427 }
428 }
429
430 // in any case the item should become the current one
431 if ( DoSetCurrent(item) )
432 {
433 if ( !HasMultipleSelection() )
434 {
435 // this has also changed the selection for single selection case
436 notify = true;
437 }
438 }
439
440 if ( notify )
441 {
442 // notify the user about the selection change
443 SendSelectedEvent();
444 }
445 //else: nothing changed at all
446}
447
e0c6027b 448// ----------------------------------------------------------------------------
be465555 449// keyboard handling
e0c6027b
VZ
450// ----------------------------------------------------------------------------
451
452void wxVListBox::OnKeyDown(wxKeyEvent& event)
453{
be465555 454 int current = 0; // just to silent the stupid compiler warnings
e0c6027b
VZ
455 switch ( event.GetKeyCode() )
456 {
457 case WXK_HOME:
be465555 458 current = 0;
e0c6027b
VZ
459 break;
460
461 case WXK_END:
be465555 462 current = GetLineCount() - 1;
e0c6027b
VZ
463 break;
464
465 case WXK_DOWN:
be465555 466 if ( m_current == (int)GetLineCount() - 1 )
e0c6027b
VZ
467 return;
468
be465555 469 current = m_current + 1;
e0c6027b
VZ
470 break;
471
472 case WXK_UP:
be465555
VZ
473 if ( m_current == wxNOT_FOUND )
474 current = GetLineCount() - 1;
475 else if ( m_current != 0 )
476 current = m_current - 1;
477 else // m_current == 0
e0c6027b
VZ
478 return;
479 break;
480
481 case WXK_PAGEDOWN:
482 case WXK_NEXT:
483 PageDown();
be465555 484 current = GetFirstVisibleLine();
e0c6027b
VZ
485 break;
486
487 case WXK_PAGEUP:
488 case WXK_PRIOR:
be465555 489 if ( m_current == (int)GetFirstVisibleLine() )
e0c6027b
VZ
490 {
491 PageUp();
492 }
493
be465555 494 current = GetFirstVisibleLine();
e0c6027b
VZ
495 break;
496
497 default:
498 event.Skip();
499 return;
500 }
501
be465555 502 DoHandleItemClick(current, event.ShiftDown(), event.ControlDown());
e0c6027b
VZ
503}
504
505// ----------------------------------------------------------------------------
506// wxVListBox mouse handling
507// ----------------------------------------------------------------------------
508
509void wxVListBox::OnLeftDown(wxMouseEvent& event)
510{
511 int item = HitTest(event.GetPosition());
512
6c9210a7
VZ
513 if ( item != wxNOT_FOUND )
514 {
515 // under Mac Apple-click is used in the same way as Ctrl-click
516 // elsewhere
517 DoHandleItemClick(item, event.ShiftDown(),
be465555 518#ifdef __WXMAC__
6c9210a7 519 event.MetaDown()
be465555 520#else
6c9210a7 521 event.ControlDown()
be465555 522#endif
6c9210a7
VZ
523 );
524 }
e0c6027b
VZ
525}
526
527void wxVListBox::OnLeftDClick(wxMouseEvent& event)
528{
529 int item = HitTest(event.GetPosition());
be465555 530 if ( item != wxNOT_FOUND )
e0c6027b
VZ
531 {
532 wxCommandEvent event(wxEVT_COMMAND_LISTBOX_DOUBLECLICKED, GetId());
533 event.SetEventObject(this);
534 event.m_commandInt = item;
535
536 (void)GetEventHandler()->ProcessEvent(event);
537 }
538}
539