X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/8907154c1a8a6882c6797d1f16393ddfb23e7f3a..6ba718d7aee2f519d9146b90424ccec9e2243479:/src/common/bookctrl.cpp diff --git a/src/common/bookctrl.cpp b/src/common/bookctrl.cpp index 2a995a9e51..4b321322b4 100644 --- a/src/common/bookctrl.cpp +++ b/src/common/bookctrl.cpp @@ -1,10 +1,9 @@ /////////////////////////////////////////////////////////////////////////////// -// Name: common/bookctrl.cpp +// Name: src/common/bookctrl.cpp // Purpose: wxBookCtrlBase implementation // Author: Vadim Zeitlin // Modified by: // Created: 19.08.03 -// RCS-ID: $Id$ // Copyright: (c) 2003 Vadim Zeitlin // Licence: wxWindows licence /////////////////////////////////////////////////////////////////////////////// @@ -34,14 +33,37 @@ // implementation // ============================================================================ +// ---------------------------------------------------------------------------- +// event table +// ---------------------------------------------------------------------------- + +IMPLEMENT_ABSTRACT_CLASS(wxBookCtrlBase, wxControl) + +BEGIN_EVENT_TABLE(wxBookCtrlBase, wxControl) + EVT_SIZE(wxBookCtrlBase::OnSize) +#if wxUSE_HELP + EVT_HELP(wxID_ANY, wxBookCtrlBase::OnHelp) +#endif // wxUSE_HELP +END_EVENT_TABLE() + // ---------------------------------------------------------------------------- // constructors and destructors // ---------------------------------------------------------------------------- void wxBookCtrlBase::Init() { - m_imageList = NULL; - m_ownsImageList = false; + m_selection = wxNOT_FOUND; + m_bookctrl = NULL; + m_fitToCurrentPage = false; + +#if defined(__WXWINCE__) + m_internalBorder = 1; +#else + m_internalBorder = 5; +#endif + + m_controlMargin = 0; + m_controlSizer = NULL; } bool @@ -64,43 +86,43 @@ wxBookCtrlBase::Create(wxWindow *parent, ); } -wxBookCtrlBase::~wxBookCtrlBase() -{ - if ( m_ownsImageList ) - { - // may be NULL, ok - delete m_imageList; - } -} - // ---------------------------------------------------------------------------- -// image list +// geometry // ---------------------------------------------------------------------------- -void wxBookCtrlBase::SetImageList(wxImageList *imageList) +void wxBookCtrlBase::DoInvalidateBestSize() { - if ( m_ownsImageList ) - { - // may be NULL, ok - delete m_imageList; - - m_ownsImageList = false; - } - - m_imageList = imageList; + // notice that it is not necessary to invalidate our own best size + // explicitly if we have m_bookctrl as it will already invalidate the best + // size of its parent when its own size is invalidated and its parent is + // this control + if ( m_bookctrl ) + m_bookctrl->InvalidateBestSize(); + else + wxControl::InvalidateBestSize(); } -void wxBookCtrlBase::AssignImageList(wxImageList* imageList) +wxSize wxBookCtrlBase::CalcSizeFromPage(const wxSize& sizePage) const { - SetImageList(imageList); - - m_ownsImageList = true; + // Add the size of the controller and the border between if it's shown. + if ( !m_bookctrl || !m_bookctrl->IsShown() ) + return sizePage; + + // Notice that the controller size is its current size while we really want + // to have its best size. So we only take into account its size in the + // direction in which we should add it but not in the other one, where the + // controller size is determined by the size of wxBookCtrl itself. + const wxSize sizeController = GetControllerSize(); + + wxSize size = sizePage; + if ( IsVertical() ) + size.y += sizeController.y + GetInternalBorder(); + else // left/right aligned + size.x += sizeController.x + GetInternalBorder(); + + return size; } -// ---------------------------------------------------------------------------- -// geometry -// ---------------------------------------------------------------------------- - void wxBookCtrlBase::SetPageSize(const wxSize& size) { SetClientSize(CalcSizeFromPage(size)); @@ -110,18 +132,20 @@ wxSize wxBookCtrlBase::DoGetBestSize() const { wxSize bestSize; - // iterate over all pages, get the largest width and height - const size_t nCount = m_pages.size(); - for ( size_t nPage = 0; nPage < nCount; nPage++ ) + if (m_fitToCurrentPage && GetCurrentPage()) { - wxWindow *pPage = m_pages[nPage]; - wxSize childBestSize(pPage->GetBestSize()); - - if ( childBestSize.x > bestSize.x ) - bestSize.x = childBestSize.x; - - if ( childBestSize.y > bestSize.y ) - bestSize.y = childBestSize.y; + bestSize = GetCurrentPage()->GetBestSize(); + } + else + { + // iterate over all pages, get the largest width and height + const size_t nCount = m_pages.size(); + for ( size_t nPage = 0; nPage < nCount; nPage++ ) + { + const wxWindow * const pPage = m_pages[nPage]; + if ( pPage ) + bestSize.IncTo(pPage->GetBestSize()); + } } // convert display area to window area, adding the size necessary for the @@ -131,6 +155,209 @@ wxSize wxBookCtrlBase::DoGetBestSize() const return best; } +wxRect wxBookCtrlBase::GetPageRect() const +{ + const wxSize size = GetControllerSize(); + + wxPoint pt; + wxRect rectPage(pt, GetClientSize()); + + switch ( GetWindowStyle() & wxBK_ALIGN_MASK ) + { + default: + wxFAIL_MSG( wxT("unexpected alignment") ); + // fall through + + case wxBK_TOP: + rectPage.y = size.y + GetInternalBorder(); + // fall through + + case wxBK_BOTTOM: + rectPage.height -= size.y + GetInternalBorder(); + if (rectPage.height < 0) + rectPage.height = 0; + break; + + case wxBK_LEFT: + rectPage.x = size.x + GetInternalBorder(); + // fall through + + case wxBK_RIGHT: + rectPage.width -= size.x + GetInternalBorder(); + if (rectPage.width < 0) + rectPage.width = 0; + break; + } + + return rectPage; +} + +// Lay out controls +void wxBookCtrlBase::DoSize() +{ + if ( !m_bookctrl ) + { + // we're not fully created yet or OnSize() should be hidden by derived class + return; + } + + if (GetSizer()) + Layout(); + else + { + // resize controller and the page area to fit inside our new size + const wxSize sizeClient( GetClientSize() ), + sizeBorder( m_bookctrl->GetSize() - m_bookctrl->GetClientSize() ), + sizeCtrl( GetControllerSize() ); + + m_bookctrl->SetClientSize( sizeCtrl.x - sizeBorder.x, sizeCtrl.y - sizeBorder.y ); + // if this changes the visibility of the scrollbars the best size changes, relayout in this case + wxSize sizeCtrl2 = GetControllerSize(); + if ( sizeCtrl != sizeCtrl2 ) + { + wxSize sizeBorder2 = m_bookctrl->GetSize() - m_bookctrl->GetClientSize(); + m_bookctrl->SetClientSize( sizeCtrl2.x - sizeBorder2.x, sizeCtrl2.y - sizeBorder2.y ); + } + + const wxSize sizeNew = m_bookctrl->GetSize(); + wxPoint posCtrl; + switch ( GetWindowStyle() & wxBK_ALIGN_MASK ) + { + default: + wxFAIL_MSG( wxT("unexpected alignment") ); + // fall through + + case wxBK_TOP: + case wxBK_LEFT: + // posCtrl is already ok + break; + + case wxBK_BOTTOM: + posCtrl.y = sizeClient.y - sizeNew.y; + break; + + case wxBK_RIGHT: + posCtrl.x = sizeClient.x - sizeNew.x; + break; + } + + if ( m_bookctrl->GetPosition() != posCtrl ) + m_bookctrl->Move(posCtrl); + } + + // resize all pages to fit the new control size + const wxRect pageRect = GetPageRect(); + const unsigned pagesCount = m_pages.GetCount(); + for ( unsigned int i = 0; i < pagesCount; ++i ) + { + wxWindow * const page = m_pages[i]; + if ( !page ) + { + wxASSERT_MSG( AllowNullPage(), + wxT("Null page in a control that does not allow null pages?") ); + continue; + } + + page->SetSize(pageRect); + } +} + +void wxBookCtrlBase::OnSize(wxSizeEvent& event) +{ + event.Skip(); + + DoSize(); +} + +wxSize wxBookCtrlBase::GetControllerSize() const +{ + // For at least some book controls (e.g. wxChoicebook) it may make sense to + // (temporarily?) hide the controller and we shouldn't leave extra space + // for the hidden control in this case. + if ( !m_bookctrl || !m_bookctrl->IsShown() ) + return wxSize(0, 0); + + const wxSize sizeClient = GetClientSize(); + + wxSize size; + + // Ask for the best width/height considering the other direction. + if ( IsVertical() ) + { + size.x = sizeClient.x; + size.y = m_bookctrl->GetBestHeight(sizeClient.x); + } + else // left/right aligned + { + size.x = m_bookctrl->GetBestWidth(sizeClient.y); + size.y = sizeClient.y; + } + + return size; +} + +// ---------------------------------------------------------------------------- +// miscellaneous stuff +// ---------------------------------------------------------------------------- + +#if wxUSE_HELP + +void wxBookCtrlBase::OnHelp(wxHelpEvent& event) +{ + // determine where does this even originate from to avoid redirecting it + // back to the page which generated it (resulting in an infinite loop) + + // notice that we have to check in the hard(er) way instead of just testing + // if the event object == this because the book control can have other + // subcontrols inside it (e.g. wxSpinButton in case of a notebook in wxUniv) + wxWindow *source = wxStaticCast(event.GetEventObject(), wxWindow); + while ( source && source != this && source->GetParent() != this ) + { + source = source->GetParent(); + } + + if ( source && m_pages.Index(source) == wxNOT_FOUND ) + { + // this event is for the book control itself, redirect it to the + // corresponding page + wxWindow *page = NULL; + + if ( event.GetOrigin() == wxHelpEvent::Origin_HelpButton ) + { + // show help for the page under the mouse + const int pagePos = HitTest(ScreenToClient(event.GetPosition())); + + if ( pagePos != wxNOT_FOUND) + { + page = GetPage((size_t)pagePos); + } + } + else // event from keyboard or unknown source + { + // otherwise show the current page help + page = GetCurrentPage(); + } + + if ( page ) + { + // change event object to the page to avoid infinite recursion if + // we get this event ourselves if the page doesn't handle it + event.SetEventObject(page); + + if ( page->GetEventHandler()->ProcessEvent(event) ) + { + // don't call event.Skip() + return; + } + } + } + //else: event coming from one of our pages already + + event.Skip(); +} + +#endif // wxUSE_HELP + // ---------------------------------------------------------------------------- // pages management // ---------------------------------------------------------------------------- @@ -142,12 +369,16 @@ wxBookCtrlBase::InsertPage(size_t nPage, bool WXUNUSED(bSelect), int WXUNUSED(imageId)) { - wxCHECK_MSG( page, false, _T("NULL page in wxBookCtrlBase::InsertPage()") ); + wxCHECK_MSG( page || AllowNullPage(), false, + wxT("NULL page in wxBookCtrlBase::InsertPage()") ); wxCHECK_MSG( nPage <= m_pages.size(), false, - _T("invalid page index in wxBookCtrlBase::InsertPage()") ); + wxT("invalid page index in wxBookCtrlBase::InsertPage()") ); m_pages.Insert(page, nPage); - InvalidateBestSize(); + if ( page ) + page->SetSize(GetPageRect()); + + DoInvalidateBestSize(); return true; } @@ -155,9 +386,10 @@ wxBookCtrlBase::InsertPage(size_t nPage, bool wxBookCtrlBase::DeletePage(size_t nPage) { wxWindow *page = DoRemovePage(nPage); - if ( !page ) + if ( !(page || AllowNullPage()) ) return false; + // delete NULL is harmless delete page; return true; @@ -166,11 +398,11 @@ bool wxBookCtrlBase::DeletePage(size_t nPage) wxWindow *wxBookCtrlBase::DoRemovePage(size_t nPage) { wxCHECK_MSG( nPage < m_pages.size(), NULL, - _T("invalid page index in wxBookCtrlBase::DoRemovePage()") ); + wxT("invalid page index in wxBookCtrlBase::DoRemovePage()") ); wxWindow *pageRemoved = m_pages[nPage]; m_pages.RemoveAt(nPage); - InvalidateBestSize(); + DoInvalidateBestSize(); return pageRemoved; } @@ -192,11 +424,104 @@ int wxBookCtrlBase::GetNextPage(bool forward) const } else // notebook is empty, no next page { - nPage = -1; + nPage = wxNOT_FOUND; } return nPage; } -#endif // wxUSE_BOOKCTRL +int wxBookCtrlBase::FindPage(const wxWindow* page) const +{ + const size_t nCount = m_pages.size(); + for ( size_t nPage = 0; nPage < nCount; nPage++ ) + { + if ( m_pages[nPage] == page ) + return (int)nPage; + } + + return wxNOT_FOUND; +} + +bool wxBookCtrlBase::DoSetSelectionAfterInsertion(size_t n, bool bSelect) +{ + if ( bSelect ) + SetSelection(n); + else if ( m_selection == wxNOT_FOUND ) + ChangeSelection(0); + else // We're not going to select this page. + return false; + + // Return true to indicate that we selected this page. + return true; +} + +void wxBookCtrlBase::DoSetSelectionAfterRemoval(size_t n) +{ + if ( m_selection >= (int)n ) + { + // ensure that the selection is valid + int sel; + if ( GetPageCount() == 0 ) + sel = wxNOT_FOUND; + else + sel = m_selection ? m_selection - 1 : 0; + + // if deleting current page we shouldn't try to hide it + m_selection = m_selection == (int)n ? wxNOT_FOUND + : m_selection - 1; + + if ( sel != wxNOT_FOUND && sel != m_selection ) + SetSelection(sel); + } +} +int wxBookCtrlBase::DoSetSelection(size_t n, int flags) +{ + wxCHECK_MSG( n < GetPageCount(), wxNOT_FOUND, + wxT("invalid page index in wxBookCtrlBase::DoSetSelection()") ); + + const int oldSel = GetSelection(); + + if ( n != (size_t)oldSel ) + { + wxBookCtrlEvent *event = CreatePageChangingEvent(); + bool allowed = false; + + if ( flags & SetSelection_SendEvent ) + { + event->SetSelection(n); + event->SetOldSelection(oldSel); + event->SetEventObject(this); + + allowed = !GetEventHandler()->ProcessEvent(*event) || event->IsAllowed(); + } + + if ( !(flags & SetSelection_SendEvent) || allowed) + { + if ( oldSel != wxNOT_FOUND ) + DoShowPage(m_pages[oldSel], false); + + wxWindow *page = m_pages[n]; + page->SetSize(GetPageRect()); + DoShowPage(page, true); + + // change selection now to ignore the selection change event + UpdateSelectedPage(n); + + if ( flags & SetSelection_SendEvent ) + { + // program allows the page change + MakeChangedEvent(*event); + (void)GetEventHandler()->ProcessEvent(*event); + } + } + + delete event; + } + + return oldSel; +} + +IMPLEMENT_DYNAMIC_CLASS(wxBookCtrlEvent, wxNotifyEvent) + +#endif // wxUSE_BOOKCTRL