1 /////////////////////////////////////////////////////////////////////////////// 
   3 // Purpose:     implementation of wxNotebook 
   4 // Author:      Stefan Csomor 
   8 // Copyright:   (c) Stefan Csomor 
   9 // Licence:     wxWindows licence 
  10 /////////////////////////////////////////////////////////////////////////////// 
  13 #pragma implementation "notebook.h" 
  16 // ============================================================================ 
  18 // ============================================================================ 
  20 // ---------------------------------------------------------------------------- 
  22 // ---------------------------------------------------------------------------- 
  24 #include "wx/string.h" 
  26 #include "wx/imaglist.h" 
  28 #include "wx/notebook.h" 
  29 #include "wx/mac/uma.h" 
  30 // ---------------------------------------------------------------------------- 
  32 // ---------------------------------------------------------------------------- 
  34 // check that the page index is valid 
  35 #define IS_VALID_PAGE(nPage) ((nPage) < GetPageCount()) 
  38 // ---------------------------------------------------------------------------- 
  40 // ---------------------------------------------------------------------------- 
  42 DEFINE_EVENT_TYPE(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
) 
  43 DEFINE_EVENT_TYPE(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
) 
  45 BEGIN_EVENT_TABLE(wxNotebook
, wxControl
) 
  46     EVT_NOTEBOOK_PAGE_CHANGED(-1, wxNotebook::OnSelChange
) 
  47     EVT_MOUSE_EVENTS(wxNotebook::OnMouse
) 
  49     EVT_SIZE(wxNotebook::OnSize
) 
  50     EVT_SET_FOCUS(wxNotebook::OnSetFocus
) 
  51     EVT_NAVIGATION_KEY(wxNotebook::OnNavigationKey
) 
  54 IMPLEMENT_DYNAMIC_CLASS(wxNotebook
, wxControl
) 
  55 IMPLEMENT_DYNAMIC_CLASS(wxNotebookEvent
, wxCommandEvent
) 
  57 // ============================================================================ 
  59 // ============================================================================ 
  61 // The Appearance Manager docs show using tab controls in either edge to edge 
  62 // mode, or inset.  I think edge to edge conforms better to the other ports, 
  63 // and inset mode is better accomplished with space around the wxNotebook rather 
  64 // than within it.    --Robin 
  66 // CS : had to switch off tight spacing due to 10.3 problems 
  67 #define wxMAC_EDGE_TO_EDGE 0 
  69 static inline int wxMacTabMargin(long nbStyle
, long side
) 
  71     static int tabMargin 
= -1; 
  72     static int otherMargin 
= -1; 
  76         if ( UMAHasAquaLayout() ) 
  78             tabMargin 
= 26;    // From Appearance Manager docs for small tab control dimensions 
  79 #if wxMAC_EDGE_TO_EDGE 
  83             // JACS - this seems fine on 10.3; 20 is way too much 
  90 #if wxMAC_EDGE_TO_EDGE 
  98     // If the style matches the side asked for then return the tab margin, 
  99     // but we have to special case wxNB_TOP since it is zero... 
 100     if ( side 
== wxNB_TOP
) 
 102         if ( nbStyle 
!= 0 && nbStyle 
& (wxNB_LEFT
|wxNB_RIGHT
|wxNB_BOTTOM
)) 
 111     else if ( nbStyle 
& side
) 
 117 static inline int wxMacTabLeftMargin(long style
) 
 119     return wxMacTabMargin(style
, wxNB_LEFT
); 
 122 static inline int wxMacTabTopMargin(long style
) 
 124     return wxMacTabMargin(style
, wxNB_TOP
); 
 127 static inline int wxMacTabRightMargin(long style
) 
 129     return wxMacTabMargin(style
, wxNB_RIGHT
); 
 132 static inline int wxMacTabBottomMargin(long style
) 
 134     return wxMacTabMargin(style
, wxNB_BOTTOM
); 
 137 // ---------------------------------------------------------------------------- 
 138 // wxNotebook construction 
 139 // ---------------------------------------------------------------------------- 
 141 // common part of all ctors 
 142 void wxNotebook::Init() 
 144     if ( UMAHasAquaLayout() ) 
 146         // Should these depend on wxMAC_EDGE_TO_EDGE too? 
 147         m_macHorizontalBorder 
= 7; 
 148         m_macVerticalBorder 
= 8; 
 154 // default for dynamic class 
 155 wxNotebook::wxNotebook() 
 160 // the same arguments as for wxControl 
 161 wxNotebook::wxNotebook(wxWindow 
*parent
, 
 166                        const wxString
& name
) 
 170     Create(parent
, id
, pos
, size
, style
, name
); 
 174 bool wxNotebook::Create(wxWindow 
*parent
, 
 179                         const wxString
& name
) 
 181     if ( !wxNotebookBase::Create(parent
, id
, pos
, size
, style
, name
) ) 
 187     MacPreControlCreate( parent 
, id 
,  wxEmptyString 
, pos 
, size 
,style
, wxDefaultValidator 
, name 
, &bounds 
, title 
) ; 
 189     int tabstyle 
= kControlTabSmallNorthProc 
; 
 190     if ( HasFlag(wxNB_LEFT
) ) 
 191         tabstyle 
= kControlTabSmallWestProc 
; 
 192     else if ( HasFlag( wxNB_RIGHT 
) ) 
 193         tabstyle 
= kControlTabSmallEastProc 
; 
 194     else if ( HasFlag( wxNB_BOTTOM 
) ) 
 195         tabstyle 
= kControlTabSmallSouthProc 
; 
 198     m_macControl 
= (WXWidget
) ::NewControl( MAC_WXHWND(parent
->MacGetRootWindow()) , &bounds 
, title 
, false , 0 , 0 , 1, 
 199         tabstyle 
, (long) this ) ; 
 201     MacPostControlCreate() ; 
 206 wxNotebook::~wxNotebook() 
 210 // ---------------------------------------------------------------------------- 
 211 // wxNotebook accessors 
 212 // ---------------------------------------------------------------------------- 
 214 void wxNotebook::SetPadding(const wxSize
& padding
) 
 219 void wxNotebook::SetTabSize(const wxSize
& sz
) 
 224 void wxNotebook::SetPageSize(const wxSize
& size
) 
 226     SetSize( CalcSizeFromPage( size 
) ); 
 229 wxSize 
wxNotebook::CalcSizeFromPage(const wxSize
& sizePage
) const 
 231     wxSize sizeTotal 
= sizePage
; 
 232     sizeTotal
.x 
+= 2 * m_macHorizontalBorder 
+ wxMacTabLeftMargin(GetWindowStyle()) +  
 233         wxMacTabRightMargin(GetWindowStyle()) ; 
 234     sizeTotal
.y 
+= 2 * m_macVerticalBorder 
+ wxMacTabTopMargin(GetWindowStyle()) +  
 235         wxMacTabBottomMargin(GetWindowStyle()) ; 
 240 wxSize 
wxNotebook::DoGetBestSize() const 
 242     // calculate the max page size 
 245     size_t count 
= GetPageCount(); 
 248         for ( size_t n 
= 0; n 
< count
; n
++ ) 
 250             wxSize sizePage 
= m_pages
[n
]->GetSize(); 
 252             if ( size
.x 
< sizePage
.x 
) 
 254             if ( size
.y 
< sizePage
.y 
) 
 260         // use some arbitrary default size 
 265     return CalcSizeFromPage(size
); 
 268 int wxNotebook::SetSelection(size_t nPage
) 
 270     wxCHECK_MSG( IS_VALID_PAGE(nPage
), -1, wxT("notebook page out of range") ); 
 272     if ( int(nPage
) != m_nSelection 
) 
 274         wxNotebookEvent 
event(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
, m_windowId
); 
 275         event
.SetSelection(nPage
); 
 276         event
.SetOldSelection(m_nSelection
); 
 277         event
.SetEventObject(this); 
 278         if ( !GetEventHandler()->ProcessEvent(event
) || event
.IsAllowed() ) 
 280             // program allows the page change 
 281             event
.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
); 
 282             (void)GetEventHandler()->ProcessEvent(event
); 
 284             ChangePage(m_nSelection
, nPage
); 
 291 bool wxNotebook::SetPageText(size_t nPage
, const wxString
& strText
) 
 293     wxASSERT( IS_VALID_PAGE(nPage
) ); 
 295     wxNotebookPage 
*page 
= m_pages
[nPage
]; 
 296     page
->SetLabel(strText
); 
 302 wxString 
wxNotebook::GetPageText(size_t nPage
) const 
 304     wxASSERT( IS_VALID_PAGE(nPage
) ); 
 306     wxNotebookPage 
*page 
= m_pages
[nPage
]; 
 307     return page
->GetLabel(); 
 310 int wxNotebook::GetPageImage(size_t nPage
) const 
 312     wxCHECK_MSG( IS_VALID_PAGE(nPage
), -1, _T("invalid notebook page") ); 
 314     return m_images
[nPage
]; 
 317 bool wxNotebook::SetPageImage(size_t nPage
, int nImage
) 
 319     wxCHECK_MSG( IS_VALID_PAGE(nPage
), FALSE
, _T("invalid notebook page") ); 
 321     wxCHECK_MSG( m_imageList 
&& nImage 
< m_imageList
->GetImageCount(), FALSE
, 
 322         _T("invalid image index in SetPageImage()") ); 
 324     if ( nImage 
!= m_images
[nPage
] ) 
 326         // if the item didn't have an icon before or, on the contrary, did have 
 327         // it but has lost it now, its size will change - but if the icon just 
 329         m_images
[nPage
] = nImage
; 
 337 // ---------------------------------------------------------------------------- 
 338 // wxNotebook operations 
 339 // ---------------------------------------------------------------------------- 
 341 // remove one page from the notebook, without deleting the window 
 342 wxNotebookPage
* wxNotebook::DoRemovePage(size_t nPage
) 
 344     wxCHECK( IS_VALID_PAGE(nPage
), NULL 
); 
 345     wxNotebookPage
* page 
= m_pages
[nPage
] ; 
 346     m_pages
.RemoveAt(nPage
); 
 350     if(m_nSelection 
>= (int)GetPageCount()) { 
 351         m_nSelection 
= GetPageCount() - 1; 
 353     if(m_nSelection 
>= 0) { 
 354         m_pages
[m_nSelection
]->Show(true); 
 356     InvalidateBestSize(); 
 361 bool wxNotebook::DeleteAllPages() 
 363     WX_CLEAR_ARRAY(m_pages
) ; 
 366     InvalidateBestSize(); 
 371 // same as AddPage() but does it at given position 
 372 bool wxNotebook::InsertPage(size_t nPage
, 
 373                             wxNotebookPage 
*pPage
, 
 374                             const wxString
& strText
, 
 378     if ( !wxNotebookBase::InsertPage(nPage
, pPage
, strText
, bSelect
, imageId
) ) 
 381     wxASSERT_MSG( pPage
->GetParent() == this, 
 382                     _T("notebook pages must have notebook as parent") ); 
 384     // don't show pages by default (we'll need to adjust their size first) 
 385     pPage
->Show( false ) ; 
 387     pPage
->SetLabel(strText
); 
 389     m_images
.Insert(imageId
, nPage
); 
 393     wxRect rect 
= GetPageRect() ; 
 394     pPage
->SetSize(rect
); 
 395     if ( pPage
->GetAutoLayout() ) { 
 400     // now deal with the selection 
 401     // --------------------------- 
 403     // if the inserted page is before the selected one, we must update the 
 404     // index of the selected page 
 406     if ( int(nPage
) <= m_nSelection 
)  
 409         // while this still is the same page showing, we need to update the tabs 
 410         SetControl32BitValue( (ControlHandle
) m_macControl 
, m_nSelection 
+ 1 ) ; 
 413     // some page should be selected: either this one or the first one if there 
 414     // is still no selection 
 418     else if ( m_nSelection 
== -1 ) 
 422         SetSelection(selNew
); 
 424     InvalidateBestSize(); 
 428 /* Added by Mark Newsam 
 429 * When a page is added or deleted to the notebook this function updates 
 430 * information held in the m_macControl so that it matches the order 
 431 * the user would expect. 
 433 void wxNotebook::MacSetupTabs() 
 435     SetControl32BitMaximum( (ControlHandle
) m_macControl 
, GetPageCount() ) ; 
 437     wxNotebookPage 
*page
; 
 438     ControlTabInfoRec info
; 
 440     const size_t countPages 
= GetPageCount(); 
 441     for(size_t ii 
= 0; ii 
< countPages
; ii
++) 
 445         info
.iconSuiteID 
= 0; 
 446         wxMacStringToPascal( page
->GetLabel() , info
.name 
) ; 
 448         SetControlData( (ControlHandle
) m_macControl
, ii
+1, kControlTabInfoTag
, 
 449             sizeof( ControlTabInfoRec
) , (char*) &info 
) ; 
 450         SetTabEnabled( (ControlHandle
) m_macControl 
, ii
+1 , true ) ; 
 452         if ( GetImageList() && GetPageImage(ii
) >= 0 && UMAGetSystemVersion() >= 0x1020 ) 
 454             // tab controls only support very specific types of images, therefore we are doing an odyssee 
 455             // accross the icon worlds (even Apple DTS did not find a shorter path) 
 456             // in order not to pollute the icon registry we put every icon into (OSType) 1 and immediately 
 457             // afterwards Unregister it (IconRef is ref counted, so it will stay on the tab even if we 
 458             // unregister it) in case this will ever lead to having the same icon everywhere add some kind 
 460             const wxBitmap
* bmap 
= GetImageList()->GetBitmapPtr( GetPageImage(ii 
) ) ; 
 463                 wxBitmap scaledBitmap 
; 
 464                 if ( bmap
->GetWidth() != 16 || bmap
->GetHeight() != 16 ) 
 466                     scaledBitmap 
= wxBitmap( bmap
->ConvertToImage().Scale(16,16) ) ; 
 467                     bmap 
= &scaledBitmap 
; 
 469                 ControlButtonContentInfo info 
; 
 470                 wxMacCreateBitmapButton( &info 
, *bmap 
, kControlContentPictHandle
) ; 
 471                 IconFamilyHandle iconFamily 
= (IconFamilyHandle
) NewHandle(0) ; 
 472                 OSErr err 
= SetIconFamilyData( iconFamily
, 'PICT' , (Handle
) info
.u
.picture 
) ; 
 473                 wxASSERT_MSG( err 
== noErr 
, wxT("Error when adding bitmap") ) ; 
 475                 err 
= RegisterIconRefFromIconFamily( 'WXNG' , (OSType
) 1, iconFamily
, &iconRef 
) ; 
 476                 wxASSERT_MSG( err 
== noErr 
, wxT("Error when adding bitmap") ) ; 
 477                 info
.contentType 
= kControlContentIconRef 
; 
 478                 info
.u
.iconRef 
= iconRef 
; 
 479                 SetControlData( (ControlHandle
) m_macControl
, ii
+1,kControlTabImageContentTag
, 
 480                     sizeof( info 
), (Ptr
)&info 
); 
 481                 wxASSERT_MSG( err 
== noErr 
, wxT("Error when setting icon on tab") ) ; 
 482                 if ( UMAGetSystemVersion() < 0x1030 ) 
 484                         UnregisterIconRef( 'WXNG' , (OSType
) 1 ) ; 
 487                 ReleaseIconRef( iconRef 
) ; 
 488                 DisposeHandle( (Handle
) iconFamily 
) ; 
 494     GetControlBounds((ControlHandle
)m_macControl
, &bounds
); 
 495     InvalWindowRect((WindowRef
)MacGetRootWindow(), &bounds
); 
 498 wxRect 
wxNotebook::GetPageRect() const 
 500     // fit the notebook page to the tab control's display area 
 505         wxMacTabLeftMargin(GetWindowStyle()) + m_macHorizontalBorder
,  
 506         wxMacTabTopMargin(GetWindowStyle()) + m_macVerticalBorder
, 
 507         w 
- wxMacTabLeftMargin(GetWindowStyle()) - wxMacTabRightMargin(GetWindowStyle()) - 2*m_macHorizontalBorder
, 
 508         h 
- wxMacTabTopMargin(GetWindowStyle()) - wxMacTabBottomMargin(GetWindowStyle()) - 2*m_macVerticalBorder
);     
 510 // ---------------------------------------------------------------------------- 
 511 // wxNotebook callbacks 
 512 // ---------------------------------------------------------------------------- 
 514 // @@@ OnSize() is used for setting the font when it's called for the first 
 515 //     time because doing it in ::Create() doesn't work (for unknown reasons) 
 516 void wxNotebook::OnSize(wxSizeEvent
& event
) 
 519     unsigned int nCount 
= m_pages
.Count(); 
 520     wxRect rect 
= GetPageRect() ; 
 521     for ( unsigned int nPage 
= 0; nPage 
< nCount
; nPage
++ ) { 
 522         wxNotebookPage 
*pPage 
= m_pages
[nPage
]; 
 523         pPage
->SetSize(rect
); 
 524         if ( pPage
->GetAutoLayout() ) { 
 529     // Processing continues to next OnSize 
 533 void wxNotebook::OnSelChange(wxNotebookEvent
& event
) 
 535     // is it our tab control? 
 536     if ( event
.GetEventObject() == this ) 
 537         ChangePage(event
.GetOldSelection(), event
.GetSelection()); 
 539     // we want to give others a chance to process this message as well 
 543 void wxNotebook::OnSetFocus(wxFocusEvent
& event
) 
 545     // set focus to the currently selected page if any 
 546     if ( m_nSelection 
!= -1 ) 
 547         m_pages
[m_nSelection
]->SetFocus(); 
 552 void wxNotebook::OnNavigationKey(wxNavigationKeyEvent
& event
) 
 554     if ( event
.IsWindowChange() ) { 
 556         AdvanceSelection(event
.GetDirection()); 
 559         // we get this event in 2 cases 
 561         // a) one of our pages might have generated it because the user TABbed 
 562         // out from it in which case we should propagate the event upwards and 
 563         // our parent will take care of setting the focus to prev/next sibling 
 567         // b) the parent panel wants to give the focus to us so that we 
 568         // forward it to our selected page. We can't deal with this in 
 569         // OnSetFocus() because we don't know which direction the focus came 
 570         // from in this case and so can't choose between setting the focus to 
 571         // first or last panel child 
 572         wxWindow 
*parent 
= GetParent(); 
 573         // the cast is here to fic a GCC ICE 
 574         if ( ((wxWindow
*)event
.GetEventObject()) == parent 
) 
 576             // no, it doesn't come from child, case (b): forward to a page 
 577             if ( m_nSelection 
!= -1 ) 
 579                 // so that the page knows that the event comes from it's parent 
 580                 // and is being propagated downwards 
 581                 event
.SetEventObject(this); 
 583                 wxWindow 
*page 
= m_pages
[m_nSelection
]; 
 584                 if ( !page
->GetEventHandler()->ProcessEvent(event
) ) 
 588                 //else: page manages focus inside it itself 
 592                 // we have no pages - still have to give focus to _something_ 
 598             // it comes from our child, case (a), pass to the parent 
 600                 event
.SetCurrentFocus(this); 
 601                 parent
->GetEventHandler()->ProcessEvent(event
); 
 607 // ---------------------------------------------------------------------------- 
 608 // wxNotebook base class virtuals 
 609 // ---------------------------------------------------------------------------- 
 611 #if wxUSE_CONSTRAINTS 
 613 // override these 2 functions to do nothing: everything is done in OnSize 
 615 void wxNotebook::SetConstraintSizes(bool WXUNUSED(recurse
)) 
 617   // don't set the sizes of the pages - their correct size is not yet known 
 618   wxControl::SetConstraintSizes(FALSE
); 
 621 bool wxNotebook::DoPhase(int WXUNUSED(nPhase
)) 
 626 #endif // wxUSE_CONSTRAINTS 
 628 void wxNotebook::Command(wxCommandEvent
& event
) 
 630     wxFAIL_MSG(wxT("wxNotebook::Command not implemented")); 
 633 // ---------------------------------------------------------------------------- 
 634 // wxNotebook helper functions 
 635 // ---------------------------------------------------------------------------- 
 637 // hide the currently active panel and show the new one 
 638 void wxNotebook::ChangePage(int nOldSel
, int nSel
) 
 642         m_pages
[nOldSel
]->Show(FALSE
); 
 647         wxNotebookPage 
*pPage 
= m_pages
[nSel
]; 
 653     SetControl32BitValue( (ControlHandle
) m_macControl 
, m_nSelection 
+ 1 ) ; 
 657 void  wxNotebook::OnMouse( wxMouseEvent 
&event 
) 
 659     if ( (ControlHandle
) m_macControl 
== NULL 
) 
 665     if (event
.GetEventType() == wxEVT_LEFT_DOWN 
|| event
.GetEventType() == wxEVT_LEFT_DCLICK 
) 
 670         MacClientToRootWindow( &x 
, &y 
) ; 
 672         ControlHandle   control 
; 
 681         if ( !event
.m_leftDown 
&& !event
.m_rightDown 
) 
 682             modifiers  
|= btnState 
; 
 684         if ( event
.m_shiftDown 
) 
 685             modifiers 
|= shiftKey 
; 
 687         if ( event
.m_controlDown 
) 
 688             modifiers 
|= controlKey 
; 
 690         if ( event
.m_altDown 
) 
 691             modifiers 
|= optionKey 
; 
 693         if ( event
.m_metaDown 
) 
 694             modifiers 
|= cmdKey 
; 
 696         control 
= (ControlHandle
) m_macControl 
; 
 697         if ( control 
&& ::IsControlActive( control 
) ) 
 700                 wxNotebookEvent 
changing(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
, m_windowId
, 
 701                     ::GetControl32BitValue(control
) - 1, m_nSelection
); 
 702                 changing
.SetEventObject(this); 
 703                 GetEventHandler()->ProcessEvent(changing
); 
 705                 if(changing
.IsAllowed()) 
 707                     controlpart 
= ::HandleControlClick(control
, localwhere
, modifiers
, 
 708                         (ControlActionUPP
) -1); 
 709                     wxTheApp
->s_lastMouseDown 
= 0 ; 
 711                     wxNotebookEvent 
event(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
, m_windowId
, 
 712                         ::GetControl32BitValue(control
) - 1, m_nSelection
); 
 713                     event
.SetEventObject(this); 
 715                     GetEventHandler()->ProcessEvent(event
); 
 723 void wxNotebook::MacHandleControlClick( WXWidget control 
, wxInt16 controlpart 
, bool WXUNUSED( mouseStillDown 
) ) 
 726     wxNotebookEvent 
event(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
, m_windowId 
, ::GetControl32BitValue((ControlHandle
)m_macControl
) - 1, m_nSelection
); 
 727     event
.SetEventObject(this);