fixed bug with SetSelection() not hiding the previously shown page
[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 wxWindows 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 -1,
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 // we should always have some selection if possible
266 if ( m_selection == wxNOT_FOUND && GetPageCount() )
267 {
268 SetSelection(0);
269 }
270
271 if ( m_selection != wxNOT_FOUND )
272 {
273 wxWindow *page = m_pages[m_selection];
274 wxCHECK_RET( page, _T("NULL page in wxListbook?") );
275
276 page->SetSize(GetPageRect());
277 if ( !page->IsShown() )
278 {
279 page->Show();
280 }
281 }
282 }
283
284 wxSize wxListbook::CalcSizeFromPage(const wxSize& sizePage) const
285 {
286 // we need to add the size of the list control and the margin
287 const wxSize sizeList = GetListSize();
288
289 wxSize size = sizePage;
290 if ( IsVertical() )
291 {
292 size.y += sizeList.y + MARGIN;
293 }
294 else // left/right aligned
295 {
296 size.x += sizeList.x + MARGIN;
297 }
298
299 return size;
300 }
301
302
303 // ----------------------------------------------------------------------------
304 // accessing the pages
305 // ----------------------------------------------------------------------------
306
307 bool wxListbook::SetPageText(size_t n, const wxString& strText)
308 {
309 m_list->SetItemText(n, strText);
310
311 return true;
312 }
313
314 wxString wxListbook::GetPageText(size_t n) const
315 {
316 return m_list->GetItemText(n);
317 }
318
319 int wxListbook::GetPageImage(size_t WXUNUSED(n)) const
320 {
321 wxFAIL_MSG( _T("wxListbook::GetPageImage() not implemented") );
322
323 return -1;
324 }
325
326 bool wxListbook::SetPageImage(size_t n, int imageId)
327 {
328 return m_list->SetItemImage(n, imageId, imageId);
329 }
330
331 // ----------------------------------------------------------------------------
332 // image list stuff
333 // ----------------------------------------------------------------------------
334
335 void wxListbook::SetImageList(wxImageList *imageList)
336 {
337 m_list->SetImageList(imageList, wxIMAGE_LIST_NORMAL);
338
339 wxBookCtrl::SetImageList(imageList);
340 }
341
342 // ----------------------------------------------------------------------------
343 // selection
344 // ----------------------------------------------------------------------------
345
346 int wxListbook::GetSelection() const
347 {
348 return m_selection;
349 }
350
351 int wxListbook::SetSelection(size_t n)
352 {
353 wxCHECK_MSG( n < GetPageCount(), wxNOT_FOUND,
354 _T("invalid page index in wxListbook::SetSelection()") );
355
356 int selOld = m_selection;
357
358 if ( (int)n != m_selection )
359 {
360 m_list->Select(n);
361 m_list->Focus(n);
362
363 // change m_selection only now, otherwise OnListSelected() would ignore
364 // the selection change event
365 m_selection = n;
366 }
367
368 return selOld;
369 }
370
371
372 // ----------------------------------------------------------------------------
373 // adding/removing the pages
374 // ----------------------------------------------------------------------------
375
376 bool
377 wxListbook::InsertPage(size_t n,
378 wxWindow *page,
379 const wxString& text,
380 bool bSelect,
381 int imageId)
382 {
383 if ( !wxBookCtrl::InsertPage(n, page, text, bSelect, imageId) )
384 return false;
385
386 m_list->InsertItem(n, text, imageId);
387
388 if ( bSelect )
389 {
390 m_list->Select(n);
391 m_list->Focus(n);
392 }
393 else // don't select this page
394 {
395 // it will be shown only when selected
396 page->Hide();
397 }
398
399 return true;
400 }
401
402 wxWindow *wxListbook::DoRemovePage(size_t page)
403 {
404 wxWindow *win = wxBookCtrl::DoRemovePage(page);
405 if ( win )
406 {
407 m_list->DeleteItem(page);
408 }
409
410 return win;
411 }
412
413 // ----------------------------------------------------------------------------
414 // wxListbook events
415 // ----------------------------------------------------------------------------
416
417 void wxListbook::OnListSelected(wxListEvent& eventList)
418 {
419 const int selNew = eventList.GetIndex();
420
421 if ( selNew == m_selection )
422 {
423 // this event can only come from our own Select(m_selection) below
424 // which we call when the page change is vetoed, so we should simply
425 // ignore it
426 return;
427 }
428
429 // first send "change in progress" event which may be vetoed by user
430 wxListbookEvent eventIng(wxEVT_COMMAND_LISTBOOK_PAGE_CHANGING, GetId());
431
432 eventIng.SetEventObject(this);
433 eventIng.SetSelection(selNew);
434 eventIng.SetOldSelection(m_selection);
435 if ( GetEventHandler()->ProcessEvent(eventIng) && !eventIng.IsAllowed() )
436 {
437 m_list->Select(m_selection);
438 return;
439 }
440
441 // change allowed: do change the page and notify the user about it
442 if ( m_selection != wxNOT_FOUND )
443 m_pages[m_selection]->Hide();
444 wxWindow *page = m_pages[m_selection = selNew];
445 page->SetSize(GetPageRect());
446 page->Show();
447
448 wxListbookEvent eventEd(wxEVT_COMMAND_LISTBOOK_PAGE_CHANGED, GetId());
449
450 eventEd.SetEventObject(this);
451 eventEd.SetSelection(selNew);
452 eventEd.SetOldSelection(m_selection);
453
454 (void)GetEventHandler()->ProcessEvent(eventEd);
455 }
456
457 #endif // wxUSE_LISTBOOK
458