1 /////////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/mac/classic/notebmac.cpp 
   3 // Purpose:     implementation of wxNotebook 
   4 // Author:      Stefan Csomor 
   8 // Copyright:   (c) Stefan Csomor 
   9 // Licence:     wxWindows licence 
  10 /////////////////////////////////////////////////////////////////////////////// 
  12 #include "wx/wxprec.h" 
  14 // ============================================================================ 
  16 // ============================================================================ 
  18 // ---------------------------------------------------------------------------- 
  20 // ---------------------------------------------------------------------------- 
  22 #include "wx/notebook.h" 
  25     #include "wx/string.h" 
  30 #include "wx/imaglist.h" 
  32 #include "wx/mac/uma.h" 
  33 // ---------------------------------------------------------------------------- 
  35 // ---------------------------------------------------------------------------- 
  37 // check that the page index is valid 
  38 #define IS_VALID_PAGE(nPage) ((nPage) < GetPageCount()) 
  41 // ---------------------------------------------------------------------------- 
  43 // ---------------------------------------------------------------------------- 
  45 DEFINE_EVENT_TYPE(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
) 
  46 DEFINE_EVENT_TYPE(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
) 
  48 BEGIN_EVENT_TABLE(wxNotebook
, wxControl
) 
  49     EVT_NOTEBOOK_PAGE_CHANGED(wxID_ANY
, wxNotebook::OnSelChange
) 
  50     EVT_MOUSE_EVENTS(wxNotebook::OnMouse
) 
  52     EVT_SIZE(wxNotebook::OnSize
) 
  53     EVT_SET_FOCUS(wxNotebook::OnSetFocus
) 
  54     EVT_NAVIGATION_KEY(wxNotebook::OnNavigationKey
) 
  57 IMPLEMENT_DYNAMIC_CLASS(wxNotebook
, wxControl
) 
  58 IMPLEMENT_DYNAMIC_CLASS(wxNotebookEvent
, wxCommandEvent
) 
  60 // ============================================================================ 
  62 // ============================================================================ 
  64 // The Appearance Manager docs show using tab controls in either edge to edge 
  65 // mode, or inset.  I think edge to edge conforms better to the other ports, 
  66 // and inset mode is better accomplished with space around the wxNotebook rather 
  67 // than within it.    --Robin 
  69 // CS : had to switch off tight spacing due to 10.3 problems 
  70 #define wxMAC_EDGE_TO_EDGE 0 
  72 static inline int wxMacTabMargin(long nbStyle
, long side
) 
  74     static int tabMargin 
= -1; 
  75     static int otherMargin 
= -1; 
  79         if ( UMAHasAquaLayout() ) 
  81             tabMargin 
= 26;    // From Appearance Manager docs for small tab control dimensions 
  82 #if wxMAC_EDGE_TO_EDGE 
  86             // JACS - this seems fine on 10.3; 20 is way too much 
  93 #if wxMAC_EDGE_TO_EDGE 
 101     // If the style matches the side asked for then return the tab margin, 
 102     // but we have to special case wxBK_TOP since it is zero... 
 103     if ( side 
== wxBK_TOP
) 
 105         if ( nbStyle 
!= 0 && nbStyle 
& (wxBK_LEFT
|wxBK_RIGHT
|wxBK_BOTTOM
)) 
 114     else if ( nbStyle 
& side
) 
 120 static inline int wxMacTabLeftMargin(long style
) 
 122     return wxMacTabMargin(style
, wxBK_LEFT
); 
 125 static inline int wxMacTabTopMargin(long style
) 
 127     return wxMacTabMargin(style
, wxBK_TOP
); 
 130 static inline int wxMacTabRightMargin(long style
) 
 132     return wxMacTabMargin(style
, wxBK_RIGHT
); 
 135 static inline int wxMacTabBottomMargin(long style
) 
 137     return wxMacTabMargin(style
, wxBK_BOTTOM
); 
 140 // ---------------------------------------------------------------------------- 
 141 // wxNotebook construction 
 142 // ---------------------------------------------------------------------------- 
 144 // common part of all ctors 
 145 void wxNotebook::Init() 
 147     if ( UMAHasAquaLayout() ) 
 149         // Should these depend on wxMAC_EDGE_TO_EDGE too? 
 150         m_macHorizontalBorder 
= 7; 
 151         m_macVerticalBorder 
= 8; 
 157 // default for dynamic class 
 158 wxNotebook::wxNotebook() 
 163 // the same arguments as for wxControl 
 164 wxNotebook::wxNotebook(wxWindow 
*parent
, 
 169                        const wxString
& name
) 
 173     Create(parent
, id
, pos
, size
, style
, name
); 
 177 bool wxNotebook::Create(wxWindow 
*parent
, 
 182                         const wxString
& name
) 
 184     if ( !wxNotebookBase::Create(parent
, id
, pos
, size
, style
, name
) ) 
 190     if ( (style 
& wxBK_ALIGN_MASK
) == wxBK_DEFAULT 
) 
 193     MacPreControlCreate( parent 
, id 
,  wxEmptyString 
, pos 
, size 
,style
, wxDefaultValidator 
, name 
, &bounds 
, title 
) ; 
 195     int tabstyle 
= kControlTabSmallNorthProc 
; 
 196     if ( HasFlag(wxBK_LEFT
) ) 
 197         tabstyle 
= kControlTabSmallWestProc 
; 
 198     else if ( HasFlag( wxBK_RIGHT 
) ) 
 199         tabstyle 
= kControlTabSmallEastProc 
; 
 200     else if ( HasFlag( wxBK_BOTTOM 
) ) 
 201         tabstyle 
= kControlTabSmallSouthProc 
; 
 204     m_macControl 
= (WXWidget
) ::NewControl( MAC_WXHWND(parent
->MacGetRootWindow()) , &bounds 
, title 
, false , 0 , 0 , 1, 
 205         tabstyle 
, (long) this ) ; 
 207     MacPostControlCreate() ; 
 212 wxNotebook::~wxNotebook() 
 216 // ---------------------------------------------------------------------------- 
 217 // wxNotebook accessors 
 218 // ---------------------------------------------------------------------------- 
 220 void wxNotebook::SetPadding(const wxSize
& padding
) 
 225 void wxNotebook::SetTabSize(const wxSize
& sz
) 
 230 void wxNotebook::SetPageSize(const wxSize
& size
) 
 232     SetSize( CalcSizeFromPage( size 
) ); 
 235 wxSize 
wxNotebook::CalcSizeFromPage(const wxSize
& sizePage
) const 
 237     wxSize sizeTotal 
= sizePage
; 
 238     sizeTotal
.x 
+= 2 * m_macHorizontalBorder 
+ wxMacTabLeftMargin(GetWindowStyle()) + 
 239         wxMacTabRightMargin(GetWindowStyle()) ; 
 240     sizeTotal
.y 
+= 2 * m_macVerticalBorder 
+ wxMacTabTopMargin(GetWindowStyle()) + 
 241         wxMacTabBottomMargin(GetWindowStyle()) ; 
 246 wxSize 
wxNotebook::DoGetBestSize() const 
 248     // calculate the max page size 
 251     size_t count 
= GetPageCount(); 
 254         for ( size_t n 
= 0; n 
< count
; n
++ ) 
 256             wxSize sizePage 
= m_pages
[n
]->GetSize(); 
 258             if ( size
.x 
< sizePage
.x 
) 
 260             if ( size
.y 
< sizePage
.y 
) 
 266         // use some arbitrary default size 
 271     return CalcSizeFromPage(size
); 
 274 int wxNotebook::SetSelection(size_t nPage
) 
 276     wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxNOT_FOUND
, wxT("notebook page out of range") ); 
 278     if ( int(nPage
) != m_nSelection 
) 
 280         wxNotebookEvent 
event(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
, m_windowId
); 
 281         event
.SetSelection(nPage
); 
 282         event
.SetOldSelection(m_nSelection
); 
 283         event
.SetEventObject(this); 
 284         if ( !GetEventHandler()->ProcessEvent(event
) || event
.IsAllowed() ) 
 286             // program allows the page change 
 287             event
.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
); 
 288             (void)GetEventHandler()->ProcessEvent(event
); 
 290             ChangePage(m_nSelection
, nPage
); 
 297 bool wxNotebook::SetPageText(size_t nPage
, const wxString
& strText
) 
 299     wxASSERT( IS_VALID_PAGE(nPage
) ); 
 301     wxNotebookPage 
*page 
= m_pages
[nPage
]; 
 302     page
->SetLabel(strText
); 
 308 wxString 
wxNotebook::GetPageText(size_t nPage
) const 
 310     wxASSERT( IS_VALID_PAGE(nPage
) ); 
 312     wxNotebookPage 
*page 
= m_pages
[nPage
]; 
 313     return page
->GetLabel(); 
 316 int wxNotebook::GetPageImage(size_t nPage
) const 
 318     wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxNOT_FOUND
, _T("invalid notebook page") ); 
 320     return m_images
[nPage
]; 
 323 bool wxNotebook::SetPageImage(size_t nPage
, int nImage
) 
 325     wxCHECK_MSG( IS_VALID_PAGE(nPage
), false, _T("invalid notebook page") ); 
 327     wxCHECK_MSG( m_imageList 
&& nImage 
< m_imageList
->GetImageCount(), false, 
 328         _T("invalid image index in SetPageImage()") ); 
 330     if ( nImage 
!= m_images
[nPage
] ) 
 332         // if the item didn't have an icon before or, on the contrary, did have 
 333         // it but has lost it now, its size will change - but if the icon just 
 335         m_images
[nPage
] = nImage
; 
 343 // ---------------------------------------------------------------------------- 
 344 // wxNotebook operations 
 345 // ---------------------------------------------------------------------------- 
 347 // remove one page from the notebook, without deleting the window 
 348 wxNotebookPage
* wxNotebook::DoRemovePage(size_t nPage
) 
 350     wxCHECK( IS_VALID_PAGE(nPage
), NULL 
); 
 351     wxNotebookPage
* page 
= m_pages
[nPage
] ; 
 352     m_pages
.RemoveAt(nPage
); 
 356     if(m_nSelection 
>= (int)GetPageCount()) { 
 357         m_nSelection 
= GetPageCount() - 1; 
 359     if(m_nSelection 
>= 0) { 
 360         m_pages
[m_nSelection
]->Show(true); 
 362     InvalidateBestSize(); 
 367 bool wxNotebook::DeleteAllPages() 
 369     WX_CLEAR_ARRAY(m_pages
) ; 
 372     InvalidateBestSize(); 
 377 // same as AddPage() but does it at given position 
 378 bool wxNotebook::InsertPage(size_t nPage
, 
 379                             wxNotebookPage 
*pPage
, 
 380                             const wxString
& strText
, 
 384     if ( !wxNotebookBase::InsertPage(nPage
, pPage
, strText
, bSelect
, imageId
) ) 
 387     wxASSERT_MSG( pPage
->GetParent() == this, 
 388                     _T("notebook pages must have notebook as parent") ); 
 390     // don't show pages by default (we'll need to adjust their size first) 
 391     pPage
->Show( false ) ; 
 393     pPage
->SetLabel(strText
); 
 395     m_images
.Insert(imageId
, nPage
); 
 399     wxRect rect 
= GetPageRect() ; 
 400     pPage
->SetSize(rect
); 
 401     if ( pPage
->GetAutoLayout() ) { 
 406     // now deal with the selection 
 407     // --------------------------- 
 409     // if the inserted page is before the selected one, we must update the 
 410     // index of the selected page 
 412     if ( int(nPage
) <= m_nSelection 
) 
 415         // while this still is the same page showing, we need to update the tabs 
 416         SetControl32BitValue( (ControlHandle
) m_macControl 
, m_nSelection 
+ 1 ) ; 
 419     // some page should be selected: either this one or the first one if there 
 420     // is still no selection 
 424     else if ( m_nSelection 
== -1 ) 
 428         SetSelection(selNew
); 
 430     InvalidateBestSize(); 
 434 /* Added by Mark Newsam 
 435 * When a page is added or deleted to the notebook this function updates 
 436 * information held in the m_macControl so that it matches the order 
 437 * the user would expect. 
 439 void wxNotebook::MacSetupTabs() 
 441     SetControl32BitMaximum( (ControlHandle
) m_macControl 
, GetPageCount() ) ; 
 443     wxNotebookPage 
*page
; 
 444     ControlTabInfoRec info
; 
 446     const size_t countPages 
= GetPageCount(); 
 447     for(size_t ii 
= 0; ii 
< countPages
; ii
++) 
 451         info
.iconSuiteID 
= 0; 
 452         wxMacStringToPascal( page
->GetLabel() , info
.name 
) ; 
 454         SetControlData( (ControlHandle
) m_macControl
, ii
+1, kControlTabInfoTag
, 
 455             sizeof( ControlTabInfoRec
) , (char*) &info 
) ; 
 456         SetTabEnabled( (ControlHandle
) m_macControl 
, ii
+1 , true ) ; 
 458         if ( GetImageList() && GetPageImage(ii
) >= 0 && UMAGetSystemVersion() >= 0x1020 ) 
 460             // tab controls only support very specific types of images, therefore we are doing an odyssee 
 461             // accross the icon worlds (even Apple DTS did not find a shorter path) 
 462             // in order not to pollute the icon registry we put every icon into (OSType) 1 and immediately 
 463             // afterwards Unregister it (IconRef is ref counted, so it will stay on the tab even if we 
 464             // unregister it) in case this will ever lead to having the same icon everywhere add some kind 
 466             const wxBitmap
* bmap 
= GetImageList()->GetBitmapPtr( GetPageImage(ii 
) ) ; 
 469                 wxBitmap scaledBitmap 
; 
 470                 if ( bmap
->GetWidth() != 16 || bmap
->GetHeight() != 16 ) 
 472                     scaledBitmap 
= wxBitmap( bmap
->ConvertToImage().Scale(16,16) ) ; 
 473                     bmap 
= &scaledBitmap 
; 
 475                 ControlButtonContentInfo info 
; 
 476                 wxMacCreateBitmapButton( &info 
, *bmap 
, kControlContentPictHandle
) ; 
 477                 IconFamilyHandle iconFamily 
= (IconFamilyHandle
) NewHandle(0) ; 
 478                 OSErr err 
= SetIconFamilyData( iconFamily
, 'PICT' , (Handle
) info
.u
.picture 
) ; 
 479                 wxASSERT_MSG( err 
== noErr 
, wxT("Error when adding bitmap") ) ; 
 481                 err 
= RegisterIconRefFromIconFamily( 'WXNG' , (OSType
) 1, iconFamily
, &iconRef 
) ; 
 482                 wxASSERT_MSG( err 
== noErr 
, wxT("Error when adding bitmap") ) ; 
 483                 info
.contentType 
= kControlContentIconRef 
; 
 484                 info
.u
.iconRef 
= iconRef 
; 
 485                 SetControlData( (ControlHandle
) m_macControl
, ii
+1,kControlTabImageContentTag
, 
 486                     sizeof( info 
), (Ptr
)&info 
); 
 487                 wxASSERT_MSG( err 
== noErr 
, wxT("Error when setting icon on tab") ) ; 
 488                 if ( UMAGetSystemVersion() < 0x1030 ) 
 490                     UnregisterIconRef( 'WXNG' , (OSType
) 1 ) ; 
 493                 ReleaseIconRef( iconRef 
) ; 
 494                 DisposeHandle( (Handle
) iconFamily 
) ; 
 500     GetControlBounds((ControlHandle
)m_macControl
, &bounds
); 
 501     InvalWindowRect((WindowRef
)MacGetRootWindow(), &bounds
); 
 504 wxRect 
wxNotebook::GetPageRect() const 
 506     // fit the notebook page to the tab control's display area 
 511         wxMacTabLeftMargin(GetWindowStyle()) + m_macHorizontalBorder
, 
 512         wxMacTabTopMargin(GetWindowStyle()) + m_macVerticalBorder
, 
 513         w 
- wxMacTabLeftMargin(GetWindowStyle()) - wxMacTabRightMargin(GetWindowStyle()) - 2*m_macHorizontalBorder
, 
 514         h 
- wxMacTabTopMargin(GetWindowStyle()) - wxMacTabBottomMargin(GetWindowStyle()) - 2*m_macVerticalBorder
); 
 516 // ---------------------------------------------------------------------------- 
 517 // wxNotebook callbacks 
 518 // ---------------------------------------------------------------------------- 
 520 // @@@ OnSize() is used for setting the font when it's called for the first 
 521 //     time because doing it in ::Create() doesn't work (for unknown reasons) 
 522 void wxNotebook::OnSize(wxSizeEvent
& event
) 
 525     unsigned int nCount 
= m_pages
.Count(); 
 526     wxRect rect 
= GetPageRect() ; 
 527     for ( unsigned int nPage 
= 0; nPage 
< nCount
; nPage
++ ) { 
 528         wxNotebookPage 
*pPage 
= m_pages
[nPage
]; 
 529         pPage
->SetSize(rect
); 
 530         if ( pPage
->GetAutoLayout() ) { 
 535     // Processing continues to next OnSize 
 539 void wxNotebook::OnSelChange(wxNotebookEvent
& event
) 
 541     // is it our tab control? 
 542     if ( event
.GetEventObject() == this ) 
 543         ChangePage(event
.GetOldSelection(), event
.GetSelection()); 
 545     // we want to give others a chance to process this message as well 
 549 void wxNotebook::OnSetFocus(wxFocusEvent
& event
) 
 551     // set focus to the currently selected page if any 
 552     if ( m_nSelection 
!= -1 ) 
 553         m_pages
[m_nSelection
]->SetFocus(); 
 558 void wxNotebook::OnNavigationKey(wxNavigationKeyEvent
& event
) 
 560     if ( event
.IsWindowChange() ) { 
 562         AdvanceSelection(event
.GetDirection()); 
 565         // we get this event in 2 cases 
 567         // a) one of our pages might have generated it because the user TABbed 
 568         // out from it in which case we should propagate the event upwards and 
 569         // our parent will take care of setting the focus to prev/next sibling 
 573         // b) the parent panel wants to give the focus to us so that we 
 574         // forward it to our selected page. We can't deal with this in 
 575         // OnSetFocus() because we don't know which direction the focus came 
 576         // from in this case and so can't choose between setting the focus to 
 577         // first or last panel child 
 578         wxWindow 
*parent 
= GetParent(); 
 579         // the cast is here to fic a GCC ICE 
 580         if ( ((wxWindow
*)event
.GetEventObject()) == parent 
) 
 582             // no, it doesn't come from child, case (b): forward to a page 
 583             if ( m_nSelection 
!= -1 ) 
 585                 // so that the page knows that the event comes from it's parent 
 586                 // and is being propagated downwards 
 587                 event
.SetEventObject(this); 
 589                 wxWindow 
*page 
= m_pages
[m_nSelection
]; 
 590                 if ( !page
->GetEventHandler()->ProcessEvent(event
) ) 
 594                 //else: page manages focus inside it itself 
 598                 // we have no pages - still have to give focus to _something_ 
 604             // it comes from our child, case (a), pass to the parent 
 606                 event
.SetCurrentFocus(this); 
 607                 parent
->GetEventHandler()->ProcessEvent(event
); 
 613 // ---------------------------------------------------------------------------- 
 614 // wxNotebook base class virtuals 
 615 // ---------------------------------------------------------------------------- 
 617 #if wxUSE_CONSTRAINTS 
 619 // override these 2 functions to do nothing: everything is done in OnSize 
 621 void wxNotebook::SetConstraintSizes(bool WXUNUSED(recurse
)) 
 623     // don't set the sizes of the pages - their correct size is not yet known 
 624     wxControl::SetConstraintSizes(false); 
 627 bool wxNotebook::DoPhase(int WXUNUSED(nPhase
)) 
 632 #endif // wxUSE_CONSTRAINTS 
 634 void wxNotebook::Command(wxCommandEvent
& event
) 
 636     wxFAIL_MSG(wxT("wxNotebook::Command not implemented")); 
 639 // ---------------------------------------------------------------------------- 
 640 // wxNotebook helper functions 
 641 // ---------------------------------------------------------------------------- 
 643 // hide the currently active panel and show the new one 
 644 void wxNotebook::ChangePage(int nOldSel
, int nSel
) 
 648         m_pages
[nOldSel
]->Show(false); 
 653         wxNotebookPage 
*pPage 
= m_pages
[nSel
]; 
 659     SetControl32BitValue( (ControlHandle
) m_macControl 
, m_nSelection 
+ 1 ) ; 
 663 void  wxNotebook::OnMouse( wxMouseEvent 
&event 
) 
 665     if ( (ControlHandle
) m_macControl 
== NULL 
) 
 671     if (event
.GetEventType() == wxEVT_LEFT_DOWN 
|| event
.GetEventType() == wxEVT_LEFT_DCLICK 
) 
 676         MacClientToRootWindow( &x 
, &y 
) ; 
 678         ControlHandle   control 
; 
 687         if ( !event
.m_leftDown 
&& !event
.m_rightDown 
) 
 688             modifiers  
|= btnState 
; 
 690         if ( event
.m_shiftDown 
) 
 691             modifiers 
|= shiftKey 
; 
 693         if ( event
.m_controlDown 
) 
 694             modifiers 
|= controlKey 
; 
 696         if ( event
.m_altDown 
) 
 697             modifiers 
|= optionKey 
; 
 699         if ( event
.m_metaDown 
) 
 700             modifiers 
|= cmdKey 
; 
 702         control 
= (ControlHandle
) m_macControl 
; 
 703         if ( control 
&& ::IsControlActive( control 
) ) 
 706                 wxNotebookEvent 
changing(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
, m_windowId
, 
 707                     ::GetControl32BitValue(control
) - 1, m_nSelection
); 
 708                 changing
.SetEventObject(this); 
 709                 GetEventHandler()->ProcessEvent(changing
); 
 711                 if(changing
.IsAllowed()) 
 713                     controlpart 
= ::HandleControlClick(control
, localwhere
, modifiers
, 
 714                         (ControlActionUPP
) -1); 
 715                     wxTheApp
->s_lastMouseDown 
= 0 ; 
 717                     wxNotebookEvent 
event(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
, m_windowId
, 
 718                         ::GetControl32BitValue(control
) - 1, m_nSelection
); 
 719                     event
.SetEventObject(this); 
 721                     GetEventHandler()->ProcessEvent(event
); 
 729 void wxNotebook::MacHandleControlClick( WXWidget control 
, wxInt16 controlpart 
, bool WXUNUSED( mouseStillDown 
) ) 
 732     wxNotebookEvent 
event(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
, m_windowId 
, ::GetControl32BitValue((ControlHandle
)m_macControl
) - 1, m_nSelection
); 
 733     event
.SetEventObject(this);