]> git.saurik.com Git - wxWidgets.git/blame - src/generic/vlbox.cpp
Make wxMDIParentFrame a regular top level window
[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{
191 if ( current == m_current )
e0c6027b
VZ
192 {
193 // nothing to do
be465555 194 return false;
e0c6027b
VZ
195 }
196
be465555
VZ
197 if ( m_current != wxNOT_FOUND )
198 RefreshLine(m_current);
e0c6027b 199
be465555 200 m_current = current;
e0c6027b 201
be465555 202 if ( m_current != wxNOT_FOUND )
e0c6027b
VZ
203 {
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
be465555 206 if ( !IsVisible(m_current) )
e0c6027b 207 {
be465555 208 ScrollToLine(m_current);
e0c6027b
VZ
209 }
210 else // line is at least partly visible
211 {
212 // it is, indeed, only partly visible, so scroll it into view to
213 // make it entirely visible
be465555 214 if ( (size_t)m_current == GetLastVisibleLine() )
e0c6027b 215 {
be465555 216 ScrollToLine(m_current);
e0c6027b
VZ
217 }
218
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
be465555 221 RefreshLine(m_current);
e0c6027b 222 }
be465555 223 }
e0c6027b 224
be465555
VZ
225 return true;
226}
e0c6027b 227
be465555
VZ
228void wxVListBox::SendSelectedEvent()
229{
230 wxASSERT_MSG( m_current != wxNOT_FOUND,
231 _T("SendSelectedEvent() shouldn't be called") );
232
233 wxCommandEvent event(wxEVT_COMMAND_LISTBOX_SELECTED, GetId());
234 event.SetEventObject(this);
235 event.m_commandInt = m_current;
236
237 (void)GetEventHandler()->ProcessEvent(event);
238}
239
240void wxVListBox::SetSelection(int selection)
241{
242 wxASSERT_MSG( !HasMultipleSelection(),
243 _T("SetSelection() is invalid with multiselection listbox") );
244
245 DoSetCurrent(selection);
246}
247
248size_t wxVListBox::GetSelectedCount() const
249{
250 return m_selStore ? m_selStore->GetSelectedCount()
251 : m_current == wxNOT_FOUND ? 0 : 1;
252}
253
254int wxVListBox::GetFirstSelected(unsigned long& cookie) const
255{
256 cookie = 0;
257
258 return GetNextSelected(cookie);
259}
260
261int wxVListBox::GetNextSelected(unsigned long& cookie) const
262{
263 wxCHECK_MSG( m_selStore, wxNOT_FOUND,
264 _T("GetFirst/NextSelected() may only be used with multiselection listboxes") );
265
266 while ( cookie < GetItemCount() )
267 {
268 if ( IsSelected(cookie++) )
269 return cookie - 1;
e0c6027b 270 }
be465555
VZ
271
272 return wxNOT_FOUND;
e0c6027b
VZ
273}
274
275// ----------------------------------------------------------------------------
276// wxVListBox painting
277// ----------------------------------------------------------------------------
278
279void wxVListBox::SetMargins(const wxPoint& pt)
280{
281 if ( pt != m_ptMargins )
282 {
283 m_ptMargins = pt;
284
285 Refresh();
286 }
287}
288
289wxCoord wxVListBox::OnGetLineHeight(size_t line) const
290{
291 return OnMeasureItem(line) + 2*m_ptMargins.y;
292}
293
294void wxVListBox::OnDrawSeparator(wxDC& WXUNUSED(dc),
295 wxRect& WXUNUSED(rect),
296 size_t WXUNUSED(n)) const
297{
298}
299
300void wxVListBox::OnPaint(wxPaintEvent& event)
301{
302 wxPaintDC dc(this);
303
304 // the update rectangle
305 wxRect rectUpdate = GetUpdateClientRect();
306
307 // the bounding rectangle of the current line
308 wxRect rectLine;
309 rectLine.width = GetClientSize().x;
310
311 // iterate over all visible lines
312 const size_t lineMax = GetLastVisibleLine();
313 for ( size_t line = GetFirstVisibleLine(); line <= lineMax; line++ )
314 {
315 const wxCoord hLine = OnGetLineHeight(line);
316
317 rectLine.height = hLine;
318
319 // and draw the ones which intersect the update rect
320 if ( rectLine.Intersects(rectUpdate) )
321 {
322 // don't allow drawing outside of the lines rectangle
323 wxDCClipper clip(dc, rectLine);
324
be465555
VZ
325 // we need to render selected and current items differently
326 const bool isSelected = IsSelected(line);
327 if ( isSelected || IsCurrent(line) )
e0c6027b 328 {
be465555
VZ
329 if ( isSelected )
330 {
331 wxBrush brush(wxSystemSettings::
332 GetColour(wxSYS_COLOUR_HIGHLIGHT),
333 wxSOLID);
334 dc.SetBrush(brush);
335 }
336 else // !selected
337 {
338 dc.SetBrush(*wxTRANSPARENT_BRUSH);
339 }
340
341 dc.SetPen(*(IsCurrent(line) ? wxBLACK_PEN : wxTRANSPARENT_PEN));
342
e0c6027b
VZ
343 dc.DrawRectangle(rectLine);
344 }
345
346 wxRect rect = rectLine;
347 OnDrawSeparator(dc, rect, line);
348
349 rect.Deflate(m_ptMargins.x, m_ptMargins.y);
350 OnDrawItem(dc, rect, line);
351 }
352 else // no intersection
353 {
354 if ( rectLine.GetTop() > rectUpdate.GetBottom() )
355 {
356 // we are already below the update rect, no need to continue
357 // further
358 break;
359 }
360 //else: the next line may intersect the update rect
361 }
362
363 rectLine.y += hLine;
364 }
365}
366
be465555
VZ
367// ============================================================================
368// wxVListBox keyboard/mouse handling
369// ============================================================================
370
371void wxVListBox::DoHandleItemClick(int item, bool shiftDown, bool ctrlDown)
372{
373 // has anything worth telling the client code about happened?
374 bool notify = false;
375
376 if ( HasMultipleSelection() )
377 {
378 // select the iteem clicked?
379 bool select = true;
380
381 // NB: the keyboard interface we implement here corresponds to
382 // wxLB_EXTENDED rather than wxLB_MULTIPLE but this one makes more
383 // sense IMHO
384 if ( shiftDown )
385 {
386 if ( m_current != wxNOT_FOUND )
387 {
388 select = false;
389
390 // only the range from old m_current to new m_current must be
391 // selected
392 if ( DeselectAll() )
393 notify = true;
394
395 if ( SelectRange(m_current, item) )
396 notify = true;
397 }
398 //else: treat it as ordinary click/keypress
399 }
400 else if ( ctrlDown )
401 {
402 select = false;
403
404 Toggle(item);
405
406 // the status of the item has definitely changed
407 notify = true;
408 }
409 //else: behave as in single selection case
410
411 if ( select )
412 {
413 // make the clicked item the only selection
414 if ( DeselectAll() )
415 notify = true;
416
417 if ( Select(item) )
418 notify = true;
419 }
420 }
421
422 // in any case the item should become the current one
423 if ( DoSetCurrent(item) )
424 {
425 if ( !HasMultipleSelection() )
426 {
427 // this has also changed the selection for single selection case
428 notify = true;
429 }
430 }
431
432 if ( notify )
433 {
434 // notify the user about the selection change
435 SendSelectedEvent();
436 }
437 //else: nothing changed at all
438}
439
e0c6027b 440// ----------------------------------------------------------------------------
be465555 441// keyboard handling
e0c6027b
VZ
442// ----------------------------------------------------------------------------
443
444void wxVListBox::OnKeyDown(wxKeyEvent& event)
445{
be465555 446 int current = 0; // just to silent the stupid compiler warnings
e0c6027b
VZ
447 switch ( event.GetKeyCode() )
448 {
449 case WXK_HOME:
be465555 450 current = 0;
e0c6027b
VZ
451 break;
452
453 case WXK_END:
be465555 454 current = GetLineCount() - 1;
e0c6027b
VZ
455 break;
456
457 case WXK_DOWN:
be465555 458 if ( m_current == (int)GetLineCount() - 1 )
e0c6027b
VZ
459 return;
460
be465555 461 current = m_current + 1;
e0c6027b
VZ
462 break;
463
464 case WXK_UP:
be465555
VZ
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
e0c6027b
VZ
470 return;
471 break;
472
473 case WXK_PAGEDOWN:
474 case WXK_NEXT:
475 PageDown();
be465555 476 current = GetFirstVisibleLine();
e0c6027b
VZ
477 break;
478
479 case WXK_PAGEUP:
480 case WXK_PRIOR:
be465555 481 if ( m_current == (int)GetFirstVisibleLine() )
e0c6027b
VZ
482 {
483 PageUp();
484 }
485
be465555 486 current = GetFirstVisibleLine();
e0c6027b
VZ
487 break;
488
489 default:
490 event.Skip();
491 return;
492 }
493
be465555 494 DoHandleItemClick(current, event.ShiftDown(), event.ControlDown());
e0c6027b
VZ
495}
496
497// ----------------------------------------------------------------------------
498// wxVListBox mouse handling
499// ----------------------------------------------------------------------------
500
501void wxVListBox::OnLeftDown(wxMouseEvent& event)
502{
503 int item = HitTest(event.GetPosition());
504
be465555
VZ
505 DoHandleItemClick(item, event.ShiftDown(),
506#ifdef __WXMAC__
507 event.MetaDown()
508#else
509 event.ControlDown()
510#endif
511 );
e0c6027b
VZ
512}
513
514void wxVListBox::OnLeftDClick(wxMouseEvent& event)
515{
516 int item = HitTest(event.GetPosition());
be465555 517 if ( item != wxNOT_FOUND )
e0c6027b
VZ
518 {
519 wxCommandEvent event(wxEVT_COMMAND_LISTBOX_DOUBLECLICKED, GetId());
520 event.SetEventObject(this);
521 event.m_commandInt = item;
522
523 (void)GetEventHandler()->ProcessEvent(event);
524 }
525}
526