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 class wxNotebookSpinBtn 
: public wxSpinButton
 
  80     wxNotebookSpinBtn(wxNotebook 
*nb
) 
  81         : wxSpinButton(nb
, wxID_ANY
, 
  82                        wxDefaultPosition
, wxDefaultSize
, 
  83                        nb
->IsVertical() ? wxSP_VERTICAL 
: wxSP_HORIZONTAL
) 
  89     void OnSpin(wxSpinEvent
& event
) 
  91         m_nb
->PerformAction(wxACTION_NOTEBOOK_GOTO
, event
.GetPosition()); 
 100 BEGIN_EVENT_TABLE(wxNotebookSpinBtn
, wxSpinButton
) 
 101     EVT_SPIN(wxID_ANY
, wxNotebookSpinBtn::OnSpin
) 
 104 // ============================================================================ 
 106 // ============================================================================ 
 108 // ---------------------------------------------------------------------------- 
 109 // wxNotebook creation 
 110 // ---------------------------------------------------------------------------- 
 112 void wxNotebook::Init() 
 119     m_lastFullyVisible 
= 0; 
 126 bool wxNotebook::Create(wxWindow 
*parent
, 
 131                         const wxString
& name
) 
 133     if ( (style 
& wxBK_ALIGN_MASK
) == wxBK_DEFAULT 
) 
 136     if ( !wxControl::Create(parent
, id
, pos
, size
, style
, 
 137                             wxDefaultValidator
, name
) ) 
 140     m_sizePad 
= GetRenderer()->GetTabPadding(); 
 142     SetInitialSize(size
); 
 144     CreateInputHandler(wxINP_HANDLER_NOTEBOOK
); 
 149 // ---------------------------------------------------------------------------- 
 150 // wxNotebook page titles and images 
 151 // ---------------------------------------------------------------------------- 
 153 wxString 
wxNotebook::GetPageText(size_t nPage
) const 
 155     wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxEmptyString
, wxT("invalid notebook page") ); 
 157     return m_titles
[nPage
]; 
 160 bool wxNotebook::SetPageText(size_t nPage
, const wxString
& strText
) 
 162     wxCHECK_MSG( IS_VALID_PAGE(nPage
), false, wxT("invalid notebook page") ); 
 164     if ( strText 
!= m_titles
[nPage
] ) 
 166         m_accels
[nPage
] = FindAccelIndex(strText
, &m_titles
[nPage
]); 
 168         if ( FixedSizeTabs() ) 
 170             // it's enough to just reresh this one 
 173         else // var width tabs 
 175             // we need to resize the tab to fit the new string 
 183 int wxNotebook::GetPageImage(size_t nPage
) const 
 185     wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxNOT_FOUND
, wxT("invalid notebook page") ); 
 187     return m_images
[nPage
]; 
 190 bool wxNotebook::SetPageImage(size_t nPage
, int nImage
) 
 192     wxCHECK_MSG( IS_VALID_PAGE(nPage
), false, wxT("invalid notebook page") ); 
 194     wxCHECK_MSG( HasImageList() && nImage 
< GetImageList()->GetImageCount(), 
 195                  false, wxT("invalid image index in SetPageImage()") ); 
 197     if ( nImage 
!= m_images
[nPage
] ) 
 199         // if the item didn't have an icon before or, on the contrary, did have 
 200         // it but has lost it now, its size will change - but if the icon just 
 202         bool tabSizeChanges 
= nImage 
== -1 || m_images
[nPage
] == -1; 
 203         m_images
[nPage
] = nImage
; 
 205         if ( tabSizeChanges 
) 
 214 wxNotebook::~wxNotebook() 
 218 // ---------------------------------------------------------------------------- 
 219 // wxNotebook page switching 
 220 // ---------------------------------------------------------------------------- 
 222 int wxNotebook::DoSetSelection(size_t nPage
, int flags
) 
 224     wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxNOT_FOUND
, wxT("invalid notebook page") ); 
 226     if ( (int)nPage 
== m_selection 
) 
 228         // don't do anything if there is nothing to do 
 232     if ( flags 
& SetSelection_SendEvent 
) 
 234         if ( !SendPageChangingEvent(nPage
) ) 
 236             // program doesn't allow the page change 
 241     // we need to change m_selection first, before calling RefreshTab() below as 
 242     // otherwise the previously selected tab wouldn't be redrawn properly under 
 243     // wxGTK which calls Refresh() immediately and not during the next event 
 244     // loop iteration as wxMSW does and as it should 
 245     int selOld 
= m_selection
; 
 249     if ( selOld 
!= wxNOT_FOUND 
) 
 251         RefreshTab(selOld
, true /* this tab was selected */); 
 253         m_pages
[selOld
]->Hide(); 
 256     if ( m_selection 
!= wxNOT_FOUND 
) // this is impossible - but test nevertheless 
 261             m_spinbtn
->SetValue(m_selection
); 
 264         if ( nPage 
< m_firstVisible 
) 
 266             // selection is to the left of visible part of tabs 
 269         else if ( nPage 
> m_lastFullyVisible 
) 
 271             // selection is to the right of visible part of tabs 
 274         else // we already see this tab 
 280         m_pages
[nPage
]->SetSize(GetPageRect()); 
 281         m_pages
[nPage
]->Show(); 
 284     if ( flags 
& SetSelection_SendEvent 
) 
 287         SendPageChangedEvent(selOld
); 
 293 // ---------------------------------------------------------------------------- 
 294 // wxNotebook pages adding/deleting 
 295 // ---------------------------------------------------------------------------- 
 297 bool wxNotebook::InsertPage(size_t nPage
, 
 298                             wxNotebookPage 
*pPage
, 
 299                             const wxString
& strText
, 
 303     size_t nPages 
= GetPageCount(); 
 304     wxCHECK_MSG( nPage 
== nPages 
|| IS_VALID_PAGE(nPage
), false, 
 305                  wxT("invalid notebook page in InsertPage()") ); 
 308     m_pages
.Insert(pPage
, nPage
); 
 311     m_accels
.Insert(FindAccelIndex(strText
, &label
), nPage
); 
 312     m_titles
.Insert(label
, nPage
); 
 314     m_images
.Insert(imageId
, nPage
); 
 316     // cache the tab geometry here 
 317     wxSize sizeTab 
= CalcTabSize(nPage
); 
 319     if ( sizeTab
.y 
> m_heightTab 
) 
 320         m_heightTab 
= sizeTab
.y
; 
 322     if ( FixedSizeTabs() && sizeTab
.x 
> m_widthMax 
) 
 323         m_widthMax 
= sizeTab
.x
; 
 325     m_widths
.Insert(sizeTab
.x
, nPage
); 
 327     // spin button may appear if we didn't have it before - but even if we did, 
 328     // its range should change, so update it unconditionally 
 331     // if the tab has just appeared, we have to relayout everything, otherwise 
 332     // it's enough to just redraw the tabs 
 335         // always select the first tab to have at least some selection 
 341     else // not the first tab 
 350     else // pages added to the notebook are initially hidden 
 358 bool wxNotebook::DeleteAllPages() 
 360     if ( !wxNotebookBase::DeleteAllPages() ) 
 363     // clear the other arrays as well 
 369     // spin button is not needed any more 
 377 wxNotebookPage 
*wxNotebook::DoRemovePage(size_t nPage
) 
 379     wxCHECK_MSG( IS_VALID_PAGE(nPage
), NULL
, wxT("invalid notebook page") ); 
 381     wxNotebookPage 
*page 
= m_pages
[nPage
]; 
 382     m_pages
.RemoveAt(nPage
); 
 383     m_titles
.RemoveAt(nPage
); 
 384     m_accels
.RemoveAt(nPage
); 
 385     m_widths
.RemoveAt(nPage
); 
 386     m_images
.RemoveAt(nPage
); 
 388     // the spin button might not be needed any more 
 389     // 2002-08-12 'if' commented out by JACS on behalf 
 390     // of Hans Van Leemputten <Hansvl@softhome.net> who 
 391     // points out that UpdateSpinBtn should always be called, 
 392     // to ensure m_lastVisible is up to date. 
 393     // if ( HasSpinBtn() ) 
 398     size_t count 
= GetPageCount(); 
 401         wxASSERT_MSG( m_selection 
!= wxNOT_FOUND
, "should have selection" ); 
 403         if ( (size_t)m_selection 
== nPage 
) 
 405             // avoid sending event to this page which doesn't exist in the 
 407             m_selection 
= wxNOT_FOUND
; 
 409             SetSelection(nPage 
== count 
? nPage 
- 1 : nPage
); 
 411         else if ( (size_t)m_selection 
> nPage 
) 
 413             // no need to change selection, just adjust the index 
 417     else // no more tabs left 
 419         m_selection 
= wxNOT_FOUND
; 
 422     // have to refresh everything 
 428 // ---------------------------------------------------------------------------- 
 429 // wxNotebook drawing 
 430 // ---------------------------------------------------------------------------- 
 432 void wxNotebook::RefreshCurrent() 
 434     if ( m_selection 
!= wxNOT_FOUND 
) 
 436         RefreshTab(m_selection
); 
 440 void wxNotebook::RefreshTab(int page
, bool forceSelected
) 
 442     wxCHECK_RET( IS_VALID_PAGE(page
), wxT("invalid notebook page") ); 
 444     wxRect rect 
= GetTabRect(page
); 
 445     if ( forceSelected 
|| (page 
== m_selection
) ) 
 447         const wxSize indent 
= GetRenderer()->GetTabIndent(); 
 448         rect
.Inflate(indent
.x
, indent
.y
); 
 454 void wxNotebook::RefreshAllTabs() 
 456     wxRect rect 
= GetAllTabsRect(); 
 457     if ( rect
.width 
|| rect
.height 
) 
 461     //else: we don't have tabs at all 
 464 void wxNotebook::DoDrawTab(wxDC
& dc
, const wxRect
& rect
, size_t n
) 
 469         int image 
= m_images
[n
]; 
 471         // Not needed now that wxGenericImageList is being 
 472         // used for wxUniversal under MSW 
 473 #if 0 // def __WXMSW__    // FIXME 
 475         GetImageList()->GetSize(n
, w
, h
); 
 478         dc
.SelectObject(bmp
); 
 479         dc
.SetBackground(wxBrush(GetBackgroundColour(), wxSOLID
)); 
 480         GetImageList()->Draw(image
, dc
, 0, 0, wxIMAGELIST_DRAW_NORMAL
, true); 
 481         dc
.SelectObject(wxNullBitmap
); 
 483         bmp 
= GetImageList()->GetBitmap(image
); 
 488     if ( (int)n 
== m_selection 
) 
 490         flags 
|= wxCONTROL_SELECTED
; 
 493             flags 
|= wxCONTROL_FOCUSED
; 
 496     GetRenderer()->DrawTab
 
 508 void wxNotebook::DoDraw(wxControlRenderer 
*renderer
) 
 510     //wxRect rectUpdate = GetUpdateClientRect(); -- unused 
 512     wxDC
& dc 
= renderer
->GetDC(); 
 513     dc
.SetFont(GetFont()); 
 514     dc
.SetTextForeground(GetForegroundColour()); 
 516     // redraw the border - it's simpler to always do it instead of checking 
 517     // whether this needs to be done 
 518     GetRenderer()->DrawBorder(dc
, wxBORDER_RAISED
, GetPagePart()); 
 520     // avoid overwriting the spin button 
 523         wxRect rectTabs 
= GetAllTabsRect(); 
 524         wxSize sizeSpinBtn 
= m_spinbtn
->GetSize(); 
 528             rectTabs
.height 
-= sizeSpinBtn
.y
; 
 530             // Allow for erasing the line under selected tab 
 535             rectTabs
.width 
-= sizeSpinBtn
.x
; 
 537             // Allow for erasing the line under selected tab 
 538             rectTabs
.height 
+= 2; 
 541         dc
.SetClippingRegion(rectTabs
); 
 544     wxRect rect 
= GetTabsPart(); 
 545     bool isVertical 
= IsVertical(); 
 548     for ( size_t n 
= m_firstVisible
; n 
< m_lastVisible
; n
++ ) 
 550         GetTabSize(n
, &rect
.width
, &rect
.height
); 
 552         if ( (int)n 
== m_selection 
) 
 554             // don't redraw it now as this tab has to be drawn over the other 
 555             // ones as it takes more place and spills over to them 
 558         else // not selected tab 
 560             // unfortunately we can't do this because the selected tab hangs 
 561             // over its neighbours and so we might need to refresh more tabs - 
 562             // of course, we could still avoid rereshing some of them with more 
 563             // complicated checks, but it doesn't seem too bad to refresh all 
 564             // of them, I still don't see flicker, so leaving as is for now 
 566             //if ( rectUpdate.Intersects(rect) ) 
 568                 DoDrawTab(dc
, rect
, n
); 
 570             //else: doesn't need to be refreshed 
 573         // move the rect to the next tab 
 575             rect
.y 
+= rect
.height
; 
 577             rect
.x 
+= rect
.width
; 
 580     // now redraw the selected tab 
 583         DoDrawTab(dc
, rectSel
, m_selection
); 
 586     dc
.DestroyClippingRegion(); 
 589 // ---------------------------------------------------------------------------- 
 590 // wxNotebook geometry 
 591 // ---------------------------------------------------------------------------- 
 593 int wxNotebook::HitTest(const wxPoint
& pt
, long *flags
) const 
 596         *flags 
= wxBK_HITTEST_NOWHERE
; 
 598     // first check that it is in this window at all 
 599     if ( !GetClientRect().Contains(pt
) ) 
 604     wxRect rectTabs 
= GetAllTabsRect(); 
 606     switch ( GetTabOrientation() ) 
 609             wxFAIL_MSG(wxT("unknown tab orientation")); 
 613             if ( pt
.y 
> rectTabs
.GetBottom() ) 
 618             if ( pt
.y 
< rectTabs
.y 
) 
 623             if ( pt
.x 
> rectTabs
.GetRight() ) 
 628             if ( pt
.x 
< rectTabs
.x 
) 
 633     for ( size_t n 
= m_firstVisible
; n 
< m_lastVisible
; n
++ ) 
 635         GetTabSize(n
, &rectTabs
.width
, &rectTabs
.height
); 
 637         if ( rectTabs
.Contains(pt
) ) 
 641                 // TODO: be more precise 
 642                 *flags 
= wxBK_HITTEST_ONITEM
; 
 648         // move the rectTabs to the next tab 
 650             rectTabs
.y 
+= rectTabs
.height
; 
 652             rectTabs
.x 
+= rectTabs
.width
; 
 658 bool wxNotebook::IsVertical() const 
 660     wxDirection dir 
= GetTabOrientation(); 
 662     return dir 
== wxLEFT 
|| dir 
== wxRIGHT
; 
 665 wxDirection 
wxNotebook::GetTabOrientation() const 
 667     long style 
= GetWindowStyle(); 
 668     if ( style 
& wxBK_BOTTOM 
) 
 670     else if ( style 
& wxBK_RIGHT 
) 
 672     else if ( style 
& wxBK_LEFT 
) 
 675     // wxBK_TOP == 0 so we don't have to test for it 
 679 wxRect 
wxNotebook::GetTabRect(int page
) const 
 682     wxCHECK_MSG( IS_VALID_PAGE(page
), rect
, wxT("invalid notebook page") ); 
 684     // calc the size of this tab and of the preceding ones 
 685     wxCoord widthThis
, widthBefore
; 
 686     if ( FixedSizeTabs() ) 
 688         widthThis 
= m_widthMax
; 
 689         widthBefore 
= page
*m_widthMax
; 
 694         for ( int n 
= 0; n 
< page
; n
++ ) 
 696             widthBefore 
+= m_widths
[n
]; 
 699         widthThis 
= m_widths
[page
]; 
 702     rect 
= GetTabsPart(); 
 705         rect
.y 
+= widthBefore 
- m_offset
; 
 706         rect
.height 
= widthThis
; 
 710         rect
.x 
+= widthBefore 
- m_offset
; 
 711         rect
.width 
= widthThis
; 
 717 wxRect 
wxNotebook::GetAllTabsRect() const 
 721     if ( GetPageCount() ) 
 723         const wxSize indent 
= GetRenderer()->GetTabIndent(); 
 724         wxSize size 
= GetClientSize(); 
 728             rect
.width 
= m_heightTab 
+ indent
.x
; 
 729             rect
.x 
= GetTabOrientation() == wxLEFT 
? 0 : size
.x 
- rect
.width
; 
 731             rect
.height 
= size
.y
; 
 737             rect
.height 
= m_heightTab 
+ indent
.y
; 
 738             rect
.y 
= GetTabOrientation() == wxTOP 
? 0 : size
.y 
- rect
.height
; 
 746 wxRect 
wxNotebook::GetTabsPart() const 
 748     wxRect rect 
= GetAllTabsRect(); 
 750     wxDirection dir 
= GetTabOrientation(); 
 752     const wxSize indent 
= GetRenderer()->GetTabIndent(); 
 759             rect
.width 
-= indent
.y
; 
 763             rect
.width 
-= indent
.y
; 
 772             rect
.height 
-= indent
.y
; 
 776             rect
.height 
-= indent
.y
; 
 783 void wxNotebook::GetTabSize(int page
, wxCoord 
*w
, wxCoord 
*h
) const 
 785     wxCHECK_RET( w 
&& h
, wxT("NULL pointer in GetTabSize") ); 
 789         // width and height have inverted meaning 
 795     // height is always fixed 
 798     // width may also be fixed and be the same for all tabs 
 799     *w 
= GetTabWidth(page
); 
 802 void wxNotebook::SetTabSize(const wxSize
& sz
) 
 804     wxCHECK_RET( FixedSizeTabs(), wxT("SetTabSize() ignored") ); 
 818 wxSize 
wxNotebook::CalcTabSize(int page
) const 
 820     // NB: don't use m_widthMax, m_heightTab or m_widths here because this 
 821     //     method is called to calculate them 
 825     wxCHECK_MSG( IS_VALID_PAGE(page
), size
, wxT("invalid notebook page") ); 
 827     GetTextExtent(m_titles
[page
], &size
.x
, &size
.y
); 
 829     if ( HasImage(page
) ) 
 832         GetImageList()->GetSize(m_images
[page
], sizeImage
.x
, sizeImage
.y
); 
 834         size
.x 
+= sizeImage
.x 
+ 5; // FIXME: hard coded margin 
 836         if ( sizeImage
.y 
> size
.y 
) 
 837             size
.y 
= sizeImage
.y
; 
 840     size
.x 
+= 2*m_sizePad
.x
; 
 841     size
.y 
+= 2*m_sizePad
.y
; 
 846 void wxNotebook::ResizeTab(int page
) 
 848     wxSize sizeTab 
= CalcTabSize(page
); 
 850     // we only need full relayout if the page size changes 
 851     bool needsRelayout 
= false; 
 856         wxCoord tmp 
= sizeTab
.x
; 
 857         sizeTab
.x 
= sizeTab
.y
; 
 861     if ( sizeTab
.y 
> m_heightTab 
) 
 863         needsRelayout 
= true; 
 865         m_heightTab 
= sizeTab
.y
; 
 868     m_widths
[page
] = sizeTab
.x
; 
 870     if ( sizeTab
.x 
> m_widthMax 
) 
 871         m_widthMax 
= sizeTab
.x
; 
 873     // the total of the tabs has changed too 
 882 void wxNotebook::SetPadding(const wxSize
& padding
) 
 884     if ( padding 
!= m_sizePad 
) 
 892 void wxNotebook::Relayout() 
 894     if ( GetPageCount() ) 
 900         if ( m_selection 
!= wxNOT_FOUND 
) 
 902             // resize the currently shown page 
 903             wxRect rectPage 
= GetPageRect(); 
 905             m_pages
[m_selection
]->SetSize(rectPage
); 
 907             // also scroll it into view if needed (note that m_lastVisible 
 908             // was updated by the call to UpdateSpinBtn() above, this is why it 
 912                 const size_t selection 
= m_selection
; 
 913                 if ( selection 
< m_firstVisible 
) 
 915                     // selection is to the left of visible part of tabs 
 918                 else if ( selection 
> m_lastFullyVisible 
) 
 920                     // selection is to the right of visible part of tabs 
 921                     ScrollLastTo(selection
); 
 926     else // we have no pages 
 928         // just refresh everything 
 933 wxRect 
wxNotebook::GetPagePart() const 
 935     wxRect rectPage 
= GetClientRect(); 
 937     if ( GetPageCount() ) 
 939         wxRect rectTabs 
= GetAllTabsRect(); 
 940         wxDirection dir 
= GetTabOrientation(); 
 943             rectPage
.width 
-= rectTabs
.width
; 
 945                 rectPage
.x 
+= rectTabs
.width
; 
 949             rectPage
.height 
-= rectTabs
.height
; 
 951                 rectPage
.y 
+= rectTabs
.height
; 
 954     //else: no pages at all 
 959 wxRect 
wxNotebook::GetPageRect() const 
 961     wxRect rect 
= GetPagePart(); 
 963     // leave space for the border 
 964     wxRect rectBorder 
= GetRenderer()->GetBorderDimensions(wxBORDER_RAISED
); 
 966     // FIXME: hardcoded +2! 
 967     rect
.Inflate(-(rectBorder
.x 
+ rectBorder
.width 
+ 2), 
 968                  -(rectBorder
.y 
+ rectBorder
.height 
+ 2)); 
 973 wxSize 
wxNotebook::GetSizeForPage(const wxSize
& size
) const 
 975     wxSize sizeNb 
= size
; 
 976     wxRect rect 
= GetAllTabsRect(); 
 978         sizeNb
.x 
+= rect
.width
; 
 980         sizeNb
.y 
+= rect
.height
; 
 985 void wxNotebook::SetPageSize(const wxSize
& size
) 
 987     SetClientSize(GetSizeForPage(size
)); 
 990 wxSize 
wxNotebook::CalcSizeFromPage(const wxSize
& sizePage
) const 
 992     return AdjustSize(GetSizeForPage(sizePage
)); 
 995 // ---------------------------------------------------------------------------- 
 996 // wxNotebook spin button 
 997 // ---------------------------------------------------------------------------- 
 999 bool wxNotebook::HasSpinBtn() const 
1001     return m_spinbtn 
&& m_spinbtn
->IsShown(); 
1004 void wxNotebook::CalcLastVisibleTab() 
1006     bool isVertical 
= IsVertical(); 
1008     wxCoord width 
= GetClientSize().x
; 
1010     wxRect rect 
= GetTabsPart(); 
1012     size_t count 
= GetPageCount(); 
1014     wxCoord widthLast 
= 0; 
1016     for ( n 
= m_firstVisible
; n 
< count
; n
++ ) 
1018         GetTabSize(n
, &rect
.width
, &rect
.height
); 
1019         if ( rect
.GetRight() > width 
) 
1024         // remember it to use below 
1025         widthLast 
= rect
.GetRight(); 
1027         // move the rect to the next tab 
1029             rect
.y 
+= rect
.height
; 
1031             rect
.x 
+= rect
.width
; 
1034     if ( n 
== m_firstVisible 
) 
1036         // even the first tab isn't fully visible - but still pretend it is as 
1037         // we have to show something 
1038         m_lastFullyVisible 
= m_firstVisible
; 
1040     else // more than one tab visible 
1042         m_lastFullyVisible 
= n 
- 1; 
1044         // but is it really fully visible? it shouldn't overlap with the spin 
1045         // button if it is present (again, even if the first button does 
1046         // overlap with it, we pretend that it doesn't as there is not much 
1048         if ( (m_lastFullyVisible 
> m_firstVisible
) && HasSpinBtn() ) 
1050             // adjust width to be the width before the spin button 
1051             wxSize sizeSpinBtn 
= m_spinbtn
->GetSize(); 
1053                 width 
-= sizeSpinBtn
.y
; 
1055                 width 
-= sizeSpinBtn
.x
; 
1057             if ( widthLast 
> width 
) 
1059                 // the last button overlaps with spin button, so take he 
1061                 m_lastFullyVisible
--; 
1068         // everything is visible 
1073         // this tab is still (partially) visible, so m_lastVisible is the 
1074         // next tab (remember, this is "exclusive" last) 
1075         m_lastVisible 
= n 
+ 1; 
1080 void wxNotebook::UpdateSpinBtn() 
1082     // first decide if we need a spin button 
1085     size_t count 
= (size_t)GetPageCount(); 
1088         // this case is special, get rid of it immediately: everything is 
1089         // visible and we don't need any spin buttons 
1090         allTabsShown 
= true; 
1092         // have to reset them manually as we don't call CalcLastVisibleTab() 
1095         m_lastFullyVisible 
= 0; 
1099         CalcLastVisibleTab(); 
1101         // if all tabs after the first visible one are shown, it doesn't yet 
1102         // mean that all tabs are shown - so we go backwards until we arrive to 
1103         // the beginning (then all tabs are indeed shown) or find a tab such 
1104         // that not all tabs after it are shown 
1105         while ( (m_lastFullyVisible 
== count 
- 1) && (m_firstVisible 
> 0) ) 
1107             // this is equivalent to ScrollTo(m_firstVisible - 1) but more 
1109             m_offset 
-= GetTabWidth(m_firstVisible
--); 
1111             // reclaculate after m_firstVisible change 
1112             CalcLastVisibleTab(); 
1115         allTabsShown 
= m_lastFullyVisible 
== count 
- 1; 
1118     if ( !allTabsShown 
) 
1122             // create it once only 
1123             m_spinbtn 
= new wxNotebookSpinBtn(this); 
1125             // set the correct value to keep it in sync 
1126             m_spinbtn
->SetValue(m_selection
); 
1129         // position it correctly 
1135         // also set/update the range 
1136         m_spinbtn
->SetRange(0, count 
- 1); 
1138         // update m_lastFullyVisible once again as it might have changed 
1139         // because the spin button appeared 
1141         // FIXME: might do it more efficiently 
1142         CalcLastVisibleTab(); 
1144     else // all tabs are visible, we don't need spin button 
1146         if ( m_spinbtn 
&& m_spinbtn 
-> IsShown() ) 
1153 void wxNotebook::PositionSpinBtn() 
1159     m_spinbtn
->GetSize(&wBtn
, &hBtn
); 
1161     wxRect rectTabs 
= GetAllTabsRect(); 
1164     switch ( GetTabOrientation() ) 
1167             wxFAIL_MSG(wxT("unknown tab orientation")); 
1171             x 
= rectTabs
.GetRight() - wBtn
; 
1172             y 
= rectTabs
.GetBottom() - hBtn
; 
1176             x 
= rectTabs
.GetRight() - wBtn
; 
1177             y 
= rectTabs
.GetTop(); 
1181             x 
= rectTabs
.GetRight() - wBtn
; 
1182             y 
= rectTabs
.GetBottom() - hBtn
; 
1186             x 
= rectTabs
.GetLeft(); 
1187             y 
= rectTabs
.GetBottom() - hBtn
; 
1191     m_spinbtn
->Move(x
, y
); 
1194 // ---------------------------------------------------------------------------- 
1195 // wxNotebook scrolling 
1196 // ---------------------------------------------------------------------------- 
1198 void wxNotebook::ScrollTo(size_t page
) 
1200     wxCHECK_RET( IS_VALID_PAGE(page
), wxT("invalid notebook page") ); 
1202     // set the first visible tab and offset (easy) 
1203     m_firstVisible 
= page
; 
1205     for ( size_t n 
= 0; n 
< m_firstVisible
; n
++ ) 
1207         m_offset 
+= GetTabWidth(n
); 
1210     // find the last visible tab too 
1211     CalcLastVisibleTab(); 
1216 void wxNotebook::ScrollLastTo(size_t page
) 
1218     wxCHECK_RET( IS_VALID_PAGE(page
), wxT("invalid notebook page") ); 
1220     // go backwards until we find the first tab which can be made visible 
1221     // without hiding the given one 
1222     wxSize size 
= GetClientSize(); 
1223     wxCoord widthAll 
= IsVertical() ? size
.y 
: size
.x
, 
1224             widthTabs 
= GetTabWidth(page
); 
1226     // the total width is less than the width of the window if we have the spin 
1230         wxSize sizeSpinBtn 
= m_spinbtn
->GetSize(); 
1232             widthAll 
-= sizeSpinBtn
.y
; 
1234             widthAll 
-= sizeSpinBtn
.x
; 
1237     m_firstVisible 
= page
; 
1238     while ( (m_firstVisible 
> 0) && (widthTabs 
<= widthAll
) ) 
1240         widthTabs 
+= GetTabWidth(--m_firstVisible
); 
1243     if ( widthTabs 
> widthAll 
) 
1245         // take one step back (that it is forward) if we can 
1246         if ( m_firstVisible 
< (size_t)GetPageCount() - 1 ) 
1251     ScrollTo(m_firstVisible
); 
1253     // consitency check: the page we were asked to show should be shown 
1254     wxASSERT_MSG( (size_t)page 
< m_lastVisible
, wxT("bug in ScrollLastTo") ); 
1257 // ---------------------------------------------------------------------------- 
1258 // wxNotebook sizing/moving 
1259 // ---------------------------------------------------------------------------- 
1261 void wxNotebook::DoMoveWindow(int x
, int y
, int width
, int height
) 
1263     wxControl::DoMoveWindow(x
, y
, width
, height
); 
1265     // move the spin ctrl too (NOP if it doesn't exist) 
1269 void wxNotebook::DoSetSize(int x
, int y
, 
1270                            int width
, int height
, 
1273     wxSize old_client_size 
= GetClientSize(); 
1275     wxControl::DoSetSize(x
, y
, width
, height
, sizeFlags
); 
1277     wxSize new_client_size 
= GetClientSize(); 
1279     if (old_client_size 
!= new_client_size
) 
1283 // ---------------------------------------------------------------------------- 
1284 // wxNotebook input processing 
1285 // ---------------------------------------------------------------------------- 
1287 bool wxNotebook::PerformAction(const wxControlAction
& action
, 
1289                                const wxString
& strArg
) 
1291     if ( action 
== wxACTION_NOTEBOOK_NEXT 
) 
1292         SetSelection(GetNextPage(true)); 
1293     else if ( action 
== wxACTION_NOTEBOOK_PREV 
) 
1294         SetSelection(GetNextPage(false)); 
1295     else if ( action 
== wxACTION_NOTEBOOK_GOTO 
) 
1296         SetSelection((int)numArg
); 
1298         return wxControl::PerformAction(action
, numArg
, strArg
); 
1304 wxInputHandler 
*wxNotebook::GetStdInputHandler(wxInputHandler 
*handlerDef
) 
1306     static wxStdNotebookInputHandler 
s_handler(handlerDef
); 
1311 // ---------------------------------------------------------------------------- 
1312 // wxStdNotebookInputHandler 
1313 // ---------------------------------------------------------------------------- 
1315 wxStdNotebookInputHandler::wxStdNotebookInputHandler(wxInputHandler 
*inphand
) 
1316                          : wxStdInputHandler(inphand
) 
1320 bool wxStdNotebookInputHandler::HandleKey(wxInputConsumer 
*consumer
, 
1321                                           const wxKeyEvent
& event
, 
1324     // ignore the key releases 
1327         wxNotebook 
*notebook 
= wxStaticCast(consumer
->GetInputWindow(), wxNotebook
); 
1330         wxControlAction action
; 
1331         switch ( event
.GetKeyCode() ) 
1334                 if ( notebook
->IsVertical() ) 
1335                     action 
= wxACTION_NOTEBOOK_PREV
; 
1339                 if ( !notebook
->IsVertical() ) 
1340                     action 
= wxACTION_NOTEBOOK_PREV
; 
1344                 if ( notebook
->IsVertical() ) 
1345                     action 
= wxACTION_NOTEBOOK_NEXT
; 
1349                 if ( !notebook
->IsVertical() ) 
1350                     action 
= wxACTION_NOTEBOOK_NEXT
; 
1354                 action 
= wxACTION_NOTEBOOK_GOTO
; 
1355                 // page = 0; -- already has this value 
1359                 action 
= wxACTION_NOTEBOOK_GOTO
; 
1360                 page 
= notebook
->GetPageCount() - 1; 
1364         if ( !action
.IsEmpty() ) 
1366             return consumer
->PerformAction(action
, page
); 
1370     return wxStdInputHandler::HandleKey(consumer
, event
, pressed
); 
1373 bool wxStdNotebookInputHandler::HandleMouse(wxInputConsumer 
*consumer
, 
1374                                             const wxMouseEvent
& event
) 
1376     if ( event
.ButtonDown(1) ) 
1378         wxNotebook 
*notebook 
= wxStaticCast(consumer
->GetInputWindow(), wxNotebook
); 
1379         int page 
= notebook
->HitTest(event
.GetPosition()); 
1382             consumer
->PerformAction(wxACTION_NOTEBOOK_GOTO
, page
); 
1388     return wxStdInputHandler::HandleMouse(consumer
, event
); 
1391 bool wxStdNotebookInputHandler::HandleMouseMove(wxInputConsumer 
*consumer
, 
1392                                                 const wxMouseEvent
& event
) 
1394     return wxStdInputHandler::HandleMouseMove(consumer
, event
); 
1398 wxStdNotebookInputHandler::HandleFocus(wxInputConsumer 
*consumer
, 
1399                                        const wxFocusEvent
& WXUNUSED(event
)) 
1401     HandleFocusChange(consumer
); 
1406 bool wxStdNotebookInputHandler::HandleActivation(wxInputConsumer 
*consumer
, 
1407                                                  bool WXUNUSED(activated
)) 
1409     // we react to the focus change in the same way as to the [de]activation 
1410     HandleFocusChange(consumer
); 
1415 void wxStdNotebookInputHandler::HandleFocusChange(wxInputConsumer 
*consumer
) 
1417     wxNotebook 
*notebook 
= wxStaticCast(consumer
->GetInputWindow(), wxNotebook
); 
1418     notebook
->RefreshCurrent(); 
1421 #endif // wxUSE_NOTEBOOK