]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/generic/treebkg.cpp
Applied patch [ 1428990 ] wxUniv: patch for toolbar button remain in pressed status
[wxWidgets.git] / src / generic / treebkg.cpp
... / ...
CommitLineData
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
44IMPLEMENT_DYNAMIC_CLASS(wxTreebook, wxBookCtrlBase)
45IMPLEMENT_DYNAMIC_CLASS(wxTreebookEvent, wxNotifyEvent)
46
47const wxEventType wxEVT_COMMAND_TREEBOOK_PAGE_CHANGING = wxNewEventType();
48const wxEventType wxEVT_COMMAND_TREEBOOK_PAGE_CHANGED = wxNewEventType();
49const wxEventType wxEVT_COMMAND_TREEBOOK_NODE_COLLAPSED = wxNewEventType();
50const wxEventType wxEVT_COMMAND_TREEBOOK_NODE_EXPANDED = wxNewEventType();
51const int wxID_TREEBOOKTREEVIEW = wxNewId();
52
53BEGIN_EVENT_TABLE(wxTreebook, wxBookCtrlBase)
54 EVT_TREE_SEL_CHANGED (wxID_TREEBOOKTREEVIEW, wxTreebook::OnTreeSelectionChange)
55 EVT_TREE_ITEM_EXPANDED (wxID_TREEBOOKTREEVIEW, wxTreebook::OnTreeNodeExpandedCollapsed)
56 EVT_TREE_ITEM_COLLAPSED(wxID_TREEBOOKTREEVIEW, wxTreebook::OnTreeNodeExpandedCollapsed)
57END_EVENT_TABLE()
58
59// ============================================================================
60// wxTreebook implementation
61// ============================================================================
62
63// ----------------------------------------------------------------------------
64// wxTreebook creation
65// ----------------------------------------------------------------------------
66
67void wxTreebook::Init()
68{
69 m_selection =
70 m_actualSelection = wxNOT_FOUND;
71}
72
73bool
74wxTreebook::Create(wxWindow *parent,
75 wxWindowID id,
76 const wxPoint& pos,
77 const wxSize& size,
78 long style,
79 const wxString& name)
80{
81 // Check the style flag to have either wxTBK_RIGHT or wxTBK_LEFT
82 if ( (style & wxBK_ALIGN_MASK) == wxBK_DEFAULT )
83 {
84 style |= wxBK_LEFT;
85 }
86
87 // no border for this control, it doesn't look nice together with the tree
88 style &= ~wxBORDER_MASK;
89 style |= wxBORDER_NONE;
90
91 if ( !wxControl::Create(parent, id, pos, size,
92 style, wxDefaultValidator, name) )
93 return false;
94
95 m_bookctrl = new wxTreeCtrl
96 (
97 this,
98 wxID_TREEBOOKTREEVIEW,
99 wxDefaultPosition,
100 wxDefaultSize,
101 wxBORDER_SIMPLE |
102 wxTR_DEFAULT_STYLE |
103 wxTR_HIDE_ROOT |
104 wxTR_SINGLE
105 );
106 GetTreeCtrl()->AddRoot(wxEmptyString); // label doesn't matter, it's hidden
107
108#ifdef __WXMSW__
109 // We need to add dummy size event to force possible scrollbar hiding
110 wxSizeEvent evt;
111 GetEventHandler()->AddPendingEvent(evt);
112#endif
113
114 return true;
115}
116
117
118// insert a new page just before the pagePos
119bool wxTreebook::InsertPage(size_t pagePos,
120 wxWindow *page,
121 const wxString& text,
122 bool bSelect,
123 int imageId)
124{
125 return DoInsertPage(pagePos, page, text, bSelect, imageId);
126}
127
128bool wxTreebook::InsertSubPage(size_t pagePos,
129 wxWindow *page,
130 const wxString& text,
131 bool bSelect,
132 int imageId)
133{
134 return DoInsertSubPage(pagePos, page, text, bSelect, imageId);
135}
136
137bool wxTreebook::AddPage(wxWindow *page, const wxString& text, bool bSelect,
138 int imageId)
139{
140 return DoInsertPage(m_treeIds.GetCount(), page, text, bSelect, imageId);
141}
142
143// insertion time is linear to the number of top-pages
144bool wxTreebook::AddSubPage(wxWindow *page, const wxString& text, bool bSelect, int imageId)
145{
146 return DoAddSubPage(page, text, bSelect, imageId);
147}
148
149
150bool wxTreebook::DoInsertPage(size_t pagePos,
151 wxWindow *page,
152 const wxString& text,
153 bool bSelect,
154 int imageId)
155{
156 wxCHECK_MSG( pagePos <= DoInternalGetPageCount(), false,
157 wxT("Invalid treebook page position") );
158
159 if ( !wxBookCtrlBase::InsertPage(pagePos, page, text, bSelect, imageId) )
160 return false;
161
162 wxTreeCtrl *tree = GetTreeCtrl();
163 wxTreeItemId newId;
164 if ( pagePos == DoInternalGetPageCount() )
165 {
166 // append the page to the end
167 wxTreeItemId rootId = tree->GetRootItem();
168
169 newId = tree->AppendItem(rootId, text, imageId);
170 }
171 else // insert the new page before the given one
172 {
173 wxTreeItemId nodeId = m_treeIds[pagePos];
174
175 wxTreeItemId previousId = tree->GetPrevSibling(nodeId);
176 wxTreeItemId parentId = tree->GetItemParent(nodeId);
177
178 if ( previousId.IsOk() )
179 {
180 // insert before the sibling - previousId
181 newId = tree->InsertItem(parentId, previousId, text, imageId);
182 }
183 else // no prev siblings -- insert as a first child
184 {
185 wxASSERT_MSG( parentId.IsOk(), wxT( "Tree has no root node?" ) );
186
187 newId = tree->PrependItem(parentId, text, imageId);
188 }
189 }
190
191 if ( !newId.IsOk() )
192 {
193 //something wrong -> cleaning and returning with false
194 (void)wxBookCtrlBase::DoRemovePage(pagePos);
195
196 wxFAIL_MSG( wxT("Failed to insert treebook page") );
197 return false;
198 }
199
200 DoInternalAddPage(pagePos, page, newId);
201
202 DoUpdateSelection(bSelect, pagePos);
203
204 m_bookctrl->InvalidateBestSize();
205
206 return true;
207}
208
209bool wxTreebook::DoAddSubPage(wxWindow *page, const wxString& text, bool bSelect, int imageId)
210{
211 wxTreeCtrl *tree = GetTreeCtrl();
212
213 wxTreeItemId rootId = tree->GetRootItem();
214
215 wxTreeItemId lastNodeId = tree->GetLastChild(rootId);
216
217 wxCHECK_MSG( lastNodeId.IsOk(), false,
218 _T("Can't insert sub page when there are no pages") );
219
220 // now calculate its position (should we save/update it too?)
221 size_t newPos = tree->GetCount() -
222 (tree->GetChildrenCount(lastNodeId, true) + 1);
223
224 return DoInsertSubPage(newPos, page, text, bSelect, imageId);
225}
226
227bool wxTreebook::DoInsertSubPage(size_t pagePos,
228 wxTreebookPage *page,
229 const wxString& text,
230 bool bSelect,
231 int imageId)
232{
233 wxTreeItemId parentId = DoInternalGetPage(pagePos);
234 wxCHECK_MSG( parentId.IsOk(), false, wxT("invalid tree item") );
235
236 wxTreeCtrl *tree = GetTreeCtrl();
237
238 size_t newPos = pagePos + tree->GetChildrenCount(parentId, true) + 1;
239 wxASSERT_MSG( newPos <= DoInternalGetPageCount(),
240 wxT("Internal error in tree insert point calculation") );
241
242 if ( !wxBookCtrlBase::InsertPage(newPos, page, text, bSelect, imageId) )
243 return false;
244
245 wxTreeItemId newId = tree->AppendItem(parentId, text, imageId);
246
247 if ( !newId.IsOk() )
248 {
249 (void)wxBookCtrlBase::DoRemovePage(newPos);
250
251 wxFAIL_MSG( wxT("Failed to insert treebook page") );
252 return false;
253 }
254
255 DoInternalAddPage(newPos, page, newId);
256
257 DoUpdateSelection(bSelect, newPos);
258
259 m_bookctrl->InvalidateBestSize();
260
261 return true;
262}
263
264bool wxTreebook::DeletePage(size_t pagePos)
265{
266 wxCHECK_MSG( IS_VALID_PAGE(pagePos), false, wxT("Invalid tree index") );
267
268 wxTreebookPage *oldPage = DoRemovePage(pagePos);
269 if ( !oldPage )
270 return false;
271
272 delete oldPage;
273
274 return true;
275}
276
277wxTreebookPage *wxTreebook::DoRemovePage(size_t pagePos)
278{
279 wxTreeItemId pageId = DoInternalGetPage(pagePos);
280 wxCHECK_MSG( pageId.IsOk(), NULL, wxT("Invalid tree index") );
281
282 wxTreebookPage * oldPage = GetPage(pagePos);
283 wxTreeCtrl *tree = GetTreeCtrl();
284
285 size_t subCount = tree->GetChildrenCount(pageId, true);
286 wxASSERT_MSG ( IS_VALID_PAGE(pagePos + subCount),
287 wxT("Internal error in wxTreebook::DoRemovePage") );
288
289 // here we are going to delete ALL the pages in the range
290 // [pagePos, pagePos + subCount] -- the page and its children
291
292 // deleting all the pages from the base class
293 for ( size_t i = 0; i <= subCount; ++i )
294 {
295 wxTreebookPage *page = wxBookCtrlBase::DoRemovePage(pagePos);
296
297 // don't delete the page itself though -- it will be deleted in
298 // DeletePage() when we return
299 if ( i )
300 {
301 delete page;
302 }
303 }
304
305 DoInternalRemovePageRange(pagePos, subCount);
306
307 tree->DeleteChildren( pageId );
308 tree->Delete( pageId );
309 tree->InvalidateBestSize();
310
311 return oldPage;
312}
313
314bool 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
327void 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
361void 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
433void 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
455wxTreeItemId 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
467int 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
479bool 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
488bool 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
508int 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
518bool 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
529wxString 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
538int 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
547bool 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
558wxSize 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
568int wxTreebook::GetSelection() const
569{
570 return m_selection;
571}
572
573int wxTreebook::SetSelection(size_t pagePos)
574{
575 if ( (size_t)m_selection != pagePos )
576 return DoSetSelection(pagePos);
577
578 return m_selection;
579}
580
581int 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 m_actualSelection = 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(++m_actualSelection);
623 }
624 }
625
626 wxASSERT_MSG( page, wxT("no page to show found!") );
627 }
628
629 if ( page )
630 {
631 page->SetSize(GetPageRect());
632 page->Show();
633 }
634
635 tree->SelectItem(DoInternalGetPage(pagePos));
636
637 // notify about the (now completed) page change
638 event.SetEventType(wxEVT_COMMAND_TREEBOOK_PAGE_CHANGED);
639 (void)GetEventHandler()->ProcessEvent(event);
640 }
641 else // page change vetoed
642 {
643 // tree selection might have already had changed
644 tree->SelectItem(DoInternalGetPage(oldSel));
645 }
646
647 return oldSel;
648}
649
650void wxTreebook::SetImageList(wxImageList *imageList)
651{
652 wxBookCtrlBase::SetImageList(imageList);
653 GetTreeCtrl()->SetImageList(imageList);
654}
655
656void wxTreebook::AssignImageList(wxImageList *imageList)
657{
658 wxBookCtrlBase::AssignImageList(imageList);
659 GetTreeCtrl()->SetImageList(imageList);
660}
661
662// ----------------------------------------------------------------------------
663// event handlers
664// ----------------------------------------------------------------------------
665
666void wxTreebook::OnTreeSelectionChange(wxTreeEvent& event)
667{
668 wxTreeItemId newId = event.GetItem();
669
670 if ( (m_selection == wxNOT_FOUND &&
671 (!newId.IsOk() || newId == GetTreeCtrl()->GetRootItem())) ||
672 (m_selection != wxNOT_FOUND && newId == m_treeIds[m_selection]) )
673 {
674 // this event can only come when we modify the tree selection ourselves
675 // so we should simply ignore it
676 return;
677 }
678
679 int newPos = DoInternalFindPageById(newId);
680
681 if ( newPos != wxNOT_FOUND )
682 SetSelection( newPos );
683}
684
685void wxTreebook::OnTreeNodeExpandedCollapsed(wxTreeEvent & event)
686{
687 wxTreeItemId nodeId = event.GetItem();
688 if ( !nodeId.IsOk() || nodeId == GetTreeCtrl()->GetRootItem() )
689 return;
690 int pagePos = DoInternalFindPageById(nodeId);
691 wxCHECK_RET( pagePos != wxNOT_FOUND, wxT("Internal problem in wxTreebook!..") );
692
693 wxTreebookEvent ev(GetTreeCtrl()->IsExpanded(nodeId)
694 ? wxEVT_COMMAND_TREEBOOK_NODE_EXPANDED
695 : wxEVT_COMMAND_TREEBOOK_NODE_COLLAPSED,
696 m_windowId);
697
698 ev.SetSelection(pagePos);
699 ev.SetOldSelection(pagePos);
700 ev.SetEventObject(this);
701
702 GetEventHandler()->ProcessEvent(ev);
703}
704
705// ----------------------------------------------------------------------------
706// wxTreebook geometry management
707// ----------------------------------------------------------------------------
708
709wxTreebookPage * wxTreebook::DoGetCurrentPage() const
710{
711 if ( m_selection == wxNOT_FOUND )
712 return NULL;
713
714 wxTreebookPage *page = wxBookCtrlBase::GetPage(m_selection);
715 if ( !page && m_actualSelection != wxNOT_FOUND )
716 {
717 page = wxBookCtrlBase::GetPage(m_actualSelection);
718 }
719
720 return page;
721}
722
723#endif // wxUSE_TREEBOOK