1 /////////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/msw/notebook.cpp 
   3 // Purpose:     implementation of wxNotebook 
   4 // Author:      Vadim Zeitlin 
   8 // Copyright:   (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr> 
   9 // Licence:     wxWindows licence 
  10 /////////////////////////////////////////////////////////////////////////////// 
  12 // For compilers that support precompilation, includes "wx.h". 
  13 #include "wx/wxprec.h" 
  21 #include "wx/notebook.h" 
  24     #include "wx/msw/wrapcctl.h" // include <commctrl.h> "properly" 
  25     #include "wx/string.h" 
  30     #include "wx/dcclient.h" 
  31     #include "wx/dcmemory.h" 
  32     #include "wx/control.h" 
  35 #include "wx/imaglist.h" 
  36 #include "wx/sysopt.h" 
  38 #include "wx/msw/private.h" 
  41 #include "wx/msw/winundef.h" 
  44     #include "wx/msw/uxtheme.h" 
  47 // ---------------------------------------------------------------------------- 
  49 // ---------------------------------------------------------------------------- 
  51 // check that the page index is valid 
  52 #define IS_VALID_PAGE(nPage) ((nPage) < GetPageCount()) 
  54 // you can set USE_NOTEBOOK_ANTIFLICKER to 0 for desktop Windows versions too 
  55 // to disable code whih results in flicker-less notebook redrawing at the 
  56 // expense of some extra GDI resource consumption 
  58     // notebooks are never resized under CE anyhow 
  59     #define USE_NOTEBOOK_ANTIFLICKER    0 
  61     #define USE_NOTEBOOK_ANTIFLICKER    1 
  64 // ---------------------------------------------------------------------------- 
  66 // ---------------------------------------------------------------------------- 
  68 // This is a work-around for missing defines in gcc-2.95 headers 
  70     #define TCS_RIGHT       0x0002 
  74     #define TCS_VERTICAL    0x0080 
  78     #define TCS_BOTTOM      TCS_RIGHT 
  81 // ---------------------------------------------------------------------------- 
  83 // ---------------------------------------------------------------------------- 
  85 #if USE_NOTEBOOK_ANTIFLICKER 
  87 // the pointer to standard spin button wnd proc 
  88 static WXFARPROC gs_wndprocNotebookSpinBtn 
= (WXFARPROC
)NULL
; 
  90 // the pointer to standard tab control wnd proc 
  91 static WXFARPROC gs_wndprocNotebook 
= (WXFARPROC
)NULL
; 
  93 LRESULT APIENTRY _EXPORT 
wxNotebookWndProc(HWND hwnd
, 
  98 #endif // USE_NOTEBOOK_ANTIFLICKER 
 100 // ---------------------------------------------------------------------------- 
 102 // ---------------------------------------------------------------------------- 
 104 #include "wx/listimpl.cpp" 
 106 WX_DEFINE_LIST( wxNotebookPageInfoList 
) 
 108 DEFINE_EVENT_TYPE(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
) 
 109 DEFINE_EVENT_TYPE(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
) 
 111 BEGIN_EVENT_TABLE(wxNotebook
, wxControl
) 
 112     EVT_NOTEBOOK_PAGE_CHANGED(wxID_ANY
, wxNotebook::OnSelChange
) 
 113     EVT_SIZE(wxNotebook::OnSize
) 
 114     EVT_NAVIGATION_KEY(wxNotebook::OnNavigationKey
) 
 116 #if USE_NOTEBOOK_ANTIFLICKER 
 117     EVT_ERASE_BACKGROUND(wxNotebook::OnEraseBackground
) 
 118     EVT_PAINT(wxNotebook::OnPaint
) 
 119 #endif // USE_NOTEBOOK_ANTIFLICKER 
 122 #if wxUSE_EXTENDED_RTTI 
 123 WX_DEFINE_FLAGS( wxNotebookStyle 
) 
 125 wxBEGIN_FLAGS( wxNotebookStyle 
) 
 126     // new style border flags, we put them first to 
 127     // use them for streaming out 
 128     wxFLAGS_MEMBER(wxBORDER_SIMPLE
) 
 129     wxFLAGS_MEMBER(wxBORDER_SUNKEN
) 
 130     wxFLAGS_MEMBER(wxBORDER_DOUBLE
) 
 131     wxFLAGS_MEMBER(wxBORDER_RAISED
) 
 132     wxFLAGS_MEMBER(wxBORDER_STATIC
) 
 133     wxFLAGS_MEMBER(wxBORDER_NONE
) 
 135     // old style border flags 
 136     wxFLAGS_MEMBER(wxSIMPLE_BORDER
) 
 137     wxFLAGS_MEMBER(wxSUNKEN_BORDER
) 
 138     wxFLAGS_MEMBER(wxDOUBLE_BORDER
) 
 139     wxFLAGS_MEMBER(wxRAISED_BORDER
) 
 140     wxFLAGS_MEMBER(wxSTATIC_BORDER
) 
 141     wxFLAGS_MEMBER(wxBORDER
) 
 143     // standard window styles 
 144     wxFLAGS_MEMBER(wxTAB_TRAVERSAL
) 
 145     wxFLAGS_MEMBER(wxCLIP_CHILDREN
) 
 146     wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW
) 
 147     wxFLAGS_MEMBER(wxWANTS_CHARS
) 
 148     wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE
) 
 149     wxFLAGS_MEMBER(wxALWAYS_SHOW_SB 
) 
 150     wxFLAGS_MEMBER(wxVSCROLL
) 
 151     wxFLAGS_MEMBER(wxHSCROLL
) 
 153     wxFLAGS_MEMBER(wxNB_FIXEDWIDTH
) 
 154     wxFLAGS_MEMBER(wxBK_DEFAULT
) 
 155     wxFLAGS_MEMBER(wxBK_TOP
) 
 156     wxFLAGS_MEMBER(wxBK_LEFT
) 
 157     wxFLAGS_MEMBER(wxBK_RIGHT
) 
 158     wxFLAGS_MEMBER(wxBK_BOTTOM
) 
 159     wxFLAGS_MEMBER(wxNB_NOPAGETHEME
) 
 160     wxFLAGS_MEMBER(wxNB_FLAT
) 
 162 wxEND_FLAGS( wxNotebookStyle 
) 
 164 IMPLEMENT_DYNAMIC_CLASS_XTI(wxNotebook
, wxControl
,"wx/notebook.h") 
 165 IMPLEMENT_DYNAMIC_CLASS_XTI(wxNotebookPageInfo
, wxObject 
, "wx/notebook.h" ) 
 167 wxCOLLECTION_TYPE_INFO( wxNotebookPageInfo 
* , wxNotebookPageInfoList 
) ; 
 169 template<> void wxCollectionToVariantArray( wxNotebookPageInfoList 
const &theList
, wxxVariantArray 
&value
) 
 171     wxListCollectionToVariantArray
<wxNotebookPageInfoList::compatibility_iterator
>( theList 
, value 
) ; 
 174 wxBEGIN_PROPERTIES_TABLE(wxNotebook
) 
 175     wxEVENT_PROPERTY( PageChanging 
, wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING 
, wxNotebookEvent 
) 
 176     wxEVENT_PROPERTY( PageChanged 
, wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED 
, wxNotebookEvent 
) 
 178     wxPROPERTY_COLLECTION( PageInfos 
, wxNotebookPageInfoList 
, wxNotebookPageInfo
* , AddPageInfo 
, GetPageInfos 
, 0 /*flags*/ , wxT("Helpstring") , wxT("group")) 
 179     wxPROPERTY_FLAGS( WindowStyle 
, wxNotebookStyle 
, long , SetWindowStyleFlag 
, GetWindowStyleFlag 
, EMPTY_MACROVALUE 
, 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style 
 180 wxEND_PROPERTIES_TABLE() 
 182 wxBEGIN_HANDLERS_TABLE(wxNotebook
) 
 183 wxEND_HANDLERS_TABLE() 
 185 wxCONSTRUCTOR_5( wxNotebook 
, wxWindow
* , Parent 
, wxWindowID 
, Id 
, wxPoint 
, Position 
, wxSize 
, Size 
, long , WindowStyle
) 
 188 wxBEGIN_PROPERTIES_TABLE(wxNotebookPageInfo
) 
 189     wxREADONLY_PROPERTY( Page 
, wxNotebookPage
* , GetPage 
, EMPTY_MACROVALUE 
, 0 /*flags*/ , wxT("Helpstring") , wxT("group")) 
 190     wxREADONLY_PROPERTY( Text 
, wxString 
, GetText 
, wxString() , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) 
 191     wxREADONLY_PROPERTY( Selected 
, bool , GetSelected 
, false, 0 /*flags*/ , wxT("Helpstring") , wxT("group") ) 
 192     wxREADONLY_PROPERTY( ImageId 
, int , GetImageId 
, -1 , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) 
 193 wxEND_PROPERTIES_TABLE() 
 195 wxBEGIN_HANDLERS_TABLE(wxNotebookPageInfo
) 
 196 wxEND_HANDLERS_TABLE() 
 198 wxCONSTRUCTOR_4( wxNotebookPageInfo 
, wxNotebookPage
* , Page 
, wxString 
, Text 
, bool , Selected 
, int , ImageId 
) 
 201 IMPLEMENT_DYNAMIC_CLASS(wxNotebook
, wxControl
) 
 202 IMPLEMENT_DYNAMIC_CLASS(wxNotebookPageInfo
, wxObject 
) 
 204 IMPLEMENT_DYNAMIC_CLASS(wxNotebookEvent
, wxNotifyEvent
) 
 206 // ============================================================================ 
 208 // ============================================================================ 
 210 // ---------------------------------------------------------------------------- 
 211 // wxNotebook construction 
 212 // ---------------------------------------------------------------------------- 
 214 const wxNotebookPageInfoList
& wxNotebook::GetPageInfos() const 
 216     wxNotebookPageInfoList
* list 
= const_cast< wxNotebookPageInfoList
* >( &m_pageInfos 
) ; 
 217     WX_CLEAR_LIST( wxNotebookPageInfoList 
, *list 
) ; 
 218     for( size_t i 
= 0 ; i 
< GetPageCount() ; ++i 
) 
 220         wxNotebookPageInfo 
*info 
= new wxNotebookPageInfo() ; 
 221         info
->Create( const_cast<wxNotebook
*>(this)->GetPage(i
) , GetPageText(i
) , GetSelection() == int(i
) , GetPageImage(i
) ) ; 
 222         list
->Append( info 
) ; 
 227 // common part of all ctors 
 228 void wxNotebook::Init() 
 231     m_nSelection 
= wxNOT_FOUND
; 
 234     m_hbrBackground 
= NULL
; 
 235 #endif // wxUSE_UXTHEME 
 237 #if USE_NOTEBOOK_ANTIFLICKER 
 238     m_hasSubclassedUpdown 
= false; 
 239 #endif // USE_NOTEBOOK_ANTIFLICKER 
 242 // default for dynamic class 
 243 wxNotebook::wxNotebook() 
 248 // the same arguments as for wxControl 
 249 wxNotebook::wxNotebook(wxWindow 
*parent
, 
 254                        const wxString
& name
) 
 258   Create(parent
, id
, pos
, size
, style
, name
); 
 262 bool wxNotebook::Create(wxWindow 
*parent
, 
 267                         const wxString
& name
) 
 269     if ( (style 
& wxBK_ALIGN_MASK
) == wxBK_DEFAULT 
) 
 271 #if defined(__POCKETPC__) 
 272         style 
|= wxBK_BOTTOM 
| wxNB_FLAT
; 
 279     // Not sure why, but without this style, there is no border 
 280     // around the notebook tabs. 
 281     if (style 
& wxNB_FLAT
) 
 282         style 
|= wxBORDER_SUNKEN
; 
 286     // ComCtl32 notebook tabs simply don't work unless they're on top if we have uxtheme, we can 
 287     // work around it later (after control creation), but if we don't have uxtheme, we have to clear 
 289     const int verComCtl32 
= wxApp::GetComCtl32Version(); 
 290     if ( verComCtl32 
== 600 ) 
 292         style 
&= ~(wxBK_BOTTOM 
| wxBK_LEFT 
| wxBK_RIGHT
); 
 294 #endif //wxUSE_UXTHEME 
 296     LPCTSTR className 
= WC_TABCONTROL
; 
 298 #if USE_NOTEBOOK_ANTIFLICKER 
 299     // SysTabCtl32 class has natively CS_HREDRAW and CS_VREDRAW enabled and it 
 300     // causes horrible flicker when resizing notebook, so get rid of it by 
 301     // using a class without these styles (but otherwise identical to it) 
 302     if ( !HasFlag(wxFULL_REPAINT_ON_RESIZE
) ) 
 304         static ClassRegistrar s_clsNotebook
; 
 305         if ( !s_clsNotebook
.IsInitialized() ) 
 307             // get a copy of standard class and modify it 
 310             if ( ::GetClassInfo(NULL
, WC_TABCONTROL
, &wc
) ) 
 313                     wx_reinterpret_cast(WXFARPROC
, wc
.lpfnWndProc
); 
 314                 wc
.lpszClassName 
= wxT("_wx_SysTabCtl32"); 
 315                 wc
.style 
&= ~(CS_HREDRAW 
| CS_VREDRAW
); 
 316                 wc
.hInstance 
= wxGetInstance(); 
 317                 wc
.lpfnWndProc 
= wxNotebookWndProc
; 
 318                 s_clsNotebook
.Register(wc
); 
 322                 wxLogLastError(_T("GetClassInfoEx(SysTabCtl32)")); 
 326         // use our custom class if available but fall back to the standard 
 327         // notebook if we failed to register it 
 328         if ( s_clsNotebook
.IsRegistered() ) 
 330             // it's ok to use c_str() here as the static s_clsNotebook object 
 331             // has sufficiently long lifetime 
 332             className 
= s_clsNotebook
.GetName().c_str(); 
 335 #endif // USE_NOTEBOOK_ANTIFLICKER 
 337     if ( !CreateControl(parent
, id
, pos
, size
, style 
| wxTAB_TRAVERSAL
, 
 338                         wxDefaultValidator
, name
) ) 
 341     if ( !MSWCreateControl(className
, wxEmptyString
, pos
, size
) ) 
 345     if ( HasFlag(wxNB_NOPAGETHEME
) || 
 346             wxSystemOptions::IsFalse(wxT("msw.notebook.themed-background")) ) 
 348         SetBackgroundColour(GetThemeBackgroundColour()); 
 350     else // use themed background by default 
 352         // create backing store 
 356     // comctl32.dll 6.0 doesn't support non-top tabs with visual styles (the 
 357     // control is simply not rendered correctly), so we disable themes 
 358     // if possible, otherwise we simply clear the styles. 
 359     // It's probably not possible to have UXTHEME without ComCtl32 6 or better, but lets 
 361     const int verComCtl32 
= wxApp::GetComCtl32Version(); 
 362     if ( verComCtl32 
== 600 ) 
 364         // check if we use themes at all -- if we don't, we're still okay 
 365         if ( wxUxThemeEngine::GetIfActive() && (style 
& (wxBK_BOTTOM
|wxBK_LEFT
|wxBK_RIGHT
))) 
 367             wxUxThemeEngine::GetIfActive()->SetWindowTheme((HWND
)this->GetHandle(), L
"", L
""); 
 368             SetBackgroundColour(GetThemeBackgroundColour());    //correct the background color for the new non-themed control 
 371 #endif // wxUSE_UXTHEME 
 373     // Undocumented hack to get flat notebook style 
 374     // In fact, we should probably only do this in some 
 375     // curcumstances, i.e. if we know we will have a border 
 376     // at the bottom (the tab control doesn't draw it itself) 
 377 #if defined(__POCKETPC__) || defined(__SMARTPHONE__) 
 378     if (HasFlag(wxNB_FLAT
)) 
 380         SendMessage(GetHwnd(), CCM_SETVERSION
, COMCTL32_VERSION
, 0); 
 382             SetBackgroundColour(*wxWHITE
); 
 388 WXDWORD 
wxNotebook::MSWGetStyle(long style
, WXDWORD 
*exstyle
) const 
 390     WXDWORD tabStyle 
= wxControl::MSWGetStyle(style
, exstyle
); 
 392     tabStyle 
|= WS_TABSTOP 
| TCS_TABS
; 
 394     if ( style 
& wxNB_MULTILINE 
) 
 395         tabStyle 
|= TCS_MULTILINE
; 
 396     if ( style 
& wxNB_FIXEDWIDTH 
) 
 397         tabStyle 
|= TCS_FIXEDWIDTH
; 
 399     if ( style 
& wxBK_BOTTOM 
) 
 400         tabStyle 
|= TCS_RIGHT
; 
 401     else if ( style 
& wxBK_LEFT 
) 
 402         tabStyle 
|= TCS_VERTICAL
; 
 403     else if ( style 
& wxBK_RIGHT 
) 
 404         tabStyle 
|= TCS_VERTICAL 
| TCS_RIGHT
; 
 409         // note that we never want to have the default WS_EX_CLIENTEDGE style 
 410         // as it looks too ugly for the notebooks 
 417 wxNotebook::~wxNotebook() 
 420     if ( m_hbrBackground 
) 
 421         ::DeleteObject((HBRUSH
)m_hbrBackground
); 
 422 #endif // wxUSE_UXTHEME 
 425 // ---------------------------------------------------------------------------- 
 426 // wxNotebook accessors 
 427 // ---------------------------------------------------------------------------- 
 429 size_t wxNotebook::GetPageCount() const 
 432     wxASSERT( (int)m_pages
.Count() == TabCtrl_GetItemCount(GetHwnd()) ); 
 434     return m_pages
.Count(); 
 437 int wxNotebook::GetRowCount() const 
 439     return TabCtrl_GetRowCount(GetHwnd()); 
 442 int wxNotebook::SetSelection(size_t nPage
) 
 444     wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxNOT_FOUND
, wxT("notebook page out of range") ); 
 446     if ( m_nSelection 
== wxNOT_FOUND 
|| nPage 
!= (size_t)m_nSelection 
) 
 448         if ( SendPageChangingEvent(nPage
) ) 
 450             // program allows the page change 
 451             SendPageChangedEvent(m_nSelection
, nPage
); 
 453             TabCtrl_SetCurSel(GetHwnd(), nPage
); 
 460 void wxNotebook::UpdateSelection(int selNew
) 
 462     if ( m_nSelection 
!= wxNOT_FOUND 
) 
 463         m_pages
[m_nSelection
]->Show(false); 
 465     if ( selNew 
!= wxNOT_FOUND 
) 
 467         wxNotebookPage 
*pPage 
= m_pages
[selNew
]; 
 471     // Changing the page should give the focus to it but, as per bug report 
 472     // http://sf.net/tracker/index.php?func=detail&aid=1150659&group_id=9863&atid=109863, 
 473     // we should not set the focus to it directly since it erroneously 
 474     // selects radio buttons and breaks keyboard handling for a notebook's 
 475     // scroll buttons. So give focus to the notebook and not the page. 
 477     // but don't do this is the notebook is hidden 
 478     if ( ::IsWindowVisible(GetHwnd()) ) 
 481     m_nSelection 
= selNew
; 
 484 int wxNotebook::ChangeSelection(size_t nPage
) 
 486     wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxNOT_FOUND
, wxT("notebook page out of range") ); 
 488     if ( m_nSelection 
== wxNOT_FOUND 
|| nPage 
!= (size_t)m_nSelection 
) 
 490         TabCtrl_SetCurSel(GetHwnd(), nPage
); 
 492         UpdateSelection(nPage
); 
 498 bool wxNotebook::SetPageText(size_t nPage
, const wxString
& strText
) 
 500     wxCHECK_MSG( IS_VALID_PAGE(nPage
), false, wxT("notebook page out of range") ); 
 503     tcItem
.mask 
= TCIF_TEXT
; 
 504     tcItem
.pszText 
= (wxChar 
*)strText
.c_str(); 
 506     if ( !HasFlag(wxNB_MULTILINE
) ) 
 507         return TabCtrl_SetItem(GetHwnd(), nPage
, &tcItem
) != 0; 
 509     // multiline - we need to set new page size if a line is added or removed 
 510     int rows 
= GetRowCount(); 
 511     bool ret 
= TabCtrl_SetItem(GetHwnd(), nPage
, &tcItem
) != 0; 
 513     if ( ret 
&& rows 
!= GetRowCount() ) 
 515         const wxRect r 
= GetPageSize(); 
 516         const size_t count 
= m_pages
.Count(); 
 517         for ( size_t page 
= 0; page 
< count
; page
++ ) 
 518             m_pages
[page
]->SetSize(r
); 
 524 wxString 
wxNotebook::GetPageText(size_t nPage
) const 
 526     wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxEmptyString
, wxT("notebook page out of range") ); 
 530     tcItem
.mask 
= TCIF_TEXT
; 
 531     tcItem
.pszText 
= buf
; 
 532     tcItem
.cchTextMax 
= WXSIZEOF(buf
); 
 535     if ( TabCtrl_GetItem(GetHwnd(), nPage
, &tcItem
) ) 
 536         str 
= tcItem
.pszText
; 
 541 int wxNotebook::GetPageImage(size_t nPage
) const 
 543     wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxNOT_FOUND
, wxT("notebook page out of range") ); 
 546     tcItem
.mask 
= TCIF_IMAGE
; 
 548     return TabCtrl_GetItem(GetHwnd(), nPage
, &tcItem
) ? tcItem
.iImage
 
 552 bool wxNotebook::SetPageImage(size_t nPage
, int nImage
) 
 554     wxCHECK_MSG( IS_VALID_PAGE(nPage
), false, wxT("notebook page out of range") ); 
 557     tcItem
.mask 
= TCIF_IMAGE
; 
 558     tcItem
.iImage 
= nImage
; 
 560     return TabCtrl_SetItem(GetHwnd(), nPage
, &tcItem
) != 0; 
 563 void wxNotebook::SetImageList(wxImageList
* imageList
) 
 565     wxNotebookBase::SetImageList(imageList
); 
 569         (void) TabCtrl_SetImageList(GetHwnd(), GetHimagelistOf(imageList
)); 
 573 // ---------------------------------------------------------------------------- 
 574 // wxNotebook size settings 
 575 // ---------------------------------------------------------------------------- 
 577 wxRect 
wxNotebook::GetPageSize() const 
 582     ::GetClientRect(GetHwnd(), &rc
); 
 584     // This check is to work around a bug in TabCtrl_AdjustRect which will 
 585     // cause a crash on win2k or on XP with themes disabled if either 
 586     // wxNB_MULTILINE is used or tabs are placed on a side, if the rectangle 
 589     // The value of 20 is chosen arbitrarily but seems to work 
 590     if ( rc
.right 
> 20 && rc
.bottom 
> 20 ) 
 592         TabCtrl_AdjustRect(GetHwnd(), false, &rc
); 
 594         wxCopyRECTToRect(rc
, r
); 
 600 void wxNotebook::SetPageSize(const wxSize
& size
) 
 602     // transform the page size into the notebook size 
 609     TabCtrl_AdjustRect(GetHwnd(), true, &rc
); 
 612     SetSize(rc
.right 
- rc
.left
, rc
.bottom 
- rc
.top
); 
 615 void wxNotebook::SetPadding(const wxSize
& padding
) 
 617     TabCtrl_SetPadding(GetHwnd(), padding
.x
, padding
.y
); 
 620 // Windows-only at present. Also, you must use the wxNB_FIXEDWIDTH 
 622 void wxNotebook::SetTabSize(const wxSize
& sz
) 
 624     ::SendMessage(GetHwnd(), TCM_SETITEMSIZE
, 0, MAKELPARAM(sz
.x
, sz
.y
)); 
 627 wxSize 
wxNotebook::CalcSizeFromPage(const wxSize
& sizePage
) const 
 629     // we can't use TabCtrl_AdjustRect here because it only works for wxNB_TOP 
 630     wxSize sizeTotal 
= sizePage
; 
 633     if ( GetPageCount() > 0 ) 
 636         TabCtrl_GetItemRect(GetHwnd(), 0, &rect
); 
 637         tabSize
.x 
= rect
.right 
- rect
.left
; 
 638         tabSize
.y 
= rect
.bottom 
- rect
.top
; 
 641     // add an extra margin in both directions 
 642     const int MARGIN 
= 8; 
 645         sizeTotal
.x 
+= MARGIN
; 
 646         sizeTotal
.y 
+= tabSize
.y 
+ MARGIN
; 
 648     else // horizontal layout 
 650         sizeTotal
.x 
+= tabSize
.x 
+ MARGIN
; 
 651         sizeTotal
.y 
+= MARGIN
; 
 657 void wxNotebook::AdjustPageSize(wxNotebookPage 
*page
) 
 659     wxCHECK_RET( page
, _T("NULL page in wxNotebook::AdjustPageSize") ); 
 661     const wxRect r 
= GetPageSize(); 
 668 // ---------------------------------------------------------------------------- 
 669 // wxNotebook operations 
 670 // ---------------------------------------------------------------------------- 
 672 // remove one page from the notebook, without deleting 
 673 wxNotebookPage 
*wxNotebook::DoRemovePage(size_t nPage
) 
 675     wxNotebookPage 
*pageRemoved 
= wxNotebookBase::DoRemovePage(nPage
); 
 679     TabCtrl_DeleteItem(GetHwnd(), nPage
); 
 681     if ( m_pages
.IsEmpty() ) 
 683         // no selection any more, the notebook becamse empty 
 684         m_nSelection 
= wxNOT_FOUND
; 
 686     else // notebook still not empty 
 688         int selNew 
= TabCtrl_GetCurSel(GetHwnd()); 
 689         if ( selNew 
!= wxNOT_FOUND 
) 
 691             // No selection change, just refresh the current selection. 
 692             // Because it could be that the slection index changed 
 693             // we need to update it. 
 694             // Note: this does not mean the selection it self changed. 
 695             m_nSelection 
= selNew
; 
 696             m_pages
[m_nSelection
]->Refresh(); 
 698         else if (int(nPage
) == m_nSelection
) 
 700             // The selection was deleted. 
 702             // Determine new selection. 
 703             if (m_nSelection 
== int(GetPageCount())) 
 704                 selNew 
= m_nSelection 
- 1; 
 706                 selNew 
= m_nSelection
; 
 708             // m_nSelection must be always valid so reset it before calling 
 710             m_nSelection 
= wxNOT_FOUND
; 
 711             SetSelection(selNew
); 
 715             wxFAIL
; // Windows did not behave ok. 
 723 bool wxNotebook::DeleteAllPages() 
 725     size_t nPageCount 
= GetPageCount(); 
 727     for ( nPage 
= 0; nPage 
< nPageCount
; nPage
++ ) 
 728         delete m_pages
[nPage
]; 
 732     TabCtrl_DeleteAllItems(GetHwnd()); 
 734     m_nSelection 
= wxNOT_FOUND
; 
 736     InvalidateBestSize(); 
 740 // same as AddPage() but does it at given position 
 741 bool wxNotebook::InsertPage(size_t nPage
, 
 742                             wxNotebookPage 
*pPage
, 
 743                             const wxString
& strText
, 
 747     wxCHECK_MSG( pPage 
!= NULL
, false, _T("NULL page in wxNotebook::InsertPage") ); 
 748     wxCHECK_MSG( IS_VALID_PAGE(nPage
) || nPage 
== GetPageCount(), false, 
 749                  _T("invalid index in wxNotebook::InsertPage") ); 
 751     wxASSERT_MSG( pPage
->GetParent() == this, 
 752                     _T("notebook pages must have notebook as parent") ); 
 754     // add a new tab to the control 
 755     // ---------------------------- 
 757     // init all fields to 0 
 759     wxZeroMemory(tcItem
); 
 761     // set the image, if any 
 764         tcItem
.mask 
|= TCIF_IMAGE
; 
 765         tcItem
.iImage  
= imageId
; 
 769     if ( !strText
.empty() ) 
 771         tcItem
.mask 
|= TCIF_TEXT
; 
 772         tcItem
.pszText 
= (wxChar 
*)strText
.c_str(); // const_cast 
 775     // hide the page: unless it is selected, it shouldn't be shown (and if it 
 776     // is selected it will be shown later) 
 777     HWND hwnd 
= GetWinHwnd(pPage
); 
 778     SetWindowLong(hwnd
, GWL_STYLE
, GetWindowLong(hwnd
, GWL_STYLE
) & ~WS_VISIBLE
); 
 780     // this updates internal flag too -- otherwise it would get out of sync 
 781     // with the real state 
 785     // fit the notebook page to the tab control's display area: this should be 
 786     // done before adding it to the notebook or TabCtrl_InsertItem() will 
 787     // change the notebooks size itself! 
 788     AdjustPageSize(pPage
); 
 790     // finally do insert it 
 791     if ( TabCtrl_InsertItem(GetHwnd(), nPage
, &tcItem
) == -1 ) 
 793         wxLogError(wxT("Can't create the notebook page '%s'."), strText
.c_str()); 
 798     // need to update the bg brush when the first page is added 
 799     // so the first panel gets the correct themed background 
 800     if ( m_pages
.empty() ) 
 804 #endif // wxUSE_UXTHEME 
 807     // succeeded: save the pointer to the page 
 808     m_pages
.Insert(pPage
, nPage
); 
 810     // we may need to adjust the size again if the notebook size changed: 
 811     // normally this only happens for the first page we add (the tabs which 
 812     // hadn't been there before are now shown) but for a multiline notebook it 
 813     // can happen for any page at all as a new row could have been started 
 814     if ( m_pages
.GetCount() == 1 || HasFlag(wxNB_MULTILINE
) ) 
 816         AdjustPageSize(pPage
); 
 819     // now deal with the selection 
 820     // --------------------------- 
 822     // if the inserted page is before the selected one, we must update the 
 823     // index of the selected page 
 824     if ( int(nPage
) <= m_nSelection 
) 
 826         // one extra page added 
 830     // some page should be selected: either this one or the first one if there 
 831     // is still no selection 
 832     int selNew 
= wxNOT_FOUND
; 
 835     else if ( m_nSelection 
== wxNOT_FOUND 
) 
 838     if ( selNew 
!= wxNOT_FOUND 
) 
 839         SetSelection(selNew
); 
 841     InvalidateBestSize(); 
 846 int wxNotebook::HitTest(const wxPoint
& pt
, long *flags
) const 
 848     TC_HITTESTINFO hitTestInfo
; 
 849     hitTestInfo
.pt
.x 
= pt
.x
; 
 850     hitTestInfo
.pt
.y 
= pt
.y
; 
 851     int item 
= TabCtrl_HitTest(GetHwnd(), &hitTestInfo
); 
 857         if ((hitTestInfo
.flags 
& TCHT_NOWHERE
) == TCHT_NOWHERE
) 
 858             *flags 
|= wxBK_HITTEST_NOWHERE
; 
 859         if ((hitTestInfo
.flags 
& TCHT_ONITEM
) == TCHT_ONITEM
) 
 860             *flags 
|= wxBK_HITTEST_ONITEM
; 
 861         if ((hitTestInfo
.flags 
& TCHT_ONITEMICON
) == TCHT_ONITEMICON
) 
 862             *flags 
|= wxBK_HITTEST_ONICON
; 
 863         if ((hitTestInfo
.flags 
& TCHT_ONITEMLABEL
) == TCHT_ONITEMLABEL
) 
 864             *flags 
|= wxBK_HITTEST_ONLABEL
; 
 865         if ( item 
== wxNOT_FOUND 
&& GetPageSize().Contains(pt
) ) 
 866             *flags 
|= wxBK_HITTEST_ONPAGE
; 
 872 // ---------------------------------------------------------------------------- 
 873 // flicker-less notebook redraw 
 874 // ---------------------------------------------------------------------------- 
 876 #if USE_NOTEBOOK_ANTIFLICKER 
 878 // wnd proc for the spin button 
 879 LRESULT APIENTRY _EXPORT 
wxNotebookSpinBtnWndProc(HWND hwnd
, 
 884     if ( message 
== WM_ERASEBKGND 
) 
 887     return ::CallWindowProc(CASTWNDPROC gs_wndprocNotebookSpinBtn
, 
 888                             hwnd
, message
, wParam
, lParam
); 
 891 LRESULT APIENTRY _EXPORT 
wxNotebookWndProc(HWND hwnd
, 
 896     return ::CallWindowProc(CASTWNDPROC gs_wndprocNotebook
, 
 897                             hwnd
, message
, wParam
, lParam
); 
 900 void wxNotebook::OnEraseBackground(wxEraseEvent
& WXUNUSED(event
)) 
 905 void wxNotebook::OnPaint(wxPaintEvent
& WXUNUSED(event
)) 
 910     ::GetClientRect(GetHwnd(), &rc
); 
 911     wxBitmap 
bmp(rc
.right
, rc
.bottom
); 
 912     memdc
.SelectObject(bmp
); 
 914     const wxLayoutDirection dir 
= dc
.GetLayoutDirection(); 
 915     memdc
.SetLayoutDirection(dir
); 
 917     // if there is no special brush just use the solid background colour 
 919     HBRUSH hbr 
= (HBRUSH
)m_hbrBackground
; 
 926         brush 
= wxBrush(GetBackgroundColour()); 
 927         hbr 
= GetHbrushOf(brush
); 
 930     ::FillRect(GetHdcOf(memdc
), &rc
, hbr
); 
 932     MSWDefWindowProc(WM_PAINT
, (WPARAM
)memdc
.GetHDC(), 0); 
 934     // For some reason in RTL mode, source offset has to be -1, otherwise the  
 935     // right border (physical) remains unpainted. 
 936     const wxCoord ofs 
= dir 
== wxLayout_RightToLeft 
? -1 : 0; 
 937     dc
.Blit(ofs
, 0, rc
.right
, rc
.bottom
, &memdc
, ofs
, 0); 
 940 #endif // USE_NOTEBOOK_ANTIFLICKER 
 942 // ---------------------------------------------------------------------------- 
 943 // wxNotebook callbacks 
 944 // ---------------------------------------------------------------------------- 
 946 void wxNotebook::OnSize(wxSizeEvent
& event
) 
 948     if ( GetPageCount() == 0 ) 
 950         // Prevents droppings on resize, but does cause some flicker 
 951         // when there are no pages. 
 959         // Without this, we can sometimes get droppings at the edges 
 960         // of a notebook, for example a notebook in a splitter window. 
 961         // This needs to be reconciled with the RefreshRect calls 
 962         // at the end of this function, which weren't enough to prevent 
 965         wxSize sz 
= GetClientSize(); 
 967         // Refresh right side 
 968         wxRect 
rect(sz
.x
-4, 0, 4, sz
.y
); 
 971         // Refresh bottom side 
 972         rect 
= wxRect(0, sz
.y
-4, sz
.x
, 4); 
 976         rect 
= wxRect(0, 0, 4, sz
.y
); 
 979 #endif // !__WXWINCE__ 
 981     // fit all the notebook pages to the tab control's display area 
 984     rc
.left 
= rc
.top 
= 0; 
 985     GetSize((int *)&rc
.right
, (int *)&rc
.bottom
); 
 987     // save the total size, we'll use it below 
 988     int widthNbook 
= rc
.right 
- rc
.left
, 
 989         heightNbook 
= rc
.bottom 
- rc
.top
; 
 991     // there seems to be a bug in the implementation of TabCtrl_AdjustRect(): it 
 992     // returns completely false values for multiline tab controls after the tabs 
 993     // are added but before getting the first WM_SIZE (off by ~50 pixels, see 
 995     // http://sf.net/tracker/index.php?func=detail&aid=645323&group_id=9863&atid=109863 
 997     // and the only work around I could find was this ugly hack... without it 
 998     // simply toggling the "multiline" checkbox in the notebook sample resulted 
 999     // in a noticeable page displacement 
1000     if ( HasFlag(wxNB_MULTILINE
) ) 
1002         // avoid an infinite recursion: we get another notification too! 
1003         static bool s_isInOnSize 
= false; 
1005         if ( !s_isInOnSize 
) 
1007             s_isInOnSize 
= true; 
1008             SendMessage(GetHwnd(), WM_SIZE
, SIZE_RESTORED
, 
1009                     MAKELPARAM(rc
.right
, rc
.bottom
)); 
1010             s_isInOnSize 
= false; 
1015     // background bitmap size has changed, update the brush using it too 
1017 #endif // wxUSE_UXTHEME 
1019     TabCtrl_AdjustRect(GetHwnd(), false, &rc
); 
1021     int width 
= rc
.right 
- rc
.left
, 
1022         height 
= rc
.bottom 
- rc
.top
; 
1023     size_t nCount 
= m_pages
.Count(); 
1024     for ( size_t nPage 
= 0; nPage 
< nCount
; nPage
++ ) { 
1025         wxNotebookPage 
*pPage 
= m_pages
[nPage
]; 
1026         pPage
->SetSize(rc
.left
, rc
.top
, width
, height
); 
1030     // unless we had already repainted everything, we now need to refresh 
1031     if ( !HasFlag(wxFULL_REPAINT_ON_RESIZE
) ) 
1033         // invalidate areas not covered by pages 
1034         RefreshRect(wxRect(0, 0, widthNbook
, rc
.top
), false); 
1035         RefreshRect(wxRect(0, rc
.top
, rc
.left
, height
), false); 
1036         RefreshRect(wxRect(0, rc
.bottom
, widthNbook
, heightNbook 
- rc
.bottom
), 
1038         RefreshRect(wxRect(rc
.right
, rc
.top
, widthNbook 
- rc
.right
, height
), 
1042 #if USE_NOTEBOOK_ANTIFLICKER 
1043     // subclass the spin control used by the notebook to scroll pages to 
1044     // prevent it from flickering on resize 
1045     if ( !m_hasSubclassedUpdown 
) 
1047         // iterate over all child windows to find spin button 
1048         for ( HWND child 
= ::GetWindow(GetHwnd(), GW_CHILD
); 
1050               child 
= ::GetWindow(child
, GW_HWNDNEXT
) ) 
1052             wxWindow 
*childWindow 
= wxFindWinFromHandle((WXHWND
)child
); 
1054             // see if it exists, if no wxWindow found then assume it's the spin 
1058                 // subclass the spin button to override WM_ERASEBKGND 
1059                 if ( !gs_wndprocNotebookSpinBtn 
) 
1060                     gs_wndprocNotebookSpinBtn 
= (WXFARPROC
)wxGetWindowProc(child
); 
1062                 wxSetWindowProc(child
, wxNotebookSpinBtnWndProc
); 
1063                 m_hasSubclassedUpdown 
= true; 
1068 #endif // USE_NOTEBOOK_ANTIFLICKER 
1073 void wxNotebook::OnSelChange(wxNotebookEvent
& event
) 
1075     // is it our tab control? 
1076     if ( event
.GetEventObject() == this ) 
1078         UpdateSelection(event
.GetSelection()); 
1081     // we want to give others a chance to process this message as well 
1085 void wxNotebook::OnNavigationKey(wxNavigationKeyEvent
& event
) 
1087     if ( event
.IsWindowChange() ) { 
1089         AdvanceSelection(event
.GetDirection()); 
1092         // we get this event in 3 cases 
1094         // a) one of our pages might have generated it because the user TABbed 
1095         // out from it in which case we should propagate the event upwards and 
1096         // our parent will take care of setting the focus to prev/next sibling 
1100         // b) the parent panel wants to give the focus to us so that we 
1101         // forward it to our selected page. We can't deal with this in 
1102         // OnSetFocus() because we don't know which direction the focus came 
1103         // from in this case and so can't choose between setting the focus to 
1104         // first or last panel child 
1108         // c) we ourselves (see MSWTranslateMessage) generated the event 
1110         wxWindow 
* const parent 
= GetParent(); 
1112         // the wxObject* casts are required to avoid MinGW GCC 2.95.3 ICE 
1113         const bool isFromParent 
= event
.GetEventObject() == (wxObject
*) parent
; 
1114         const bool isFromSelf 
= event
.GetEventObject() == (wxObject
*) this; 
1116         if ( isFromParent 
|| isFromSelf 
) 
1118             // no, it doesn't come from child, case (b) or (c): forward to a 
1119             // page but only if direction is backwards (TAB) or from ourselves, 
1120             if ( m_nSelection 
!= wxNOT_FOUND 
&& 
1121                     (!event
.GetDirection() || isFromSelf
) ) 
1123                 // so that the page knows that the event comes from it's parent 
1124                 // and is being propagated downwards 
1125                 event
.SetEventObject(this); 
1127                 wxWindow 
*page 
= m_pages
[m_nSelection
]; 
1128                 if ( !page
->GetEventHandler()->ProcessEvent(event
) ) 
1132                 //else: page manages focus inside it itself 
1134             else // otherwise set the focus to the notebook itself 
1141             // it comes from our child, case (a), pass to the parent, but only 
1142             // if the direction is forwards. Otherwise set the focus to the 
1143             // notebook itself. The notebook is always the 'first' control of a 
1145             if ( !event
.GetDirection() ) 
1151                 event
.SetCurrentFocus(this); 
1152                 parent
->GetEventHandler()->ProcessEvent(event
); 
1160 bool wxNotebook::DoDrawBackground(WXHDC hDC
, wxWindow 
*child
) 
1162     wxUxThemeHandle 
theme(child 
? child 
: this, L
"TAB"); 
1166     // get the notebook client rect (we're not interested in drawing tabs 
1168     wxRect r 
= GetPageSize(); 
1173     wxCopyRectToRECT(r
, rc
); 
1175     // map rect to the coords of the window we're drawing in 
1177         ::MapWindowPoints(GetHwnd(), GetHwndOf(child
), (POINT 
*)&rc
, 2); 
1179     // we have the content area (page size), but we need to draw all of the 
1180     // background for it to be aligned correctly 
1181     wxUxThemeEngine::Get()->GetThemeBackgroundExtent
 
1190     wxUxThemeEngine::Get()->DrawThemeBackground
 
1203 WXHBRUSH 
wxNotebook::QueryBgBitmap() 
1205     wxRect r 
= GetPageSize(); 
1209     WindowHDC 
hDC(GetHwnd()); 
1210     MemoryHDC 
hDCMem(hDC
); 
1211     CompatibleBitmap 
hBmp(hDC
, r
.x 
+ r
.width
, r
.y 
+ r
.height
); 
1213     SelectInHDC 
selectBmp(hDCMem
, hBmp
); 
1215     if ( !DoDrawBackground((WXHDC
)(HDC
)hDCMem
) ) 
1218     return (WXHBRUSH
)::CreatePatternBrush(hBmp
); 
1221 void wxNotebook::UpdateBgBrush() 
1223     if ( m_hbrBackground 
) 
1224         ::DeleteObject((HBRUSH
)m_hbrBackground
); 
1226     if ( !m_hasBgCol 
&& wxUxThemeEngine::GetIfActive() ) 
1228         m_hbrBackground 
= QueryBgBitmap(); 
1230     else // no themes or we've got user-defined solid colour 
1232         m_hbrBackground 
= NULL
; 
1236 WXHBRUSH 
wxNotebook::MSWGetBgBrushForChild(WXHDC hDC
, WXHWND hWnd
) 
1238     if ( m_hbrBackground 
) 
1240         // before drawing with the background brush, we need to position it 
1243         ::GetWindowRect((HWND
)hWnd
, &rc
); 
1245         ::MapWindowPoints(NULL
, GetHwnd(), (POINT 
*)&rc
, 1); 
1247         if ( !::SetBrushOrgEx((HDC
)hDC
, -rc
.left
, -rc
.top
, NULL
) ) 
1249             wxLogLastError(_T("SetBrushOrgEx(notebook bg brush)")); 
1252         return m_hbrBackground
; 
1255     return wxNotebookBase::MSWGetBgBrushForChild(hDC
, hWnd
); 
1258 bool wxNotebook::MSWPrintChild(WXHDC hDC
, wxWindow 
*child
) 
1260     // solid background colour overrides themed background drawing 
1261     if ( !UseBgCol() && DoDrawBackground(hDC
, child
) ) 
1264     // If we're using a solid colour (for example if we've switched off 
1265     // theming for this notebook), paint it 
1268         wxRect r 
= GetPageSize(); 
1273         wxCopyRectToRECT(r
, rc
); 
1275         // map rect to the coords of the window we're drawing in 
1277             ::MapWindowPoints(GetHwnd(), GetHwndOf(child
), (POINT 
*)&rc
, 2); 
1279         wxBrush 
brush(GetBackgroundColour()); 
1280         HBRUSH hbr 
= GetHbrushOf(brush
); 
1282         ::FillRect((HDC
) hDC
, &rc
, hbr
); 
1287     return wxNotebookBase::MSWPrintChild(hDC
, child
); 
1290 #endif // wxUSE_UXTHEME 
1292 // Windows only: attempts to get colour for UX theme page background 
1293 wxColour 
wxNotebook::GetThemeBackgroundColour() const 
1296     if (wxUxThemeEngine::Get()) 
1298         wxUxThemeHandle 
hTheme((wxNotebook
*) this, L
"TAB"); 
1301             // This is total guesswork. 
1302             // See PlatformSDK\Include\Tmschema.h for values 
1303             COLORREF themeColor
; 
1304             wxUxThemeEngine::Get()->GetThemeColor( 
1308                                         3821 /* FILLCOLORHINT */, 
1312             [DS] Workaround for WindowBlinds: 
1313             Some themes return a near black theme color using FILLCOLORHINT, 
1314             this makes notebook pages have an ugly black background and makes 
1315             text (usually black) unreadable. Retry again with FILLCOLOR. 
1317             This workaround potentially breaks appearance of some themes, 
1318             but in practice it already fixes some themes. 
1320             if (themeColor 
== 1) 
1322                 wxUxThemeEngine::Get()->GetThemeColor( 
1326                                             3802 /* FILLCOLOR */, 
1330             return wxRGBToColour(themeColor
); 
1333 #endif // wxUSE_UXTHEME 
1335     return GetBackgroundColour(); 
1338 // ---------------------------------------------------------------------------- 
1339 // wxNotebook base class virtuals 
1340 // ---------------------------------------------------------------------------- 
1342 #if wxUSE_CONSTRAINTS 
1344 // override these 2 functions to do nothing: everything is done in OnSize 
1346 void wxNotebook::SetConstraintSizes(bool WXUNUSED(recurse
)) 
1348   // don't set the sizes of the pages - their correct size is not yet known 
1349   wxControl::SetConstraintSizes(false); 
1352 bool wxNotebook::DoPhase(int WXUNUSED(nPhase
)) 
1357 #endif // wxUSE_CONSTRAINTS 
1359 // ---------------------------------------------------------------------------- 
1360 // wxNotebook Windows message handlers 
1361 // ---------------------------------------------------------------------------- 
1363 bool wxNotebook::MSWOnScroll(int orientation
, WXWORD nSBCode
, 
1364                              WXWORD pos
, WXHWND control
) 
1366     // don't generate EVT_SCROLLWIN events for the WM_SCROLLs coming from the 
1371     return wxNotebookBase::MSWOnScroll(orientation
, nSBCode
, pos
, control
); 
1374 bool wxNotebook::MSWOnNotify(int idCtrl
, WXLPARAM lParam
, WXLPARAM
* result
) 
1376   wxNotebookEvent 
event(wxEVT_NULL
, m_windowId
); 
1378   NMHDR
* hdr 
= (NMHDR 
*)lParam
; 
1379   switch ( hdr
->code 
) { 
1381       event
.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
); 
1384     case TCN_SELCHANGING
: 
1385       event
.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
); 
1389       return wxControl::MSWOnNotify(idCtrl
, lParam
, result
); 
1392   event
.SetSelection(TabCtrl_GetCurSel(GetHwnd())); 
1393   event
.SetOldSelection(m_nSelection
); 
1394   event
.SetEventObject(this); 
1395   event
.SetInt(idCtrl
); 
1397   bool processed 
= GetEventHandler()->ProcessEvent(event
); 
1398   *result 
= !event
.IsAllowed(); 
1402 #endif // wxUSE_NOTEBOOK