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 else // use themed background by default
350 // create backing store
353 #endif // wxUSE_UXTHEME
355 // Undocumented hack to get flat notebook style
356 // In fact, we should probably only do this in some
357 // curcumstances, i.e. if we know we will have a border
358 // at the bottom (the tab control doesn't draw it itself)
359 #if defined(__POCKETPC__) || defined(__SMARTPHONE__)
360 if (HasFlag(wxNB_FLAT
))
362 SendMessage(GetHwnd(), CCM_SETVERSION
, COMCTL32_VERSION
, 0);
364 SetBackgroundColour(*wxWHITE
);
370 WXDWORD
wxNotebook::MSWGetStyle(long style
, WXDWORD
*exstyle
) const
372 WXDWORD tabStyle
= wxControl::MSWGetStyle(style
, exstyle
);
374 tabStyle
|= WS_TABSTOP
| TCS_TABS
;
376 if ( style
& wxNB_MULTILINE
)
377 tabStyle
|= TCS_MULTILINE
;
378 if ( style
& wxNB_FIXEDWIDTH
)
379 tabStyle
|= TCS_FIXEDWIDTH
;
381 if ( style
& wxNB_BOTTOM
)
382 tabStyle
|= TCS_RIGHT
;
383 else if ( style
& wxNB_LEFT
)
384 tabStyle
|= TCS_VERTICAL
;
385 else if ( style
& wxNB_RIGHT
)
386 tabStyle
|= TCS_VERTICAL
| TCS_RIGHT
;
391 // note that we never want to have the default WS_EX_CLIENTEDGE style
392 // as it looks too ugly for the notebooks
399 wxNotebook::~wxNotebook()
402 if ( m_hbrBackground
)
403 ::DeleteObject((HBRUSH
)m_hbrBackground
);
404 #endif // wxUSE_UXTHEME
407 // ----------------------------------------------------------------------------
408 // wxNotebook accessors
409 // ----------------------------------------------------------------------------
411 size_t wxNotebook::GetPageCount() const
414 wxASSERT( (int)m_pages
.Count() == TabCtrl_GetItemCount(GetHwnd()) );
416 return m_pages
.Count();
419 int wxNotebook::GetRowCount() const
421 return TabCtrl_GetRowCount(GetHwnd());
424 int wxNotebook::SetSelection(size_t nPage
)
426 wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxNOT_FOUND
, wxT("notebook page out of range") );
428 if ( int(nPage
) != m_nSelection
)
430 wxNotebookEvent
event(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
, m_windowId
);
431 event
.SetSelection(nPage
);
432 event
.SetOldSelection(m_nSelection
);
433 event
.SetEventObject(this);
434 if ( !GetEventHandler()->ProcessEvent(event
) || event
.IsAllowed() )
436 // program allows the page change
437 event
.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
);
438 (void)GetEventHandler()->ProcessEvent(event
);
440 TabCtrl_SetCurSel(GetHwnd(), nPage
);
447 bool wxNotebook::SetPageText(size_t nPage
, const wxString
& strText
)
449 wxCHECK_MSG( IS_VALID_PAGE(nPage
), false, wxT("notebook page out of range") );
452 tcItem
.mask
= TCIF_TEXT
;
453 tcItem
.pszText
= (wxChar
*)strText
.c_str();
455 return TabCtrl_SetItem(GetHwnd(), nPage
, &tcItem
) != 0;
458 wxString
wxNotebook::GetPageText(size_t nPage
) const
460 wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxEmptyString
, wxT("notebook page out of range") );
464 tcItem
.mask
= TCIF_TEXT
;
465 tcItem
.pszText
= buf
;
466 tcItem
.cchTextMax
= WXSIZEOF(buf
);
469 if ( TabCtrl_GetItem(GetHwnd(), nPage
, &tcItem
) )
470 str
= tcItem
.pszText
;
475 int wxNotebook::GetPageImage(size_t nPage
) const
477 wxCHECK_MSG( IS_VALID_PAGE(nPage
), -1, wxT("notebook page out of range") );
480 tcItem
.mask
= TCIF_IMAGE
;
482 return TabCtrl_GetItem(GetHwnd(), nPage
, &tcItem
) ? tcItem
.iImage
: -1;
485 bool wxNotebook::SetPageImage(size_t nPage
, int nImage
)
487 wxCHECK_MSG( IS_VALID_PAGE(nPage
), false, wxT("notebook page out of range") );
490 tcItem
.mask
= TCIF_IMAGE
;
491 tcItem
.iImage
= nImage
;
493 return TabCtrl_SetItem(GetHwnd(), nPage
, &tcItem
) != 0;
496 void wxNotebook::SetImageList(wxImageList
* imageList
)
498 wxNotebookBase::SetImageList(imageList
);
502 TabCtrl_SetImageList(GetHwnd(), (HIMAGELIST
)imageList
->GetHIMAGELIST());
506 // ----------------------------------------------------------------------------
507 // wxNotebook size settings
508 // ----------------------------------------------------------------------------
510 wxRect
wxNotebook::GetPageSize() const
515 ::GetClientRect(GetHwnd(), &rc
);
517 // This check is to work around a bug in TabCtrl_AdjustRect which will
518 // cause a crash on win2k or on XP with themes disabled if either
519 // wxNB_MULTILINE is used or tabs are placed on a side, if the rectangle
522 // The value of 20 is chosen arbitrarily but seems to work
523 if ( rc
.right
> 20 && rc
.bottom
> 20 )
525 TabCtrl_AdjustRect(GetHwnd(), false, &rc
);
527 wxCopyRECTToRect(rc
, r
);
533 void wxNotebook::SetPageSize(const wxSize
& size
)
535 // transform the page size into the notebook size
542 TabCtrl_AdjustRect(GetHwnd(), true, &rc
);
545 SetSize(rc
.right
- rc
.left
, rc
.bottom
- rc
.top
);
548 void wxNotebook::SetPadding(const wxSize
& padding
)
550 TabCtrl_SetPadding(GetHwnd(), padding
.x
, padding
.y
);
553 // Windows-only at present. Also, you must use the wxNB_FIXEDWIDTH
555 void wxNotebook::SetTabSize(const wxSize
& sz
)
557 ::SendMessage(GetHwnd(), TCM_SETITEMSIZE
, 0, MAKELPARAM(sz
.x
, sz
.y
));
560 wxSize
wxNotebook::CalcSizeFromPage(const wxSize
& sizePage
) const
562 wxSize sizeTotal
= sizePage
;
564 // We need to make getting tab size part of the wxWidgets API.
566 if (GetPageCount() > 0)
569 TabCtrl_GetItemRect((HWND
) GetHWND(), 0, & rect
);
570 tabSize
.x
= rect
.right
- rect
.left
;
571 tabSize
.y
= rect
.bottom
- rect
.top
;
573 if ( HasFlag(wxNB_LEFT
) || HasFlag(wxNB_RIGHT
) )
575 sizeTotal
.x
+= tabSize
.x
+ 7;
581 sizeTotal
.y
+= tabSize
.y
+ 7;
587 void wxNotebook::AdjustPageSize(wxNotebookPage
*page
)
589 wxCHECK_RET( page
, _T("NULL page in wxNotebook::AdjustPageSize") );
591 const wxRect r
= GetPageSize();
598 // ----------------------------------------------------------------------------
599 // wxNotebook operations
600 // ----------------------------------------------------------------------------
602 // remove one page from the notebook, without deleting
603 wxNotebookPage
*wxNotebook::DoRemovePage(size_t nPage
)
605 wxNotebookPage
*pageRemoved
= wxNotebookBase::DoRemovePage(nPage
);
609 TabCtrl_DeleteItem(GetHwnd(), nPage
);
611 if ( m_pages
.IsEmpty() )
613 // no selection any more, the notebook becamse empty
616 else // notebook still not empty
618 int selNew
= TabCtrl_GetCurSel(GetHwnd());
621 // No selection change, just refresh the current selection.
622 // Because it could be that the slection index changed
623 // we need to update it.
624 // Note: this does not mean the selection it self changed.
625 m_nSelection
= selNew
;
626 m_pages
[m_nSelection
]->Refresh();
628 else if (int(nPage
) == m_nSelection
)
630 // The selection was deleted.
632 // Determine new selection.
633 if (m_nSelection
== int(GetPageCount()))
634 selNew
= m_nSelection
- 1;
636 selNew
= m_nSelection
;
638 // m_nSelection must be always valid so reset it before calling
641 SetSelection(selNew
);
645 wxFAIL
; // Windows did not behave ok.
653 bool wxNotebook::DeleteAllPages()
655 size_t nPageCount
= GetPageCount();
657 for ( nPage
= 0; nPage
< nPageCount
; nPage
++ )
658 delete m_pages
[nPage
];
662 TabCtrl_DeleteAllItems(GetHwnd());
666 InvalidateBestSize();
670 // same as AddPage() but does it at given position
671 bool wxNotebook::InsertPage(size_t nPage
,
672 wxNotebookPage
*pPage
,
673 const wxString
& strText
,
677 wxCHECK_MSG( pPage
!= NULL
, false, _T("NULL page in wxNotebook::InsertPage") );
678 wxCHECK_MSG( IS_VALID_PAGE(nPage
) || nPage
== GetPageCount(), false,
679 _T("invalid index in wxNotebook::InsertPage") );
681 wxASSERT_MSG( pPage
->GetParent() == this,
682 _T("notebook pages must have notebook as parent") );
684 // add a new tab to the control
685 // ----------------------------
687 // init all fields to 0
689 wxZeroMemory(tcItem
);
691 // set the image, if any
694 tcItem
.mask
|= TCIF_IMAGE
;
695 tcItem
.iImage
= imageId
;
699 if ( !strText
.empty() )
701 tcItem
.mask
|= TCIF_TEXT
;
702 tcItem
.pszText
= (wxChar
*)strText
.c_str(); // const_cast
705 // hide the page: unless it is selected, it shouldn't be shown (and if it
706 // is selected it will be shown later)
707 HWND hwnd
= GetWinHwnd(pPage
);
708 SetWindowLong(hwnd
, GWL_STYLE
, GetWindowLong(hwnd
, GWL_STYLE
) & ~WS_VISIBLE
);
710 // this updates internal flag too -- otherwise it would get out of sync
711 // with the real state
715 // fit the notebook page to the tab control's display area: this should be
716 // done before adding it to the notebook or TabCtrl_InsertItem() will
717 // change the notebooks size itself!
718 AdjustPageSize(pPage
);
720 // finally do insert it
721 if ( TabCtrl_InsertItem(GetHwnd(), nPage
, &tcItem
) == -1 )
723 wxLogError(wxT("Can't create the notebook page '%s'."), strText
.c_str());
728 // succeeded: save the pointer to the page
729 m_pages
.Insert(pPage
, nPage
);
731 // we may need to adjust the size again if the notebook size changed:
732 // normally this only happens for the first page we add (the tabs which
733 // hadn't been there before are now shown) but for a multiline notebook it
734 // can happen for any page at all as a new row could have been started
735 if ( m_pages
.GetCount() == 1 || HasFlag(wxNB_MULTILINE
) )
737 AdjustPageSize(pPage
);
740 // now deal with the selection
741 // ---------------------------
743 // if the inserted page is before the selected one, we must update the
744 // index of the selected page
745 if ( int(nPage
) <= m_nSelection
)
747 // one extra page added
751 // some page should be selected: either this one or the first one if there
752 // is still no selection
756 else if ( m_nSelection
== -1 )
760 SetSelection(selNew
);
762 InvalidateBestSize();
767 int wxNotebook::HitTest(const wxPoint
& pt
, long *flags
) const
769 TC_HITTESTINFO hitTestInfo
;
770 hitTestInfo
.pt
.x
= pt
.x
;
771 hitTestInfo
.pt
.y
= pt
.y
;
772 int item
= TabCtrl_HitTest(GetHwnd(), &hitTestInfo
);
778 if ((hitTestInfo
.flags
& TCHT_NOWHERE
) == TCHT_NOWHERE
)
779 *flags
|= wxNB_HITTEST_NOWHERE
;
780 if ((hitTestInfo
.flags
& TCHT_ONITEM
) == TCHT_ONITEM
)
781 *flags
|= wxNB_HITTEST_ONITEM
;
782 if ((hitTestInfo
.flags
& TCHT_ONITEMICON
) == TCHT_ONITEMICON
)
783 *flags
|= wxNB_HITTEST_ONICON
;
784 if ((hitTestInfo
.flags
& TCHT_ONITEMLABEL
) == TCHT_ONITEMLABEL
)
785 *flags
|= wxNB_HITTEST_ONLABEL
;
791 // ----------------------------------------------------------------------------
792 // flicker-less notebook redraw
793 // ----------------------------------------------------------------------------
795 #if USE_NOTEBOOK_ANTIFLICKER
797 // wnd proc for the spin button
798 LRESULT APIENTRY _EXPORT
wxNotebookSpinBtnWndProc(HWND hwnd
,
803 if ( message
== WM_ERASEBKGND
)
806 return ::CallWindowProc(CASTWNDPROC gs_wndprocNotebookSpinBtn
,
807 hwnd
, message
, wParam
, lParam
);
810 LRESULT APIENTRY _EXPORT
wxNotebookWndProc(HWND hwnd
,
815 return ::CallWindowProc(CASTWNDPROC gs_wndprocNotebook
,
816 hwnd
, message
, wParam
, lParam
);
821 void wxNotebook::OnEraseBackground(wxEraseEvent
& WXUNUSED(event
))
826 void wxNotebook::OnPaint(wxPaintEvent
& WXUNUSED(event
))
831 ::GetClientRect(GetHwnd(), &rc
);
832 wxBitmap
bmp(rc
.right
, rc
.bottom
);
833 memdc
.SelectObject(bmp
);
835 // if there is no special brush just use the solid background colour
836 HBRUSH hbr
= (HBRUSH
)m_hbrBackground
;
840 brush
= wxBrush(GetBackgroundColour());
841 hbr
= GetHbrushOf(brush
);
844 ::FillRect(GetHdcOf(memdc
), &rc
, hbr
);
846 MSWDefWindowProc(WM_PAINT
, (WPARAM
)memdc
.GetHDC(), 0);
848 dc
.Blit(0, 0, rc
.right
, rc
.bottom
, &memdc
, 0, 0);
851 #endif // USE_NOTEBOOK_ANTIFLICKER
853 // ----------------------------------------------------------------------------
854 // wxNotebook callbacks
855 // ----------------------------------------------------------------------------
857 void wxNotebook::OnSize(wxSizeEvent
& event
)
859 if ( GetPageCount() == 0 )
861 // Prevents droppings on resize, but does cause some flicker
862 // when there are no pages.
870 // Without this, we can sometimes get droppings at the edges
871 // of a notebook, for example a notebook in a splitter window.
872 // This needs to be reconciled with the RefreshRect calls
873 // at the end of this function, which weren't enough to prevent
876 wxSize sz
= GetClientSize();
878 // Refresh right side
879 wxRect
rect(sz
.x
-4, 0, 4, sz
.y
);
882 // Refresh bottom side
883 rect
= wxRect(0, sz
.y
-4, sz
.x
, 4);
887 rect
= wxRect(0, 0, 4, sz
.y
);
890 #endif // !__WXWINCE__
892 // fit all the notebook pages to the tab control's display area
895 rc
.left
= rc
.top
= 0;
896 GetSize((int *)&rc
.right
, (int *)&rc
.bottom
);
898 // save the total size, we'll use it below
899 int widthNbook
= rc
.right
- rc
.left
,
900 heightNbook
= rc
.bottom
- rc
.top
;
902 // there seems to be a bug in the implementation of TabCtrl_AdjustRect(): it
903 // returns completely false values for multiline tab controls after the tabs
904 // are added but before getting the first WM_SIZE (off by ~50 pixels, see
906 // http://sf.net/tracker/index.php?func=detail&aid=645323&group_id=9863&atid=109863
908 // and the only work around I could find was this ugly hack... without it
909 // simply toggling the "multiline" checkbox in the notebook sample resulted
910 // in a noticeable page displacement
911 if ( HasFlag(wxNB_MULTILINE
) )
913 // avoid an infinite recursion: we get another notification too!
914 static bool s_isInOnSize
= false;
919 SendMessage(GetHwnd(), WM_SIZE
, SIZE_RESTORED
,
920 MAKELPARAM(rc
.right
, rc
.bottom
));
921 s_isInOnSize
= false;
926 // background bitmap size has changed, update the brush using it too
928 #endif // wxUSE_UXTHEME
930 TabCtrl_AdjustRect(GetHwnd(), false, &rc
);
932 int width
= rc
.right
- rc
.left
,
933 height
= rc
.bottom
- rc
.top
;
934 size_t nCount
= m_pages
.Count();
935 for ( size_t nPage
= 0; nPage
< nCount
; nPage
++ ) {
936 wxNotebookPage
*pPage
= m_pages
[nPage
];
937 pPage
->SetSize(rc
.left
, rc
.top
, width
, height
);
941 // unless we had already repainted everything, we now need to refresh
942 if ( !HasFlag(wxFULL_REPAINT_ON_RESIZE
) )
944 // invalidate areas not covered by pages
945 RefreshRect(wxRect(0, 0, widthNbook
, rc
.top
), false);
946 RefreshRect(wxRect(0, rc
.top
, rc
.left
, height
), false);
947 RefreshRect(wxRect(0, rc
.bottom
, widthNbook
, heightNbook
- rc
.bottom
),
949 RefreshRect(wxRect(rc
.right
, rc
.top
, widthNbook
- rc
.right
, height
),
953 #if USE_NOTEBOOK_ANTIFLICKER
954 // subclass the spin control used by the notebook to scroll pages to
955 // prevent it from flickering on resize
956 if ( !m_hasSubclassedUpdown
)
958 // iterate over all child windows to find spin button
959 for ( HWND child
= ::GetWindow(GetHwnd(), GW_CHILD
);
961 child
= ::GetWindow(child
, GW_HWNDNEXT
) )
963 wxWindow
*childWindow
= wxFindWinFromHandle((WXHWND
)child
);
965 // see if it exists, if no wxWindow found then assume it's the spin
969 // subclass the spin button to override WM_ERASEBKGND
970 if ( !gs_wndprocNotebookSpinBtn
)
971 gs_wndprocNotebookSpinBtn
= (WXFARPROC
)wxGetWindowProc(child
);
973 wxSetWindowProc(child
, wxNotebookSpinBtnWndProc
);
974 m_hasSubclassedUpdown
= true;
979 #endif // USE_NOTEBOOK_ANTIFLICKER
984 void wxNotebook::OnSelChange(wxNotebookEvent
& event
)
986 // is it our tab control?
987 if ( event
.GetEventObject() == this )
989 int sel
= event
.GetOldSelection();
991 m_pages
[sel
]->Show(false);
993 sel
= event
.GetSelection();
996 wxNotebookPage
*pPage
= m_pages
[sel
];
999 // As per bug report:
1000 // http://sourceforge.net/tracker/index.php?func=detail&aid=1150659&group_id=9863&atid=109863,
1001 // we should not set the page focus (and thereby the focus for
1002 // a child window) since it erroneously selects radio button controls and also
1003 // breaks keyboard handling for a notebook's scroll buttons. So
1004 // we always focus the notebook and not the page.
1008 else // no pages in the notebook, give the focus to itself
1016 // we want to give others a chance to process this message as well
1020 bool wxNotebook::MSWTranslateMessage(WXMSG
*wxmsg
)
1022 const MSG
* const msg
= (MSG
*)wxmsg
;
1024 // intercept TAB, CTRL+TAB and CTRL+SHIFT+TAB for processing by wxNotebook.
1025 // TAB will be passed to the currently selected page, CTRL+TAB and
1026 // CTRL+SHIFT+TAB will be processed by the notebook itself. do not
1027 // intercept SHIFT+TAB. This goes to the parent of the notebook which will
1029 if ( msg
->message
== WM_KEYDOWN
&& msg
->wParam
== VK_TAB
&&
1030 msg
->hwnd
== GetHwnd() &&
1031 (wxIsCtrlDown() || !wxIsShiftDown()) )
1033 return MSWProcessMessage(wxmsg
);
1039 void wxNotebook::OnNavigationKey(wxNavigationKeyEvent
& event
)
1041 if ( event
.IsWindowChange() ) {
1043 AdvanceSelection(event
.GetDirection());
1046 // we get this event in 3 cases
1048 // a) one of our pages might have generated it because the user TABbed
1049 // out from it in which case we should propagate the event upwards and
1050 // our parent will take care of setting the focus to prev/next sibling
1054 // b) the parent panel wants to give the focus to us so that we
1055 // forward it to our selected page. We can't deal with this in
1056 // OnSetFocus() because we don't know which direction the focus came
1057 // from in this case and so can't choose between setting the focus to
1058 // first or last panel child
1062 // c) we ourselves (see MSWTranslateMessage) generated the event
1064 wxWindow
* const parent
= GetParent();
1066 // the wxObject* casts are required to avoid MinGW GCC 2.95.3 ICE
1067 const bool isFromParent
= event
.GetEventObject() == (wxObject
*) parent
;
1068 const bool isFromSelf
= event
.GetEventObject() == (wxObject
*) this;
1070 if ( isFromParent
|| isFromSelf
)
1072 // no, it doesn't come from child, case (b) or (c): forward to a
1073 // page but only if direction is backwards (TAB) or from ourselves,
1074 if ( m_nSelection
!= -1 &&
1075 (!event
.GetDirection() || isFromSelf
) )
1077 // so that the page knows that the event comes from it's parent
1078 // and is being propagated downwards
1079 event
.SetEventObject(this);
1081 wxWindow
*page
= m_pages
[m_nSelection
];
1082 if ( !page
->GetEventHandler()->ProcessEvent(event
) )
1086 //else: page manages focus inside it itself
1088 else // otherwise set the focus to the notebook itself
1095 // it comes from our child, case (a), pass to the parent, but only
1096 // if the direction is forwards. Otherwise set the focus to the
1097 // notebook itself. The notebook is always the 'first' control of a
1099 if ( !event
.GetDirection() )
1105 event
.SetCurrentFocus(this);
1106 parent
->GetEventHandler()->ProcessEvent(event
);
1114 bool wxNotebook::DoDrawBackground(WXHDC hDC
, wxWindow
*child
)
1116 wxUxThemeHandle
theme(child
? child
: this, L
"TAB");
1120 // get the notebook client rect (we're not interested in drawing tabs
1122 wxRect r
= GetPageSize();
1127 wxCopyRectToRECT(r
, rc
);
1129 // map rect to the coords of the window we're drawing in
1131 ::MapWindowPoints(GetHwnd(), GetHwndOf(child
), (POINT
*)&rc
, 2);
1133 // we have the content area (page size), but we need to draw all of the
1134 // background for it to be aligned correctly
1135 wxUxThemeEngine::Get()->GetThemeBackgroundExtent
1144 wxUxThemeEngine::Get()->DrawThemeBackground
1157 WXHBRUSH
wxNotebook::QueryBgBitmap()
1159 wxRect r
= GetPageSize();
1163 WindowHDC
hDC(GetHwnd());
1164 MemoryHDC
hDCMem(hDC
);
1165 CompatibleBitmap
hBmp(hDC
, r
.x
+ r
.width
, r
.y
+ r
.height
);
1167 SelectInHDC
selectBmp(hDCMem
, hBmp
);
1169 if ( !DoDrawBackground((WXHDC
)(HDC
)hDCMem
) )
1172 return (WXHBRUSH
)::CreatePatternBrush(hBmp
);
1175 void wxNotebook::UpdateBgBrush()
1177 if ( m_hbrBackground
)
1178 ::DeleteObject((HBRUSH
)m_hbrBackground
);
1180 if ( !m_hasBgCol
&& wxUxThemeEngine::GetIfActive() )
1182 m_hbrBackground
= QueryBgBitmap();
1184 else // no themes or we've got user-defined solid colour
1186 m_hbrBackground
= NULL
;
1190 WXHBRUSH
wxNotebook::MSWGetBgBrushForChild(WXHDC hDC
, WXHWND hWnd
)
1192 if ( m_hbrBackground
)
1194 // before drawing with the background brush, we need to position it
1197 ::GetWindowRect((HWND
)hWnd
, &rc
);
1199 ::MapWindowPoints(NULL
, GetHwnd(), (POINT
*)&rc
, 1);
1201 if ( !::SetBrushOrgEx((HDC
)hDC
, -rc
.left
, -rc
.top
, NULL
) )
1203 wxLogLastError(_T("SetBrushOrgEx(notebook bg brush)"));
1206 return m_hbrBackground
;
1209 return wxNotebookBase::MSWGetBgBrushForChild(hDC
, hWnd
);
1212 bool wxNotebook::MSWPrintChild(WXHDC hDC
, wxWindow
*child
)
1214 // solid background colour overrides themed background drawing
1215 if ( !UseBgCol() && DoDrawBackground(hDC
, child
) )
1218 // If we're using a solid colour (for example if we've switched off
1219 // theming for this notebook), paint it
1222 wxRect r
= GetPageSize();
1227 wxCopyRectToRECT(r
, rc
);
1229 // map rect to the coords of the window we're drawing in
1231 ::MapWindowPoints(GetHwnd(), GetHwndOf(child
), (POINT
*)&rc
, 2);
1233 wxBrush
brush(GetBackgroundColour());
1234 HBRUSH hbr
= GetHbrushOf(brush
);
1236 ::FillRect((HDC
) hDC
, &rc
, hbr
);
1241 return wxNotebookBase::MSWPrintChild(hDC
, child
);
1244 #endif // wxUSE_UXTHEME
1246 // Windows only: attempts to get colour for UX theme page background
1247 wxColour
wxNotebook::GetThemeBackgroundColour() const
1250 if (wxUxThemeEngine::Get())
1252 wxUxThemeHandle
hTheme((wxNotebook
*) this, L
"TAB");
1255 // This is total guesswork.
1256 // See PlatformSDK\Include\Tmschema.h for values
1257 COLORREF themeColor
;
1258 wxUxThemeEngine::Get()->GetThemeColor(
1262 3821 /* FILLCOLORHINT */,
1266 [DS] Workaround for WindowBlinds:
1267 Some themes return a near black theme color using FILLCOLORHINT,
1268 this makes notebook pages have an ugly black background and makes
1269 text (usually black) unreadable. Retry again with FILLCOLOR.
1271 This workaround potentially breaks appearance of some themes,
1272 but in practice it already fixes some themes.
1274 if (themeColor
== 1)
1276 wxUxThemeEngine::Get()->GetThemeColor(
1280 3802 /* FILLCOLOR */,
1284 return wxRGBToColour(themeColor
);
1287 #endif // wxUSE_UXTHEME
1289 return GetBackgroundColour();
1292 // ----------------------------------------------------------------------------
1293 // wxNotebook base class virtuals
1294 // ----------------------------------------------------------------------------
1296 #if wxUSE_CONSTRAINTS
1298 // override these 2 functions to do nothing: everything is done in OnSize
1300 void wxNotebook::SetConstraintSizes(bool WXUNUSED(recurse
))
1302 // don't set the sizes of the pages - their correct size is not yet known
1303 wxControl::SetConstraintSizes(false);
1306 bool wxNotebook::DoPhase(int WXUNUSED(nPhase
))
1311 #endif // wxUSE_CONSTRAINTS
1313 // ----------------------------------------------------------------------------
1314 // wxNotebook Windows message handlers
1315 // ----------------------------------------------------------------------------
1317 bool wxNotebook::MSWOnScroll(int orientation
, WXWORD nSBCode
,
1318 WXWORD pos
, WXHWND control
)
1320 // don't generate EVT_SCROLLWIN events for the WM_SCROLLs coming from the
1325 return wxNotebookBase::MSWOnScroll(orientation
, nSBCode
, pos
, control
);
1328 bool wxNotebook::MSWOnNotify(int idCtrl
, WXLPARAM lParam
, WXLPARAM
* result
)
1330 wxNotebookEvent
event(wxEVT_NULL
, m_windowId
);
1332 NMHDR
* hdr
= (NMHDR
*)lParam
;
1333 switch ( hdr
->code
) {
1335 event
.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
);
1338 case TCN_SELCHANGING
:
1339 event
.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
);
1343 return wxControl::MSWOnNotify(idCtrl
, lParam
, result
);
1346 event
.SetSelection(TabCtrl_GetCurSel(GetHwnd()));
1347 event
.SetOldSelection(m_nSelection
);
1348 event
.SetEventObject(this);
1349 event
.SetInt(idCtrl
);
1351 bool processed
= GetEventHandler()->ProcessEvent(event
);
1352 *result
= !event
.IsAllowed();
1356 #endif // wxUSE_NOTEBOOK