Fixed tree book controller sizing by invalidating size after first
[wxWidgets.git] / src / generic / treebkg.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/generic/treebkg.cpp
3 // Purpose: generic implementation of wxTreebook
4 // Author: Evgeniy Tarassov, Vadim Zeitlin
5 // Modified by:
6 // Created: 2005-09-15
7 // RCS-ID: $Id$
8 // Copyright: (c) 2005 Vadim Zeitlin <vadim@wxwidgets.org>
9 // Licence: wxWindows licence
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 #if wxUSE_TREEBOOK
28
29 #include "wx/treebook.h"
30 #include "wx/imaglist.h"
31 #include "wx/settings.h"
32
33 // ----------------------------------------------------------------------------
34 // various wxWidgets macros
35 // ----------------------------------------------------------------------------
36
37 // check that the page index is valid
38 #define IS_VALID_PAGE(nPage) ((nPage) < DoInternalGetPageCount())
39
40 // ----------------------------------------------------------------------------
41 // event table
42 // ----------------------------------------------------------------------------
43
44 IMPLEMENT_DYNAMIC_CLASS(wxTreebook, wxBookCtrlBase)
45 IMPLEMENT_DYNAMIC_CLASS(wxTreebookEvent, wxNotifyEvent)
46
47 #if !WXWIN_COMPATIBILITY_EVENT_TYPES
48 const wxEventType wxEVT_COMMAND_TREEBOOK_PAGE_CHANGING = wxNewEventType();
49 const wxEventType wxEVT_COMMAND_TREEBOOK_PAGE_CHANGED = wxNewEventType();
50 const wxEventType wxEVT_COMMAND_TREEBOOK_NODE_COLLAPSED = wxNewEventType();
51 const wxEventType wxEVT_COMMAND_TREEBOOK_NODE_EXPANDED = wxNewEventType();
52 #endif
53 const int wxID_TREEBOOKTREEVIEW = wxNewId();
54
55 BEGIN_EVENT_TABLE(wxTreebook, wxBookCtrlBase)
56 EVT_TREE_SEL_CHANGED (wxID_TREEBOOKTREEVIEW, wxTreebook::OnTreeSelectionChange)
57 EVT_TREE_ITEM_EXPANDED (wxID_TREEBOOKTREEVIEW, wxTreebook::OnTreeNodeExpandedCollapsed)
58 EVT_TREE_ITEM_COLLAPSED(wxID_TREEBOOKTREEVIEW, wxTreebook::OnTreeNodeExpandedCollapsed)
59 END_EVENT_TABLE()
60
61 // ============================================================================
62 // wxTreebook implementation
63 // ============================================================================
64
65 // ----------------------------------------------------------------------------
66 // wxTreebook creation
67 // ----------------------------------------------------------------------------
68
69 void wxTreebook::Init()
70 {
71 m_selection =
72 m_actualSelection = wxNOT_FOUND;
73 }
74
75 bool
76 wxTreebook::Create(wxWindow *parent,
77 wxWindowID id,
78 const wxPoint& pos,
79 const wxSize& size,
80 long style,
81 const wxString& name)
82 {
83 // Check the style flag to have either wxTBK_RIGHT or wxTBK_LEFT
84 if ( (style & wxBK_ALIGN_MASK) == wxBK_DEFAULT )
85 {
86 style |= wxBK_LEFT;
87 }
88
89 // no border for this control, it doesn't look nice together with the tree
90 style &= ~wxBORDER_MASK;
91 style |= wxBORDER_NONE;
92
93 if ( !wxControl::Create(parent, id, pos, size,
94 style, wxDefaultValidator, name) )
95 return false;
96
97 m_bookctrl = new wxTreeCtrl
98 (
99 this,
100 wxID_TREEBOOKTREEVIEW,
101 wxDefaultPosition,
102 wxDefaultSize,
103 wxBORDER_SIMPLE |
104 wxTR_DEFAULT_STYLE |
105 wxTR_HIDE_ROOT |
106 wxTR_SINGLE
107 );
108 GetTreeCtrl()->SetQuickBestSize(false); // do full size calculation
109 GetTreeCtrl()->AddRoot(wxEmptyString); // label doesn't matter, it's hidden
110
111 #ifdef __WXMSW__
112 // We need to add dummy size event to force possible scrollbar hiding
113 wxSizeEvent evt;
114 GetEventHandler()->AddPendingEvent(evt);
115 #endif
116
117 return true;
118 }
119
120
121 // insert a new page just before the pagePos
122 bool wxTreebook::InsertPage(size_t pagePos,
123 wxWindow *page,
124 const wxString& text,
125 bool bSelect,
126 int imageId)
127 {
128 return DoInsertPage(pagePos, page, text, bSelect, imageId);
129 }
130
131 bool wxTreebook::InsertSubPage(size_t pagePos,
132 wxWindow *page,
133 const wxString& text,
134 bool bSelect,
135 int imageId)
136 {
137 return DoInsertSubPage(pagePos, page, text, bSelect, imageId);
138 }
139
140 bool wxTreebook::AddPage(wxWindow *page, const wxString& text, bool bSelect,
141 int imageId)
142 {
143 return DoInsertPage(m_treeIds.GetCount(), page, text, bSelect, imageId);
144 }
145
146 // insertion time is linear to the number of top-pages
147 bool wxTreebook::AddSubPage(wxWindow *page, const wxString& text, bool bSelect, int imageId)
148 {
149 return DoAddSubPage(page, text, bSelect, imageId);
150 }
151
152
153 bool wxTreebook::DoInsertPage(size_t pagePos,
154 wxWindow *page,
155 const wxString& text,
156 bool bSelect,
157 int imageId)
158 {
159 wxCHECK_MSG( pagePos <= DoInternalGetPageCount(), false,
160 wxT("Invalid treebook page position") );
161
162 if ( !wxBookCtrlBase::InsertPage(pagePos, page, text, bSelect, imageId) )
163 return false;
164
165 wxTreeCtrl *tree = GetTreeCtrl();
166 wxTreeItemId newId;
167 if ( pagePos == DoInternalGetPageCount() )
168 {
169 // append the page to the end
170 wxTreeItemId rootId = tree->GetRootItem();
171
172 newId = tree->AppendItem(rootId, text, imageId);
173 }
174 else // insert the new page before the given one
175 {
176 wxTreeItemId nodeId = m_treeIds[pagePos];
177
178 wxTreeItemId previousId = tree->GetPrevSibling(nodeId);
179 wxTreeItemId parentId = tree->GetItemParent(nodeId);
180
181 if ( previousId.IsOk() )
182 {
183 // insert before the sibling - previousId
184 newId = tree->InsertItem(parentId, previousId, text, imageId);
185 }
186 else // no prev siblings -- insert as a first child
187 {
188 wxASSERT_MSG( parentId.IsOk(), wxT( "Tree has no root node?" ) );
189
190 newId = tree->PrependItem(parentId, text, imageId);
191 }
192 }
193
194 if ( !newId.IsOk() )
195 {
196 //something wrong -> cleaning and returning with false
197 (void)wxBookCtrlBase::DoRemovePage(pagePos);
198
199 wxFAIL_MSG( wxT("Failed to insert treebook page") );
200 return false;
201 }
202
203 DoInternalAddPage(pagePos, page, newId);
204
205 DoUpdateSelection(bSelect, pagePos);
206
207 return true;
208 }
209
210 bool wxTreebook::DoAddSubPage(wxWindow *page, const wxString& text, bool bSelect, int imageId)
211 {
212 wxTreeCtrl *tree = GetTreeCtrl();
213
214 wxTreeItemId rootId = tree->GetRootItem();
215
216 wxTreeItemId lastNodeId = tree->GetLastChild(rootId);
217
218 wxCHECK_MSG( lastNodeId.IsOk(), false,
219 _T("Can't insert sub page when there are no pages") );
220
221 // now calculate its position (should we save/update it too?)
222 size_t newPos = tree->GetCount() -
223 (tree->GetChildrenCount(lastNodeId, true) + 1);
224
225 return DoInsertSubPage(newPos, page, text, bSelect, imageId);
226 }
227
228 bool wxTreebook::DoInsertSubPage(size_t pagePos,
229 wxTreebookPage *page,
230 const wxString& text,
231 bool bSelect,
232 int imageId)
233 {
234 wxTreeItemId parentId = DoInternalGetPage(pagePos);
235 wxCHECK_MSG( parentId.IsOk(), false, wxT("invalid tree item") );
236
237 wxTreeCtrl *tree = GetTreeCtrl();
238
239 size_t newPos = pagePos + tree->GetChildrenCount(parentId, true) + 1;
240 wxASSERT_MSG( newPos <= DoInternalGetPageCount(),
241 wxT("Internal error in tree insert point calculation") );
242
243 if ( !wxBookCtrlBase::InsertPage(newPos, page, text, bSelect, imageId) )
244 return false;
245
246 wxTreeItemId newId = tree->AppendItem(parentId, text, imageId);
247
248 tree->InvalidateBestSize();
249
250 if ( !newId.IsOk() )
251 {
252 (void)wxBookCtrlBase::DoRemovePage(newPos);
253
254 wxFAIL_MSG( wxT("Failed to insert treebook page") );
255 return false;
256 }
257
258 DoInternalAddPage(newPos, page, newId);
259
260 DoUpdateSelection(bSelect, newPos);
261
262 return true;
263 }
264
265 bool wxTreebook::DeletePage(size_t pagePos)
266 {
267 wxCHECK_MSG( IS_VALID_PAGE(pagePos), false, wxT("Invalid tree index") );
268
269 wxTreebookPage *oldPage = DoRemovePage(pagePos);
270 if ( !oldPage )
271 return false;
272
273 delete oldPage;
274
275 return true;
276 }
277
278 wxTreebookPage *wxTreebook::DoRemovePage(size_t pagePos)
279 {
280 wxTreeItemId pageId = DoInternalGetPage(pagePos);
281 wxCHECK_MSG( pageId.IsOk(), NULL, wxT("Invalid tree index") );
282
283 wxTreebookPage * oldPage = GetPage(pagePos);
284 wxTreeCtrl *tree = GetTreeCtrl();
285
286 size_t subCount = tree->GetChildrenCount(pageId, true);
287 wxASSERT_MSG ( IS_VALID_PAGE(pagePos + subCount),
288 wxT("Internal error in wxTreebook::DoRemovePage") );
289
290 // here we are going to delete ALL the pages in the range
291 // [pagePos, pagePos + subCount] -- the page and its children
292
293 // deleting all the pages from the base class
294 for ( size_t i = 0; i <= subCount; ++i )
295 {
296 wxTreebookPage *page = wxBookCtrlBase::DoRemovePage(pagePos);
297
298 // don't delete the page itself though -- it will be deleted in
299 // DeletePage() when we return
300 if ( i )
301 {
302 delete page;
303 }
304 }
305
306 DoInternalRemovePageRange(pagePos, subCount);
307
308 tree->DeleteChildren( pageId );
309 tree->Delete( pageId );
310
311 return oldPage;
312 }
313
314 bool wxTreebook::DeleteAllPages()
315 {
316 wxBookCtrlBase::DeleteAllPages();
317 m_treeIds.Clear();
318 m_selection =
319 m_actualSelection = wxNOT_FOUND;
320
321 wxTreeCtrl *tree = GetTreeCtrl();
322 tree->DeleteChildren(tree->GetRootItem());
323
324 return true;
325 }
326
327 void wxTreebook::DoInternalAddPage(size_t newPos,
328 wxTreebookPage *page,
329 wxTreeItemId pageId)
330 {
331 wxASSERT_MSG( newPos <= m_treeIds.GetCount(), wxT("Ivalid index passed to wxTreebook::DoInternalAddPage") );
332
333 // hide newly inserted page initially (it will be shown when selected)
334 if ( page )
335 page->Hide();
336
337 if ( newPos == m_treeIds.GetCount() )
338 {
339 // append
340 m_treeIds.Add(pageId);
341 }
342 else // insert
343 {
344 m_treeIds.Insert(pageId, newPos);
345
346 if ( m_selection != wxNOT_FOUND && newPos <= (size_t)m_selection )
347 {
348 // selection has been moved one unit toward the end
349 ++m_selection;
350 if ( m_actualSelection != wxNOT_FOUND )
351 ++m_actualSelection;
352 }
353 else if ( m_actualSelection != wxNOT_FOUND &&
354 newPos <= (size_t)m_actualSelection )
355 {
356 DoSetSelection(m_selection);
357 }
358 }
359 }
360
361 void wxTreebook::DoInternalRemovePageRange(size_t pagePos, size_t subCount)
362 {
363 // Attention: this function is only for a situation when we delete a node
364 // with all its children so pagePos is the node's index and subCount is the
365 // node children count
366 wxASSERT_MSG( pagePos + subCount < m_treeIds.GetCount(),
367 wxT("Ivalid page index") );
368
369 wxTreeItemId pageId = m_treeIds[pagePos];
370
371 m_treeIds.RemoveAt(pagePos, subCount + 1);
372
373 if ( m_selection != wxNOT_FOUND )
374 {
375 if ( (size_t)m_selection > pagePos + subCount)
376 {
377 // selection is far after the deleted page, so just update the index and move on
378 m_selection -= 1 + subCount;
379 if ( m_actualSelection != wxNOT_FOUND)
380 {
381 m_actualSelection -= subCount + 1;
382 }
383 }
384 else if ( (size_t)m_selection >= pagePos )
385 {
386 wxTreeCtrl *tree = GetTreeCtrl();
387
388 // as selected page is going to be deleted, try to select the next
389 // sibling if exists, if not then the parent
390 wxTreeItemId nodeId = tree->GetNextSibling(pageId);
391
392 m_selection = wxNOT_FOUND;
393 m_actualSelection = wxNOT_FOUND;
394
395 if ( nodeId.IsOk() )
396 {
397 // selecting next siblings
398 tree->SelectItem(nodeId);
399 }
400 else // no next sibling, select the parent
401 {
402 wxTreeItemId parentId = tree->GetItemParent(pageId);
403
404 if ( parentId.IsOk() && parentId != tree->GetRootItem() )
405 {
406 tree->SelectItem(parentId);
407 }
408 else // parent is root
409 {
410 // we can't select it as it's hidden
411 DoUpdateSelection(false, wxNOT_FOUND);
412 }
413 }
414 }
415 else if ( m_actualSelection != wxNOT_FOUND &&
416 (size_t)m_actualSelection >= pagePos )
417 {
418 // nothing to do -- selection is before the deleted node, but
419 // actually shown page (the first (sub)child with page != NULL) is
420 // already deleted
421 m_actualSelection = m_selection;
422 DoSetSelection(m_selection);
423 }
424 //else: nothing to do -- selection is before the deleted node
425 }
426 else
427 {
428 DoUpdateSelection(false, wxNOT_FOUND);
429 }
430 }
431
432
433 void wxTreebook::DoUpdateSelection(bool bSelect, int newPos)
434 {
435 int newSelPos;
436 if ( bSelect )
437 {
438 newSelPos = newPos;
439 }
440 else if ( m_selection == wxNOT_FOUND && DoInternalGetPageCount() > 0 )
441 {
442 newSelPos = 0;
443 }
444 else
445 {
446 newSelPos = wxNOT_FOUND;
447 }
448
449 if ( newSelPos != wxNOT_FOUND )
450 {
451 SetSelection((size_t)newSelPos);
452 }
453 }
454
455 wxTreeItemId wxTreebook::DoInternalGetPage(size_t pagePos) const
456 {
457 if ( pagePos >= m_treeIds.GetCount() )
458 {
459 // invalid position but ok here, in this internal function, don't assert
460 // (the caller will do it)
461 return wxTreeItemId();
462 }
463
464 return m_treeIds[pagePos];
465 }
466
467 int wxTreebook::DoInternalFindPageById(wxTreeItemId pageId) const
468 {
469 const size_t count = m_treeIds.GetCount();
470 for ( size_t i = 0; i < count; ++i )
471 {
472 if ( m_treeIds[i] == pageId )
473 return i;
474 }
475
476 return wxNOT_FOUND;
477 }
478
479 bool wxTreebook::IsNodeExpanded(size_t pagePos) const
480 {
481 wxTreeItemId pageId = DoInternalGetPage(pagePos);
482
483 wxCHECK_MSG( pageId.IsOk(), false, wxT("invalid tree item") );
484
485 return GetTreeCtrl()->IsExpanded(pageId);
486 }
487
488 bool wxTreebook::ExpandNode(size_t pagePos, bool expand)
489 {
490 wxTreeItemId pageId = DoInternalGetPage(pagePos);
491
492 wxCHECK_MSG( pageId.IsOk(), false, wxT("invalid tree item") );
493
494 if ( expand )
495 {
496 GetTreeCtrl()->Expand( pageId );
497 }
498 else // collapse
499 {
500 GetTreeCtrl()->Collapse( pageId );
501
502 // rely on the events generated by wxTreeCtrl to update selection
503 }
504
505 return true;
506 }
507
508 int wxTreebook::GetPageParent(size_t pagePos) const
509 {
510 wxTreeItemId nodeId = DoInternalGetPage( pagePos );
511 wxCHECK_MSG( nodeId.IsOk(), wxNOT_FOUND, wxT("Invalid page index spacified!") );
512
513 const wxTreeItemId parent = GetTreeCtrl()->GetItemParent( nodeId );
514
515 return parent.IsOk() ? DoInternalFindPageById(parent) : wxNOT_FOUND;
516 }
517
518 bool wxTreebook::SetPageText(size_t n, const wxString& strText)
519 {
520 wxTreeItemId pageId = DoInternalGetPage(n);
521
522 wxCHECK_MSG( pageId.IsOk(), false, wxT("invalid tree item") );
523
524 GetTreeCtrl()->SetItemText(pageId, strText);
525
526 return true;
527 }
528
529 wxString wxTreebook::GetPageText(size_t n) const
530 {
531 wxTreeItemId pageId = DoInternalGetPage(n);
532
533 wxCHECK_MSG( pageId.IsOk(), wxString(), wxT("invalid tree item") );
534
535 return GetTreeCtrl()->GetItemText(pageId);
536 }
537
538 int wxTreebook::GetPageImage(size_t n) const
539 {
540 wxTreeItemId pageId = DoInternalGetPage(n);
541
542 wxCHECK_MSG( pageId.IsOk(), wxNOT_FOUND, wxT("invalid tree item") );
543
544 return GetTreeCtrl()->GetItemImage(pageId);
545 }
546
547 bool wxTreebook::SetPageImage(size_t n, int imageId)
548 {
549 wxTreeItemId pageId = DoInternalGetPage(n);
550
551 wxCHECK_MSG( pageId.IsOk(), false, wxT("invalid tree item") );
552
553 GetTreeCtrl()->SetItemImage(pageId, imageId);
554
555 return true;
556 }
557
558 wxSize wxTreebook::CalcSizeFromPage(const wxSize& sizePage) const
559 {
560 const wxSize sizeTree = GetControllerSize();
561
562 wxSize size = sizePage;
563 size.x += sizeTree.x;
564
565 return size;
566 }
567
568 int wxTreebook::GetSelection() const
569 {
570 return m_selection;
571 }
572
573 int wxTreebook::SetSelection(size_t pagePos)
574 {
575 if ( (size_t)m_selection != pagePos )
576 return DoSetSelection(pagePos);
577
578 return m_selection;
579 }
580
581 int wxTreebook::DoSetSelection(size_t pagePos)
582 {
583 wxCHECK_MSG( IS_VALID_PAGE(pagePos), wxNOT_FOUND,
584 wxT("invalid page index in wxListbook::SetSelection()") );
585 wxASSERT_MSG( GetPageCount() == DoInternalGetPageCount(),
586 wxT("wxTreebook logic error: m_treeIds and m_pages not in sync!"));
587
588 const int oldSel = m_selection;
589 wxTreeCtrl *tree = GetTreeCtrl();
590
591 wxTreebookEvent event(wxEVT_COMMAND_TREEBOOK_PAGE_CHANGING, m_windowId);
592 event.SetEventObject(this);
593 event.SetSelection(pagePos);
594 event.SetOldSelection(m_selection);
595
596 // don't send the event if the old and new pages are the same; do send it
597 // otherwise and be prepared for it to be vetoed
598 if ( (int)pagePos == m_selection ||
599 !GetEventHandler()->ProcessEvent(event) ||
600 event.IsAllowed() )
601 {
602 // hide the previously shown page
603 wxTreebookPage * const oldPage = DoGetCurrentPage();
604 if ( oldPage )
605 oldPage->Hide();
606
607 // then show the new one
608 m_selection = pagePos;
609 wxTreebookPage *page = wxBookCtrlBase::GetPage(m_selection);
610 if ( !page )
611 {
612 // find the next page suitable to be shown: the first (grand)child
613 // of this one with a non-NULL associated page
614 wxTreeItemId childId = m_treeIds[pagePos];
615 int actualPagePos = pagePos;
616 while ( !page && childId.IsOk() )
617 {
618 wxTreeItemIdValue cookie;
619 childId = tree->GetFirstChild( childId, cookie );
620 if ( childId.IsOk() )
621 {
622 page = wxBookCtrlBase::GetPage(++actualPagePos);
623 }
624 }
625
626 m_actualSelection = page ? actualPagePos : m_selection;
627 }
628
629 if ( page )
630 page->Show();
631
632 tree->SelectItem(DoInternalGetPage(pagePos));
633
634 // notify about the (now completed) page change
635 event.SetEventType(wxEVT_COMMAND_TREEBOOK_PAGE_CHANGED);
636 (void)GetEventHandler()->ProcessEvent(event);
637 }
638 else // page change vetoed
639 {
640 // tree selection might have already had changed
641 tree->SelectItem(DoInternalGetPage(oldSel));
642 }
643
644 return oldSel;
645 }
646
647 void wxTreebook::SetImageList(wxImageList *imageList)
648 {
649 wxBookCtrlBase::SetImageList(imageList);
650 GetTreeCtrl()->SetImageList(imageList);
651 }
652
653 void wxTreebook::AssignImageList(wxImageList *imageList)
654 {
655 wxBookCtrlBase::AssignImageList(imageList);
656 GetTreeCtrl()->SetImageList(imageList);
657 }
658
659 // ----------------------------------------------------------------------------
660 // event handlers
661 // ----------------------------------------------------------------------------
662
663 void wxTreebook::OnTreeSelectionChange(wxTreeEvent& event)
664 {
665 wxTreeItemId newId = event.GetItem();
666
667 if ( (m_selection == wxNOT_FOUND &&
668 (!newId.IsOk() || newId == GetTreeCtrl()->GetRootItem())) ||
669 (m_selection != wxNOT_FOUND && newId == m_treeIds[m_selection]) )
670 {
671 // this event can only come when we modify the tree selection ourselves
672 // so we should simply ignore it
673 return;
674 }
675
676 int newPos = DoInternalFindPageById(newId);
677
678 if ( newPos != wxNOT_FOUND )
679 SetSelection( newPos );
680 }
681
682 void wxTreebook::OnTreeNodeExpandedCollapsed(wxTreeEvent & event)
683 {
684 wxTreeItemId nodeId = event.GetItem();
685 if ( !nodeId.IsOk() || nodeId == GetTreeCtrl()->GetRootItem() )
686 return;
687 int pagePos = DoInternalFindPageById(nodeId);
688 wxCHECK_RET( pagePos != wxNOT_FOUND, wxT("Internal problem in wxTreebook!..") );
689
690 wxTreebookEvent ev(GetTreeCtrl()->IsExpanded(nodeId)
691 ? wxEVT_COMMAND_TREEBOOK_NODE_EXPANDED
692 : wxEVT_COMMAND_TREEBOOK_NODE_COLLAPSED,
693 m_windowId);
694
695 ev.SetSelection(pagePos);
696 ev.SetOldSelection(pagePos);
697 ev.SetEventObject(this);
698
699 GetEventHandler()->ProcessEvent(ev);
700 }
701
702 // ----------------------------------------------------------------------------
703 // wxTreebook geometry management
704 // ----------------------------------------------------------------------------
705
706 wxTreebookPage * wxTreebook::DoGetCurrentPage() const
707 {
708 if ( m_selection == wxNOT_FOUND )
709 return NULL;
710
711 wxTreebookPage *page = wxBookCtrlBase::GetPage(m_selection);
712 if ( !page && m_actualSelection != wxNOT_FOUND )
713 {
714 page = wxBookCtrlBase::GetPage(m_actualSelection);
715 }
716
717 return page;
718 }
719
720 #endif // wxUSE_TREEBOOK