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 
  88 #if wxMAC_EDGE_TO_EDGE 
  96     // If the style matches the side asked for then return the tab margin, 
  97     // but we have to special case wxNB_TOP since it is zero... 
  98     if ( side 
== wxNB_TOP
) 
 100         if ( nbStyle 
!= 0 && nbStyle 
& (wxNB_LEFT
|wxNB_RIGHT
|wxNB_BOTTOM
)) 
 109     else if ( nbStyle 
& side
) 
 115 static inline int wxMacTabLeftMargin(long style
) 
 117     return wxMacTabMargin(style
, wxNB_LEFT
); 
 120 static inline int wxMacTabTopMargin(long style
) 
 122     return wxMacTabMargin(style
, wxNB_TOP
); 
 125 static inline int wxMacTabRightMargin(long style
) 
 127     return wxMacTabMargin(style
, wxNB_RIGHT
); 
 130 static inline int wxMacTabBottomMargin(long style
) 
 132     return wxMacTabMargin(style
, wxNB_BOTTOM
); 
 135 // ---------------------------------------------------------------------------- 
 136 // wxNotebook construction 
 137 // ---------------------------------------------------------------------------- 
 139 // common part of all ctors 
 140 void wxNotebook::Init() 
 142     if ( UMAHasAquaLayout() ) 
 144         // Should these depend on wxMAC_EDGE_TO_EDGE too? 
 145         m_macHorizontalBorder 
= 7; 
 146         m_macVerticalBorder 
= 8; 
 152 // default for dynamic class 
 153 wxNotebook::wxNotebook() 
 158 // the same arguments as for wxControl 
 159 wxNotebook::wxNotebook(wxWindow 
*parent
, 
 164                        const wxString
& name
) 
 168     Create(parent
, id
, pos
, size
, style
, name
); 
 172 bool wxNotebook::Create(wxWindow 
*parent
, 
 177                         const wxString
& name
) 
 179     if ( !wxNotebookBase::Create(parent
, id
, pos
, size
, style
, name
) ) 
 185     MacPreControlCreate( parent 
, id 
,  wxEmptyString 
, pos 
, size 
,style
, wxDefaultValidator 
, name 
, &bounds 
, title 
) ; 
 187     int tabstyle 
= kControlTabSmallNorthProc 
; 
 188     if ( HasFlag(wxNB_LEFT
) ) 
 189         tabstyle 
= kControlTabSmallWestProc 
; 
 190     else if ( HasFlag( wxNB_RIGHT 
) ) 
 191         tabstyle 
= kControlTabSmallEastProc 
; 
 192     else if ( HasFlag( wxNB_BOTTOM 
) ) 
 193         tabstyle 
= kControlTabSmallSouthProc 
; 
 196     m_macControl 
= ::NewControl( MAC_WXHWND(parent
->MacGetRootWindow()) , &bounds 
, title 
, false , 0 , 0 , 1, 
 197         tabstyle 
, (long) this ) ; 
 199     MacPostControlCreate() ; 
 204 wxNotebook::~wxNotebook() 
 208 wxSize 
wxNotebook::CalcSizeFromPage(const wxSize
& sizePage
) const 
 210     wxSize sizeTotal 
= sizePage
; 
 213     wxGetOsVersion( &major
, &minor 
); 
 215     // Mac has large notebook borders. Aqua even more so. 
 217     if ( HasFlag(wxNB_LEFT
) || HasFlag(wxNB_RIGHT
) ) 
 243 // ---------------------------------------------------------------------------- 
 244 // wxNotebook accessors 
 245 // ---------------------------------------------------------------------------- 
 247 void wxNotebook::SetPadding(const wxSize
& padding
) 
 249     wxFAIL_MSG( wxT("wxNotebook::SetPadding not implemented") ); 
 252 void wxNotebook::SetTabSize(const wxSize
& sz
) 
 254     wxFAIL_MSG( wxT("wxNotebook::SetTabSize not implemented") ); 
 257 void wxNotebook::SetPageSize(const wxSize
& size
) 
 259     wxFAIL_MSG( wxT("wxNotebook::SetPageSize not implemented") ); 
 262 int wxNotebook::SetSelection(size_t nPage
) 
 264     wxCHECK_MSG( IS_VALID_PAGE(nPage
), -1, wxT("notebook page out of range") ); 
 266     if ( int(nPage
) != m_nSelection 
) 
 268         wxNotebookEvent 
event(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
, m_windowId
); 
 269         event
.SetSelection(nPage
); 
 270         event
.SetOldSelection(m_nSelection
); 
 271         event
.SetEventObject(this); 
 272         if ( !GetEventHandler()->ProcessEvent(event
) || event
.IsAllowed() ) 
 274             // program allows the page change 
 275             event
.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
); 
 276             (void)GetEventHandler()->ProcessEvent(event
); 
 278             ChangePage(m_nSelection
, nPage
); 
 285 bool wxNotebook::SetPageText(size_t nPage
, const wxString
& strText
) 
 287     wxASSERT( IS_VALID_PAGE(nPage
) ); 
 289     wxNotebookPage 
*page 
= m_pages
[nPage
]; 
 290     page
->SetLabel(strText
); 
 296 wxString 
wxNotebook::GetPageText(size_t nPage
) const 
 298     wxASSERT( IS_VALID_PAGE(nPage
) ); 
 300     wxNotebookPage 
*page 
= m_pages
[nPage
]; 
 301     return page
->GetLabel(); 
 304 int wxNotebook::GetPageImage(size_t nPage
) const 
 306     wxCHECK_MSG( IS_VALID_PAGE(nPage
), -1, _T("invalid notebook page") ); 
 308     return m_images
[nPage
]; 
 311 bool wxNotebook::SetPageImage(size_t nPage
, int nImage
) 
 313     wxCHECK_MSG( IS_VALID_PAGE(nPage
), FALSE
, _T("invalid notebook page") ); 
 315     wxCHECK_MSG( m_imageList 
&& nImage 
< m_imageList
->GetImageCount(), FALSE
, 
 316         _T("invalid image index in SetPageImage()") ); 
 318     if ( nImage 
!= m_images
[nPage
] ) 
 320         // if the item didn't have an icon before or, on the contrary, did have 
 321         // it but has lost it now, its size will change - but if the icon just 
 323         m_images
[nPage
] = nImage
; 
 331 // ---------------------------------------------------------------------------- 
 332 // wxNotebook operations 
 333 // ---------------------------------------------------------------------------- 
 335 // remove one page from the notebook, without deleting the window 
 336 wxNotebookPage
* wxNotebook::DoRemovePage(size_t nPage
) 
 338     wxCHECK( IS_VALID_PAGE(nPage
), NULL 
); 
 339     wxNotebookPage
* page 
= m_pages
[nPage
] ; 
 340     m_pages
.RemoveAt(nPage
); 
 344     if(m_nSelection 
>= (int)GetPageCount()) { 
 345         m_nSelection 
= GetPageCount() - 1; 
 347     if(m_nSelection 
>= 0) { 
 348         m_pages
[m_nSelection
]->Show(true); 
 354 bool wxNotebook::DeleteAllPages() 
 356     WX_CLEAR_ARRAY(m_pages
) ; 
 363 // same as AddPage() but does it at given position 
 364 bool wxNotebook::InsertPage(size_t nPage
, 
 365                             wxNotebookPage 
*pPage
, 
 366                             const wxString
& strText
, 
 370     if ( !wxNotebookBase::InsertPage(nPage
, pPage
, strText
, bSelect
, imageId
) ) 
 373     wxASSERT_MSG( pPage
->GetParent() == this, 
 374                     _T("notebook pages must have notebook as parent") ); 
 376     // don't show pages by default (we'll need to adjust their size first) 
 377     pPage
->Show( false ) ; 
 379     pPage
->SetLabel(strText
); 
 381     m_images
.Insert(imageId
, nPage
); 
 387     pPage
->SetSize(wxMacTabLeftMargin(GetWindowStyle()) + m_macHorizontalBorder
, 
 388                    wxMacTabTopMargin(GetWindowStyle()) + m_macVerticalBorder
, 
 389                    w 
- wxMacTabLeftMargin(GetWindowStyle()) - wxMacTabRightMargin(GetWindowStyle()) - 2*m_macHorizontalBorder
, 
 390                    h 
- wxMacTabTopMargin(GetWindowStyle()) - wxMacTabBottomMargin(GetWindowStyle()) - 2*m_macVerticalBorder
); 
 391     if ( pPage
->GetAutoLayout() ) { 
 396     // now deal with the selection 
 397     // --------------------------- 
 399     // if the inserted page is before the selected one, we must update the 
 400     // index of the selected page 
 402     if ( int(nPage
) <= m_nSelection 
)  
 405         // while this still is the same page showing, we need to update the tabs 
 406         SetControl32BitValue( (ControlHandle
) m_macControl 
, m_nSelection 
+ 1 ) ; 
 409     // some page should be selected: either this one or the first one if there 
 410     // is still no selection 
 414     else if ( m_nSelection 
== -1 ) 
 418         SetSelection(selNew
); 
 423 /* Added by Mark Newsam 
 424 * When a page is added or deleted to the notebook this function updates 
 425 * information held in the m_macControl so that it matches the order 
 426 * the user would expect. 
 428 void wxNotebook::MacSetupTabs() 
 430     SetControl32BitMaximum( (ControlHandle
) m_macControl 
, GetPageCount() ) ; 
 432     wxNotebookPage 
*page
; 
 433     ControlTabInfoRec info
; 
 435     const size_t countPages 
= GetPageCount(); 
 436     for(size_t ii 
= 0; ii 
< countPages
; ii
++) 
 440         info
.iconSuiteID 
= 0; 
 441         wxMacStringToPascal( page
->GetLabel() , info
.name 
) ; 
 443         SetControlData( (ControlHandle
) m_macControl
, ii
+1, kControlTabInfoTag
, 
 444             sizeof( ControlTabInfoRec
) , (char*) &info 
) ; 
 445         SetTabEnabled( (ControlHandle
) m_macControl 
, ii
+1 , true ) ; 
 447         if ( GetImageList() && GetPageImage(ii
) >= 0 && UMAGetSystemVersion() >= 0x1020 ) 
 449             // tab controls only support very specific types of images, therefore we are doing an odyssee 
 450             // accross the icon worlds (even Apple DTS did not find a shorter path) 
 451             // in order not to pollute the icon registry we put every icon into (OSType) 1 and immediately 
 452             // afterwards Unregister it (IconRef is ref counted, so it will stay on the tab even if we 
 453             // unregister it) in case this will ever lead to having the same icon everywhere add some kind 
 455             const wxBitmap
* bmap 
= GetImageList()->GetBitmap( GetPageImage(ii 
) ) ; 
 458                 wxBitmap scaledBitmap 
; 
 459                 if ( bmap
->GetWidth() != 16 || bmap
->GetHeight() != 16 ) 
 461                     scaledBitmap 
= wxBitmap( bmap
->ConvertToImage().Scale(16,16) ) ; 
 462                     bmap 
= &scaledBitmap 
; 
 464                 ControlButtonContentInfo info 
; 
 465                 wxMacCreateBitmapButton( &info 
, *bmap 
, kControlContentPictHandle
) ; 
 466                 IconFamilyHandle iconFamily 
= (IconFamilyHandle
) NewHandle(0) ; 
 467                 OSErr err 
= SetIconFamilyData( iconFamily
, 'PICT' , (Handle
) info
.u
.picture 
) ; 
 468                 wxASSERT_MSG( err 
== noErr 
, wxT("Error when adding bitmap") ) ; 
 470                 err 
= RegisterIconRefFromIconFamily( 'WXNG' , (OSType
) 1 , iconFamily
, &iconRef 
) ; 
 471                 wxASSERT_MSG( err 
== noErr 
, wxT("Error when adding bitmap") ) ; 
 472                 info
.contentType 
= kControlContentIconRef 
; 
 473                 info
.u
.iconRef 
= iconRef 
; 
 474                 SetControlData( (ControlHandle
) m_macControl
, ii
+1,kControlTabImageContentTag
, 
 475                     sizeof( info 
), (Ptr
)&info 
); 
 476                 wxASSERT_MSG( err 
== noErr 
, wxT("Error when setting icon on tab") ) ; 
 477                    UnregisterIconRef( 'WXNG' , (OSType
) 1 ) ; 
 478                 ReleaseIconRef( iconRef 
) ; 
 479                 DisposeHandle( (Handle
) iconFamily 
) ; 
 485     GetControlBounds((ControlHandle
)m_macControl
, &bounds
); 
 486     InvalWindowRect((WindowRef
)MacGetRootWindow(), &bounds
); 
 489 // ---------------------------------------------------------------------------- 
 490 // wxNotebook callbacks 
 491 // ---------------------------------------------------------------------------- 
 493 // @@@ OnSize() is used for setting the font when it's called for the first 
 494 //     time because doing it in ::Create() doesn't work (for unknown reasons) 
 495 void wxNotebook::OnSize(wxSizeEvent
& event
) 
 497     // emulate page change (it's esp. important to do it first time because 
 498     // otherwise our page would stay invisible) 
 500     int nSel = m_nSelection; 
 505     // fit the notebook page to the tab control's display area 
 509     unsigned int nCount 
= m_pages
.Count(); 
 510     for ( unsigned int nPage 
= 0; nPage 
< nCount
; nPage
++ ) { 
 511         wxNotebookPage 
*pPage 
= m_pages
[nPage
]; 
 512         pPage
->SetSize(wxMacTabLeftMargin(GetWindowStyle()) + m_macHorizontalBorder
, 
 513                        wxMacTabTopMargin(GetWindowStyle()) + m_macVerticalBorder
, 
 514                        w 
- wxMacTabLeftMargin(GetWindowStyle()) - wxMacTabRightMargin(GetWindowStyle()) - 2*m_macHorizontalBorder
, 
 515                        h 
- wxMacTabTopMargin(GetWindowStyle()) - wxMacTabBottomMargin(GetWindowStyle()) - 2*m_macVerticalBorder
); 
 516         if ( pPage
->GetAutoLayout() ) { 
 521     // Processing continues to next OnSize 
 525 void wxNotebook::OnSelChange(wxNotebookEvent
& event
) 
 527     // is it our tab control? 
 528     if ( event
.GetEventObject() == this ) 
 529         ChangePage(event
.GetOldSelection(), event
.GetSelection()); 
 531     // we want to give others a chance to process this message as well 
 535 void wxNotebook::OnSetFocus(wxFocusEvent
& event
) 
 537     // set focus to the currently selected page if any 
 538     if ( m_nSelection 
!= -1 ) 
 539         m_pages
[m_nSelection
]->SetFocus(); 
 544 void wxNotebook::OnNavigationKey(wxNavigationKeyEvent
& event
) 
 546     if ( event
.IsWindowChange() ) { 
 548         AdvanceSelection(event
.GetDirection()); 
 551         // we get this event in 2 cases 
 553         // a) one of our pages might have generated it because the user TABbed 
 554         // out from it in which case we should propagate the event upwards and 
 555         // our parent will take care of setting the focus to prev/next sibling 
 559         // b) the parent panel wants to give the focus to us so that we 
 560         // forward it to our selected page. We can't deal with this in 
 561         // OnSetFocus() because we don't know which direction the focus came 
 562         // from in this case and so can't choose between setting the focus to 
 563         // first or last panel child 
 564         wxWindow 
*parent 
= GetParent(); 
 565         // the cast is here to fic a GCC ICE 
 566         if ( ((wxWindow
*)event
.GetEventObject()) == parent 
) 
 568             // no, it doesn't come from child, case (b): forward to a page 
 569             if ( m_nSelection 
!= -1 ) 
 571                 // so that the page knows that the event comes from it's parent 
 572                 // and is being propagated downwards 
 573                 event
.SetEventObject(this); 
 575                 wxWindow 
*page 
= m_pages
[m_nSelection
]; 
 576                 if ( !page
->GetEventHandler()->ProcessEvent(event
) ) 
 580                 //else: page manages focus inside it itself 
 584                 // we have no pages - still have to give focus to _something_ 
 590             // it comes from our child, case (a), pass to the parent 
 592                 event
.SetCurrentFocus(this); 
 593                 parent
->GetEventHandler()->ProcessEvent(event
); 
 599 // ---------------------------------------------------------------------------- 
 600 // wxNotebook base class virtuals 
 601 // ---------------------------------------------------------------------------- 
 603 #if wxUSE_CONSTRAINTS 
 605 // override these 2 functions to do nothing: everything is done in OnSize 
 607 void wxNotebook::SetConstraintSizes(bool WXUNUSED(recurse
)) 
 609   // don't set the sizes of the pages - their correct size is not yet known 
 610   wxControl::SetConstraintSizes(FALSE
); 
 613 bool wxNotebook::DoPhase(int WXUNUSED(nPhase
)) 
 618 #endif // wxUSE_CONSTRAINTS 
 620 void wxNotebook::Command(wxCommandEvent
& event
) 
 622     wxFAIL_MSG(wxT("wxNotebook::Command not implemented")); 
 625 // ---------------------------------------------------------------------------- 
 626 // wxNotebook helper functions 
 627 // ---------------------------------------------------------------------------- 
 629 // hide the currently active panel and show the new one 
 630 void wxNotebook::ChangePage(int nOldSel
, int nSel
) 
 634         m_pages
[nOldSel
]->Show(FALSE
); 
 639         wxNotebookPage 
*pPage 
= m_pages
[nSel
]; 
 645     SetControl32BitValue( (ControlHandle
) m_macControl 
, m_nSelection 
+ 1 ) ; 
 649 void  wxNotebook::OnMouse( wxMouseEvent 
&event 
) 
 651     if ( (ControlHandle
) m_macControl 
== NULL 
) 
 657     if (event
.GetEventType() == wxEVT_LEFT_DOWN 
|| event
.GetEventType() == wxEVT_LEFT_DCLICK 
) 
 662         MacClientToRootWindow( &x 
, &y 
) ; 
 664         ControlHandle   control 
; 
 673         if ( !event
.m_leftDown 
&& !event
.m_rightDown 
) 
 674             modifiers  
|= btnState 
; 
 676         if ( event
.m_shiftDown 
) 
 677             modifiers 
|= shiftKey 
; 
 679         if ( event
.m_controlDown 
) 
 680             modifiers 
|= controlKey 
; 
 682         if ( event
.m_altDown 
) 
 683             modifiers 
|= optionKey 
; 
 685         if ( event
.m_metaDown 
) 
 686             modifiers 
|= cmdKey 
; 
 688         control 
= (ControlHandle
) m_macControl 
; 
 689         if ( control 
&& ::IsControlActive( control 
) ) 
 692                 wxNotebookEvent 
changing(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
, m_windowId
, 
 693                     ::GetControl32BitValue(control
) - 1, m_nSelection
); 
 694                 changing
.SetEventObject(this); 
 695                 GetEventHandler()->ProcessEvent(changing
); 
 697                 if(changing
.IsAllowed()) 
 699                     controlpart 
= ::HandleControlClick(control
, localwhere
, modifiers
, 
 700                         (ControlActionUPP
) -1); 
 701                     wxTheApp
->s_lastMouseDown 
= 0 ; 
 703                     wxNotebookEvent 
event(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
, m_windowId
, 
 704                         ::GetControl32BitValue(control
) - 1, m_nSelection
); 
 705                     event
.SetEventObject(this); 
 707                     GetEventHandler()->ProcessEvent(event
); 
 715 void wxNotebook::MacHandleControlClick( WXWidget control 
, wxInt16 controlpart 
, bool WXUNUSED( mouseStillDown 
) ) 
 718     wxNotebookEvent 
event(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
, m_windowId 
, ::GetControl32BitValue((ControlHandle
)m_macControl
) - 1, m_nSelection
); 
 719     event
.SetEventObject(this);