1 /////////////////////////////////////////////////////////////////////////////// 
   2 // Name:        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 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA) 
  13 #pragma implementation "notebook.h" 
  16 // For compilers that support precompilation, includes "wx.h". 
  17 #include "wx/wxprec.h" 
  27   #include  "wx/string.h" 
  32 #include  "wx/imaglist.h" 
  34 #include  "wx/control.h" 
  35 #include  "wx/notebook.h" 
  37 #include  "wx/sysopt.h" 
  38 #include  "wx/dcclient.h" 
  39 #include  "wx/dcmemory.h" 
  41 #include  "wx/msw/private.h" 
  47 #include "wx/msw/winundef.h" 
  50     #include "wx/msw/uxtheme.h" 
  53 // ---------------------------------------------------------------------------- 
  55 // ---------------------------------------------------------------------------- 
  57 // check that the page index is valid 
  58 #define IS_VALID_PAGE(nPage) ((nPage) < GetPageCount()) 
  60 // you can set USE_NOTEBOOK_ANTIFLICKER to 0 for desktop Windows versions too 
  61 // to disable code whih results in flicker-less notebook redrawing at the 
  62 // expense of some extra GDI resource consumption 
  64     // notebooks are never resized under CE anyhow 
  65     #define USE_NOTEBOOK_ANTIFLICKER    0 
  67     #define USE_NOTEBOOK_ANTIFLICKER    1 
  70 // ---------------------------------------------------------------------------- 
  72 // ---------------------------------------------------------------------------- 
  74 // This is a work-around for missing defines in gcc-2.95 headers 
  76     #define TCS_RIGHT       0x0002 
  80     #define TCS_VERTICAL    0x0080 
  84     #define TCS_BOTTOM      TCS_RIGHT 
  87 // ---------------------------------------------------------------------------- 
  89 // ---------------------------------------------------------------------------- 
  91 #if USE_NOTEBOOK_ANTIFLICKER 
  93 // the pointer to standard spin button wnd proc 
  94 static WXFARPROC gs_wndprocNotebookSpinBtn 
= (WXFARPROC
)NULL
; 
  96 // the pointer to standard tab control wnd proc 
  97 static WXFARPROC gs_wndprocNotebook 
= (WXFARPROC
)NULL
;  
  99 LRESULT APIENTRY _EXPORT 
wxNotebookWndProc(HWND hwnd
, 
 104 #endif // USE_NOTEBOOK_ANTIFLICKER 
 106 // ---------------------------------------------------------------------------- 
 108 // ---------------------------------------------------------------------------- 
 110 #include <wx/listimpl.cpp> 
 112 WX_DEFINE_LIST( wxNotebookPageInfoList 
) ; 
 114 DEFINE_EVENT_TYPE(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
) 
 115 DEFINE_EVENT_TYPE(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
) 
 117 BEGIN_EVENT_TABLE(wxNotebook
, wxControl
) 
 118     EVT_NOTEBOOK_PAGE_CHANGED(-1, wxNotebook::OnSelChange
) 
 119     EVT_SIZE(wxNotebook::OnSize
) 
 120     EVT_NAVIGATION_KEY(wxNotebook::OnNavigationKey
) 
 122 #if USE_NOTEBOOK_ANTIFLICKER 
 123     EVT_ERASE_BACKGROUND(wxNotebook::OnEraseBackground
) 
 124     EVT_PAINT(wxNotebook::OnPaint
) 
 125 #endif // USE_NOTEBOOK_ANTIFLICKER 
 128 #if wxUSE_EXTENDED_RTTI 
 129 WX_DEFINE_FLAGS( wxNotebookStyle 
) 
 131 wxBEGIN_FLAGS( wxNotebookStyle 
) 
 132     // new style border flags, we put them first to 
 133     // use them for streaming out 
 134     wxFLAGS_MEMBER(wxBORDER_SIMPLE
) 
 135     wxFLAGS_MEMBER(wxBORDER_SUNKEN
) 
 136     wxFLAGS_MEMBER(wxBORDER_DOUBLE
) 
 137     wxFLAGS_MEMBER(wxBORDER_RAISED
) 
 138     wxFLAGS_MEMBER(wxBORDER_STATIC
) 
 139     wxFLAGS_MEMBER(wxBORDER_NONE
) 
 141     // old style border flags 
 142     wxFLAGS_MEMBER(wxSIMPLE_BORDER
) 
 143     wxFLAGS_MEMBER(wxSUNKEN_BORDER
) 
 144     wxFLAGS_MEMBER(wxDOUBLE_BORDER
) 
 145     wxFLAGS_MEMBER(wxRAISED_BORDER
) 
 146     wxFLAGS_MEMBER(wxSTATIC_BORDER
) 
 147     wxFLAGS_MEMBER(wxBORDER
) 
 149     // standard window styles 
 150     wxFLAGS_MEMBER(wxTAB_TRAVERSAL
) 
 151     wxFLAGS_MEMBER(wxCLIP_CHILDREN
) 
 152     wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW
) 
 153     wxFLAGS_MEMBER(wxWANTS_CHARS
) 
 154     wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE
) 
 155     wxFLAGS_MEMBER(wxALWAYS_SHOW_SB 
) 
 156     wxFLAGS_MEMBER(wxVSCROLL
) 
 157     wxFLAGS_MEMBER(wxHSCROLL
) 
 159     wxFLAGS_MEMBER(wxNB_FIXEDWIDTH
) 
 160     wxFLAGS_MEMBER(wxNB_LEFT
) 
 161     wxFLAGS_MEMBER(wxNB_RIGHT
) 
 162     wxFLAGS_MEMBER(wxNB_BOTTOM
) 
 163     wxFLAGS_MEMBER(wxNB_NOPAGETHEME
) 
 164     wxFLAGS_MEMBER(wxNB_FLAT
) 
 166 wxEND_FLAGS( wxNotebookStyle 
) 
 168 IMPLEMENT_DYNAMIC_CLASS_XTI(wxNotebook
, wxControl
,"wx/notebook.h") 
 169 IMPLEMENT_DYNAMIC_CLASS_XTI(wxNotebookPageInfo
, wxObject 
, "wx/notebook.h" ) 
 171 wxCOLLECTION_TYPE_INFO( wxNotebookPageInfo 
* , wxNotebookPageInfoList 
) ; 
 173 template<> void wxCollectionToVariantArray( wxNotebookPageInfoList 
const &theList
, wxxVariantArray 
&value
) 
 175     wxListCollectionToVariantArray
<wxNotebookPageInfoList::compatibility_iterator
>( theList 
, value 
) ; 
 178 wxBEGIN_PROPERTIES_TABLE(wxNotebook
) 
 179     wxEVENT_PROPERTY( PageChanging 
, wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING 
, wxNotebookEvent 
) 
 180     wxEVENT_PROPERTY( PageChanged 
, wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED 
, wxNotebookEvent 
) 
 182     wxPROPERTY_COLLECTION( PageInfos 
, wxNotebookPageInfoList 
, wxNotebookPageInfo
* , AddPageInfo 
, GetPageInfos 
, 0 /*flags*/ , wxT("Helpstring") , wxT("group")) 
 183     wxPROPERTY_FLAGS( WindowStyle 
, wxNotebookStyle 
, long , SetWindowStyleFlag 
, GetWindowStyleFlag 
, EMPTY_MACROVALUE 
, 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style 
 184 wxEND_PROPERTIES_TABLE() 
 186 wxBEGIN_HANDLERS_TABLE(wxNotebook
) 
 187 wxEND_HANDLERS_TABLE() 
 189 wxCONSTRUCTOR_5( wxNotebook 
, wxWindow
* , Parent 
, wxWindowID 
, Id 
, wxPoint 
, Position 
, wxSize 
, Size 
, long , WindowStyle
) 
 192 wxBEGIN_PROPERTIES_TABLE(wxNotebookPageInfo
) 
 193     wxREADONLY_PROPERTY( Page 
, wxNotebookPage
* , GetPage 
, EMPTY_MACROVALUE 
, 0 /*flags*/ , wxT("Helpstring") , wxT("group")) 
 194     wxREADONLY_PROPERTY( Text 
, wxString 
, GetText 
, wxString() , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) 
 195     wxREADONLY_PROPERTY( Selected 
, bool , GetSelected 
, false, 0 /*flags*/ , wxT("Helpstring") , wxT("group") ) 
 196     wxREADONLY_PROPERTY( ImageId 
, int , GetImageId 
, -1 , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) 
 197 wxEND_PROPERTIES_TABLE() 
 199 wxBEGIN_HANDLERS_TABLE(wxNotebookPageInfo
) 
 200 wxEND_HANDLERS_TABLE() 
 202 wxCONSTRUCTOR_4( wxNotebookPageInfo 
, wxNotebookPage
* , Page 
, wxString 
, Text 
, bool , Selected 
, int , ImageId 
) 
 205 IMPLEMENT_DYNAMIC_CLASS(wxNotebook
, wxControl
) 
 206 IMPLEMENT_DYNAMIC_CLASS(wxNotebookPageInfo
, wxObject 
) 
 208 IMPLEMENT_DYNAMIC_CLASS(wxNotebookEvent
, wxNotifyEvent
) 
 210 // ============================================================================ 
 212 // ============================================================================ 
 214 // ---------------------------------------------------------------------------- 
 215 // wxNotebook construction 
 216 // ---------------------------------------------------------------------------- 
 218 const wxNotebookPageInfoList
& wxNotebook::GetPageInfos() const 
 220     wxNotebookPageInfoList
* list 
= const_cast< wxNotebookPageInfoList
* >( &m_pageInfos 
) ; 
 221     WX_CLEAR_LIST( wxNotebookPageInfoList 
, *list 
) ; 
 222     for( size_t i 
= 0 ; i 
< GetPageCount() ; ++i 
) 
 224         wxNotebookPageInfo 
*info 
= new wxNotebookPageInfo() ; 
 225         info
->Create( const_cast<wxNotebook
*>(this)->GetPage(i
) , GetPageText(i
) , GetSelection() == int(i
) , GetPageImage(i
) ) ; 
 226         list
->Append( info 
) ; 
 231 // common part of all ctors 
 232 void wxNotebook::Init() 
 238   m_hbrBackground 
= NULL
; 
 239 #endif // wxUSE_UXTHEME 
 241 #if USE_NOTEBOOK_ANTIFLICKER 
 242   m_hasSubclassedUpdown 
= false; 
 243 #endif // USE_NOTEBOOK_ANTIFLICKER 
 246 // default for dynamic class 
 247 wxNotebook::wxNotebook() 
 252 // the same arguments as for wxControl 
 253 wxNotebook::wxNotebook(wxWindow 
*parent
, 
 258                        const wxString
& name
) 
 262   Create(parent
, id
, pos
, size
, style
, name
); 
 266 bool wxNotebook::Create(wxWindow 
*parent
, 
 271                         const wxString
& name
) 
 274     // Not sure why, but without this style, there is no border 
 275     // around the notebook tabs. 
 276     if (style 
& wxNB_FLAT
) 
 277         style 
|= wxBORDER_SUNKEN
; 
 280     // comctl32.dll 6.0 doesn't support non-top tabs with visual styles (the 
 281     // control is simply not rendered correctly), so disable them in this case 
 282     const int verComCtl32 
= wxApp::GetComCtl32Version(); 
 283     if ( verComCtl32 
== 600 ) 
 285         // check if we use themes at all -- if we don't, we're still ok 
 287         if ( wxUxThemeEngine::GetIfActive() ) 
 290             style 
&= ~(wxNB_BOTTOM 
| wxNB_LEFT 
| wxNB_RIGHT
); 
 294     LPCTSTR className 
= WC_TABCONTROL
; 
 296 #if USE_NOTEBOOK_ANTIFLICKER 
 297     // SysTabCtl32 class has natively CS_HREDRAW and CS_VREDRAW enabled and it 
 298     // causes horrible flicker when resizing notebook, so get rid of it by 
 299     // using a class without these styles (but otherwise identical to it) 
 300     if ( !HasFlag(wxFULL_REPAINT_ON_RESIZE
) ) 
 302         static ClassRegistrar s_clsNotebook
; 
 303         if ( !s_clsNotebook
.IsInitialized() ) 
 305             // get a copy of standard class and modify it 
 308             if ( ::GetClassInfo(NULL
, WC_TABCONTROL
, &wc
) ) 
 311                     wx_reinterpret_cast(WXFARPROC
, wc
.lpfnWndProc
); 
 312                 wc
.lpszClassName 
= wxT("_wx_SysTabCtl32"); 
 313                 wc
.style 
&= ~(CS_HREDRAW 
| CS_VREDRAW
); 
 314                 wc
.hInstance 
= wxGetInstance(); 
 315                 wc
.lpfnWndProc 
= wxNotebookWndProc
; 
 316                 s_clsNotebook
.Register(wc
); 
 320                 wxLogLastError(_T("GetClassInfoEx(SysTabCtl32)")); 
 324         // use our custom class if available but fall back to the standard 
 325         // notebook if we failed to register it 
 326         if ( s_clsNotebook
.IsRegistered() ) 
 328             // it's ok to use c_str() here as the static s_clsNotebook object 
 329             // has sufficiently long lifetime 
 330             className 
= s_clsNotebook
.GetName().c_str(); 
 333 #endif // USE_NOTEBOOK_ANTIFLICKER 
 335     if ( !CreateControl(parent
, id
, pos
, size
, style 
| wxTAB_TRAVERSAL
, 
 336                         wxDefaultValidator
, name
) ) 
 339     if ( !MSWCreateControl(className
, wxEmptyString
, pos
, size
) ) 
 343     if ( HasFlag(wxNB_NOPAGETHEME
) || 
 344             wxSystemOptions::IsFalse(wxT("msw.notebook.themed-background")) ) 
 346         SetBackgroundColour(GetThemeBackgroundColour()); 
 348 #endif // wxUSE_UXTHEME 
 350     // Undocumented hack to get flat notebook style 
 351     // In fact, we should probably only do this in some 
 352     // curcumstances, i.e. if we know we will have a border 
 353     // at the bottom (the tab control doesn't draw it itself) 
 354 #if defined(__POCKETPC__) || defined(__SMARTPHONE__) 
 355     if (HasFlag(wxNB_FLAT
)) 
 357         SendMessage(GetHwnd(), CCM_SETVERSION
, COMCTL32_VERSION
, 0); 
 359             SetBackgroundColour(*wxWHITE
); 
 365 WXDWORD 
wxNotebook::MSWGetStyle(long style
, WXDWORD 
*exstyle
) const 
 367     WXDWORD tabStyle 
= wxControl::MSWGetStyle(style
, exstyle
); 
 369     tabStyle 
|= WS_TABSTOP 
| TCS_TABS
; 
 371     if ( style 
& wxNB_MULTILINE 
) 
 372         tabStyle 
|= TCS_MULTILINE
; 
 373     if ( style 
& wxNB_FIXEDWIDTH 
) 
 374         tabStyle 
|= TCS_FIXEDWIDTH
; 
 376     if ( style 
& wxNB_BOTTOM 
) 
 377         tabStyle 
|= TCS_RIGHT
; 
 378     else if ( style 
& wxNB_LEFT 
) 
 379         tabStyle 
|= TCS_VERTICAL
; 
 380     else if ( style 
& wxNB_RIGHT 
) 
 381         tabStyle 
|= TCS_VERTICAL 
| TCS_RIGHT
; 
 386         // note that we never want to have the default WS_EX_CLIENTEDGE style 
 387         // as it looks too ugly for the notebooks 
 394 wxNotebook::~wxNotebook() 
 397     if ( m_hbrBackground 
) 
 398         ::DeleteObject((HBRUSH
)m_hbrBackground
); 
 399 #endif // wxUSE_UXTHEME 
 402 // ---------------------------------------------------------------------------- 
 403 // wxNotebook accessors 
 404 // ---------------------------------------------------------------------------- 
 406 size_t wxNotebook::GetPageCount() const 
 409   wxASSERT( (int)m_pages
.Count() == TabCtrl_GetItemCount(GetHwnd()) ); 
 411   return m_pages
.Count(); 
 414 int wxNotebook::GetRowCount() const 
 416   return TabCtrl_GetRowCount(GetHwnd()); 
 419 int wxNotebook::SetSelection(size_t nPage
) 
 421   wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxNOT_FOUND
, wxT("notebook page out of range") ); 
 423   if ( int(nPage
) != m_nSelection 
) 
 425     wxNotebookEvent 
event(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
, m_windowId
); 
 426     event
.SetSelection(nPage
); 
 427     event
.SetOldSelection(m_nSelection
); 
 428     event
.SetEventObject(this); 
 429     if ( !GetEventHandler()->ProcessEvent(event
) || event
.IsAllowed() ) 
 431       // program allows the page change 
 432       event
.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
); 
 433       (void)GetEventHandler()->ProcessEvent(event
); 
 435       TabCtrl_SetCurSel(GetHwnd(), nPage
); 
 442 bool wxNotebook::SetPageText(size_t nPage
, const wxString
& strText
) 
 444   wxCHECK_MSG( IS_VALID_PAGE(nPage
), false, wxT("notebook page out of range") ); 
 447   tcItem
.mask 
= TCIF_TEXT
; 
 448   tcItem
.pszText 
= (wxChar 
*)strText
.c_str(); 
 450   return TabCtrl_SetItem(GetHwnd(), nPage
, &tcItem
) != 0; 
 453 wxString 
wxNotebook::GetPageText(size_t nPage
) const 
 455   wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxEmptyString
, wxT("notebook page out of range") ); 
 459   tcItem
.mask 
= TCIF_TEXT
; 
 460   tcItem
.pszText 
= buf
; 
 461   tcItem
.cchTextMax 
= WXSIZEOF(buf
); 
 464   if ( TabCtrl_GetItem(GetHwnd(), nPage
, &tcItem
) ) 
 465     str 
= tcItem
.pszText
; 
 470 int wxNotebook::GetPageImage(size_t nPage
) const 
 472   wxCHECK_MSG( IS_VALID_PAGE(nPage
), -1, wxT("notebook page out of range") ); 
 475   tcItem
.mask 
= TCIF_IMAGE
; 
 477   return TabCtrl_GetItem(GetHwnd(), nPage
, &tcItem
) ? tcItem
.iImage 
: -1; 
 480 bool wxNotebook::SetPageImage(size_t nPage
, int nImage
) 
 482   wxCHECK_MSG( IS_VALID_PAGE(nPage
), false, wxT("notebook page out of range") ); 
 485   tcItem
.mask 
= TCIF_IMAGE
; 
 486   tcItem
.iImage 
= nImage
; 
 488   return TabCtrl_SetItem(GetHwnd(), nPage
, &tcItem
) != 0; 
 491 void wxNotebook::SetImageList(wxImageList
* imageList
) 
 493   wxNotebookBase::SetImageList(imageList
); 
 497     TabCtrl_SetImageList(GetHwnd(), (HIMAGELIST
)imageList
->GetHIMAGELIST()); 
 501 // ---------------------------------------------------------------------------- 
 502 // wxNotebook size settings 
 503 // ---------------------------------------------------------------------------- 
 505 wxRect 
wxNotebook::GetPageSize() const 
 510     ::GetClientRect(GetHwnd(), &rc
); 
 512     // This check is to work around a bug in TabCtrl_AdjustRect which will 
 513     // cause a crash on win2k or on XP with themes disabled if either 
 514     // wxNB_MULTILINE is used or tabs are placed on a side, if the rectangle 
 517     // The value of 20 is chosen arbitrarily but seems to work 
 518     if ( rc
.right 
> 20 && rc
.bottom 
> 20 ) 
 520         TabCtrl_AdjustRect(GetHwnd(), false, &rc
); 
 522         wxCopyRECTToRect(rc
, r
); 
 528 void wxNotebook::SetPageSize(const wxSize
& size
) 
 530     // transform the page size into the notebook size 
 537     TabCtrl_AdjustRect(GetHwnd(), true, &rc
); 
 540     SetSize(rc
.right 
- rc
.left
, rc
.bottom 
- rc
.top
); 
 543 void wxNotebook::SetPadding(const wxSize
& padding
) 
 545     TabCtrl_SetPadding(GetHwnd(), padding
.x
, padding
.y
); 
 548 // Windows-only at present. Also, you must use the wxNB_FIXEDWIDTH 
 550 void wxNotebook::SetTabSize(const wxSize
& sz
) 
 552     ::SendMessage(GetHwnd(), TCM_SETITEMSIZE
, 0, MAKELPARAM(sz
.x
, sz
.y
)); 
 555 wxSize 
wxNotebook::CalcSizeFromPage(const wxSize
& sizePage
) const 
 557     wxSize sizeTotal 
= sizePage
; 
 559     // We need to make getting tab size part of the wxWidgets API. 
 561     if (GetPageCount() > 0) 
 564         TabCtrl_GetItemRect((HWND
) GetHWND(), 0, & rect
); 
 565         tabSize
.x 
= rect
.right 
- rect
.left
; 
 566         tabSize
.y 
= rect
.bottom 
- rect
.top
; 
 568     if ( HasFlag(wxNB_LEFT
) || HasFlag(wxNB_RIGHT
) ) 
 570         sizeTotal
.x 
+= tabSize
.x 
+ 7; 
 576         sizeTotal
.y 
+= tabSize
.y 
+ 7; 
 582 void wxNotebook::AdjustPageSize(wxNotebookPage 
*page
) 
 584     wxCHECK_RET( page
, _T("NULL page in wxNotebook::AdjustPageSize") ); 
 586     const wxRect r 
= GetPageSize(); 
 593 // ---------------------------------------------------------------------------- 
 594 // wxNotebook operations 
 595 // ---------------------------------------------------------------------------- 
 597 // remove one page from the notebook, without deleting 
 598 wxNotebookPage 
*wxNotebook::DoRemovePage(size_t nPage
) 
 600     wxNotebookPage 
*pageRemoved 
= wxNotebookBase::DoRemovePage(nPage
); 
 604     TabCtrl_DeleteItem(GetHwnd(), nPage
); 
 606     if ( m_pages
.IsEmpty() ) 
 608         // no selection any more, the notebook becamse empty 
 611     else // notebook still not empty 
 613         int selNew 
= TabCtrl_GetCurSel(GetHwnd()); 
 616             // No selection change, just refresh the current selection. 
 617             // Because it could be that the slection index changed 
 618             // we need to update it. 
 619             // Note: this does not mean the selection it self changed. 
 620             m_nSelection 
= selNew
; 
 621             m_pages
[m_nSelection
]->Refresh(); 
 623         else if (int(nPage
) == m_nSelection
) 
 625             // The selection was deleted. 
 627             // Determine new selection. 
 628             if (m_nSelection 
== int(GetPageCount())) 
 629                 selNew 
= m_nSelection 
- 1; 
 631                 selNew 
= m_nSelection
; 
 633             // m_nSelection must be always valid so reset it before calling 
 636             SetSelection(selNew
); 
 640             wxFAIL
; // Windows did not behave ok. 
 648 bool wxNotebook::DeleteAllPages() 
 650   size_t nPageCount 
= GetPageCount(); 
 652   for ( nPage 
= 0; nPage 
< nPageCount
; nPage
++ ) 
 653     delete m_pages
[nPage
]; 
 657   TabCtrl_DeleteAllItems(GetHwnd()); 
 661   InvalidateBestSize(); 
 665 // same as AddPage() but does it at given position 
 666 bool wxNotebook::InsertPage(size_t nPage
, 
 667                             wxNotebookPage 
*pPage
, 
 668                             const wxString
& strText
, 
 672     wxCHECK_MSG( pPage 
!= NULL
, false, _T("NULL page in wxNotebook::InsertPage") ); 
 673     wxCHECK_MSG( IS_VALID_PAGE(nPage
) || nPage 
== GetPageCount(), false, 
 674                  _T("invalid index in wxNotebook::InsertPage") ); 
 676     wxASSERT_MSG( pPage
->GetParent() == this, 
 677                     _T("notebook pages must have notebook as parent") ); 
 679     // add a new tab to the control 
 680     // ---------------------------- 
 682     // init all fields to 0 
 684     wxZeroMemory(tcItem
); 
 686     // set the image, if any 
 689         tcItem
.mask 
|= TCIF_IMAGE
; 
 690         tcItem
.iImage  
= imageId
; 
 694     if ( !strText
.empty() ) 
 696         tcItem
.mask 
|= TCIF_TEXT
; 
 697         tcItem
.pszText 
= (wxChar 
*)strText
.c_str(); // const_cast 
 700     // hide the page: unless it is selected, it shouldn't be shown (and if it 
 701     // is selected it will be shown later) 
 702     HWND hwnd 
= GetWinHwnd(pPage
); 
 703     SetWindowLong(hwnd
, GWL_STYLE
, GetWindowLong(hwnd
, GWL_STYLE
) & ~WS_VISIBLE
); 
 705     // this updates internal flag too -- otherwise it would get out of sync 
 706     // with the real state 
 710     // fit the notebook page to the tab control's display area: this should be 
 711     // done before adding it to the notebook or TabCtrl_InsertItem() will 
 712     // change the notebooks size itself! 
 713     AdjustPageSize(pPage
); 
 715     // finally do insert it 
 716     if ( TabCtrl_InsertItem(GetHwnd(), nPage
, &tcItem
) == -1 ) 
 718         wxLogError(wxT("Can't create the notebook page '%s'."), strText
.c_str()); 
 723     // succeeded: save the pointer to the page 
 724     m_pages
.Insert(pPage
, nPage
); 
 726     // we may need to adjust the size again if the notebook size changed: 
 727     // normally this only happens for the first page we add (the tabs which 
 728     // hadn't been there before are now shown) but for a multiline notebook it 
 729     // can happen for any page at all as a new row could have been started 
 730     if ( m_pages
.GetCount() == 1 || HasFlag(wxNB_MULTILINE
) ) 
 732         AdjustPageSize(pPage
); 
 735     // now deal with the selection 
 736     // --------------------------- 
 738     // if the inserted page is before the selected one, we must update the 
 739     // index of the selected page 
 740     if ( int(nPage
) <= m_nSelection 
) 
 742         // one extra page added 
 746     // some page should be selected: either this one or the first one if there 
 747     // is still no selection 
 751     else if ( m_nSelection 
== -1 ) 
 755         SetSelection(selNew
); 
 757     InvalidateBestSize(); 
 762 int wxNotebook::HitTest(const wxPoint
& pt
, long *flags
) const 
 764     TC_HITTESTINFO hitTestInfo
; 
 765     hitTestInfo
.pt
.x 
= pt
.x
; 
 766     hitTestInfo
.pt
.y 
= pt
.y
; 
 767     int item 
= TabCtrl_HitTest(GetHwnd(), &hitTestInfo
); 
 773         if ((hitTestInfo
.flags 
& TCHT_NOWHERE
) == TCHT_NOWHERE
) 
 774             *flags 
|= wxNB_HITTEST_NOWHERE
; 
 775         if ((hitTestInfo
.flags 
& TCHT_ONITEM
) == TCHT_ONITEM
) 
 776             *flags 
|= wxNB_HITTEST_ONITEM
; 
 777         if ((hitTestInfo
.flags 
& TCHT_ONITEMICON
) == TCHT_ONITEMICON
) 
 778             *flags 
|= wxNB_HITTEST_ONICON
; 
 779         if ((hitTestInfo
.flags 
& TCHT_ONITEMLABEL
) == TCHT_ONITEMLABEL
) 
 780             *flags 
|= wxNB_HITTEST_ONLABEL
; 
 786 // ---------------------------------------------------------------------------- 
 787 // flicker-less notebook redraw 
 788 // ---------------------------------------------------------------------------- 
 790 #if USE_NOTEBOOK_ANTIFLICKER 
 792 // wnd proc for the spin button 
 793 LRESULT APIENTRY _EXPORT 
wxNotebookSpinBtnWndProc(HWND hwnd
, 
 798     if ( message 
== WM_ERASEBKGND 
) 
 801     return ::CallWindowProc(CASTWNDPROC gs_wndprocNotebookSpinBtn
, 
 802                             hwnd
, message
, wParam
, lParam
); 
 805 LRESULT APIENTRY _EXPORT 
wxNotebookWndProc(HWND hwnd
, 
 810     return ::CallWindowProc(CASTWNDPROC gs_wndprocNotebook
, 
 811                             hwnd
, message
, wParam
, lParam
); 
 816 void wxNotebook::OnEraseBackground(wxEraseEvent
& WXUNUSED(event
)) 
 821 void wxNotebook::OnPaint(wxPaintEvent
& WXUNUSED(event
)) 
 826     ::GetClientRect(GetHwnd(), &rc
); 
 827     wxBitmap 
bmp(rc
.right
, rc
.bottom
); 
 828     memdc
.SelectObject(bmp
); 
 830     // if there is no special brush just use the solid background colour 
 831     HBRUSH hbr 
= (HBRUSH
)m_hbrBackground
; 
 835         brush 
= wxBrush(GetBackgroundColour()); 
 836         hbr 
= GetHbrushOf(brush
); 
 839     ::FillRect(GetHdcOf(memdc
), &rc
, hbr
); 
 841     MSWDefWindowProc(WM_PAINT
, (WPARAM
)memdc
.GetHDC(), 0); 
 843     dc
.Blit(0, 0, rc
.right
, rc
.bottom
, &memdc
, 0, 0); 
 846 #endif // USE_NOTEBOOK_ANTIFLICKER 
 848 // ---------------------------------------------------------------------------- 
 849 // wxNotebook callbacks 
 850 // ---------------------------------------------------------------------------- 
 852 void wxNotebook::OnSize(wxSizeEvent
& event
) 
 854     if ( GetPageCount() == 0 ) 
 856         // Prevents droppings on resize, but does cause some flicker 
 857         // when there are no pages. 
 865         // Without this, we can sometimes get droppings at the edges 
 866         // of a notebook, for example a notebook in a splitter window. 
 867         // This needs to be reconciled with the RefreshRect calls 
 868         // at the end of this function, which weren't enough to prevent 
 871         wxSize sz 
= GetClientSize(); 
 873         // Refresh right side 
 874         wxRect 
rect(sz
.x
-4, 0, 4, sz
.y
); 
 877         // Refresh bottom side 
 878         rect 
= wxRect(0, sz
.y
-4, sz
.x
, 4); 
 882         rect 
= wxRect(0, 0, 4, sz
.y
); 
 885 #endif // !__WXWINCE__ 
 887     // fit all the notebook pages to the tab control's display area 
 890     rc
.left 
= rc
.top 
= 0; 
 891     GetSize((int *)&rc
.right
, (int *)&rc
.bottom
); 
 893     // save the total size, we'll use it below 
 894     int widthNbook 
= rc
.right 
- rc
.left
, 
 895         heightNbook 
= rc
.bottom 
- rc
.top
; 
 897     // there seems to be a bug in the implementation of TabCtrl_AdjustRect(): it 
 898     // returns completely false values for multiline tab controls after the tabs 
 899     // are added but before getting the first WM_SIZE (off by ~50 pixels, see 
 901     // http://sf.net/tracker/index.php?func=detail&aid=645323&group_id=9863&atid=109863 
 903     // and the only work around I could find was this ugly hack... without it 
 904     // simply toggling the "multiline" checkbox in the notebook sample resulted 
 905     // in a noticeable page displacement 
 906     if ( HasFlag(wxNB_MULTILINE
) ) 
 908         // avoid an infinite recursion: we get another notification too! 
 909         static bool s_isInOnSize 
= false; 
 914             SendMessage(GetHwnd(), WM_SIZE
, SIZE_RESTORED
, 
 915                     MAKELPARAM(rc
.right
, rc
.bottom
)); 
 916             s_isInOnSize 
= false; 
 921     // background bitmap size has changed, update the brush using it too 
 923 #endif // wxUSE_UXTHEME 
 925     TabCtrl_AdjustRect(GetHwnd(), false, &rc
); 
 927     int width 
= rc
.right 
- rc
.left
, 
 928         height 
= rc
.bottom 
- rc
.top
; 
 929     size_t nCount 
= m_pages
.Count(); 
 930     for ( size_t nPage 
= 0; nPage 
< nCount
; nPage
++ ) { 
 931         wxNotebookPage 
*pPage 
= m_pages
[nPage
]; 
 932         pPage
->SetSize(rc
.left
, rc
.top
, width
, height
); 
 936     // unless we had already repainted everything, we now need to refresh 
 937     if ( !HasFlag(wxFULL_REPAINT_ON_RESIZE
) ) 
 939         // invalidate areas not covered by pages 
 940         RefreshRect(wxRect(0, 0, widthNbook
, rc
.top
), false); 
 941         RefreshRect(wxRect(0, rc
.top
, rc
.left
, height
), false); 
 942         RefreshRect(wxRect(0, rc
.bottom
, widthNbook
, heightNbook 
- rc
.bottom
), 
 944         RefreshRect(wxRect(rc
.right
, rc
.top
, widthNbook 
- rc
.right
, height
), 
 948 #if USE_NOTEBOOK_ANTIFLICKER 
 949     // subclass the spin control used by the notebook to scroll pages to 
 950     // prevent it from flickering on resize 
 951     if ( !m_hasSubclassedUpdown 
) 
 953         // iterate over all child windows to find spin button 
 954         for ( HWND child 
= ::GetWindow(GetHwnd(), GW_CHILD
); 
 956               child 
= ::GetWindow(child
, GW_HWNDNEXT
) ) 
 958             wxWindow 
*childWindow 
= wxFindWinFromHandle((WXHWND
)child
); 
 960             // see if it exists, if no wxWindow found then assume it's the spin 
 964                 // subclass the spin button to override WM_ERASEBKGND 
 965                 if ( !gs_wndprocNotebookSpinBtn 
) 
 966                     gs_wndprocNotebookSpinBtn 
= (WXFARPROC
)wxGetWindowProc(child
); 
 968                 wxSetWindowProc(child
, wxNotebookSpinBtnWndProc
); 
 969                 m_hasSubclassedUpdown 
= true; 
 974 #endif // USE_NOTEBOOK_ANTIFLICKER 
 979 void wxNotebook::OnSelChange(wxNotebookEvent
& event
) 
 981   // is it our tab control? 
 982   if ( event
.GetEventObject() == this ) 
 984       int sel 
= event
.GetOldSelection(); 
 986         m_pages
[sel
]->Show(false); 
 988       sel 
= event
.GetSelection(); 
 991         wxNotebookPage 
*pPage 
= m_pages
[sel
]; 
 994         // As per bug report: 
 995         // http://sourceforge.net/tracker/index.php?func=detail&aid=1150659&group_id=9863&atid=109863, 
 996         // we should not set the page focus (and thereby the focus for 
 997         // a child window) since it erroneously selects radio button controls and also 
 998         // breaks keyboard handling for a notebook's scroll buttons. So 
 999         // we always focus the notebook and not the page. 
1003       else // no pages in the notebook, give the focus to itself 
1011   // we want to give others a chance to process this message as well 
1015 bool wxNotebook::MSWTranslateMessage(WXMSG 
*wxmsg
) 
1017     const MSG 
* const msg 
= (MSG 
*)wxmsg
; 
1019     // intercept TAB, CTRL+TAB and CTRL+SHIFT+TAB for processing by wxNotebook. 
1020     // TAB will be passed to the currently selected page, CTRL+TAB and 
1021     // CTRL+SHIFT+TAB will be processed by the notebook itself. do not 
1022     // intercept SHIFT+TAB. This goes to the parent of the notebook which will 
1024     if ( msg
->message 
== WM_KEYDOWN 
&& msg
->wParam 
== VK_TAB 
&& 
1025             msg
->hwnd 
== GetHwnd() && 
1026                 (wxIsCtrlDown() || !wxIsShiftDown()) ) 
1028         return MSWProcessMessage(wxmsg
); 
1034 void wxNotebook::OnNavigationKey(wxNavigationKeyEvent
& event
) 
1036     if ( event
.IsWindowChange() ) { 
1038         AdvanceSelection(event
.GetDirection()); 
1041         // we get this event in 3 cases 
1043         // a) one of our pages might have generated it because the user TABbed 
1044         // out from it in which case we should propagate the event upwards and 
1045         // our parent will take care of setting the focus to prev/next sibling 
1049         // b) the parent panel wants to give the focus to us so that we 
1050         // forward it to our selected page. We can't deal with this in 
1051         // OnSetFocus() because we don't know which direction the focus came 
1052         // from in this case and so can't choose between setting the focus to 
1053         // first or last panel child 
1057         // c) we ourselves (see MSWTranslateMessage) generated the event 
1059         wxWindow 
* const parent 
= GetParent(); 
1061         // the wxObject* casts are required to avoid MinGW GCC 2.95.3 ICE 
1062         const bool isFromParent 
= event
.GetEventObject() == (wxObject
*) parent
; 
1063         const bool isFromSelf 
= event
.GetEventObject() == (wxObject
*) this; 
1065         if ( isFromParent 
|| isFromSelf 
) 
1067             // no, it doesn't come from child, case (b) or (c): forward to a 
1068             // page but only if direction is backwards (TAB) or from ourselves, 
1069             if ( m_nSelection 
!= -1 && 
1070                     (!event
.GetDirection() || isFromSelf
) ) 
1072                 // so that the page knows that the event comes from it's parent 
1073                 // and is being propagated downwards 
1074                 event
.SetEventObject(this); 
1076                 wxWindow 
*page 
= m_pages
[m_nSelection
]; 
1077                 if ( !page
->GetEventHandler()->ProcessEvent(event
) ) 
1081                 //else: page manages focus inside it itself 
1083             else // otherwise set the focus to the notebook itself 
1090             // it comes from our child, case (a), pass to the parent, but only 
1091             // if the direction is forwards. Otherwise set the focus to the 
1092             // notebook itself. The notebook is always the 'first' control of a 
1094             if ( !event
.GetDirection() ) 
1100                 event
.SetCurrentFocus(this); 
1101                 parent
->GetEventHandler()->ProcessEvent(event
); 
1109 bool wxNotebook::DoDrawBackground(WXHDC hDC
, wxWindow 
*child
) 
1111     wxUxThemeHandle 
theme(child 
? child 
: this, L
"TAB"); 
1115     // get the notebook client rect (we're not interested in drawing tabs 
1117     wxRect r 
= GetPageSize(); 
1122     wxCopyRectToRECT(r
, rc
); 
1124     // map rect to the coords of the window we're drawing in 
1126         ::MapWindowPoints(GetHwnd(), GetHwndOf(child
), (POINT 
*)&rc
, 2); 
1128     // we have the content area (page size), but we need to draw all of the 
1129     // background for it to be aligned correctly 
1130     wxUxThemeEngine::Get()->GetThemeBackgroundExtent
 
1139     wxUxThemeEngine::Get()->DrawThemeBackground
 
1152 WXHBRUSH 
wxNotebook::QueryBgBitmap() 
1154     wxRect r 
= GetPageSize(); 
1158     WindowHDC 
hDC(GetHwnd()); 
1159     MemoryHDC 
hDCMem(hDC
); 
1160     CompatibleBitmap 
hBmp(hDC
, r
.x 
+ r
.width
, r
.y 
+ r
.height
); 
1162     SelectInHDC 
selectBmp(hDCMem
, hBmp
); 
1164     if ( !DoDrawBackground((WXHDC
)(HDC
)hDCMem
) ) 
1167     return (WXHBRUSH
)::CreatePatternBrush(hBmp
); 
1170 void wxNotebook::UpdateBgBrush() 
1172     if ( m_hbrBackground 
) 
1173         ::DeleteObject((HBRUSH
)m_hbrBackground
); 
1175     if ( !m_hasBgCol 
&& wxUxThemeEngine::GetIfActive() ) 
1177         m_hbrBackground 
= QueryBgBitmap(); 
1179     else // no themes or we've got user-defined solid colour 
1181         m_hbrBackground 
= NULL
; 
1185 WXHBRUSH 
wxNotebook::MSWGetBgBrushForChild(WXHDC hDC
, WXHWND hWnd
) 
1187     if ( m_hbrBackground 
) 
1189         // before drawing with the background brush, we need to position it 
1192         ::GetWindowRect((HWND
)hWnd
, &rc
); 
1194         ::MapWindowPoints(NULL
, GetHwnd(), (POINT 
*)&rc
, 1); 
1196         if ( !::SetBrushOrgEx((HDC
)hDC
, -rc
.left
, -rc
.top
, NULL
) ) 
1198             wxLogLastError(_T("SetBrushOrgEx(notebook bg brush)")); 
1201         return m_hbrBackground
; 
1204     return wxNotebookBase::MSWGetBgBrushForChild(hDC
, hWnd
); 
1207 bool wxNotebook::MSWPrintChild(WXHDC hDC
, wxWindow 
*child
) 
1209     // solid background colour overrides themed background drawing 
1210     if ( !UseBgCol() && DoDrawBackground(hDC
, child
) ) 
1213     // If we're using a solid colour (for example if we've switched off 
1214     // theming for this notebook), paint it 
1217         wxRect r 
= GetPageSize(); 
1222         wxCopyRectToRECT(r
, rc
); 
1224         // map rect to the coords of the window we're drawing in 
1226             ::MapWindowPoints(GetHwnd(), GetHwndOf(child
), (POINT 
*)&rc
, 2); 
1228         wxBrush 
brush(GetBackgroundColour()); 
1229         HBRUSH hbr 
= GetHbrushOf(brush
); 
1231         ::FillRect((HDC
) hDC
, &rc
, hbr
); 
1236     return wxNotebookBase::MSWPrintChild(hDC
, child
); 
1239 #endif // wxUSE_UXTHEME 
1241 // Windows only: attempts to get colour for UX theme page background 
1242 wxColour 
wxNotebook::GetThemeBackgroundColour() const 
1245     if (wxUxThemeEngine::Get()) 
1247         wxUxThemeHandle 
hTheme((wxNotebook
*) this, L
"TAB"); 
1250             // This is total guesswork. 
1251             // See PlatformSDK\Include\Tmschema.h for values 
1252             COLORREF themeColor
; 
1253             wxUxThemeEngine::Get()->GetThemeColor( 
1257                                         3821 /* FILLCOLORHINT */, 
1261             [DS] Workaround for WindowBlinds: 
1262             Some themes return a near black theme color using FILLCOLORHINT, 
1263             this makes notebook pages have an ugly black background and makes 
1264             text (usually black) unreadable. Retry again with FILLCOLOR. 
1266             This workaround potentially breaks appearance of some themes, 
1267             but in practice it already fixes some themes. 
1269             if (themeColor 
== 1) 
1271                 wxUxThemeEngine::Get()->GetThemeColor( 
1275                                             3802 /* FILLCOLOR */, 
1279             return wxRGBToColour(themeColor
); 
1282 #endif // wxUSE_UXTHEME 
1284     return GetBackgroundColour(); 
1287 // ---------------------------------------------------------------------------- 
1288 // wxNotebook base class virtuals 
1289 // ---------------------------------------------------------------------------- 
1291 #if wxUSE_CONSTRAINTS 
1293 // override these 2 functions to do nothing: everything is done in OnSize 
1295 void wxNotebook::SetConstraintSizes(bool WXUNUSED(recurse
)) 
1297   // don't set the sizes of the pages - their correct size is not yet known 
1298   wxControl::SetConstraintSizes(false); 
1301 bool wxNotebook::DoPhase(int WXUNUSED(nPhase
)) 
1306 #endif // wxUSE_CONSTRAINTS 
1308 // ---------------------------------------------------------------------------- 
1309 // wxNotebook Windows message handlers 
1310 // ---------------------------------------------------------------------------- 
1312 bool wxNotebook::MSWOnScroll(int orientation
, WXWORD nSBCode
, 
1313                              WXWORD pos
, WXHWND control
) 
1315     // don't generate EVT_SCROLLWIN events for the WM_SCROLLs coming from the 
1320     return wxNotebookBase::MSWOnScroll(orientation
, nSBCode
, pos
, control
); 
1323 bool wxNotebook::MSWOnNotify(int idCtrl
, WXLPARAM lParam
, WXLPARAM
* result
) 
1325   wxNotebookEvent 
event(wxEVT_NULL
, m_windowId
); 
1327   NMHDR
* hdr 
= (NMHDR 
*)lParam
; 
1328   switch ( hdr
->code 
) { 
1330       event
.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
); 
1333     case TCN_SELCHANGING
: 
1334       event
.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
); 
1338       return wxControl::MSWOnNotify(idCtrl
, lParam
, result
); 
1341   event
.SetSelection(TabCtrl_GetCurSel(GetHwnd())); 
1342   event
.SetOldSelection(m_nSelection
); 
1343   event
.SetEventObject(this); 
1344   event
.SetInt(idCtrl
); 
1346   bool processed 
= GetEventHandler()->ProcessEvent(event
); 
1347   *result 
= !event
.IsAllowed(); 
1351 #endif // wxUSE_NOTEBOOK