made wxListbook events more consistent with wxNotebook ones (patch 1001271)
[wxWidgets.git] / src / generic / listbkg.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: generic/listbkg.cpp
3 // Purpose: generic implementation of wxListbook
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 19.08.03
7 // RCS-ID: $Id$
8 // Copyright: (c) 2003 Vadim Zeitlin <vadim@wxwindows.org>
9 // Licence: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
21 #pragma implementation "listbook.h"
22 #endif
23
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
26
27 #ifdef __BORLANDC__
28 #pragma hdrstop
29 #endif
30
31 #if wxUSE_LISTBOOK
32
33 #include "wx/listctrl.h"
34 #include "wx/statline.h"
35 #include "wx/listbook.h"
36 #include "wx/imaglist.h"
37 #include "wx/settings.h"
38
39 // ----------------------------------------------------------------------------
40 // constants
41 // ----------------------------------------------------------------------------
42
43 // margin between the list and the page, should be bigger than wxStaticLine
44 // size
45 const wxCoord MARGIN = 5;
46
47 // ----------------------------------------------------------------------------
48 // various wxWidgets macros
49 // ----------------------------------------------------------------------------
50
51 IMPLEMENT_DYNAMIC_CLASS(wxListbook, wxControl)
52 IMPLEMENT_DYNAMIC_CLASS(wxListbookEvent, wxNotifyEvent)
53
54 const wxEventType wxEVT_COMMAND_LISTBOOK_PAGE_CHANGING = wxNewEventType();
55 const wxEventType wxEVT_COMMAND_LISTBOOK_PAGE_CHANGED = wxNewEventType();
56 const int wxID_LISTBOOKLISTVIEW = wxNewId();
57
58 BEGIN_EVENT_TABLE(wxListbook, wxBookCtrl)
59 EVT_SIZE(wxListbook::OnSize)
60 EVT_LIST_ITEM_SELECTED(wxID_LISTBOOKLISTVIEW, wxListbook::OnListSelected)
61 END_EVENT_TABLE()
62
63 // ============================================================================
64 // wxListbook implementation
65 // ============================================================================
66
67 // ----------------------------------------------------------------------------
68 // wxListbook creation
69 // ----------------------------------------------------------------------------
70
71 void wxListbook::Init()
72 {
73 m_list = NULL;
74 #if wxUSE_LINE_IN_LISTBOOK
75 m_line = NULL;
76 #endif // wxUSE_LINE_IN_LISTBOOK
77 m_selection = wxNOT_FOUND;
78 }
79
80 bool
81 wxListbook::Create(wxWindow *parent,
82 wxWindowID id,
83 const wxPoint& pos,
84 const wxSize& size,
85 long style,
86 const wxString& name)
87 {
88 if ( (style & wxLB_ALIGN_MASK) == wxLB_DEFAULT )
89 {
90 #ifdef __WXMAC__
91 style |= wxLB_TOP;
92 #else // !__WXMAC__
93 style |= wxLB_LEFT;
94 #endif // __WXMAC__/!__WXMAC__
95 }
96
97 // no border for this control, it doesn't look nice together with
98 // wxListCtrl border
99 style &= ~wxBORDER_MASK;
100 style |= wxBORDER_NONE;
101
102 if ( !wxControl::Create(parent, id, pos, size, style,
103 wxDefaultValidator, name) )
104 return false;
105
106 m_list = new wxListView
107 (
108 this,
109 wxID_LISTBOOKLISTVIEW,
110 wxDefaultPosition,
111 wxDefaultSize,
112 wxLC_ICON | wxLC_SINGLE_SEL |
113 (IsVertical() ? wxLC_ALIGN_LEFT : wxLC_ALIGN_TOP)
114 );
115
116 #if wxUSE_LINE_IN_LISTBOOK
117 m_line = new wxStaticLine
118 (
119 this,
120 wxID_ANY,
121 wxDefaultPosition,
122 wxDefaultSize,
123 IsVertical() ? wxLI_HORIZONTAL : wxLI_VERTICAL
124 );
125 #endif // wxUSE_LINE_IN_LISTBOOK
126
127 #ifdef __WXMSW__
128 // On XP with themes enabled the GetViewRect used in GetListSize to
129 // determine the space needed for the list view will incorrectly return
130 // (0,0,0,0) the first time. So send a pending event so OnSize wiull be
131 // called again after the window is ready to go. Technically we don't
132 // need to do this on non-XP windows, but if things are already sized
133 // correctly then nothing changes and so there is no harm.
134 wxSizeEvent evt;
135 GetEventHandler()->AddPendingEvent(evt);
136 #endif
137 return true;
138 }
139
140 // ----------------------------------------------------------------------------
141 // wxListbook geometry management
142 // ----------------------------------------------------------------------------
143
144 wxSize wxListbook::GetListSize() const
145 {
146 const wxSize sizeClient = GetClientSize(),
147 sizeList = m_list->GetViewRect().GetSize();
148
149 wxSize size;
150 if ( IsVertical() )
151 {
152 size.x = sizeClient.x;
153 size.y = sizeList.y;
154 }
155 else // left/right aligned
156 {
157 size.x = sizeList.x;
158 size.y = sizeClient.y;
159 }
160
161 return size;
162 }
163
164 wxRect wxListbook::GetPageRect() const
165 {
166 const wxSize sizeList = m_list->GetSize();
167
168 wxRect rectPage(wxPoint(0, 0), GetClientSize());
169 switch ( GetWindowStyle() & wxLB_ALIGN_MASK )
170 {
171 default:
172 wxFAIL_MSG( _T("unexpected wxListbook alignment") );
173 // fall through
174
175 case wxLB_TOP:
176 rectPage.y = sizeList.y + MARGIN;
177 // fall through
178
179 case wxLB_BOTTOM:
180 rectPage.height -= sizeList.y + MARGIN;
181 break;
182
183 case wxLB_LEFT:
184 rectPage.x = sizeList.x + MARGIN;
185 // fall through
186
187 case wxLB_RIGHT:
188 rectPage.width -= sizeList.x + MARGIN;
189 break;
190 }
191
192 return rectPage;
193 }
194
195 void wxListbook::OnSize(wxSizeEvent& event)
196 {
197 event.Skip();
198
199 if ( !m_list )
200 {
201 // we're not fully created yet
202 return;
203 }
204
205 // resize the list control and the page area to fit inside our new size
206 const wxSize sizeClient = GetClientSize(),
207 sizeList = GetListSize();
208
209 wxPoint posList;
210 switch ( GetWindowStyle() & wxLB_ALIGN_MASK )
211 {
212 default:
213 wxFAIL_MSG( _T("unexpected wxListbook alignment") );
214 // fall through
215
216 case wxLB_TOP:
217 case wxLB_LEFT:
218 // posList is already ok
219 break;
220
221 case wxLB_BOTTOM:
222 posList.y = sizeClient.y - sizeList.y;
223 break;
224
225 case wxLB_RIGHT:
226 posList.x = sizeClient.x - sizeList.x;
227 break;
228 }
229
230 m_list->Move(posList.x, posList.y);
231 m_list->SetClientSize(sizeList.x, sizeList.y);
232
233 #if wxUSE_LINE_IN_LISTBOOK
234 if ( m_line )
235 {
236 wxRect rectLine(wxPoint(0, 0), sizeClient);
237
238 switch ( GetWindowStyle() & wxLB_ALIGN_MASK )
239 {
240 case wxLB_TOP:
241 rectLine.y = sizeList.y + 1;
242 rectLine.height = MARGIN - 2;
243 break;
244
245 case wxLB_BOTTOM:
246 rectLine.height = MARGIN - 2;
247 rectLine.y = sizeClient.y - sizeList.y - rectLine.height;
248 break;
249
250 case wxLB_LEFT:
251 rectLine.x = sizeList.x + 1;
252 rectLine.width = MARGIN - 2;
253 break;
254
255 case wxLB_RIGHT:
256 rectLine.width = MARGIN - 2;
257 rectLine.x = sizeClient.x - sizeList.x - rectLine.width;
258 break;
259 }
260
261 m_line->SetSize(rectLine);
262 }
263 #endif // wxUSE_LINE_IN_LISTBOOK
264
265 // resize the currently shown page
266 if (m_selection != wxNOT_FOUND )
267 {
268 wxWindow *page = m_pages[m_selection];
269 wxCHECK_RET( page, _T("NULL page in wxListbook?") );
270 page->SetSize(GetPageRect());
271 }
272 }
273
274 wxSize wxListbook::CalcSizeFromPage(const wxSize& sizePage) const
275 {
276 // we need to add the size of the list control and the margin
277 const wxSize sizeList = GetListSize();
278
279 wxSize size = sizePage;
280 if ( IsVertical() )
281 {
282 size.y += sizeList.y + MARGIN;
283 }
284 else // left/right aligned
285 {
286 size.x += sizeList.x + MARGIN;
287 }
288
289 return size;
290 }
291
292
293 // ----------------------------------------------------------------------------
294 // accessing the pages
295 // ----------------------------------------------------------------------------
296
297 bool wxListbook::SetPageText(size_t n, const wxString& strText)
298 {
299 m_list->SetItemText(n, strText);
300
301 return true;
302 }
303
304 wxString wxListbook::GetPageText(size_t n) const
305 {
306 return m_list->GetItemText(n);
307 }
308
309 int wxListbook::GetPageImage(size_t WXUNUSED(n)) const
310 {
311 wxFAIL_MSG( _T("wxListbook::GetPageImage() not implemented") );
312
313 return -1;
314 }
315
316 bool wxListbook::SetPageImage(size_t n, int imageId)
317 {
318 return m_list->SetItemImage(n, imageId, imageId);
319 }
320
321 // ----------------------------------------------------------------------------
322 // image list stuff
323 // ----------------------------------------------------------------------------
324
325 void wxListbook::SetImageList(wxImageList *imageList)
326 {
327 m_list->SetImageList(imageList, wxIMAGE_LIST_NORMAL);
328
329 wxBookCtrl::SetImageList(imageList);
330 }
331
332 // ----------------------------------------------------------------------------
333 // selection
334 // ----------------------------------------------------------------------------
335
336 int wxListbook::GetSelection() const
337 {
338 return m_selection;
339 }
340
341 int wxListbook::SetSelection(size_t n)
342 {
343 wxCHECK_MSG( n < GetPageCount(), wxNOT_FOUND,
344 _T("invalid page index in wxListbook::SetSelection()") );
345
346 const int selOld = m_selection;
347
348 if ( (int)n != m_selection )
349 {
350 if ( m_selection != wxNOT_FOUND )
351 m_pages[m_selection]->Hide();
352 wxWindow *page = m_pages[n];
353 page->SetSize(GetPageRect());
354 page->Show();
355
356 // change m_selection only now to ignore the selection change event
357 m_selection = n;
358
359 m_list->Select(n);
360 m_list->Focus(n);
361 }
362
363 return selOld;
364 }
365
366 // ----------------------------------------------------------------------------
367 // adding/removing the pages
368 // ----------------------------------------------------------------------------
369
370 bool
371 wxListbook::InsertPage(size_t n,
372 wxWindow *page,
373 const wxString& text,
374 bool bSelect,
375 int imageId)
376 {
377 if ( !wxBookCtrl::InsertPage(n, page, text, bSelect, imageId) )
378 return false;
379
380 m_list->InsertItem(n, text, imageId);
381
382 // we should always have some selection if possible
383 if ( bSelect || (m_selection == wxNOT_FOUND) )
384 {
385 SetSelection(n);
386 }
387 else // don't select this page
388 {
389 // it will be shown only when selected
390 page->Hide();
391 }
392
393 InvalidateBestSize();
394 return true;
395 }
396
397 wxWindow *wxListbook::DoRemovePage(size_t page)
398 {
399 const int page_count = GetPageCount();
400 wxWindow *win = wxBookCtrl::DoRemovePage(page);
401
402 if ( win )
403 {
404 m_list->DeleteItem(page);
405
406 if (m_selection >= (int)page)
407 {
408 // force new sel valid if possible
409 int sel = m_selection - 1;
410 if (page_count == 1)
411 sel = -1;
412 else if ((page_count == 2) || (sel == -1))
413 sel = 0;
414
415 // force sel invalid if deleting current page - don't try to hide it
416 m_selection = (m_selection == (int)page) ? -1 : m_selection - 1;
417
418 if ((sel != -1) && (sel != m_selection))
419 SetSelection(sel);
420 }
421 }
422
423 return win;
424 }
425
426
427 bool wxListbook::DeleteAllPages()
428 {
429 m_list->DeleteAllItems();
430 return wxBookCtrl::DeleteAllPages();
431 }
432
433 // ----------------------------------------------------------------------------
434 // wxListbook events
435 // ----------------------------------------------------------------------------
436
437 void wxListbook::OnListSelected(wxListEvent& eventList)
438 {
439 const int selNew = eventList.GetIndex();
440 const int selOld = m_selection;
441
442 if ( selNew == m_selection )
443 {
444 // this event can only come from our own Select(m_selection) below
445 // which we call when the page change is vetoed, so we should simply
446 // ignore it
447 return;
448 }
449
450 // first send "change in progress" event which may be vetoed by user
451 wxListbookEvent eventIng(wxEVT_COMMAND_LISTBOOK_PAGE_CHANGING, GetId());
452
453 eventIng.SetEventObject(this);
454 eventIng.SetSelection(selNew);
455 eventIng.SetOldSelection(selOld);
456 if ( GetEventHandler()->ProcessEvent(eventIng) && !eventIng.IsAllowed() )
457 {
458 m_list->Select(m_selection);
459 return;
460 }
461
462 // change allowed: do change the page and notify the user about it
463 SetSelection(selNew);
464
465 wxListbookEvent eventEd(wxEVT_COMMAND_LISTBOOK_PAGE_CHANGED, GetId());
466
467 eventEd.SetEventObject(this);
468 eventEd.SetSelection(selNew);
469 eventEd.SetOldSelection(selOld);
470
471 (void)GetEventHandler()->ProcessEvent(eventEd);
472 }
473
474 #endif // wxUSE_LISTBOOK
475