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"
23 #include "wx/string.h"
28 #include "wx/imaglist.h"
30 #include "wx/control.h"
31 #include "wx/notebook.h"
33 #include "wx/sysopt.h"
34 #include "wx/dcclient.h"
35 #include "wx/dcmemory.h"
37 #include "wx/msw/private.h"
43 #include "wx/msw/winundef.h"
46 #include "wx/msw/uxtheme.h"
49 // ----------------------------------------------------------------------------
51 // ----------------------------------------------------------------------------
53 // check that the page index is valid
54 #define IS_VALID_PAGE(nPage) ((nPage) < GetPageCount())
56 // you can set USE_NOTEBOOK_ANTIFLICKER to 0 for desktop Windows versions too
57 // to disable code whih results in flicker-less notebook redrawing at the
58 // expense of some extra GDI resource consumption
60 // notebooks are never resized under CE anyhow
61 #define USE_NOTEBOOK_ANTIFLICKER 0
63 #define USE_NOTEBOOK_ANTIFLICKER 1
66 // ----------------------------------------------------------------------------
68 // ----------------------------------------------------------------------------
70 // This is a work-around for missing defines in gcc-2.95 headers
72 #define TCS_RIGHT 0x0002
76 #define TCS_VERTICAL 0x0080
80 #define TCS_BOTTOM TCS_RIGHT
83 // ----------------------------------------------------------------------------
85 // ----------------------------------------------------------------------------
87 #if USE_NOTEBOOK_ANTIFLICKER
89 // the pointer to standard spin button wnd proc
90 static WXFARPROC gs_wndprocNotebookSpinBtn
= (WXFARPROC
)NULL
;
92 // the pointer to standard tab control wnd proc
93 static WXFARPROC gs_wndprocNotebook
= (WXFARPROC
)NULL
;
95 LRESULT APIENTRY _EXPORT
wxNotebookWndProc(HWND hwnd
,
100 #endif // USE_NOTEBOOK_ANTIFLICKER
102 // ----------------------------------------------------------------------------
104 // ----------------------------------------------------------------------------
106 #include "wx/listimpl.cpp"
108 WX_DEFINE_LIST( wxNotebookPageInfoList
)
110 DEFINE_EVENT_TYPE(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
)
111 DEFINE_EVENT_TYPE(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
)
113 BEGIN_EVENT_TABLE(wxNotebook
, wxControl
)
114 EVT_NOTEBOOK_PAGE_CHANGED(wxID_ANY
, wxNotebook::OnSelChange
)
115 EVT_SIZE(wxNotebook::OnSize
)
116 EVT_NAVIGATION_KEY(wxNotebook::OnNavigationKey
)
118 #if USE_NOTEBOOK_ANTIFLICKER
119 EVT_ERASE_BACKGROUND(wxNotebook::OnEraseBackground
)
120 EVT_PAINT(wxNotebook::OnPaint
)
121 #endif // USE_NOTEBOOK_ANTIFLICKER
124 #if wxUSE_EXTENDED_RTTI
125 WX_DEFINE_FLAGS( wxNotebookStyle
)
127 wxBEGIN_FLAGS( wxNotebookStyle
)
128 // new style border flags, we put them first to
129 // use them for streaming out
130 wxFLAGS_MEMBER(wxBORDER_SIMPLE
)
131 wxFLAGS_MEMBER(wxBORDER_SUNKEN
)
132 wxFLAGS_MEMBER(wxBORDER_DOUBLE
)
133 wxFLAGS_MEMBER(wxBORDER_RAISED
)
134 wxFLAGS_MEMBER(wxBORDER_STATIC
)
135 wxFLAGS_MEMBER(wxBORDER_NONE
)
137 // old style border flags
138 wxFLAGS_MEMBER(wxSIMPLE_BORDER
)
139 wxFLAGS_MEMBER(wxSUNKEN_BORDER
)
140 wxFLAGS_MEMBER(wxDOUBLE_BORDER
)
141 wxFLAGS_MEMBER(wxRAISED_BORDER
)
142 wxFLAGS_MEMBER(wxSTATIC_BORDER
)
143 wxFLAGS_MEMBER(wxBORDER
)
145 // standard window styles
146 wxFLAGS_MEMBER(wxTAB_TRAVERSAL
)
147 wxFLAGS_MEMBER(wxCLIP_CHILDREN
)
148 wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW
)
149 wxFLAGS_MEMBER(wxWANTS_CHARS
)
150 wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE
)
151 wxFLAGS_MEMBER(wxALWAYS_SHOW_SB
)
152 wxFLAGS_MEMBER(wxVSCROLL
)
153 wxFLAGS_MEMBER(wxHSCROLL
)
155 wxFLAGS_MEMBER(wxNB_FIXEDWIDTH
)
156 wxFLAGS_MEMBER(wxBK_DEFAULT
)
157 wxFLAGS_MEMBER(wxBK_TOP
)
158 wxFLAGS_MEMBER(wxBK_LEFT
)
159 wxFLAGS_MEMBER(wxBK_RIGHT
)
160 wxFLAGS_MEMBER(wxBK_BOTTOM
)
161 wxFLAGS_MEMBER(wxNB_NOPAGETHEME
)
162 wxFLAGS_MEMBER(wxNB_FLAT
)
164 wxEND_FLAGS( wxNotebookStyle
)
166 IMPLEMENT_DYNAMIC_CLASS_XTI(wxNotebook
, wxControl
,"wx/notebook.h")
167 IMPLEMENT_DYNAMIC_CLASS_XTI(wxNotebookPageInfo
, wxObject
, "wx/notebook.h" )
169 wxCOLLECTION_TYPE_INFO( wxNotebookPageInfo
* , wxNotebookPageInfoList
) ;
171 template<> void wxCollectionToVariantArray( wxNotebookPageInfoList
const &theList
, wxxVariantArray
&value
)
173 wxListCollectionToVariantArray
<wxNotebookPageInfoList::compatibility_iterator
>( theList
, value
) ;
176 wxBEGIN_PROPERTIES_TABLE(wxNotebook
)
177 wxEVENT_PROPERTY( PageChanging
, wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
, wxNotebookEvent
)
178 wxEVENT_PROPERTY( PageChanged
, wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
, wxNotebookEvent
)
180 wxPROPERTY_COLLECTION( PageInfos
, wxNotebookPageInfoList
, wxNotebookPageInfo
* , AddPageInfo
, GetPageInfos
, 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
181 wxPROPERTY_FLAGS( WindowStyle
, wxNotebookStyle
, long , SetWindowStyleFlag
, GetWindowStyleFlag
, EMPTY_MACROVALUE
, 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
182 wxEND_PROPERTIES_TABLE()
184 wxBEGIN_HANDLERS_TABLE(wxNotebook
)
185 wxEND_HANDLERS_TABLE()
187 wxCONSTRUCTOR_5( wxNotebook
, wxWindow
* , Parent
, wxWindowID
, Id
, wxPoint
, Position
, wxSize
, Size
, long , WindowStyle
)
190 wxBEGIN_PROPERTIES_TABLE(wxNotebookPageInfo
)
191 wxREADONLY_PROPERTY( Page
, wxNotebookPage
* , GetPage
, EMPTY_MACROVALUE
, 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
192 wxREADONLY_PROPERTY( Text
, wxString
, GetText
, wxString() , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
193 wxREADONLY_PROPERTY( Selected
, bool , GetSelected
, false, 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
194 wxREADONLY_PROPERTY( ImageId
, int , GetImageId
, -1 , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
195 wxEND_PROPERTIES_TABLE()
197 wxBEGIN_HANDLERS_TABLE(wxNotebookPageInfo
)
198 wxEND_HANDLERS_TABLE()
200 wxCONSTRUCTOR_4( wxNotebookPageInfo
, wxNotebookPage
* , Page
, wxString
, Text
, bool , Selected
, int , ImageId
)
203 IMPLEMENT_DYNAMIC_CLASS(wxNotebook
, wxControl
)
204 IMPLEMENT_DYNAMIC_CLASS(wxNotebookPageInfo
, wxObject
)
206 IMPLEMENT_DYNAMIC_CLASS(wxNotebookEvent
, wxNotifyEvent
)
208 // ============================================================================
210 // ============================================================================
212 // ----------------------------------------------------------------------------
213 // wxNotebook construction
214 // ----------------------------------------------------------------------------
216 const wxNotebookPageInfoList
& wxNotebook::GetPageInfos() const
218 wxNotebookPageInfoList
* list
= const_cast< wxNotebookPageInfoList
* >( &m_pageInfos
) ;
219 WX_CLEAR_LIST( wxNotebookPageInfoList
, *list
) ;
220 for( size_t i
= 0 ; i
< GetPageCount() ; ++i
)
222 wxNotebookPageInfo
*info
= new wxNotebookPageInfo() ;
223 info
->Create( const_cast<wxNotebook
*>(this)->GetPage(i
) , GetPageText(i
) , GetSelection() == int(i
) , GetPageImage(i
) ) ;
224 list
->Append( info
) ;
229 // common part of all ctors
230 void wxNotebook::Init()
236 m_hbrBackground
= NULL
;
237 #endif // wxUSE_UXTHEME
239 #if USE_NOTEBOOK_ANTIFLICKER
240 m_hasSubclassedUpdown
= false;
241 #endif // USE_NOTEBOOK_ANTIFLICKER
244 // default for dynamic class
245 wxNotebook::wxNotebook()
250 // the same arguments as for wxControl
251 wxNotebook::wxNotebook(wxWindow
*parent
,
256 const wxString
& name
)
260 Create(parent
, id
, pos
, size
, style
, name
);
264 bool wxNotebook::Create(wxWindow
*parent
,
269 const wxString
& name
)
272 // Not sure why, but without this style, there is no border
273 // around the notebook tabs.
274 if (style
& wxNB_FLAT
)
275 style
|= wxBORDER_SUNKEN
;
278 // comctl32.dll 6.0 doesn't support non-top tabs with visual styles (the
279 // control is simply not rendered correctly), so disable them in this case
280 const int verComCtl32
= wxApp::GetComCtl32Version();
281 if ( verComCtl32
== 600 )
283 // check if we use themes at all -- if we don't, we're still ok
285 if ( wxUxThemeEngine::GetIfActive() )
288 style
&= ~(wxBK_BOTTOM
| wxBK_LEFT
| wxBK_RIGHT
);
292 LPCTSTR className
= WC_TABCONTROL
;
294 #if USE_NOTEBOOK_ANTIFLICKER
295 // SysTabCtl32 class has natively CS_HREDRAW and CS_VREDRAW enabled and it
296 // causes horrible flicker when resizing notebook, so get rid of it by
297 // using a class without these styles (but otherwise identical to it)
298 if ( !HasFlag(wxFULL_REPAINT_ON_RESIZE
) )
300 static ClassRegistrar s_clsNotebook
;
301 if ( !s_clsNotebook
.IsInitialized() )
303 // get a copy of standard class and modify it
306 if ( ::GetClassInfo(NULL
, WC_TABCONTROL
, &wc
) )
309 wx_reinterpret_cast(WXFARPROC
, wc
.lpfnWndProc
);
310 wc
.lpszClassName
= wxT("_wx_SysTabCtl32");
311 wc
.style
&= ~(CS_HREDRAW
| CS_VREDRAW
);
312 wc
.hInstance
= wxGetInstance();
313 wc
.lpfnWndProc
= wxNotebookWndProc
;
314 s_clsNotebook
.Register(wc
);
318 wxLogLastError(_T("GetClassInfoEx(SysTabCtl32)"));
322 // use our custom class if available but fall back to the standard
323 // notebook if we failed to register it
324 if ( s_clsNotebook
.IsRegistered() )
326 // it's ok to use c_str() here as the static s_clsNotebook object
327 // has sufficiently long lifetime
328 className
= s_clsNotebook
.GetName().c_str();
331 #endif // USE_NOTEBOOK_ANTIFLICKER
333 if ( !CreateControl(parent
, id
, pos
, size
, style
| wxTAB_TRAVERSAL
,
334 wxDefaultValidator
, name
) )
337 if ( !MSWCreateControl(className
, wxEmptyString
, pos
, size
) )
341 if ( HasFlag(wxNB_NOPAGETHEME
) ||
342 wxSystemOptions::IsFalse(wxT("msw.notebook.themed-background")) )
344 SetBackgroundColour(GetThemeBackgroundColour());
346 else // use themed background by default
348 // create backing store
351 #endif // wxUSE_UXTHEME
353 // Undocumented hack to get flat notebook style
354 // In fact, we should probably only do this in some
355 // curcumstances, i.e. if we know we will have a border
356 // at the bottom (the tab control doesn't draw it itself)
357 #if defined(__POCKETPC__) || defined(__SMARTPHONE__)
358 if (HasFlag(wxNB_FLAT
))
360 SendMessage(GetHwnd(), CCM_SETVERSION
, COMCTL32_VERSION
, 0);
362 SetBackgroundColour(*wxWHITE
);
368 WXDWORD
wxNotebook::MSWGetStyle(long style
, WXDWORD
*exstyle
) const
370 WXDWORD tabStyle
= wxControl::MSWGetStyle(style
, exstyle
);
372 tabStyle
|= WS_TABSTOP
| TCS_TABS
;
374 if ( style
& wxNB_MULTILINE
)
375 tabStyle
|= TCS_MULTILINE
;
376 if ( style
& wxNB_FIXEDWIDTH
)
377 tabStyle
|= TCS_FIXEDWIDTH
;
379 if ( style
& wxBK_BOTTOM
)
380 tabStyle
|= TCS_RIGHT
;
381 else if ( style
& wxBK_LEFT
)
382 tabStyle
|= TCS_VERTICAL
;
383 else if ( style
& wxBK_RIGHT
)
384 tabStyle
|= TCS_VERTICAL
| TCS_RIGHT
;
389 // note that we never want to have the default WS_EX_CLIENTEDGE style
390 // as it looks too ugly for the notebooks
397 wxNotebook::~wxNotebook()
400 if ( m_hbrBackground
)
401 ::DeleteObject((HBRUSH
)m_hbrBackground
);
402 #endif // wxUSE_UXTHEME
405 // ----------------------------------------------------------------------------
406 // wxNotebook accessors
407 // ----------------------------------------------------------------------------
409 size_t wxNotebook::GetPageCount() const
412 wxASSERT( (int)m_pages
.Count() == TabCtrl_GetItemCount(GetHwnd()) );
414 return m_pages
.Count();
417 int wxNotebook::GetRowCount() const
419 return TabCtrl_GetRowCount(GetHwnd());
422 int wxNotebook::SetSelection(size_t nPage
)
424 wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxNOT_FOUND
, wxT("notebook page out of range") );
426 if ( int(nPage
) != m_nSelection
)
428 wxNotebookEvent
event(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
, m_windowId
);
429 event
.SetSelection(nPage
);
430 event
.SetOldSelection(m_nSelection
);
431 event
.SetEventObject(this);
432 if ( !GetEventHandler()->ProcessEvent(event
) || event
.IsAllowed() )
434 // program allows the page change
435 event
.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
);
436 (void)GetEventHandler()->ProcessEvent(event
);
438 TabCtrl_SetCurSel(GetHwnd(), nPage
);
445 bool wxNotebook::SetPageText(size_t nPage
, const wxString
& strText
)
447 wxCHECK_MSG( IS_VALID_PAGE(nPage
), false, wxT("notebook page out of range") );
450 tcItem
.mask
= TCIF_TEXT
;
451 tcItem
.pszText
= (wxChar
*)strText
.c_str();
453 if ( !HasFlag(wxNB_MULTILINE
) )
454 return TabCtrl_SetItem(GetHwnd(), nPage
, &tcItem
) != 0;
456 // multiline - we need to set new page size if a line is added or removed
457 int rows
= GetRowCount();
458 bool ret
= TabCtrl_SetItem(GetHwnd(), nPage
, &tcItem
) != 0;
460 if ( ret
&& rows
!= GetRowCount() )
462 const wxRect r
= GetPageSize();
463 const size_t count
= m_pages
.Count();
464 for ( size_t page
= 0; page
< count
; page
++ )
465 m_pages
[page
]->SetSize(r
);
471 wxString
wxNotebook::GetPageText(size_t nPage
) const
473 wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxEmptyString
, wxT("notebook page out of range") );
477 tcItem
.mask
= TCIF_TEXT
;
478 tcItem
.pszText
= buf
;
479 tcItem
.cchTextMax
= WXSIZEOF(buf
);
482 if ( TabCtrl_GetItem(GetHwnd(), nPage
, &tcItem
) )
483 str
= tcItem
.pszText
;
488 int wxNotebook::GetPageImage(size_t nPage
) const
490 wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxNOT_FOUND
, wxT("notebook page out of range") );
493 tcItem
.mask
= TCIF_IMAGE
;
495 return TabCtrl_GetItem(GetHwnd(), nPage
, &tcItem
) ? tcItem
.iImage
: wxNOT_FOUND
;
498 bool wxNotebook::SetPageImage(size_t nPage
, int nImage
)
500 wxCHECK_MSG( IS_VALID_PAGE(nPage
), false, wxT("notebook page out of range") );
503 tcItem
.mask
= TCIF_IMAGE
;
504 tcItem
.iImage
= nImage
;
506 return TabCtrl_SetItem(GetHwnd(), nPage
, &tcItem
) != 0;
509 void wxNotebook::SetImageList(wxImageList
* imageList
)
511 wxNotebookBase::SetImageList(imageList
);
515 (void) TabCtrl_SetImageList(GetHwnd(), (HIMAGELIST
)imageList
->GetHIMAGELIST());
519 // ----------------------------------------------------------------------------
520 // wxNotebook size settings
521 // ----------------------------------------------------------------------------
523 wxRect
wxNotebook::GetPageSize() const
528 ::GetClientRect(GetHwnd(), &rc
);
530 // This check is to work around a bug in TabCtrl_AdjustRect which will
531 // cause a crash on win2k or on XP with themes disabled if either
532 // wxNB_MULTILINE is used or tabs are placed on a side, if the rectangle
535 // The value of 20 is chosen arbitrarily but seems to work
536 if ( rc
.right
> 20 && rc
.bottom
> 20 )
538 TabCtrl_AdjustRect(GetHwnd(), false, &rc
);
540 wxCopyRECTToRect(rc
, r
);
546 void wxNotebook::SetPageSize(const wxSize
& size
)
548 // transform the page size into the notebook size
555 TabCtrl_AdjustRect(GetHwnd(), true, &rc
);
558 SetSize(rc
.right
- rc
.left
, rc
.bottom
- rc
.top
);
561 void wxNotebook::SetPadding(const wxSize
& padding
)
563 TabCtrl_SetPadding(GetHwnd(), padding
.x
, padding
.y
);
566 // Windows-only at present. Also, you must use the wxNB_FIXEDWIDTH
568 void wxNotebook::SetTabSize(const wxSize
& sz
)
570 ::SendMessage(GetHwnd(), TCM_SETITEMSIZE
, 0, MAKELPARAM(sz
.x
, sz
.y
));
573 wxSize
wxNotebook::CalcSizeFromPage(const wxSize
& sizePage
) const
575 wxSize sizeTotal
= sizePage
;
577 // We need to make getting tab size part of the wxWidgets API.
579 if (GetPageCount() > 0)
582 TabCtrl_GetItemRect((HWND
) GetHWND(), 0, & rect
);
583 tabSize
.x
= rect
.right
- rect
.left
;
584 tabSize
.y
= rect
.bottom
- rect
.top
;
586 if ( HasFlag(wxBK_LEFT
) || HasFlag(wxBK_RIGHT
) )
588 sizeTotal
.x
+= tabSize
.x
+ 7;
594 sizeTotal
.y
+= tabSize
.y
+ 7;
600 void wxNotebook::AdjustPageSize(wxNotebookPage
*page
)
602 wxCHECK_RET( page
, _T("NULL page in wxNotebook::AdjustPageSize") );
604 const wxRect r
= GetPageSize();
611 // ----------------------------------------------------------------------------
612 // wxNotebook operations
613 // ----------------------------------------------------------------------------
615 // remove one page from the notebook, without deleting
616 wxNotebookPage
*wxNotebook::DoRemovePage(size_t nPage
)
618 wxNotebookPage
*pageRemoved
= wxNotebookBase::DoRemovePage(nPage
);
622 TabCtrl_DeleteItem(GetHwnd(), nPage
);
624 if ( m_pages
.IsEmpty() )
626 // no selection any more, the notebook becamse empty
629 else // notebook still not empty
631 int selNew
= TabCtrl_GetCurSel(GetHwnd());
634 // No selection change, just refresh the current selection.
635 // Because it could be that the slection index changed
636 // we need to update it.
637 // Note: this does not mean the selection it self changed.
638 m_nSelection
= selNew
;
639 m_pages
[m_nSelection
]->Refresh();
641 else if (int(nPage
) == m_nSelection
)
643 // The selection was deleted.
645 // Determine new selection.
646 if (m_nSelection
== int(GetPageCount()))
647 selNew
= m_nSelection
- 1;
649 selNew
= m_nSelection
;
651 // m_nSelection must be always valid so reset it before calling
654 SetSelection(selNew
);
658 wxFAIL
; // Windows did not behave ok.
666 bool wxNotebook::DeleteAllPages()
668 size_t nPageCount
= GetPageCount();
670 for ( nPage
= 0; nPage
< nPageCount
; nPage
++ )
671 delete m_pages
[nPage
];
675 TabCtrl_DeleteAllItems(GetHwnd());
679 InvalidateBestSize();
683 // same as AddPage() but does it at given position
684 bool wxNotebook::InsertPage(size_t nPage
,
685 wxNotebookPage
*pPage
,
686 const wxString
& strText
,
690 wxCHECK_MSG( pPage
!= NULL
, false, _T("NULL page in wxNotebook::InsertPage") );
691 wxCHECK_MSG( IS_VALID_PAGE(nPage
) || nPage
== GetPageCount(), false,
692 _T("invalid index in wxNotebook::InsertPage") );
694 wxASSERT_MSG( pPage
->GetParent() == this,
695 _T("notebook pages must have notebook as parent") );
697 // add a new tab to the control
698 // ----------------------------
700 // init all fields to 0
702 wxZeroMemory(tcItem
);
704 // set the image, if any
707 tcItem
.mask
|= TCIF_IMAGE
;
708 tcItem
.iImage
= imageId
;
712 if ( !strText
.empty() )
714 tcItem
.mask
|= TCIF_TEXT
;
715 tcItem
.pszText
= (wxChar
*)strText
.c_str(); // const_cast
718 // hide the page: unless it is selected, it shouldn't be shown (and if it
719 // is selected it will be shown later)
720 HWND hwnd
= GetWinHwnd(pPage
);
721 SetWindowLong(hwnd
, GWL_STYLE
, GetWindowLong(hwnd
, GWL_STYLE
) & ~WS_VISIBLE
);
723 // this updates internal flag too -- otherwise it would get out of sync
724 // with the real state
728 // fit the notebook page to the tab control's display area: this should be
729 // done before adding it to the notebook or TabCtrl_InsertItem() will
730 // change the notebooks size itself!
731 AdjustPageSize(pPage
);
733 // finally do insert it
734 if ( TabCtrl_InsertItem(GetHwnd(), nPage
, &tcItem
) == -1 )
736 wxLogError(wxT("Can't create the notebook page '%s'."), strText
.c_str());
741 // succeeded: save the pointer to the page
742 m_pages
.Insert(pPage
, nPage
);
744 // we may need to adjust the size again if the notebook size changed:
745 // normally this only happens for the first page we add (the tabs which
746 // hadn't been there before are now shown) but for a multiline notebook it
747 // can happen for any page at all as a new row could have been started
748 if ( m_pages
.GetCount() == 1 || HasFlag(wxNB_MULTILINE
) )
750 AdjustPageSize(pPage
);
753 // now deal with the selection
754 // ---------------------------
756 // if the inserted page is before the selected one, we must update the
757 // index of the selected page
758 if ( int(nPage
) <= m_nSelection
)
760 // one extra page added
764 // some page should be selected: either this one or the first one if there
765 // is still no selection
769 else if ( m_nSelection
== -1 )
773 SetSelection(selNew
);
775 InvalidateBestSize();
780 int wxNotebook::HitTest(const wxPoint
& pt
, long *flags
) const
782 TC_HITTESTINFO hitTestInfo
;
783 hitTestInfo
.pt
.x
= pt
.x
;
784 hitTestInfo
.pt
.y
= pt
.y
;
785 int item
= TabCtrl_HitTest(GetHwnd(), &hitTestInfo
);
791 if ((hitTestInfo
.flags
& TCHT_NOWHERE
) == TCHT_NOWHERE
)
792 *flags
|= wxNB_HITTEST_NOWHERE
;
793 if ((hitTestInfo
.flags
& TCHT_ONITEM
) == TCHT_ONITEM
)
794 *flags
|= wxNB_HITTEST_ONITEM
;
795 if ((hitTestInfo
.flags
& TCHT_ONITEMICON
) == TCHT_ONITEMICON
)
796 *flags
|= wxNB_HITTEST_ONICON
;
797 if ((hitTestInfo
.flags
& TCHT_ONITEMLABEL
) == TCHT_ONITEMLABEL
)
798 *flags
|= wxNB_HITTEST_ONLABEL
;
804 // ----------------------------------------------------------------------------
805 // flicker-less notebook redraw
806 // ----------------------------------------------------------------------------
808 #if USE_NOTEBOOK_ANTIFLICKER
810 // wnd proc for the spin button
811 LRESULT APIENTRY _EXPORT
wxNotebookSpinBtnWndProc(HWND hwnd
,
816 if ( message
== WM_ERASEBKGND
)
819 return ::CallWindowProc(CASTWNDPROC gs_wndprocNotebookSpinBtn
,
820 hwnd
, message
, wParam
, lParam
);
823 LRESULT APIENTRY _EXPORT
wxNotebookWndProc(HWND hwnd
,
828 return ::CallWindowProc(CASTWNDPROC gs_wndprocNotebook
,
829 hwnd
, message
, wParam
, lParam
);
832 void wxNotebook::OnEraseBackground(wxEraseEvent
& WXUNUSED(event
))
837 void wxNotebook::OnPaint(wxPaintEvent
& WXUNUSED(event
))
842 ::GetClientRect(GetHwnd(), &rc
);
843 wxBitmap
bmp(rc
.right
, rc
.bottom
);
844 memdc
.SelectObject(bmp
);
846 // if there is no special brush just use the solid background colour
848 HBRUSH hbr
= (HBRUSH
)m_hbrBackground
;
855 brush
= wxBrush(GetBackgroundColour());
856 hbr
= GetHbrushOf(brush
);
859 ::FillRect(GetHdcOf(memdc
), &rc
, hbr
);
861 MSWDefWindowProc(WM_PAINT
, (WPARAM
)memdc
.GetHDC(), 0);
863 dc
.Blit(0, 0, rc
.right
, rc
.bottom
, &memdc
, 0, 0);
866 #endif // USE_NOTEBOOK_ANTIFLICKER
868 // ----------------------------------------------------------------------------
869 // wxNotebook callbacks
870 // ----------------------------------------------------------------------------
872 void wxNotebook::OnSize(wxSizeEvent
& event
)
874 if ( GetPageCount() == 0 )
876 // Prevents droppings on resize, but does cause some flicker
877 // when there are no pages.
885 // Without this, we can sometimes get droppings at the edges
886 // of a notebook, for example a notebook in a splitter window.
887 // This needs to be reconciled with the RefreshRect calls
888 // at the end of this function, which weren't enough to prevent
891 wxSize sz
= GetClientSize();
893 // Refresh right side
894 wxRect
rect(sz
.x
-4, 0, 4, sz
.y
);
897 // Refresh bottom side
898 rect
= wxRect(0, sz
.y
-4, sz
.x
, 4);
902 rect
= wxRect(0, 0, 4, sz
.y
);
905 #endif // !__WXWINCE__
907 // fit all the notebook pages to the tab control's display area
910 rc
.left
= rc
.top
= 0;
911 GetSize((int *)&rc
.right
, (int *)&rc
.bottom
);
913 // save the total size, we'll use it below
914 int widthNbook
= rc
.right
- rc
.left
,
915 heightNbook
= rc
.bottom
- rc
.top
;
917 // there seems to be a bug in the implementation of TabCtrl_AdjustRect(): it
918 // returns completely false values for multiline tab controls after the tabs
919 // are added but before getting the first WM_SIZE (off by ~50 pixels, see
921 // http://sf.net/tracker/index.php?func=detail&aid=645323&group_id=9863&atid=109863
923 // and the only work around I could find was this ugly hack... without it
924 // simply toggling the "multiline" checkbox in the notebook sample resulted
925 // in a noticeable page displacement
926 if ( HasFlag(wxNB_MULTILINE
) )
928 // avoid an infinite recursion: we get another notification too!
929 static bool s_isInOnSize
= false;
934 SendMessage(GetHwnd(), WM_SIZE
, SIZE_RESTORED
,
935 MAKELPARAM(rc
.right
, rc
.bottom
));
936 s_isInOnSize
= false;
941 // background bitmap size has changed, update the brush using it too
943 #endif // wxUSE_UXTHEME
945 TabCtrl_AdjustRect(GetHwnd(), false, &rc
);
947 int width
= rc
.right
- rc
.left
,
948 height
= rc
.bottom
- rc
.top
;
949 size_t nCount
= m_pages
.Count();
950 for ( size_t nPage
= 0; nPage
< nCount
; nPage
++ ) {
951 wxNotebookPage
*pPage
= m_pages
[nPage
];
952 pPage
->SetSize(rc
.left
, rc
.top
, width
, height
);
956 // unless we had already repainted everything, we now need to refresh
957 if ( !HasFlag(wxFULL_REPAINT_ON_RESIZE
) )
959 // invalidate areas not covered by pages
960 RefreshRect(wxRect(0, 0, widthNbook
, rc
.top
), false);
961 RefreshRect(wxRect(0, rc
.top
, rc
.left
, height
), false);
962 RefreshRect(wxRect(0, rc
.bottom
, widthNbook
, heightNbook
- rc
.bottom
),
964 RefreshRect(wxRect(rc
.right
, rc
.top
, widthNbook
- rc
.right
, height
),
968 #if USE_NOTEBOOK_ANTIFLICKER
969 // subclass the spin control used by the notebook to scroll pages to
970 // prevent it from flickering on resize
971 if ( !m_hasSubclassedUpdown
)
973 // iterate over all child windows to find spin button
974 for ( HWND child
= ::GetWindow(GetHwnd(), GW_CHILD
);
976 child
= ::GetWindow(child
, GW_HWNDNEXT
) )
978 wxWindow
*childWindow
= wxFindWinFromHandle((WXHWND
)child
);
980 // see if it exists, if no wxWindow found then assume it's the spin
984 // subclass the spin button to override WM_ERASEBKGND
985 if ( !gs_wndprocNotebookSpinBtn
)
986 gs_wndprocNotebookSpinBtn
= (WXFARPROC
)wxGetWindowProc(child
);
988 wxSetWindowProc(child
, wxNotebookSpinBtnWndProc
);
989 m_hasSubclassedUpdown
= true;
994 #endif // USE_NOTEBOOK_ANTIFLICKER
999 void wxNotebook::OnSelChange(wxNotebookEvent
& event
)
1001 // is it our tab control?
1002 if ( event
.GetEventObject() == this )
1004 int sel
= event
.GetOldSelection();
1006 m_pages
[sel
]->Show(false);
1008 sel
= event
.GetSelection();
1011 wxNotebookPage
*pPage
= m_pages
[sel
];
1015 // Changing the page should give the focus to it but, as per bug report
1016 // http://sf.net/tracker/index.php?func=detail&aid=1150659&group_id=9863&atid=109863,
1017 // we should not set the focus to it directly since it erroneously
1018 // selects radio buttons and breaks keyboard handling for a notebook's
1019 // scroll buttons. So give focus to the notebook and not the page.
1021 // but don't do this is the notebook is hidden
1022 if ( ::IsWindowVisible(GetHwnd()) )
1028 // we want to give others a chance to process this message as well
1032 bool wxNotebook::MSWTranslateMessage(WXMSG
*wxmsg
)
1034 const MSG
* const msg
= (MSG
*)wxmsg
;
1036 // intercept TAB, CTRL+TAB and CTRL+SHIFT+TAB for processing by wxNotebook.
1037 // TAB will be passed to the currently selected page, CTRL+TAB and
1038 // CTRL+SHIFT+TAB will be processed by the notebook itself. do not
1039 // intercept SHIFT+TAB. This goes to the parent of the notebook which will
1041 if ( msg
->message
== WM_KEYDOWN
&& msg
->wParam
== VK_TAB
&&
1042 msg
->hwnd
== GetHwnd() &&
1043 (wxIsCtrlDown() || !wxIsShiftDown()) )
1045 return MSWProcessMessage(wxmsg
);
1051 void wxNotebook::OnNavigationKey(wxNavigationKeyEvent
& event
)
1053 if ( event
.IsWindowChange() ) {
1055 AdvanceSelection(event
.GetDirection());
1058 // we get this event in 3 cases
1060 // a) one of our pages might have generated it because the user TABbed
1061 // out from it in which case we should propagate the event upwards and
1062 // our parent will take care of setting the focus to prev/next sibling
1066 // b) the parent panel wants to give the focus to us so that we
1067 // forward it to our selected page. We can't deal with this in
1068 // OnSetFocus() because we don't know which direction the focus came
1069 // from in this case and so can't choose between setting the focus to
1070 // first or last panel child
1074 // c) we ourselves (see MSWTranslateMessage) generated the event
1076 wxWindow
* const parent
= GetParent();
1078 // the wxObject* casts are required to avoid MinGW GCC 2.95.3 ICE
1079 const bool isFromParent
= event
.GetEventObject() == (wxObject
*) parent
;
1080 const bool isFromSelf
= event
.GetEventObject() == (wxObject
*) this;
1082 if ( isFromParent
|| isFromSelf
)
1084 // no, it doesn't come from child, case (b) or (c): forward to a
1085 // page but only if direction is backwards (TAB) or from ourselves,
1086 if ( m_nSelection
!= -1 &&
1087 (!event
.GetDirection() || isFromSelf
) )
1089 // so that the page knows that the event comes from it's parent
1090 // and is being propagated downwards
1091 event
.SetEventObject(this);
1093 wxWindow
*page
= m_pages
[m_nSelection
];
1094 if ( !page
->GetEventHandler()->ProcessEvent(event
) )
1098 //else: page manages focus inside it itself
1100 else // otherwise set the focus to the notebook itself
1107 // it comes from our child, case (a), pass to the parent, but only
1108 // if the direction is forwards. Otherwise set the focus to the
1109 // notebook itself. The notebook is always the 'first' control of a
1111 if ( !event
.GetDirection() )
1117 event
.SetCurrentFocus(this);
1118 parent
->GetEventHandler()->ProcessEvent(event
);
1126 bool wxNotebook::DoDrawBackground(WXHDC hDC
, wxWindow
*child
)
1128 wxUxThemeHandle
theme(child
? child
: this, L
"TAB");
1132 // get the notebook client rect (we're not interested in drawing tabs
1134 wxRect r
= GetPageSize();
1139 wxCopyRectToRECT(r
, rc
);
1141 // map rect to the coords of the window we're drawing in
1143 ::MapWindowPoints(GetHwnd(), GetHwndOf(child
), (POINT
*)&rc
, 2);
1145 // we have the content area (page size), but we need to draw all of the
1146 // background for it to be aligned correctly
1147 wxUxThemeEngine::Get()->GetThemeBackgroundExtent
1156 wxUxThemeEngine::Get()->DrawThemeBackground
1169 WXHBRUSH
wxNotebook::QueryBgBitmap()
1171 wxRect r
= GetPageSize();
1175 WindowHDC
hDC(GetHwnd());
1176 MemoryHDC
hDCMem(hDC
);
1177 CompatibleBitmap
hBmp(hDC
, r
.x
+ r
.width
, r
.y
+ r
.height
);
1179 SelectInHDC
selectBmp(hDCMem
, hBmp
);
1181 if ( !DoDrawBackground((WXHDC
)(HDC
)hDCMem
) )
1184 return (WXHBRUSH
)::CreatePatternBrush(hBmp
);
1187 void wxNotebook::UpdateBgBrush()
1189 if ( m_hbrBackground
)
1190 ::DeleteObject((HBRUSH
)m_hbrBackground
);
1192 if ( !m_hasBgCol
&& wxUxThemeEngine::GetIfActive() )
1194 m_hbrBackground
= QueryBgBitmap();
1196 else // no themes or we've got user-defined solid colour
1198 m_hbrBackground
= NULL
;
1202 WXHBRUSH
wxNotebook::MSWGetBgBrushForChild(WXHDC hDC
, WXHWND hWnd
)
1204 if ( m_hbrBackground
)
1206 // before drawing with the background brush, we need to position it
1209 ::GetWindowRect((HWND
)hWnd
, &rc
);
1211 ::MapWindowPoints(NULL
, GetHwnd(), (POINT
*)&rc
, 1);
1213 if ( !::SetBrushOrgEx((HDC
)hDC
, -rc
.left
, -rc
.top
, NULL
) )
1215 wxLogLastError(_T("SetBrushOrgEx(notebook bg brush)"));
1218 return m_hbrBackground
;
1221 return wxNotebookBase::MSWGetBgBrushForChild(hDC
, hWnd
);
1224 bool wxNotebook::MSWPrintChild(WXHDC hDC
, wxWindow
*child
)
1226 // solid background colour overrides themed background drawing
1227 if ( !UseBgCol() && DoDrawBackground(hDC
, child
) )
1230 // If we're using a solid colour (for example if we've switched off
1231 // theming for this notebook), paint it
1234 wxRect r
= GetPageSize();
1239 wxCopyRectToRECT(r
, rc
);
1241 // map rect to the coords of the window we're drawing in
1243 ::MapWindowPoints(GetHwnd(), GetHwndOf(child
), (POINT
*)&rc
, 2);
1245 wxBrush
brush(GetBackgroundColour());
1246 HBRUSH hbr
= GetHbrushOf(brush
);
1248 ::FillRect((HDC
) hDC
, &rc
, hbr
);
1253 return wxNotebookBase::MSWPrintChild(hDC
, child
);
1256 #endif // wxUSE_UXTHEME
1258 // Windows only: attempts to get colour for UX theme page background
1259 wxColour
wxNotebook::GetThemeBackgroundColour() const
1262 if (wxUxThemeEngine::Get())
1264 wxUxThemeHandle
hTheme((wxNotebook
*) this, L
"TAB");
1267 // This is total guesswork.
1268 // See PlatformSDK\Include\Tmschema.h for values
1269 COLORREF themeColor
;
1270 wxUxThemeEngine::Get()->GetThemeColor(
1274 3821 /* FILLCOLORHINT */,
1278 [DS] Workaround for WindowBlinds:
1279 Some themes return a near black theme color using FILLCOLORHINT,
1280 this makes notebook pages have an ugly black background and makes
1281 text (usually black) unreadable. Retry again with FILLCOLOR.
1283 This workaround potentially breaks appearance of some themes,
1284 but in practice it already fixes some themes.
1286 if (themeColor
== 1)
1288 wxUxThemeEngine::Get()->GetThemeColor(
1292 3802 /* FILLCOLOR */,
1296 return wxRGBToColour(themeColor
);
1299 #endif // wxUSE_UXTHEME
1301 return GetBackgroundColour();
1304 // ----------------------------------------------------------------------------
1305 // wxNotebook base class virtuals
1306 // ----------------------------------------------------------------------------
1308 #if wxUSE_CONSTRAINTS
1310 // override these 2 functions to do nothing: everything is done in OnSize
1312 void wxNotebook::SetConstraintSizes(bool WXUNUSED(recurse
))
1314 // don't set the sizes of the pages - their correct size is not yet known
1315 wxControl::SetConstraintSizes(false);
1318 bool wxNotebook::DoPhase(int WXUNUSED(nPhase
))
1323 #endif // wxUSE_CONSTRAINTS
1325 // ----------------------------------------------------------------------------
1326 // wxNotebook Windows message handlers
1327 // ----------------------------------------------------------------------------
1329 bool wxNotebook::MSWOnScroll(int orientation
, WXWORD nSBCode
,
1330 WXWORD pos
, WXHWND control
)
1332 // don't generate EVT_SCROLLWIN events for the WM_SCROLLs coming from the
1337 return wxNotebookBase::MSWOnScroll(orientation
, nSBCode
, pos
, control
);
1340 bool wxNotebook::MSWOnNotify(int idCtrl
, WXLPARAM lParam
, WXLPARAM
* result
)
1342 wxNotebookEvent
event(wxEVT_NULL
, m_windowId
);
1344 NMHDR
* hdr
= (NMHDR
*)lParam
;
1345 switch ( hdr
->code
) {
1347 event
.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
);
1350 case TCN_SELCHANGING
:
1351 event
.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
);
1355 return wxControl::MSWOnNotify(idCtrl
, lParam
, result
);
1358 event
.SetSelection(TabCtrl_GetCurSel(GetHwnd()));
1359 event
.SetOldSelection(m_nSelection
);
1360 event
.SetEventObject(this);
1361 event
.SetInt(idCtrl
);
1363 bool processed
= GetEventHandler()->ProcessEvent(event
);
1364 *result
= !event
.IsAllowed();
1368 #endif // wxUSE_NOTEBOOK