1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/univ/notebook.cpp 
   3 // Purpose:     wxNotebook implementation 
   4 // Author:      Vadim Zeitlin 
   8 // Copyright:   (c) 2001 SciTech Software, Inc. (www.scitechsoft.com) 
   9 // Licence:     wxWindows licence 
  10 ///////////////////////////////////////////////////////////////////////////// 
  12 // ============================================================================ 
  14 // ============================================================================ 
  16 // ---------------------------------------------------------------------------- 
  18 // ---------------------------------------------------------------------------- 
  20 #include "wx/wxprec.h" 
  28 #include "wx/notebook.h" 
  31     #include "wx/dcmemory.h" 
  34 #include "wx/imaglist.h" 
  35 #include "wx/spinbutt.h" 
  37 #include "wx/univ/renderer.h" 
  39 // ---------------------------------------------------------------------------- 
  40 // wxStdNotebookInputHandler: translates SPACE and ENTER keys and the left mouse 
  41 // click into button press/release actions 
  42 // ---------------------------------------------------------------------------- 
  44 class WXDLLEXPORT wxStdNotebookInputHandler 
: public wxStdInputHandler
 
  47     wxStdNotebookInputHandler(wxInputHandler 
*inphand
); 
  49     virtual bool HandleKey(wxInputConsumer 
*consumer
, 
  50                            const wxKeyEvent
& event
, 
  52     virtual bool HandleMouse(wxInputConsumer 
*consumer
, 
  53                              const wxMouseEvent
& event
); 
  54     virtual bool HandleMouseMove(wxInputConsumer 
*consumer
, const wxMouseEvent
& event
); 
  55     virtual bool HandleFocus(wxInputConsumer 
*consumer
, const wxFocusEvent
& event
); 
  56     virtual bool HandleActivation(wxInputConsumer 
*consumer
, bool activated
); 
  59     void HandleFocusChange(wxInputConsumer 
*consumer
); 
  62 // ---------------------------------------------------------------------------- 
  64 // ---------------------------------------------------------------------------- 
  67 // due to unsigned type nPage is always >= 0 
  68 #define IS_VALID_PAGE(nPage) (((nPage) >= 0) && ((size_t(nPage)) < GetPageCount())) 
  70 #define IS_VALID_PAGE(nPage) (((size_t)nPage) < GetPageCount()) 
  73 // ---------------------------------------------------------------------------- 
  75 // ---------------------------------------------------------------------------- 
  77 static const size_t INVALID_PAGE 
= (size_t)-1; 
  79 DEFINE_EVENT_TYPE(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
) 
  80 DEFINE_EVENT_TYPE(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
) 
  82 // ---------------------------------------------------------------------------- 
  84 // ---------------------------------------------------------------------------- 
  86 class wxNotebookSpinBtn 
: public wxSpinButton
 
  89     wxNotebookSpinBtn(wxNotebook 
*nb
) 
  90         : wxSpinButton(nb
, wxID_ANY
, 
  91                        wxDefaultPosition
, wxDefaultSize
, 
  92                        nb
->IsVertical() ? wxSP_VERTICAL 
: wxSP_HORIZONTAL
) 
  98     void OnSpin(wxSpinEvent
& event
) 
 100         m_nb
->PerformAction(wxACTION_NOTEBOOK_GOTO
, event
.GetPosition()); 
 106     DECLARE_EVENT_TABLE() 
 109 BEGIN_EVENT_TABLE(wxNotebookSpinBtn
, wxSpinButton
) 
 110     EVT_SPIN(wxID_ANY
, wxNotebookSpinBtn::OnSpin
) 
 113 // ============================================================================ 
 115 // ============================================================================ 
 117 IMPLEMENT_DYNAMIC_CLASS(wxNotebook
, wxControl
) 
 118 IMPLEMENT_DYNAMIC_CLASS(wxNotebookEvent
, wxCommandEvent
) 
 120 // ---------------------------------------------------------------------------- 
 121 // wxNotebook creation 
 122 // ---------------------------------------------------------------------------- 
 124 void wxNotebook::Init() 
 126     m_sel 
= INVALID_PAGE
; 
 133     m_lastFullyVisible 
= 0; 
 140 bool wxNotebook::Create(wxWindow 
*parent
, 
 145                         const wxString
& name
) 
 147     if ( (style 
& wxBK_ALIGN_MASK
) == wxBK_DEFAULT 
) 
 150     if ( !wxControl::Create(parent
, id
, pos
, size
, style
, 
 151                             wxDefaultValidator
, name
) ) 
 154     m_sizePad 
= GetRenderer()->GetTabPadding(); 
 158     CreateInputHandler(wxINP_HANDLER_NOTEBOOK
); 
 163 // ---------------------------------------------------------------------------- 
 164 // wxNotebook page titles and images 
 165 // ---------------------------------------------------------------------------- 
 167 wxString 
wxNotebook::GetPageText(size_t nPage
) const 
 169     wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxEmptyString
, _T("invalid notebook page") ); 
 171     return m_titles
[nPage
]; 
 174 bool wxNotebook::SetPageText(size_t nPage
, const wxString
& strText
) 
 176     wxCHECK_MSG( IS_VALID_PAGE(nPage
), false, _T("invalid notebook page") ); 
 178     if ( strText 
!= m_titles
[nPage
] ) 
 180         m_accels
[nPage
] = FindAccelIndex(strText
, &m_titles
[nPage
]); 
 182         if ( FixedSizeTabs() ) 
 184             // it's enough to just reresh this one 
 187         else // var width tabs 
 189             // we need to resize the tab to fit the new string 
 197 int wxNotebook::GetPageImage(size_t nPage
) const 
 199     wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxNOT_FOUND
, _T("invalid notebook page") ); 
 201     return m_images
[nPage
]; 
 204 bool wxNotebook::SetPageImage(size_t nPage
, int nImage
) 
 206     wxCHECK_MSG( IS_VALID_PAGE(nPage
), false, _T("invalid notebook page") ); 
 208     wxCHECK_MSG( m_imageList 
&& nImage 
< m_imageList
->GetImageCount(), false, 
 209                  _T("invalid image index in SetPageImage()") ); 
 211     if ( nImage 
!= m_images
[nPage
] ) 
 213         // if the item didn't have an icon before or, on the contrary, did have 
 214         // it but has lost it now, its size will change - but if the icon just 
 216         bool tabSizeChanges 
= nImage 
== -1 || m_images
[nPage
] == -1; 
 217         m_images
[nPage
] = nImage
; 
 219         if ( tabSizeChanges 
) 
 228 wxNotebook::~wxNotebook() 
 232 // ---------------------------------------------------------------------------- 
 233 // wxNotebook page switching 
 234 // ---------------------------------------------------------------------------- 
 236 int wxNotebook::SetSelection(size_t nPage
) 
 238     wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxNOT_FOUND
, _T("invalid notebook page") ); 
 240     if ( (size_t)nPage 
== m_sel 
) 
 242         // don't do anything if there is nothing to do 
 247     wxNotebookEvent 
event(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
, m_windowId
); 
 248     event
.SetSelection(nPage
); 
 249     event
.SetOldSelection(m_sel
); 
 250     event
.SetEventObject(this); 
 251     if ( GetEventHandler()->ProcessEvent(event
) && !event
.IsAllowed() ) 
 253         // program doesn't allow the page change 
 257     // we need to change m_sel first, before calling RefreshTab() below as 
 258     // otherwise the previously selected tab wouldn't be redrawn properly under 
 259     // wxGTK which calls Refresh() immediately and not during the next event 
 260     // loop iteration as wxMSW does and as it should 
 261     size_t selOld 
= m_sel
; 
 265     if ( selOld 
!= INVALID_PAGE 
) 
 267         RefreshTab(selOld
, true /* this tab was selected */); 
 269         m_pages
[selOld
]->Hide(); 
 272     if ( m_sel 
!= INVALID_PAGE 
) // this is impossible - but test nevertheless 
 277             m_spinbtn
->SetValue(m_sel
); 
 280         if ( m_sel 
< m_firstVisible 
) 
 282             // selection is to the left of visible part of tabs 
 285         else if ( m_sel 
> m_lastFullyVisible 
) 
 287             // selection is to the right of visible part of tabs 
 290         else // we already see this tab 
 296         m_pages
[m_sel
]->SetSize(GetPageRect()); 
 297         m_pages
[m_sel
]->Show(); 
 301     event
.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
); 
 302     GetEventHandler()->ProcessEvent(event
); 
 307 // ---------------------------------------------------------------------------- 
 308 // wxNotebook pages adding/deleting 
 309 // ---------------------------------------------------------------------------- 
 311 bool wxNotebook::InsertPage(size_t nPage
, 
 312                             wxNotebookPage 
*pPage
, 
 313                             const wxString
& strText
, 
 317     size_t nPages 
= GetPageCount(); 
 318     wxCHECK_MSG( nPage 
== nPages 
|| IS_VALID_PAGE(nPage
), false, 
 319                  _T("invalid notebook page in InsertPage()") ); 
 322     m_pages
.Insert(pPage
, nPage
); 
 325     m_accels
.Insert(FindAccelIndex(strText
, &label
), nPage
); 
 326     m_titles
.Insert(label
, nPage
); 
 328     m_images
.Insert(imageId
, nPage
); 
 330     // cache the tab geometry here 
 331     wxSize sizeTab 
= CalcTabSize(nPage
); 
 333     if ( sizeTab
.y 
> m_heightTab 
) 
 334         m_heightTab 
= sizeTab
.y
; 
 336     if ( FixedSizeTabs() && sizeTab
.x 
> m_widthMax 
) 
 337         m_widthMax 
= sizeTab
.x
; 
 339     m_widths
.Insert(sizeTab
.x
, nPage
); 
 341     // spin button may appear if we didn't have it before - but even if we did, 
 342     // its range should change, so update it unconditionally 
 345     // if the tab has just appeared, we have to relayout everything, otherwise 
 346     // it's enough to just redraw the tabs 
 349         // always select the first tab to have at least some selection 
 355     else // not the first tab 
 364     else // pages added to the notebook are initially hidden 
 372 bool wxNotebook::DeleteAllPages() 
 374     if ( !wxNotebookBase::DeleteAllPages() ) 
 377     // clear the other arrays as well 
 383     // it is not valid any longer 
 384     m_sel 
= INVALID_PAGE
; 
 386     // spin button is not needed any more 
 394 wxNotebookPage 
*wxNotebook::DoRemovePage(size_t nPage
) 
 396     wxCHECK_MSG( IS_VALID_PAGE(nPage
), NULL
, _T("invalid notebook page") ); 
 398     wxNotebookPage 
*page 
= m_pages
[nPage
]; 
 399     m_pages
.RemoveAt(nPage
); 
 400     m_titles
.RemoveAt(nPage
); 
 401     m_accels
.RemoveAt(nPage
); 
 402     m_widths
.RemoveAt(nPage
); 
 403     m_images
.RemoveAt(nPage
); 
 405     // the spin button might not be needed any more 
 406     // 2002-08-12 'if' commented out by JACS on behalf 
 407     // of Hans Van Leemputten <Hansvl@softhome.net> who 
 408     // points out that UpdateSpinBtn should always be called, 
 409     // to ensure m_lastVisible is up to date. 
 410     // if ( HasSpinBtn() ) 
 415     size_t count 
= GetPageCount(); 
 418         if ( m_sel 
== (size_t)nPage 
) 
 420             // avoid sending event to this page which doesn't exist in the 
 422             m_sel 
= INVALID_PAGE
; 
 424             SetSelection(nPage 
== count 
? nPage 
- 1 : nPage
); 
 426         else if ( m_sel 
> (size_t)nPage 
) 
 428             // no need to change selection, just adjust the index 
 432     else // no more tabs left 
 434         m_sel 
= INVALID_PAGE
; 
 437     // have to refresh everything 
 443 // ---------------------------------------------------------------------------- 
 444 // wxNotebook drawing 
 445 // ---------------------------------------------------------------------------- 
 447 void wxNotebook::RefreshCurrent() 
 449     if ( m_sel 
!= INVALID_PAGE 
) 
 455 void wxNotebook::RefreshTab(int page
, bool forceSelected
) 
 457     wxCHECK_RET( IS_VALID_PAGE(page
), _T("invalid notebook page") ); 
 459     wxRect rect 
= GetTabRect(page
); 
 460     if ( forceSelected 
|| ((size_t)page 
== m_sel
) ) 
 462         const wxSize indent 
= GetRenderer()->GetTabIndent(); 
 463         rect
.Inflate(indent
.x
, indent
.y
); 
 469 void wxNotebook::RefreshAllTabs() 
 471     wxRect rect 
= GetAllTabsRect(); 
 472     if ( rect
.width 
|| rect
.height 
) 
 476     //else: we don't have tabs at all 
 479 void wxNotebook::DoDrawTab(wxDC
& dc
, const wxRect
& rect
, size_t n
) 
 484         int image 
= m_images
[n
]; 
 486         // Not needed now that wxGenericImageList is being 
 487         // used for wxUniversal under MSW 
 488 #if 0 // def __WXMSW__    // FIXME 
 490         m_imageList
->GetSize(n
, w
, h
); 
 493         dc
.SelectObject(bmp
); 
 494         dc
.SetBackground(wxBrush(GetBackgroundColour(), wxSOLID
)); 
 495         m_imageList
->Draw(image
, dc
, 0, 0, wxIMAGELIST_DRAW_NORMAL
, true); 
 496         dc
.SelectObject(wxNullBitmap
); 
 498         bmp 
= m_imageList
->GetBitmap(image
); 
 505         flags 
|= wxCONTROL_SELECTED
; 
 508             flags 
|= wxCONTROL_FOCUSED
; 
 511     GetRenderer()->DrawTab
 
 523 void wxNotebook::DoDraw(wxControlRenderer 
*renderer
) 
 525     //wxRect rectUpdate = GetUpdateClientRect(); -- unused 
 527     wxDC
& dc 
= renderer
->GetDC(); 
 528     dc
.SetFont(GetFont()); 
 529     dc
.SetTextForeground(GetForegroundColour()); 
 531     // redraw the border - it's simpler to always do it instead of checking 
 532     // whether this needs to be done 
 533     GetRenderer()->DrawBorder(dc
, wxBORDER_RAISED
, GetPagePart()); 
 535     // avoid overwriting the spin button 
 538         wxRect rectTabs 
= GetAllTabsRect(); 
 539         wxSize sizeSpinBtn 
= m_spinbtn
->GetSize(); 
 543             rectTabs
.height 
-= sizeSpinBtn
.y
; 
 545             // Allow for erasing the line under selected tab 
 550             rectTabs
.width 
-= sizeSpinBtn
.x
; 
 552             // Allow for erasing the line under selected tab 
 553             rectTabs
.height 
+= 2; 
 556         dc
.SetClippingRegion(rectTabs
); 
 559     wxRect rect 
= GetTabsPart(); 
 560     bool isVertical 
= IsVertical(); 
 563     for ( size_t n 
= m_firstVisible
; n 
< m_lastVisible
; n
++ ) 
 565         GetTabSize(n
, &rect
.width
, &rect
.height
); 
 569             // don't redraw it now as this tab has to be drawn over the other 
 570             // ones as it takes more place and spills over to them 
 573         else // not selected tab 
 575             // unfortunately we can't do this because the selected tab hangs 
 576             // over its neighbours and so we might need to refresh more tabs - 
 577             // of course, we could still avoid rereshing some of them with more 
 578             // complicated checks, but it doesn't seem too bad to refresh all 
 579             // of them, I still don't see flicker, so leaving as is for now 
 581             //if ( rectUpdate.Intersects(rect) ) 
 583                 DoDrawTab(dc
, rect
, n
); 
 585             //else: doesn't need to be refreshed 
 588         // move the rect to the next tab 
 590             rect
.y 
+= rect
.height
; 
 592             rect
.x 
+= rect
.width
; 
 595     // now redraw the selected tab 
 598         DoDrawTab(dc
, rectSel
, m_sel
); 
 601     dc
.DestroyClippingRegion(); 
 604 // ---------------------------------------------------------------------------- 
 605 // wxNotebook geometry 
 606 // ---------------------------------------------------------------------------- 
 608 int wxNotebook::HitTest(const wxPoint
& pt
, long *flags
) const 
 611         *flags 
= wxBK_HITTEST_NOWHERE
; 
 613     // first check that it is in this window at all 
 614     if ( !GetClientRect().Contains(pt
) ) 
 619     wxRect rectTabs 
= GetAllTabsRect(); 
 621     switch ( GetTabOrientation() ) 
 624             wxFAIL_MSG(_T("unknown tab orientation")); 
 628             if ( pt
.y 
> rectTabs
.GetBottom() ) 
 633             if ( pt
.y 
< rectTabs
.y 
) 
 638             if ( pt
.x 
> rectTabs
.GetRight() ) 
 643             if ( pt
.x 
< rectTabs
.x 
) 
 648     for ( size_t n 
= m_firstVisible
; n 
< m_lastVisible
; n
++ ) 
 650         GetTabSize(n
, &rectTabs
.width
, &rectTabs
.height
); 
 652         if ( rectTabs
.Contains(pt
) ) 
 656                 // TODO: be more precise 
 657                 *flags 
= wxBK_HITTEST_ONITEM
; 
 663         // move the rectTabs to the next tab 
 665             rectTabs
.y 
+= rectTabs
.height
; 
 667             rectTabs
.x 
+= rectTabs
.width
; 
 673 bool wxNotebook::IsVertical() const 
 675     wxDirection dir 
= GetTabOrientation(); 
 677     return dir 
== wxLEFT 
|| dir 
== wxRIGHT
; 
 680 wxDirection 
wxNotebook::GetTabOrientation() const 
 682     long style 
= GetWindowStyle(); 
 683     if ( style 
& wxBK_BOTTOM 
) 
 685     else if ( style 
& wxBK_RIGHT 
) 
 687     else if ( style 
& wxBK_LEFT 
) 
 690     // wxBK_TOP == 0 so we don't have to test for it 
 694 wxRect 
wxNotebook::GetTabRect(int page
) const 
 697     wxCHECK_MSG( IS_VALID_PAGE(page
), rect
, _T("invalid notebook page") ); 
 699     // calc the size of this tab and of the preceding ones 
 700     wxCoord widthThis
, widthBefore
; 
 701     if ( FixedSizeTabs() ) 
 703         widthThis 
= m_widthMax
; 
 704         widthBefore 
= page
*m_widthMax
; 
 709         for ( int n 
= 0; n 
< page
; n
++ ) 
 711             widthBefore 
+= m_widths
[n
]; 
 714         widthThis 
= m_widths
[page
]; 
 717     rect 
= GetTabsPart(); 
 720         rect
.y 
+= widthBefore 
- m_offset
; 
 721         rect
.height 
= widthThis
; 
 725         rect
.x 
+= widthBefore 
- m_offset
; 
 726         rect
.width 
= widthThis
; 
 732 wxRect 
wxNotebook::GetAllTabsRect() const 
 736     if ( GetPageCount() ) 
 738         const wxSize indent 
= GetRenderer()->GetTabIndent(); 
 739         wxSize size 
= GetClientSize(); 
 743             rect
.width 
= m_heightTab 
+ indent
.x
; 
 744             rect
.x 
= GetTabOrientation() == wxLEFT 
? 0 : size
.x 
- rect
.width
; 
 746             rect
.height 
= size
.y
; 
 752             rect
.height 
= m_heightTab 
+ indent
.y
; 
 753             rect
.y 
= GetTabOrientation() == wxTOP 
? 0 : size
.y 
- rect
.height
; 
 761 wxRect 
wxNotebook::GetTabsPart() const 
 763     wxRect rect 
= GetAllTabsRect(); 
 765     wxDirection dir 
= GetTabOrientation(); 
 767     const wxSize indent 
= GetRenderer()->GetTabIndent(); 
 774             rect
.width 
-= indent
.y
; 
 778             rect
.width 
-= indent
.y
; 
 787             rect
.height 
-= indent
.y
; 
 791             rect
.height 
-= indent
.y
; 
 798 void wxNotebook::GetTabSize(int page
, wxCoord 
*w
, wxCoord 
*h
) const 
 800     wxCHECK_RET( w 
&& h
, _T("NULL pointer in GetTabSize") ); 
 804         // width and height have inverted meaning 
 810     // height is always fixed 
 813     // width may also be fixed and be the same for all tabs 
 814     *w 
= GetTabWidth(page
); 
 817 void wxNotebook::SetTabSize(const wxSize
& sz
) 
 819     wxCHECK_RET( FixedSizeTabs(), _T("SetTabSize() ignored") ); 
 833 wxSize 
wxNotebook::CalcTabSize(int page
) const 
 835     // NB: don't use m_widthMax, m_heightTab or m_widths here because this 
 836     //     method is called to calculate them 
 840     wxCHECK_MSG( IS_VALID_PAGE(page
), size
, _T("invalid notebook page") ); 
 842     GetTextExtent(m_titles
[page
], &size
.x
, &size
.y
); 
 844     if ( HasImage(page
) ) 
 847         m_imageList
->GetSize(m_images
[page
], sizeImage
.x
, sizeImage
.y
); 
 849         size
.x 
+= sizeImage
.x 
+ 5; // FIXME: hard coded margin 
 851         if ( sizeImage
.y 
> size
.y 
) 
 852             size
.y 
= sizeImage
.y
; 
 855     size
.x 
+= 2*m_sizePad
.x
; 
 856     size
.y 
+= 2*m_sizePad
.y
; 
 861 void wxNotebook::ResizeTab(int page
) 
 863     wxSize sizeTab 
= CalcTabSize(page
); 
 865     // we only need full relayout if the page size changes 
 866     bool needsRelayout 
= false; 
 871         wxCoord tmp 
= sizeTab
.x
; 
 872         sizeTab
.x 
= sizeTab
.y
; 
 876     if ( sizeTab
.y 
> m_heightTab 
) 
 878         needsRelayout 
= true; 
 880         m_heightTab 
= sizeTab
.y
; 
 883     m_widths
[page
] = sizeTab
.x
; 
 885     if ( sizeTab
.x 
> m_widthMax 
) 
 886         m_widthMax 
= sizeTab
.x
; 
 888     // the total of the tabs has changed too 
 897 void wxNotebook::SetPadding(const wxSize
& padding
) 
 899     if ( padding 
!= m_sizePad 
) 
 907 void wxNotebook::Relayout() 
 909     if ( GetPageCount() ) 
 915         if ( m_sel 
!= INVALID_PAGE 
) 
 917             // resize the currently shown page 
 918             wxRect rectPage 
= GetPageRect(); 
 920             m_pages
[m_sel
]->SetSize(rectPage
); 
 922             // also scroll it into view if needed (note that m_lastVisible 
 923             // was updated by the call to UpdateSpinBtn() above, this is why it 
 927                 if ( m_sel 
< m_firstVisible 
) 
 929                     // selection is to the left of visible part of tabs 
 932                 else if ( m_sel 
> m_lastFullyVisible 
) 
 934                     // selection is to the right of visible part of tabs 
 940     else // we have no pages 
 942         // just refresh everything 
 947 wxRect 
wxNotebook::GetPagePart() const 
 949     wxRect rectPage 
= GetClientRect(); 
 951     if ( GetPageCount() ) 
 953         wxRect rectTabs 
= GetAllTabsRect(); 
 954         wxDirection dir 
= GetTabOrientation(); 
 957             rectPage
.width 
-= rectTabs
.width
; 
 959                 rectPage
.x 
+= rectTabs
.width
; 
 963             rectPage
.height 
-= rectTabs
.height
; 
 965                 rectPage
.y 
+= rectTabs
.height
; 
 968     //else: no pages at all 
 973 wxRect 
wxNotebook::GetPageRect() const 
 975     wxRect rect 
= GetPagePart(); 
 977     // leave space for the border 
 978     wxRect rectBorder 
= GetRenderer()->GetBorderDimensions(wxBORDER_RAISED
); 
 980     // FIXME: hardcoded +2! 
 981     rect
.Inflate(-(rectBorder
.x 
+ rectBorder
.width 
+ 2), 
 982                  -(rectBorder
.y 
+ rectBorder
.height 
+ 2)); 
 987 wxSize 
wxNotebook::GetSizeForPage(const wxSize
& size
) const 
 989     wxSize sizeNb 
= size
; 
 990     wxRect rect 
= GetAllTabsRect(); 
 992         sizeNb
.x 
+= rect
.width
; 
 994         sizeNb
.y 
+= rect
.height
; 
 999 void wxNotebook::SetPageSize(const wxSize
& size
) 
1001     SetClientSize(GetSizeForPage(size
)); 
1004 wxSize 
wxNotebook::CalcSizeFromPage(const wxSize
& sizePage
) const 
1006     return AdjustSize(GetSizeForPage(sizePage
)); 
1009 // ---------------------------------------------------------------------------- 
1010 // wxNotebook spin button 
1011 // ---------------------------------------------------------------------------- 
1013 bool wxNotebook::HasSpinBtn() const 
1015     return m_spinbtn 
&& m_spinbtn
->IsShown(); 
1018 void wxNotebook::CalcLastVisibleTab() 
1020     bool isVertical 
= IsVertical(); 
1022     wxCoord width 
= GetClientSize().x
; 
1024     wxRect rect 
= GetTabsPart(); 
1026     size_t count 
= GetPageCount(); 
1028     wxCoord widthLast 
= 0; 
1030     for ( n 
= m_firstVisible
; n 
< count
; n
++ ) 
1032         GetTabSize(n
, &rect
.width
, &rect
.height
); 
1033         if ( rect
.GetRight() > width 
) 
1038         // remember it to use below 
1039         widthLast 
= rect
.GetRight(); 
1041         // move the rect to the next tab 
1043             rect
.y 
+= rect
.height
; 
1045             rect
.x 
+= rect
.width
; 
1048     if ( n 
== m_firstVisible 
) 
1050         // even the first tab isn't fully visible - but still pretend it is as 
1051         // we have to show something 
1052         m_lastFullyVisible 
= m_firstVisible
; 
1054     else // more than one tab visible 
1056         m_lastFullyVisible 
= n 
- 1; 
1058         // but is it really fully visible? it shouldn't overlap with the spin 
1059         // button if it is present (again, even if the first button does 
1060         // overlap with it, we pretend that it doesn't as there is not much 
1062         if ( (m_lastFullyVisible 
> m_firstVisible
) && HasSpinBtn() ) 
1064             // adjust width to be the width before the spin button 
1065             wxSize sizeSpinBtn 
= m_spinbtn
->GetSize(); 
1067                 width 
-= sizeSpinBtn
.y
; 
1069                 width 
-= sizeSpinBtn
.x
; 
1071             if ( widthLast 
> width 
) 
1073                 // the last button overlaps with spin button, so take he 
1075                 m_lastFullyVisible
--; 
1082         // everything is visible 
1087         // this tab is still (partially) visible, so m_lastVisible is the 
1088         // next tab (remember, this is "exclusive" last) 
1089         m_lastVisible 
= n 
+ 1; 
1094 void wxNotebook::UpdateSpinBtn() 
1096     // first decide if we need a spin button 
1099     size_t count 
= (size_t)GetPageCount(); 
1102         // this case is special, get rid of it immediately: everything is 
1103         // visible and we don't need any spin buttons 
1104         allTabsShown 
= true; 
1106         // have to reset them manually as we don't call CalcLastVisibleTab() 
1109         m_lastFullyVisible 
= 0; 
1113         CalcLastVisibleTab(); 
1115         // if all tabs after the first visible one are shown, it doesn't yet 
1116         // mean that all tabs are shown - so we go backwards until we arrive to 
1117         // the beginning (then all tabs are indeed shown) or find a tab such 
1118         // that not all tabs after it are shown 
1119         while ( (m_lastFullyVisible 
== count 
- 1) && (m_firstVisible 
> 0) ) 
1121             // this is equivalent to ScrollTo(m_firstVisible - 1) but more 
1123             m_offset 
-= GetTabWidth(m_firstVisible
--); 
1125             // reclaculate after m_firstVisible change 
1126             CalcLastVisibleTab(); 
1129         allTabsShown 
= m_lastFullyVisible 
== count 
- 1; 
1132     if ( !allTabsShown 
) 
1136             // create it once only 
1137             m_spinbtn 
= new wxNotebookSpinBtn(this); 
1139             // set the correct value to keep it in sync 
1140             m_spinbtn
->SetValue(m_sel
); 
1143         // position it correctly 
1149         // also set/update the range 
1150         m_spinbtn
->SetRange(0, count 
- 1); 
1152         // update m_lastFullyVisible once again as it might have changed 
1153         // because the spin button appeared 
1155         // FIXME: might do it more efficiently 
1156         CalcLastVisibleTab(); 
1158     else // all tabs are visible, we don't need spin button 
1160         if ( m_spinbtn 
&& m_spinbtn 
-> IsShown() ) 
1167 void wxNotebook::PositionSpinBtn() 
1173     m_spinbtn
->GetSize(&wBtn
, &hBtn
); 
1175     wxRect rectTabs 
= GetAllTabsRect(); 
1178     switch ( GetTabOrientation() ) 
1181             wxFAIL_MSG(_T("unknown tab orientation")); 
1185             x 
= rectTabs
.GetRight() - wBtn
; 
1186             y 
= rectTabs
.GetBottom() - hBtn
; 
1190             x 
= rectTabs
.GetRight() - wBtn
; 
1191             y 
= rectTabs
.GetTop(); 
1195             x 
= rectTabs
.GetRight() - wBtn
; 
1196             y 
= rectTabs
.GetBottom() - hBtn
; 
1200             x 
= rectTabs
.GetLeft(); 
1201             y 
= rectTabs
.GetBottom() - hBtn
; 
1205     m_spinbtn
->Move(x
, y
); 
1208 // ---------------------------------------------------------------------------- 
1209 // wxNotebook scrolling 
1210 // ---------------------------------------------------------------------------- 
1212 void wxNotebook::ScrollTo(int page
) 
1214     wxCHECK_RET( IS_VALID_PAGE(page
), _T("invalid notebook page") ); 
1216     // set the first visible tab and offset (easy) 
1217     m_firstVisible 
= (size_t)page
; 
1219     for ( size_t n 
= 0; n 
< m_firstVisible
; n
++ ) 
1221         m_offset 
+= GetTabWidth(n
); 
1224     // find the last visible tab too 
1225     CalcLastVisibleTab(); 
1230 void wxNotebook::ScrollLastTo(int page
) 
1232     wxCHECK_RET( IS_VALID_PAGE(page
), _T("invalid notebook page") ); 
1234     // go backwards until we find the first tab which can be made visible 
1235     // without hiding the given one 
1236     wxSize size 
= GetClientSize(); 
1237     wxCoord widthAll 
= IsVertical() ? size
.y 
: size
.x
, 
1238             widthTabs 
= GetTabWidth(page
); 
1240     // the total width is less than the width of the window if we have the spin 
1244         wxSize sizeSpinBtn 
= m_spinbtn
->GetSize(); 
1246             widthAll 
-= sizeSpinBtn
.y
; 
1248             widthAll 
-= sizeSpinBtn
.x
; 
1251     m_firstVisible 
= page
; 
1252     while ( (m_firstVisible 
> 0) && (widthTabs 
<= widthAll
) ) 
1254         widthTabs 
+= GetTabWidth(--m_firstVisible
); 
1257     if ( widthTabs 
> widthAll 
) 
1259         // take one step back (that it is forward) if we can 
1260         if ( m_firstVisible 
< (size_t)GetPageCount() - 1 ) 
1265     ScrollTo(m_firstVisible
); 
1267     // consitency check: the page we were asked to show should be shown 
1268     wxASSERT_MSG( (size_t)page 
< m_lastVisible
, _T("bug in ScrollLastTo") ); 
1271 // ---------------------------------------------------------------------------- 
1272 // wxNotebook sizing/moving 
1273 // ---------------------------------------------------------------------------- 
1275 wxSize 
wxNotebook::DoGetBestClientSize() const 
1277     // calculate the max page size 
1280     size_t count 
= GetPageCount(); 
1283         for ( size_t n 
= 0; n 
< count
; n
++ ) 
1285             wxSize sizePage 
= m_pages
[n
]->GetSize(); 
1287             if ( size
.x 
< sizePage
.x 
) 
1288                 size
.x 
= sizePage
.x
; 
1289             if ( size
.y 
< sizePage
.y 
) 
1290                 size
.y 
= sizePage
.y
; 
1295         // use some arbitrary default size 
1300     return GetSizeForPage(size
); 
1303 void wxNotebook::DoMoveWindow(int x
, int y
, int width
, int height
) 
1305     wxControl::DoMoveWindow(x
, y
, width
, height
); 
1307     // move the spin ctrl too (NOP if it doesn't exist) 
1311 void wxNotebook::DoSetSize(int x
, int y
, 
1312                            int width
, int height
, 
1315     wxSize old_client_size 
= GetClientSize(); 
1317     wxControl::DoSetSize(x
, y
, width
, height
, sizeFlags
); 
1319     wxSize new_client_size 
= GetClientSize(); 
1321     if (old_client_size 
!= new_client_size
) 
1325 // ---------------------------------------------------------------------------- 
1326 // wxNotebook input processing 
1327 // ---------------------------------------------------------------------------- 
1329 bool wxNotebook::PerformAction(const wxControlAction
& action
, 
1331                                const wxString
& strArg
) 
1333     if ( action 
== wxACTION_NOTEBOOK_NEXT 
) 
1334         SetSelection(GetNextPage(true)); 
1335     else if ( action 
== wxACTION_NOTEBOOK_PREV 
) 
1336         SetSelection(GetNextPage(false)); 
1337     else if ( action 
== wxACTION_NOTEBOOK_GOTO 
) 
1338         SetSelection((int)numArg
); 
1340         return wxControl::PerformAction(action
, numArg
, strArg
); 
1346 wxInputHandler 
*wxNotebook::GetStdInputHandler(wxInputHandler 
*handlerDef
) 
1348     static wxStdNotebookInputHandler 
s_handler(handlerDef
); 
1353 // ---------------------------------------------------------------------------- 
1354 // wxStdNotebookInputHandler 
1355 // ---------------------------------------------------------------------------- 
1357 wxStdNotebookInputHandler::wxStdNotebookInputHandler(wxInputHandler 
*inphand
) 
1358                          : wxStdInputHandler(inphand
) 
1362 bool wxStdNotebookInputHandler::HandleKey(wxInputConsumer 
*consumer
, 
1363                                           const wxKeyEvent
& event
, 
1366     // ignore the key releases 
1369         wxNotebook 
*notebook 
= wxStaticCast(consumer
->GetInputWindow(), wxNotebook
); 
1372         wxControlAction action
; 
1373         switch ( event
.GetKeyCode() ) 
1376                 if ( notebook
->IsVertical() ) 
1377                     action 
= wxACTION_NOTEBOOK_PREV
; 
1381                 if ( !notebook
->IsVertical() ) 
1382                     action 
= wxACTION_NOTEBOOK_PREV
; 
1386                 if ( notebook
->IsVertical() ) 
1387                     action 
= wxACTION_NOTEBOOK_NEXT
; 
1391                 if ( !notebook
->IsVertical() ) 
1392                     action 
= wxACTION_NOTEBOOK_NEXT
; 
1396                 action 
= wxACTION_NOTEBOOK_GOTO
; 
1397                 // page = 0; -- already has this value 
1401                 action 
= wxACTION_NOTEBOOK_GOTO
; 
1402                 page 
= notebook
->GetPageCount() - 1; 
1406         if ( !action
.IsEmpty() ) 
1408             return consumer
->PerformAction(action
, page
); 
1412     return wxStdInputHandler::HandleKey(consumer
, event
, pressed
); 
1415 bool wxStdNotebookInputHandler::HandleMouse(wxInputConsumer 
*consumer
, 
1416                                             const wxMouseEvent
& event
) 
1418     if ( event
.ButtonDown(1) ) 
1420         wxNotebook 
*notebook 
= wxStaticCast(consumer
->GetInputWindow(), wxNotebook
); 
1421         int page 
= notebook
->HitTest(event
.GetPosition()); 
1424             consumer
->PerformAction(wxACTION_NOTEBOOK_GOTO
, page
); 
1430     return wxStdInputHandler::HandleMouse(consumer
, event
); 
1433 bool wxStdNotebookInputHandler::HandleMouseMove(wxInputConsumer 
*consumer
, 
1434                                                 const wxMouseEvent
& event
) 
1436     return wxStdInputHandler::HandleMouseMove(consumer
, event
); 
1440 wxStdNotebookInputHandler::HandleFocus(wxInputConsumer 
*consumer
, 
1441                                        const wxFocusEvent
& WXUNUSED(event
)) 
1443     HandleFocusChange(consumer
); 
1448 bool wxStdNotebookInputHandler::HandleActivation(wxInputConsumer 
*consumer
, 
1449                                                  bool WXUNUSED(activated
)) 
1451     // we react to the focus change in the same way as to the [de]activation 
1452     HandleFocusChange(consumer
); 
1457 void wxStdNotebookInputHandler::HandleFocusChange(wxInputConsumer 
*consumer
) 
1459     wxNotebook 
*notebook 
= wxStaticCast(consumer
->GetInputWindow(), wxNotebook
); 
1460     notebook
->RefreshCurrent(); 
1463 #endif // wxUSE_NOTEBOOK