1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        univ/notebook.cpp 
   3 // Purpose:     wxNotebook implementation 
   4 // Author:      Vadim Zeitlin 
   8 // Copyright:   (c) 2001 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr> 
   9 // Licence:     wxWindows licence 
  10 ///////////////////////////////////////////////////////////////////////////// 
  12 // ============================================================================ 
  14 // ============================================================================ 
  16 // ---------------------------------------------------------------------------- 
  18 // ---------------------------------------------------------------------------- 
  21     #pragma implementation "univnotebook.h" 
  24 #include "wx/wxprec.h" 
  32 #include "wx/imaglist.h" 
  33 #include "wx/notebook.h" 
  34 #include "wx/spinbutt.h" 
  35 #include "wx/dcmemory.h" 
  37 #include "wx/univ/renderer.h" 
  39 // ---------------------------------------------------------------------------- 
  41 // ---------------------------------------------------------------------------- 
  43 #define IS_VALID_PAGE(nPage) (((nPage) >= 0) && ((nPage) < GetPageCount())) 
  45 // ---------------------------------------------------------------------------- 
  47 // ---------------------------------------------------------------------------- 
  49 static const size_t INVALID_PAGE 
= (size_t)-1; 
  51 DEFINE_EVENT_TYPE(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
) 
  52 DEFINE_EVENT_TYPE(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
) 
  54 // ---------------------------------------------------------------------------- 
  56 // ---------------------------------------------------------------------------- 
  58 class wxNotebookSpinBtn 
: public wxSpinButton
 
  61     wxNotebookSpinBtn(wxNotebook 
*nb
) 
  62         : wxSpinButton(nb
, -1, 
  63                        wxDefaultPosition
, wxDefaultSize
, 
  64                        nb
->IsVertical() ? wxSP_VERTICAL 
: wxSP_HORIZONTAL
) 
  70     void OnSpin(wxSpinEvent
& event
) 
  72         m_nb
->PerformAction(wxACTION_NOTEBOOK_GOTO
, event
.GetPosition()); 
  81 BEGIN_EVENT_TABLE(wxNotebookSpinBtn
, wxSpinButton
) 
  82     EVT_SPIN(-1, wxNotebookSpinBtn::OnSpin
) 
  85 // ============================================================================ 
  87 // ============================================================================ 
  89 IMPLEMENT_DYNAMIC_CLASS(wxNotebook
, wxControl
) 
  90 IMPLEMENT_DYNAMIC_CLASS(wxNotebookEvent
, wxCommandEvent
) 
  92 // ---------------------------------------------------------------------------- 
  93 // wxNotebook creation 
  94 // ---------------------------------------------------------------------------- 
  96 void wxNotebook::Init() 
 105     m_lastFullyVisible 
= 0; 
 112 bool wxNotebook::Create(wxWindow 
*parent
, 
 117                         const wxString
& name
) 
 119     if ( !wxControl::Create(parent
, id
, pos
, size
, style
, 
 120                             wxDefaultValidator
, name
) ) 
 123     m_sizePad 
= GetRenderer()->GetTabPadding(); 
 127     CreateInputHandler(wxINP_HANDLER_NOTEBOOK
); 
 132 // ---------------------------------------------------------------------------- 
 133 // wxNotebook page titles and images 
 134 // ---------------------------------------------------------------------------- 
 136 wxString 
wxNotebook::GetPageText(int nPage
) const 
 138     wxCHECK_MSG( IS_VALID_PAGE(nPage
), _T(""), _T("invalid notebook page") ); 
 140     return m_titles
[nPage
]; 
 143 bool wxNotebook::SetPageText(int nPage
, const wxString
& strText
) 
 145     wxCHECK_MSG( IS_VALID_PAGE(nPage
), FALSE
, _T("invalid notebook page") ); 
 147     if ( strText 
!= m_titles
[nPage
] ) 
 149         m_accels
[nPage
] = FindAccelIndex(strText
, &m_titles
[nPage
]); 
 151         if ( FixedSizeTabs() ) 
 153             // it's enough to just reresh this one 
 156         else // var width tabs 
 158             // we need to resize the tab to fit the new string 
 166 int wxNotebook::GetPageImage(int nPage
) const 
 168     wxCHECK_MSG( IS_VALID_PAGE(nPage
), -1, _T("invalid notebook page") ); 
 170     return m_images
[nPage
]; 
 173 bool wxNotebook::SetPageImage(int nPage
, int nImage
) 
 175     wxCHECK_MSG( IS_VALID_PAGE(nPage
), FALSE
, _T("invalid notebook page") ); 
 177     wxCHECK_MSG( m_imageList 
&& nImage 
< m_imageList
->GetImageCount(), FALSE
, 
 178                  _T("invalid image index in SetPageImage()") ); 
 180     if ( nImage 
!= m_images
[nPage
] ) 
 182         // if the item didn't have an icon before or, on the contrary, did have 
 183         // it but has lost it now, its size will change - but if the icon just 
 185         bool tabSizeChanges 
= nImage 
== -1 || m_images
[nPage
] == -1; 
 186         m_images
[nPage
] = nImage
; 
 188         if ( tabSizeChanges 
) 
 197 wxNotebook::~wxNotebook() 
 201 // ---------------------------------------------------------------------------- 
 202 // wxNotebook page switching 
 203 // ---------------------------------------------------------------------------- 
 205 int wxNotebook::SetSelection(int nPage
) 
 207     wxCHECK_MSG( IS_VALID_PAGE(nPage
), -1, _T("invalid notebook page") ); 
 209     if ( (size_t)nPage 
== m_sel 
) 
 211         // don't do anything if there is nothing to do 
 215     if ( m_sel 
!= INVALID_PAGE 
) 
 219         m_pages
[m_sel
]->Hide(); 
 224     if ( m_sel 
!= INVALID_PAGE 
) // this is impossible - but test nevertheless 
 229             m_spinbtn
->SetValue(m_sel
); 
 232         if ( m_sel 
< m_firstVisible 
) 
 234             // selection is to the left of visible part of tabs 
 237         else if ( m_sel 
> m_lastFullyVisible 
) 
 239             // selection is to the right of visible part of tabs 
 242         else // we already see this tab 
 248         m_pages
[m_sel
]->SetSize(GetPageRect()); 
 249         m_pages
[m_sel
]->Show(); 
 255 void wxNotebook::ChangePage(int nPage
) 
 257     wxCHECK_RET( IS_VALID_PAGE(nPage
), _T("invalid notebook page") ); 
 259     if ( (size_t)nPage 
== m_sel 
) 
 265     wxNotebookEvent 
event(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
, m_windowId
); 
 266     event
.SetSelection(nPage
); 
 267     event
.SetOldSelection(m_sel
); 
 268     event
.SetEventObject(this); 
 269     if ( GetEventHandler()->ProcessEvent(event
) && !event
.IsAllowed() ) 
 271         // program doesn't allow the page change 
 277     event
.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
); 
 278     GetEventHandler()->ProcessEvent(event
); 
 281 // ---------------------------------------------------------------------------- 
 282 // wxNotebook pages adding/deleting 
 283 // ---------------------------------------------------------------------------- 
 285 bool wxNotebook::InsertPage(int nPage
, 
 286                             wxNotebookPage 
*pPage
, 
 287                             const wxString
& strText
, 
 291     int nPages 
= GetPageCount(); 
 292     wxCHECK_MSG( nPage 
== nPages 
|| IS_VALID_PAGE(nPage
), FALSE
, 
 293                  _T("invalid notebook page in InsertPage()") ); 
 296     m_pages
.Insert(pPage
, nPage
); 
 299     m_accels
.Insert(FindAccelIndex(strText
, &label
), nPage
); 
 300     m_titles
.Insert(label
, nPage
); 
 302     m_images
.Insert(imageId
, nPage
); 
 304     // cache the tab geometry here 
 305     wxSize sizeTab 
= CalcTabSize(nPage
); 
 307     if ( sizeTab
.y 
> m_heightTab 
) 
 308         m_heightTab 
= sizeTab
.y
; 
 310     if ( FixedSizeTabs() && sizeTab
.x 
> m_widthMax 
) 
 311         m_widthMax 
= sizeTab
.x
; 
 313     m_widths
.Insert(sizeTab
.x
, nPage
); 
 315     // spin button may appear if we didn't have it before - but even if we did, 
 316     // its range should change, so update it unconditionally 
 319     // if the tab has just appeared, we have to relayout everything, otherwise 
 320     // it's enough to just redraw the tabs 
 323         // always select the first tab to have at least some selection 
 328     else // not the first tab 
 337     else // pages added to the notebook are initially hidden 
 345 bool wxNotebook::DeleteAllPages() 
 347     if ( !wxNotebookBase::DeleteAllPages() ) 
 350     // clear the other arrays as well 
 356     // it is not valid any longer 
 357     m_sel 
= INVALID_PAGE
; 
 359     // spin button is not needed any more 
 367 wxNotebookPage 
*wxNotebook::DoRemovePage(int nPage
) 
 369     wxCHECK_MSG( IS_VALID_PAGE(nPage
), NULL
, _T("invalid notebook page") ); 
 371     wxNotebookPage 
*page 
= m_pages
[nPage
]; 
 372     m_pages
.RemoveAt(nPage
); 
 373     m_titles
.RemoveAt(nPage
); 
 374     m_accels
.RemoveAt(nPage
); 
 375     m_widths
.RemoveAt(nPage
); 
 376     m_images
.RemoveAt(nPage
); 
 378     // the spin button might not be needed any more 
 384     int count 
= GetPageCount(); 
 387         if ( m_sel 
== (size_t)nPage 
) 
 389             // avoid sending event to this page which doesn't exist in the 
 391             m_sel 
= INVALID_PAGE
; 
 393             SetSelection(nPage 
== count 
? nPage 
- 1 : nPage
); 
 395         else if ( m_sel 
> (size_t)nPage 
) 
 397             // no need to change selection, just adjust the index 
 401     else // no more tabs left 
 403         m_sel 
= INVALID_PAGE
; 
 406     // have to refresh everything 
 412 // ---------------------------------------------------------------------------- 
 413 // wxNotebook drawing 
 414 // ---------------------------------------------------------------------------- 
 416 void wxNotebook::RefreshCurrent() 
 418     if ( m_sel 
!= INVALID_PAGE 
) 
 424 void wxNotebook::RefreshTab(int page
) 
 426     wxCHECK_RET( IS_VALID_PAGE(page
), _T("invalid notebook page") ); 
 428     wxRect rect 
= GetTabRect(page
); 
 429     if ( (size_t)page 
== m_sel 
) 
 431         const wxSize indent 
= GetRenderer()->GetTabIndent(); 
 432         rect
.Inflate(indent
.x
, indent
.y
); 
 438 void wxNotebook::RefreshAllTabs() 
 440     wxRect rect 
= GetAllTabsRect(); 
 441     if ( rect
.width 
|| rect
.height 
) 
 445     //else: we don't have tabs at all 
 448 void wxNotebook::DoDrawTab(wxDC
& dc
, const wxRect
& rect
, size_t n
) 
 453         int image 
= m_images
[n
]; 
 455 #ifdef __WXMSW__    // FIXME 
 457         m_imageList
->GetSize(n
, w
, h
); 
 460         dc
.SelectObject(bmp
); 
 461         dc
.SetBackground(wxBrush(GetBackgroundColour(), wxSOLID
)); 
 462         m_imageList
->Draw(image
, dc
, 0, 0, wxIMAGELIST_DRAW_NORMAL
, TRUE
); 
 464         bmp 
= *m_imageList
->GetBitmap(image
); 
 471         flags 
|= wxCONTROL_SELECTED
; 
 474             flags 
|= wxCONTROL_FOCUSED
; 
 477     GetRenderer()->DrawTab
 
 489 void wxNotebook::DoDraw(wxControlRenderer 
*renderer
) 
 491     //wxRect rectUpdate = GetUpdateClientRect(); -- unused 
 493     wxDC
& dc 
= renderer
->GetDC(); 
 494     dc
.SetFont(GetFont()); 
 495     dc
.SetTextForeground(GetForegroundColour()); 
 497     // redraw the border - it's simpler to always do it instead of checking 
 498     // whether this needs to be done 
 499     GetRenderer()->DrawBorder(dc
, wxBORDER_RAISED
, GetPagePart()); 
 501     // avoid overwriting the spin button 
 504         wxRect rectTabs 
= GetAllTabsRect(); 
 505         wxSize sizeSpinBtn 
= m_spinbtn
->GetSize(); 
 508             rectTabs
.height 
-= sizeSpinBtn
.y
; 
 510             rectTabs
.width 
-= sizeSpinBtn
.x
; 
 512         dc
.SetClippingRegion(rectTabs
); 
 515     wxRect rect 
= GetTabsPart(); 
 516     bool isVertical 
= IsVertical(); 
 519     for ( size_t n 
= m_firstVisible
; n 
< m_lastVisible
; n
++ ) 
 521         GetTabSize(n
, &rect
.width
, &rect
.height
); 
 525             // don't redraw it now as this tab has to be drawn over the other 
 526             // ones as it takes more place and spills over to them 
 529         else // not selected tab 
 531             // unfortunately we can't do this because the selected tab hangs 
 532             // over its neighbours and so we might need to refresh more tabs - 
 533             // of course, we could still avoid rereshing some of them with more 
 534             // complicated checks, but it doesn't seem too bad to refresh all 
 535             // of them, I still don't see flicker, so leaving as is for now 
 537             //if ( rectUpdate.Intersects(rect) ) 
 539                 DoDrawTab(dc
, rect
, n
); 
 541             //else: doesn't need to be refreshed 
 544         // move the rect to the next tab 
 546             rect
.y 
+= rect
.height
; 
 548             rect
.x 
+= rect
.width
; 
 551     // now redraw the selected tab 
 554         DoDrawTab(dc
, rectSel
, m_sel
); 
 558 // ---------------------------------------------------------------------------- 
 559 // wxNotebook geometry 
 560 // ---------------------------------------------------------------------------- 
 562 int wxNotebook::HitTest(const wxPoint
& pt
) const 
 564     // first check that it is in this window at all 
 565     if ( !GetClientRect().Inside(pt
) ) 
 570     wxRect rectTabs 
= GetAllTabsRect(); 
 572     switch ( GetTabOrientation() ) 
 575             wxFAIL_MSG(_T("unknown tab orientation")); 
 579             if ( pt
.y 
> rectTabs
.GetBottom() ) 
 584             if ( pt
.y 
< rectTabs
.y 
) 
 589             if ( pt
.x 
> rectTabs
.GetRight() ) 
 594             if ( pt
.x 
< rectTabs
.x 
) 
 599     for ( size_t n 
= m_firstVisible
; n 
< m_lastVisible
; n
++ ) 
 601         GetTabSize(n
, &rectTabs
.width
, &rectTabs
.height
); 
 603         if ( rectTabs
.Inside(pt
) ) 
 606         // move the rectTabs to the next tab 
 608             rectTabs
.y 
+= rectTabs
.height
; 
 610             rectTabs
.x 
+= rectTabs
.width
; 
 616 bool wxNotebook::IsVertical() const 
 618     wxDirection dir 
= GetTabOrientation(); 
 620     return dir 
== wxLEFT 
|| dir 
== wxRIGHT
; 
 623 wxDirection 
wxNotebook::GetTabOrientation() const 
 625     long style 
= GetWindowStyle(); 
 626     if ( style 
& wxNB_BOTTOM 
) 
 628     else if ( style 
& wxNB_RIGHT 
) 
 630     else if ( style 
& wxNB_LEFT 
) 
 633     // wxNB_TOP == 0 so we don't have to test for it 
 637 wxRect 
wxNotebook::GetTabRect(int page
) const 
 640     wxCHECK_MSG( IS_VALID_PAGE(page
), rect
, _T("invalid notebook page") ); 
 642     // calc the size of this tab and of the preceding ones 
 643     wxCoord widthThis
, widthBefore
; 
 644     if ( FixedSizeTabs() ) 
 646         widthThis 
= m_widthMax
; 
 647         widthBefore 
= page
*m_widthMax
; 
 652         for ( int n 
= 0; n 
< page
; n
++ ) 
 654             widthBefore 
+= m_widths
[n
]; 
 657         widthThis 
= m_widths
[page
]; 
 660     rect 
= GetTabsPart(); 
 663         rect
.y 
+= widthBefore 
- m_offset
; 
 664         rect
.height 
= widthThis
; 
 668         rect
.x 
+= widthBefore 
- m_offset
; 
 669         rect
.width 
= widthThis
; 
 675 wxRect 
wxNotebook::GetAllTabsRect() const 
 679     if ( GetPageCount() ) 
 681         const wxSize indent 
= GetRenderer()->GetTabIndent(); 
 682         wxSize size 
= GetClientSize(); 
 686             rect
.width 
= m_heightTab 
+ indent
.x
; 
 687             rect
.x 
= GetTabOrientation() == wxLEFT 
? 0 : size
.x 
- rect
.width
; 
 689             rect
.height 
= size
.y
; 
 695             rect
.height 
= m_heightTab 
+ indent
.y
; 
 696             rect
.y 
= GetTabOrientation() == wxTOP 
? 0 : size
.y 
- rect
.height
; 
 704 wxRect 
wxNotebook::GetTabsPart() const 
 706     wxRect rect 
= GetAllTabsRect(); 
 708     wxDirection dir 
= GetTabOrientation(); 
 710     const wxSize indent 
= GetRenderer()->GetTabIndent(); 
 722             rect
.height 
-= indent
.y
; 
 726             rect
.height 
-= indent
.y
; 
 733 void wxNotebook::GetTabSize(int page
, wxCoord 
*w
, wxCoord 
*h
) const 
 735     wxCHECK_RET( w 
&& h
, _T("NULL pointer in GetTabSize") ); 
 739         // width and height have inverted meaning 
 745     // height is always fixed 
 748     // width may also be fixed and be the same for all tabs 
 749     *w 
= GetTabWidth(page
); 
 752 void wxNotebook::SetTabSize(const wxSize
& sz
) 
 754     wxCHECK_RET( FixedSizeTabs(), _T("SetTabSize() ignored") ); 
 768 wxSize 
wxNotebook::CalcTabSize(int page
) const 
 770     // NB: don't use m_widthMax, m_heightTab or m_widths here because this 
 771     //     method is called to calculate them 
 775     wxCHECK_MSG( IS_VALID_PAGE(page
), size
, _T("invalid notebook page") ); 
 777     GetTextExtent(m_titles
[page
], &size
.x
, &size
.y
); 
 779     if ( HasImage(page
) ) 
 782         m_imageList
->GetSize(m_images
[page
], sizeImage
.x
, sizeImage
.y
); 
 784         size
.x 
+= sizeImage
.x 
+ 5; // FIXME: hard coded margin 
 786         if ( sizeImage
.y 
> size
.y 
) 
 787             size
.y 
= sizeImage
.y
; 
 790     size
.x 
+= 2*m_sizePad
.x
; 
 791     size
.y 
+= 2*m_sizePad
.y
; 
 796 void wxNotebook::ResizeTab(int page
) 
 798     wxSize sizeTab 
= CalcTabSize(page
); 
 800     // we only need full relayout if the page size changes 
 801     bool needsRelayout 
= FALSE
; 
 806         wxCoord tmp 
= sizeTab
.x
; 
 807         sizeTab
.x 
= sizeTab
.y
; 
 811     if ( sizeTab
.y 
> m_heightTab 
) 
 813         needsRelayout 
= TRUE
; 
 815         m_heightTab 
= sizeTab
.y
; 
 818     m_widths
[page
] = sizeTab
.x
; 
 820     if ( sizeTab
.x 
> m_widthMax 
) 
 821         m_widthMax 
= sizeTab
.x
; 
 823     // the total of the tabs has changed too 
 832 void wxNotebook::SetPadding(const wxSize
& padding
) 
 834     if ( padding 
!= m_sizePad 
) 
 842 void wxNotebook::Relayout() 
 844     if ( GetPageCount() ) 
 850         if ( m_sel 
!= INVALID_PAGE 
) 
 852             // resize the currently shown page 
 853             wxRect rectPage 
= GetPageRect(); 
 855             m_pages
[m_sel
]->SetSize(rectPage
); 
 857             // also scroll it into view if needed (note that m_lastVisible 
 858             // was updated by the call to UpdateSpinBtn() above, this is why it 
 862                 if ( m_sel 
< m_firstVisible 
) 
 864                     // selection is to the left of visible part of tabs 
 867                 else if ( m_sel 
> m_lastFullyVisible 
) 
 869                     // selection is to the right of visible part of tabs 
 875     else // we have no pages 
 877         // just refresh everything 
 882 wxRect 
wxNotebook::GetPagePart() const 
 884     wxRect rectPage 
= GetClientRect(); 
 886     if ( GetPageCount() ) 
 888         wxRect rectTabs 
= GetAllTabsRect(); 
 889         wxDirection dir 
= GetTabOrientation(); 
 892             rectPage
.width 
-= rectTabs
.width
; 
 894                 rectPage
.x 
+= rectTabs
.width
; 
 898             rectPage
.height 
-= rectTabs
.height
; 
 900                 rectPage
.y 
+= rectTabs
.height
; 
 903     //else: no pages at all 
 908 wxRect 
wxNotebook::GetPageRect() const 
 910     wxRect rect 
= GetPagePart(); 
 912     // leave space for the border 
 913     wxRect rectBorder 
= GetRenderer()->GetBorderDimensions(wxBORDER_RAISED
); 
 915     // FIXME: hardcoded +2! 
 916     rect
.Inflate(-(rectBorder
.x 
+ rectBorder
.width 
+ 2), 
 917                  -(rectBorder
.y 
+ rectBorder
.height 
+ 2)); 
 922 wxSize 
wxNotebook::GetSizeForPage(const wxSize
& size
) const 
 924     wxSize sizeNb 
= size
; 
 925     wxRect rect 
= GetAllTabsRect(); 
 927         sizeNb
.x 
+= rect
.width
; 
 929         sizeNb
.y 
+= rect
.height
; 
 934 void wxNotebook::SetPageSize(const wxSize
& size
) 
 936     SetClientSize(GetSizeForPage(size
)); 
 939 wxSize 
wxNotebook::CalcSizeFromPage(const wxSize
& sizePage
) 
 941     return AdjustSize(GetSizeForPage(sizePage
)); 
 944 // ---------------------------------------------------------------------------- 
 945 // wxNotebook spin button 
 946 // ---------------------------------------------------------------------------- 
 948 bool wxNotebook::HasSpinBtn() const 
 950     return m_spinbtn 
&& m_spinbtn
->IsShown(); 
 953 void wxNotebook::CalcLastVisibleTab() 
 955     bool isVertical 
= IsVertical(); 
 957     wxCoord width 
= GetClientSize().x
; 
 959     wxRect rect 
= GetTabsPart(); 
 961     size_t count 
= GetPageCount(); 
 963     wxCoord widthLast 
= 0; 
 965     for ( n 
= m_firstVisible
; n 
< count
; n
++ ) 
 967         GetTabSize(n
, &rect
.width
, &rect
.height
); 
 968         if ( rect
.GetRight() > width 
) 
 973         // remember it to use below 
 974         widthLast 
= rect
.GetRight(); 
 976         // move the rect to the next tab 
 978             rect
.y 
+= rect
.height
; 
 980             rect
.x 
+= rect
.width
; 
 983     if ( n 
== m_firstVisible 
) 
 985         // even the first tab isn't fully visible - but still pretend it is as 
 986         // we have to show something 
 987         m_lastFullyVisible 
= m_firstVisible
; 
 989     else // more than one tab visible 
 991         m_lastFullyVisible 
= n 
- 1; 
 993         // but is it really fully visible? it shouldn't overlap with the spin 
 994         // button if it is present (again, even if the first button does 
 995         // overlap with it, we pretend that it doesn't as there is not much 
 997         if ( (m_lastFullyVisible 
> m_firstVisible
) && HasSpinBtn() ) 
 999             // adjust width to be the width before the spin button 
1000             wxSize sizeSpinBtn 
= m_spinbtn
->GetSize(); 
1002                 width 
-= sizeSpinBtn
.y
; 
1004                 width 
-= sizeSpinBtn
.x
; 
1006             if ( widthLast 
> width 
) 
1008                 // the last button overlaps with spin button, so take he 
1010                 m_lastFullyVisible
--; 
1017         // everything is visible 
1022         // this tab is still (partially) visible, so m_lastVisible is the 
1023         // next tab (remember, this is "exclusive" last) 
1024         m_lastVisible 
= n 
+ 1; 
1029 void wxNotebook::UpdateSpinBtn() 
1031     // first decide if we need a spin button 
1034     size_t count 
= (size_t)GetPageCount(); 
1037         // this case is special, get rid of it immediately: everything is 
1038         // visible and we don't need any spin buttons 
1039         allTabsShown 
= TRUE
; 
1041         // have to reset them manually as we don't call CalcLastVisibleTab() 
1044         m_lastFullyVisible 
= 0; 
1048         CalcLastVisibleTab(); 
1050         // if all tabs after the first visible one are shown, it doesn't yet 
1051         // mean that all tabs are shown - so we go backwards until we arrive to 
1052         // the beginning (then all tabs are indeed shown) or find a tab such 
1053         // that not all tabs after it are shown 
1054         while ( (m_lastFullyVisible 
== count 
- 1) && (m_firstVisible 
> 0) ) 
1056             // this is equivalent to ScrollTo(m_firstVisible - 1) but more 
1058             m_offset 
-= GetTabWidth(m_firstVisible
--); 
1060             // reclaculate after m_firstVisible change 
1061             CalcLastVisibleTab(); 
1064         allTabsShown 
= m_lastFullyVisible 
== count 
- 1; 
1067     if ( !allTabsShown 
) 
1071             // create it once only 
1072             m_spinbtn 
= new wxNotebookSpinBtn(this); 
1074             // set the correct value to keep it in sync 
1075             m_spinbtn
->SetValue(m_sel
); 
1078         // position it correctly 
1084         // also set/update the range 
1085         m_spinbtn
->SetRange(0, count 
- 1); 
1087         // update m_lastFullyVisible once again as it might have changed 
1088         // because the spin button appeared 
1090         // FIXME: might do it more efficiently 
1091         CalcLastVisibleTab(); 
1093     else // all tabs are visible, we don't need spin button 
1102 void wxNotebook::PositionSpinBtn() 
1108     m_spinbtn
->GetSize(&wBtn
, &hBtn
); 
1110     wxRect rectTabs 
= GetAllTabsRect(); 
1113     switch ( GetTabOrientation() ) 
1116             wxFAIL_MSG(_T("unknown tab orientation")); 
1120             x 
= rectTabs
.GetRight() - wBtn
; 
1121             y 
= rectTabs
.GetBottom() - hBtn
; 
1125             x 
= rectTabs
.GetRight() - wBtn
; 
1126             y 
= rectTabs
.GetTop(); 
1130             x 
= rectTabs
.GetRight() - wBtn
; 
1131             y 
= rectTabs
.GetBottom() - hBtn
; 
1135             x 
= rectTabs
.GetLeft(); 
1136             y 
= rectTabs
.GetBottom() - hBtn
; 
1140     m_spinbtn
->Move(x
, y
); 
1143 // ---------------------------------------------------------------------------- 
1144 // wxNotebook scrolling 
1145 // ---------------------------------------------------------------------------- 
1147 void wxNotebook::ScrollTo(int page
) 
1149     wxCHECK_RET( IS_VALID_PAGE(page
), _T("invalid notebook page") ); 
1151     // set the first visible tab and offset (easy) 
1152     m_firstVisible 
= (size_t)page
; 
1154     for ( size_t n 
= 0; n 
< m_firstVisible
; n
++ ) 
1156         m_offset 
+= GetTabWidth(n
); 
1159     // find the last visible tab too 
1160     CalcLastVisibleTab(); 
1165 void wxNotebook::ScrollLastTo(int page
) 
1167     wxCHECK_RET( IS_VALID_PAGE(page
), _T("invalid notebook page") ); 
1169     // go backwards until we find the first tab which can be made visible 
1170     // without hiding the given one 
1171     wxSize size 
= GetClientSize(); 
1172     wxCoord widthAll 
= IsVertical() ? size
.y 
: size
.x
, 
1173             widthTabs 
= GetTabWidth(page
); 
1175     // the total width is less than the width of the window if we have the spin 
1179         wxSize sizeSpinBtn 
= m_spinbtn
->GetSize(); 
1181             widthAll 
-= sizeSpinBtn
.y
; 
1183             widthAll 
-= sizeSpinBtn
.x
; 
1186     m_firstVisible 
= page
; 
1187     while ( (m_firstVisible 
> 0) && (widthTabs 
<= widthAll
) ) 
1189         widthTabs 
+= GetTabWidth(--m_firstVisible
); 
1192     if ( widthTabs 
> widthAll 
) 
1194         // take one step back (that it is forward) if we can 
1195         if ( m_firstVisible 
< (size_t)GetPageCount() - 1 ) 
1200     ScrollTo(m_firstVisible
); 
1202     // consitency check: the page we were asked to show should be shown 
1203     wxASSERT_MSG( (size_t)page 
< m_lastVisible
, _T("bug in ScrollLastTo") ); 
1206 // ---------------------------------------------------------------------------- 
1207 // wxNotebook sizing/moving 
1208 // ---------------------------------------------------------------------------- 
1210 wxSize 
wxNotebook::DoGetBestClientSize() const 
1212     // calculate the max page size 
1215     size_t count 
= GetPageCount(); 
1218         for ( size_t n 
= 0; n 
< count
; n
++ ) 
1220             wxSize sizePage 
= m_pages
[n
]->GetSize(); 
1222             if ( size
.x 
< sizePage
.x 
) 
1223                 size
.x 
= sizePage
.x
; 
1224             if ( size
.y 
< sizePage
.y 
) 
1225                 size
.y 
= sizePage
.y
; 
1230         // use some arbitrary default size 
1235     return GetSizeForPage(size
); 
1238 void wxNotebook::DoMoveWindow(int x
, int y
, int width
, int height
) 
1240     wxControl::DoMoveWindow(x
, y
, width
, height
); 
1242     // move the spin ctrl too (NOP if it doesn't exist) 
1246 void wxNotebook::DoSetSize(int x
, int y
, 
1247                            int width
, int height
, 
1250     wxControl::DoSetSize(x
, y
, width
, height
, sizeFlags
); 
1255 // ---------------------------------------------------------------------------- 
1256 // wxNotebook input processing 
1257 // ---------------------------------------------------------------------------- 
1259 bool wxNotebook::PerformAction(const wxControlAction
& action
, 
1261                                const wxString
& strArg
) 
1263     if ( action 
== wxACTION_NOTEBOOK_NEXT 
) 
1264         ChangePage(GetNextPage(TRUE
)); 
1265     else if ( action 
== wxACTION_NOTEBOOK_PREV 
) 
1266         ChangePage(GetNextPage(FALSE
)); 
1267     else if ( action 
== wxACTION_NOTEBOOK_GOTO 
) 
1268         ChangePage((int)numArg
); 
1270         return wxControl::PerformAction(action
, numArg
, strArg
); 
1275 // ---------------------------------------------------------------------------- 
1276 // wxStdNotebookInputHandler 
1277 // ---------------------------------------------------------------------------- 
1279 wxStdNotebookInputHandler::wxStdNotebookInputHandler(wxInputHandler 
*inphand
) 
1280                          : wxStdInputHandler(inphand
) 
1284 bool wxStdNotebookInputHandler::HandleKey(wxControl 
*control
, 
1285                                           const wxKeyEvent
& event
, 
1288     // ignore the key releases 
1291         wxNotebook 
*notebook 
= wxStaticCast(control
, wxNotebook
); 
1294         wxControlAction action
; 
1295         switch ( event
.GetKeyCode() ) 
1298                 if ( notebook
->IsVertical() ) 
1299                     action 
= wxACTION_NOTEBOOK_PREV
; 
1303                 if ( !notebook
->IsVertical() ) 
1304                     action 
= wxACTION_NOTEBOOK_PREV
; 
1308                 if ( notebook
->IsVertical() ) 
1309                     action 
= wxACTION_NOTEBOOK_NEXT
; 
1313                 if ( !notebook
->IsVertical() ) 
1314                     action 
= wxACTION_NOTEBOOK_NEXT
; 
1318                 action 
= wxACTION_NOTEBOOK_GOTO
; 
1319                 // page = 0; -- already has this value 
1323                 action 
= wxACTION_NOTEBOOK_GOTO
; 
1324                 page 
= notebook
->GetPageCount() - 1; 
1330             return control
->PerformAction(action
, page
); 
1334     return wxStdInputHandler::HandleKey(control
, event
, pressed
); 
1337 bool wxStdNotebookInputHandler::HandleMouse(wxControl 
*control
, 
1338                                             const wxMouseEvent
& event
) 
1340     if ( event
.ButtonDown(1) ) 
1342         wxNotebook 
*notebook 
= wxStaticCast(control
, wxNotebook
); 
1343         int page 
= notebook
->HitTest(event
.GetPosition()); 
1346             control
->PerformAction(wxACTION_NOTEBOOK_GOTO
, page
); 
1352     return wxStdInputHandler::HandleMouse(control
, event
); 
1355 bool wxStdNotebookInputHandler::HandleMouseMove(wxControl 
*control
, 
1356                                                 const wxMouseEvent
& event
) 
1358     return wxStdInputHandler::HandleMouseMove(control
, event
); 
1361 bool wxStdNotebookInputHandler::HandleFocus(wxControl 
*control
, 
1362                                             const wxFocusEvent
& event
) 
1364     HandleFocusChange(control
); 
1369 bool wxStdNotebookInputHandler::HandleActivation(wxControl 
*control
, 
1370                                                  bool WXUNUSED(activated
)) 
1372     // we react to the focus change in the same way as to the [de]activation 
1373     HandleFocusChange(control
); 
1378 void wxStdNotebookInputHandler::HandleFocusChange(wxControl 
*control
) 
1380     wxNotebook 
*notebook 
= wxStaticCast(control
, wxNotebook
); 
1381     notebook
->RefreshCurrent(); 
1384 #endif // wxUSE_NOTEBOOK