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())
61 #define m_hwnd (HWND)GetHWND()
64 #define USE_NOTEBOOK_ANTIFLICKER 0
66 // Set this to 1 to compile anti-flicker code, which creates a potentially
67 // large bitmap for every paint event
68 #define USE_NOTEBOOK_ANTIFLICKER 0
71 // ----------------------------------------------------------------------------
73 // ----------------------------------------------------------------------------
75 // This is a work-around for missing defines in gcc-2.95 headers
77 #define TCS_RIGHT 0x0002
81 #define TCS_VERTICAL 0x0080
85 #define TCS_BOTTOM TCS_RIGHT
88 // ----------------------------------------------------------------------------
90 // ----------------------------------------------------------------------------
92 #include <wx/listimpl.cpp>
94 WX_DEFINE_LIST( wxNotebookPageInfoList
) ;
96 DEFINE_EVENT_TYPE(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
)
97 DEFINE_EVENT_TYPE(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
)
99 BEGIN_EVENT_TABLE(wxNotebook
, wxControl
)
100 #if USE_NOTEBOOK_ANTIFLICKER
101 EVT_ERASE_BACKGROUND(wxNotebook::OnEraseBackground
)
102 EVT_PAINT(wxNotebook::OnPaint
)
104 EVT_NOTEBOOK_PAGE_CHANGED(-1, wxNotebook::OnSelChange
)
105 EVT_SIZE(wxNotebook::OnSize
)
106 EVT_NAVIGATION_KEY(wxNotebook::OnNavigationKey
)
109 #if wxUSE_EXTENDED_RTTI
110 WX_DEFINE_FLAGS( wxNotebookStyle
)
112 wxBEGIN_FLAGS( wxNotebookStyle
)
113 // new style border flags, we put them first to
114 // use them for streaming out
115 wxFLAGS_MEMBER(wxBORDER_SIMPLE
)
116 wxFLAGS_MEMBER(wxBORDER_SUNKEN
)
117 wxFLAGS_MEMBER(wxBORDER_DOUBLE
)
118 wxFLAGS_MEMBER(wxBORDER_RAISED
)
119 wxFLAGS_MEMBER(wxBORDER_STATIC
)
120 wxFLAGS_MEMBER(wxBORDER_NONE
)
122 // old style border flags
123 wxFLAGS_MEMBER(wxSIMPLE_BORDER
)
124 wxFLAGS_MEMBER(wxSUNKEN_BORDER
)
125 wxFLAGS_MEMBER(wxDOUBLE_BORDER
)
126 wxFLAGS_MEMBER(wxRAISED_BORDER
)
127 wxFLAGS_MEMBER(wxSTATIC_BORDER
)
128 wxFLAGS_MEMBER(wxBORDER
)
130 // standard window styles
131 wxFLAGS_MEMBER(wxTAB_TRAVERSAL
)
132 wxFLAGS_MEMBER(wxCLIP_CHILDREN
)
133 wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW
)
134 wxFLAGS_MEMBER(wxWANTS_CHARS
)
135 wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE
)
136 wxFLAGS_MEMBER(wxALWAYS_SHOW_SB
)
137 wxFLAGS_MEMBER(wxVSCROLL
)
138 wxFLAGS_MEMBER(wxHSCROLL
)
140 wxFLAGS_MEMBER(wxNB_FIXEDWIDTH
)
141 wxFLAGS_MEMBER(wxNB_LEFT
)
142 wxFLAGS_MEMBER(wxNB_RIGHT
)
143 wxFLAGS_MEMBER(wxNB_BOTTOM
)
144 wxFLAGS_MEMBER(wxNB_NOPAGETHEME
)
145 wxFLAGS_MEMBER(wxNB_FLAT
)
147 wxEND_FLAGS( wxNotebookStyle
)
149 IMPLEMENT_DYNAMIC_CLASS_XTI(wxNotebook
, wxControl
,"wx/notebook.h")
150 IMPLEMENT_DYNAMIC_CLASS_XTI(wxNotebookPageInfo
, wxObject
, "wx/notebook.h" )
152 wxCOLLECTION_TYPE_INFO( wxNotebookPageInfo
* , wxNotebookPageInfoList
) ;
154 template<> void wxCollectionToVariantArray( wxNotebookPageInfoList
const &theList
, wxxVariantArray
&value
)
156 wxListCollectionToVariantArray
<wxNotebookPageInfoList::compatibility_iterator
>( theList
, value
) ;
159 wxBEGIN_PROPERTIES_TABLE(wxNotebook
)
160 wxEVENT_PROPERTY( PageChanging
, wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
, wxNotebookEvent
)
161 wxEVENT_PROPERTY( PageChanged
, wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
, wxNotebookEvent
)
163 wxPROPERTY_COLLECTION( PageInfos
, wxNotebookPageInfoList
, wxNotebookPageInfo
* , AddPageInfo
, GetPageInfos
, 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
164 wxPROPERTY_FLAGS( WindowStyle
, wxNotebookStyle
, long , SetWindowStyleFlag
, GetWindowStyleFlag
, EMPTY_MACROVALUE
, 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
165 wxEND_PROPERTIES_TABLE()
167 wxBEGIN_HANDLERS_TABLE(wxNotebook
)
168 wxEND_HANDLERS_TABLE()
170 wxCONSTRUCTOR_5( wxNotebook
, wxWindow
* , Parent
, wxWindowID
, Id
, wxPoint
, Position
, wxSize
, Size
, long , WindowStyle
)
173 wxBEGIN_PROPERTIES_TABLE(wxNotebookPageInfo
)
174 wxREADONLY_PROPERTY( Page
, wxNotebookPage
* , GetPage
, EMPTY_MACROVALUE
, 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
175 wxREADONLY_PROPERTY( Text
, wxString
, GetText
, wxString() , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
176 wxREADONLY_PROPERTY( Selected
, bool , GetSelected
, false, 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
177 wxREADONLY_PROPERTY( ImageId
, int , GetImageId
, -1 , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
178 wxEND_PROPERTIES_TABLE()
180 wxBEGIN_HANDLERS_TABLE(wxNotebookPageInfo
)
181 wxEND_HANDLERS_TABLE()
183 wxCONSTRUCTOR_4( wxNotebookPageInfo
, wxNotebookPage
* , Page
, wxString
, Text
, bool , Selected
, int , ImageId
)
186 IMPLEMENT_DYNAMIC_CLASS(wxNotebook
, wxControl
)
187 IMPLEMENT_DYNAMIC_CLASS(wxNotebookPageInfo
, wxObject
)
189 IMPLEMENT_DYNAMIC_CLASS(wxNotebookEvent
, wxNotifyEvent
)
191 // ---------------------------------------------------------------------------
193 // ---------------------------------------------------------------------------
195 #if USE_NOTEBOOK_ANTIFLICKER
196 // wnd proc for the spin button
197 LRESULT APIENTRY _EXPORT
wxNotebookSpinBtnWndProc(HWND hWnd
,
202 // ---------------------------------------------------------------------------
204 // ---------------------------------------------------------------------------
206 // the pointer to standard spin button wnd proc
207 static WXFARPROC s_wndprocNotebookSpinBtn
= (WXFARPROC
)NULL
;
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
242 // default for dynamic class
243 wxNotebook::wxNotebook()
248 // the same arguments as for wxControl
249 wxNotebook::wxNotebook(wxWindow
*parent
,
254 const wxString
& name
)
258 Create(parent
, id
, pos
, size
, style
, name
);
262 bool wxNotebook::Create(wxWindow
*parent
,
267 const wxString
& name
)
270 // Not sure why, but without this style, there is no border
271 // around the notebook tabs.
272 if (style
& wxNB_FLAT
)
273 style
|= wxBORDER_SUNKEN
;
276 // comctl32.dll 6.0 doesn't support non-top tabs with visual styles (the
277 // control is simply not rendered correctly), so disable them in this case
278 const int verComCtl32
= wxApp::GetComCtl32Version();
279 if ( verComCtl32
== 600 )
281 // check if we use themes at all -- if we don't, we're still ok
283 if ( wxUxThemeEngine::GetIfActive() )
286 style
&= ~(wxNB_BOTTOM
| wxNB_LEFT
| wxNB_RIGHT
);
290 LPCTSTR className
= WC_TABCONTROL
;
292 // SysTabCtl32 class has natively CS_HREDRAW and CS_VREDRAW enabled and it
293 // causes horrible flicker when resizing notebook, so get rid of it by
294 // using a class without these styles (but otherwise identical to it)
295 if ( !HasFlag(wxFULL_REPAINT_ON_RESIZE
) )
297 static ClassRegistrar s_clsNotebook
;
298 if ( !s_clsNotebook
.IsInitialized() )
300 // get a copy of standard class and modify it
303 if ( ::GetClassInfo(::GetModuleHandle(NULL
), WC_TABCONTROL
, &wc
) )
305 wc
.lpszClassName
= wxT("_wx_SysTabCtl32");
306 wc
.style
&= ~(CS_HREDRAW
| CS_VREDRAW
);
308 s_clsNotebook
.Register(wc
);
312 wxLogLastError(_T("GetClassInfoEx(SysTabCtl32)"));
316 // use our custom class if available but fall back to the standard
317 // notebook if we failed to register it
318 if ( s_clsNotebook
.IsRegistered() )
320 // it's ok to use c_str() here as the static s_clsNotebook object
321 // has sufficiently long lifetime
322 className
= s_clsNotebook
.GetName().c_str();
326 if ( !CreateControl(parent
, id
, pos
, size
, style
| wxTAB_TRAVERSAL
,
327 wxDefaultValidator
, name
) )
330 if ( !MSWCreateControl(className
, wxEmptyString
, pos
, size
) )
334 if ( HasFlag(wxNB_NOPAGETHEME
) ||
335 wxSystemOptions::IsFalse(wxT("msw.notebook.themed-background")) )
337 SetBackgroundColour(GetThemeBackgroundColour());
339 #endif // wxUSE_UXTHEME
341 // Undocumented hack to get flat notebook style
342 // In fact, we should probably only do this in some
343 // curcumstances, i.e. if we know we will have a border
344 // at the bottom (the tab control doesn't draw it itself)
345 #if defined(__POCKETPC__) || defined(__SMARTPHONE__)
346 if (HasFlag(wxNB_FLAT
))
348 SendMessage(m_hwnd
, CCM_SETVERSION
, COMCTL32_VERSION
, 0);
350 SetBackgroundColour(*wxWHITE
);
356 WXDWORD
wxNotebook::MSWGetStyle(long style
, WXDWORD
*exstyle
) const
358 WXDWORD tabStyle
= wxControl::MSWGetStyle(style
, exstyle
);
360 tabStyle
|= WS_TABSTOP
| TCS_TABS
;
362 if ( style
& wxNB_MULTILINE
)
363 tabStyle
|= TCS_MULTILINE
;
364 if ( style
& wxNB_FIXEDWIDTH
)
365 tabStyle
|= TCS_FIXEDWIDTH
;
367 if ( style
& wxNB_BOTTOM
)
368 tabStyle
|= TCS_RIGHT
;
369 else if ( style
& wxNB_LEFT
)
370 tabStyle
|= TCS_VERTICAL
;
371 else if ( style
& wxNB_RIGHT
)
372 tabStyle
|= TCS_VERTICAL
| TCS_RIGHT
;
377 // note that we never want to have the default WS_EX_CLIENTEDGE style
378 // as it looks too ugly for the notebooks
385 wxNotebook::~wxNotebook()
388 if ( m_hbrBackground
)
389 ::DeleteObject((HBRUSH
)m_hbrBackground
);
390 #endif // wxUSE_UXTHEME
393 // ----------------------------------------------------------------------------
394 // wxNotebook accessors
395 // ----------------------------------------------------------------------------
397 size_t wxNotebook::GetPageCount() const
400 wxASSERT( (int)m_pages
.Count() == TabCtrl_GetItemCount(m_hwnd
) );
402 return m_pages
.Count();
405 int wxNotebook::GetRowCount() const
407 return TabCtrl_GetRowCount(m_hwnd
);
410 int wxNotebook::SetSelection(size_t nPage
)
412 wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxNOT_FOUND
, wxT("notebook page out of range") );
414 if ( int(nPage
) != m_nSelection
)
416 wxNotebookEvent
event(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
, m_windowId
);
417 event
.SetSelection(nPage
);
418 event
.SetOldSelection(m_nSelection
);
419 event
.SetEventObject(this);
420 if ( !GetEventHandler()->ProcessEvent(event
) || event
.IsAllowed() )
422 // program allows the page change
423 event
.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
);
424 (void)GetEventHandler()->ProcessEvent(event
);
426 TabCtrl_SetCurSel(m_hwnd
, nPage
);
433 bool wxNotebook::SetPageText(size_t nPage
, const wxString
& strText
)
435 wxCHECK_MSG( IS_VALID_PAGE(nPage
), false, wxT("notebook page out of range") );
438 tcItem
.mask
= TCIF_TEXT
;
439 tcItem
.pszText
= (wxChar
*)strText
.c_str();
441 return TabCtrl_SetItem(m_hwnd
, nPage
, &tcItem
) != 0;
444 wxString
wxNotebook::GetPageText(size_t nPage
) const
446 wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxEmptyString
, wxT("notebook page out of range") );
450 tcItem
.mask
= TCIF_TEXT
;
451 tcItem
.pszText
= buf
;
452 tcItem
.cchTextMax
= WXSIZEOF(buf
);
455 if ( TabCtrl_GetItem(m_hwnd
, nPage
, &tcItem
) )
456 str
= tcItem
.pszText
;
461 int wxNotebook::GetPageImage(size_t nPage
) const
463 wxCHECK_MSG( IS_VALID_PAGE(nPage
), -1, wxT("notebook page out of range") );
466 tcItem
.mask
= TCIF_IMAGE
;
468 return TabCtrl_GetItem(m_hwnd
, nPage
, &tcItem
) ? tcItem
.iImage
: -1;
471 bool wxNotebook::SetPageImage(size_t nPage
, int nImage
)
473 wxCHECK_MSG( IS_VALID_PAGE(nPage
), false, wxT("notebook page out of range") );
476 tcItem
.mask
= TCIF_IMAGE
;
477 tcItem
.iImage
= nImage
;
479 return TabCtrl_SetItem(m_hwnd
, nPage
, &tcItem
) != 0;
482 void wxNotebook::SetImageList(wxImageList
* imageList
)
484 wxNotebookBase::SetImageList(imageList
);
488 TabCtrl_SetImageList(m_hwnd
, (HIMAGELIST
)imageList
->GetHIMAGELIST());
492 // ----------------------------------------------------------------------------
493 // wxNotebook size settings
494 // ----------------------------------------------------------------------------
496 wxRect
wxNotebook::GetPageSize() const
501 ::GetClientRect(GetHwnd(), &rc
);
503 // This check is to work around a bug in TabCtrl_AdjustRect which will
504 // cause a crash on win2k, or on XP with themes disabled, if the
505 // wxNB_MULTILINE style is used and the rectangle is very small, (such as
506 // when the notebook is first created.) The value of 20 is just
507 // arbitrarily chosen, if there is a better way to determine this value
508 // then please do so. --RD
509 if ( !HasFlag(wxNB_MULTILINE
) || (rc
.right
> 20 && rc
.bottom
> 20) )
511 TabCtrl_AdjustRect(m_hwnd
, false, &rc
);
513 wxCopyRECTToRect(rc
, r
);
519 void wxNotebook::SetPageSize(const wxSize
& size
)
521 // transform the page size into the notebook size
528 TabCtrl_AdjustRect(GetHwnd(), true, &rc
);
531 SetSize(rc
.right
- rc
.left
, rc
.bottom
- rc
.top
);
534 void wxNotebook::SetPadding(const wxSize
& padding
)
536 TabCtrl_SetPadding(GetHwnd(), padding
.x
, padding
.y
);
539 // Windows-only at present. Also, you must use the wxNB_FIXEDWIDTH
541 void wxNotebook::SetTabSize(const wxSize
& sz
)
543 ::SendMessage(GetHwnd(), TCM_SETITEMSIZE
, 0, MAKELPARAM(sz
.x
, sz
.y
));
546 wxSize
wxNotebook::CalcSizeFromPage(const wxSize
& sizePage
) const
548 wxSize sizeTotal
= sizePage
;
550 // We need to make getting tab size part of the wxWidgets API.
552 if (GetPageCount() > 0)
555 TabCtrl_GetItemRect((HWND
) GetHWND(), 0, & rect
);
556 tabSize
.x
= rect
.right
- rect
.left
;
557 tabSize
.y
= rect
.bottom
- rect
.top
;
559 if ( HasFlag(wxNB_LEFT
) || HasFlag(wxNB_RIGHT
) )
561 sizeTotal
.x
+= tabSize
.x
+ 7;
567 sizeTotal
.y
+= tabSize
.y
+ 7;
573 void wxNotebook::AdjustPageSize(wxNotebookPage
*page
)
575 wxCHECK_RET( page
, _T("NULL page in wxNotebook::AdjustPageSize") );
577 const wxRect r
= GetPageSize();
584 // ----------------------------------------------------------------------------
585 // wxNotebook operations
586 // ----------------------------------------------------------------------------
588 // remove one page from the notebook, without deleting
589 wxNotebookPage
*wxNotebook::DoRemovePage(size_t nPage
)
591 wxNotebookPage
*pageRemoved
= wxNotebookBase::DoRemovePage(nPage
);
595 TabCtrl_DeleteItem(m_hwnd
, nPage
);
597 if ( m_pages
.IsEmpty() )
599 // no selection any more, the notebook becamse empty
602 else // notebook still not empty
604 int selNew
= TabCtrl_GetCurSel(m_hwnd
);
607 // No selection change, just refresh the current selection.
608 // Because it could be that the slection index changed
609 // we need to update it.
610 // Note: this does not mean the selection it self changed.
611 m_nSelection
= selNew
;
612 m_pages
[m_nSelection
]->Refresh();
614 else if (int(nPage
) == m_nSelection
)
616 // The selection was deleted.
618 // Determine new selection.
619 if (m_nSelection
== int(GetPageCount()))
620 selNew
= m_nSelection
- 1;
622 selNew
= m_nSelection
;
624 // m_nSelection must be always valid so reset it before calling
627 SetSelection(selNew
);
631 wxFAIL
; // Windows did not behave ok.
639 bool wxNotebook::DeleteAllPages()
641 size_t nPageCount
= GetPageCount();
643 for ( nPage
= 0; nPage
< nPageCount
; nPage
++ )
644 delete m_pages
[nPage
];
648 TabCtrl_DeleteAllItems(m_hwnd
);
652 InvalidateBestSize();
656 // same as AddPage() but does it at given position
657 bool wxNotebook::InsertPage(size_t nPage
,
658 wxNotebookPage
*pPage
,
659 const wxString
& strText
,
663 wxCHECK_MSG( pPage
!= NULL
, false, _T("NULL page in wxNotebook::InsertPage") );
664 wxCHECK_MSG( IS_VALID_PAGE(nPage
) || nPage
== GetPageCount(), false,
665 _T("invalid index in wxNotebook::InsertPage") );
667 wxASSERT_MSG( pPage
->GetParent() == this,
668 _T("notebook pages must have notebook as parent") );
670 // add a new tab to the control
671 // ----------------------------
673 // init all fields to 0
675 wxZeroMemory(tcItem
);
677 // set the image, if any
680 tcItem
.mask
|= TCIF_IMAGE
;
681 tcItem
.iImage
= imageId
;
685 if ( !strText
.empty() )
687 tcItem
.mask
|= TCIF_TEXT
;
688 tcItem
.pszText
= (wxChar
*)strText
.c_str(); // const_cast
691 // hide the page: unless it is selected, it shouldn't be shown (and if it
692 // is selected it will be shown later)
693 HWND hwnd
= GetWinHwnd(pPage
);
694 SetWindowLong(hwnd
, GWL_STYLE
, GetWindowLong(hwnd
, GWL_STYLE
) & ~WS_VISIBLE
);
696 // this updates internal flag too -- otherwise it would get out of sync
697 // with the real state
701 // fit the notebook page to the tab control's display area: this should be
702 // done before adding it to the notebook or TabCtrl_InsertItem() will
703 // change the notebooks size itself!
704 AdjustPageSize(pPage
);
706 // finally do insert it
707 if ( TabCtrl_InsertItem(m_hwnd
, nPage
, &tcItem
) == -1 )
709 wxLogError(wxT("Can't create the notebook page '%s'."), strText
.c_str());
714 // succeeded: save the pointer to the page
715 m_pages
.Insert(pPage
, nPage
);
717 // we may need to adjust the size again if the notebook size changed:
718 // normally this only happens for the first page we add (the tabs which
719 // hadn't been there before are now shown) but for a multiline notebook it
720 // can happen for any page at all as a new row could have been started
721 if ( m_pages
.GetCount() == 1 || HasFlag(wxNB_MULTILINE
) )
723 AdjustPageSize(pPage
);
726 // now deal with the selection
727 // ---------------------------
729 // if the inserted page is before the selected one, we must update the
730 // index of the selected page
731 if ( int(nPage
) <= m_nSelection
)
733 // one extra page added
737 // some page should be selected: either this one or the first one if there
738 // is still no selection
742 else if ( m_nSelection
== -1 )
746 SetSelection(selNew
);
748 InvalidateBestSize();
753 int wxNotebook::HitTest(const wxPoint
& pt
, long *flags
) const
755 TC_HITTESTINFO hitTestInfo
;
756 hitTestInfo
.pt
.x
= pt
.x
;
757 hitTestInfo
.pt
.y
= pt
.y
;
758 int item
= TabCtrl_HitTest(GetHwnd(), &hitTestInfo
);
764 if ((hitTestInfo
.flags
& TCHT_NOWHERE
) == TCHT_NOWHERE
)
765 *flags
|= wxNB_HITTEST_NOWHERE
;
766 if ((hitTestInfo
.flags
& TCHT_ONITEM
) == TCHT_ONITEM
)
767 *flags
|= wxNB_HITTEST_ONITEM
;
768 if ((hitTestInfo
.flags
& TCHT_ONITEMICON
) == TCHT_ONITEMICON
)
769 *flags
|= wxNB_HITTEST_ONICON
;
770 if ((hitTestInfo
.flags
& TCHT_ONITEMLABEL
) == TCHT_ONITEMLABEL
)
771 *flags
|= wxNB_HITTEST_ONLABEL
;
778 // ----------------------------------------------------------------------------
779 // wxNotebook callbacks
780 // ----------------------------------------------------------------------------
782 void wxNotebook::OnSize(wxSizeEvent
& event
)
784 if ( GetPageCount() == 0 )
786 // Prevents droppings on resize, but does cause some flicker
787 // when there are no pages.
795 // Without this, we can sometimes get droppings at the edges
796 // of a notebook, for example a notebook in a splitter window.
797 // This needs to be reconciled with the RefreshRect calls
798 // at the end of this function, which weren't enough to prevent
801 wxSize sz
= GetClientSize();
803 // Refresh right side
804 wxRect
rect(sz
.x
-4, 0, 4, sz
.y
);
807 // Refresh bottom side
808 rect
= wxRect(0, sz
.y
-4, sz
.x
, 4);
812 rect
= wxRect(0, 0, 4, sz
.y
);
817 // fit all the notebook pages to the tab control's display area
820 rc
.left
= rc
.top
= 0;
821 GetSize((int *)&rc
.right
, (int *)&rc
.bottom
);
823 // save the total size, we'll use it below
824 int widthNbook
= rc
.right
- rc
.left
,
825 heightNbook
= rc
.bottom
- rc
.top
;
827 // there seems to be a bug in the implementation of TabCtrl_AdjustRect(): it
828 // returns completely false values for multiline tab controls after the tabs
829 // are added but before getting the first WM_SIZE (off by ~50 pixels, see
831 // http://sf.net/tracker/index.php?func=detail&aid=645323&group_id=9863&atid=109863
833 // and the only work around I could find was this ugly hack... without it
834 // simply toggling the "multiline" checkbox in the notebook sample resulted
835 // in a noticeable page displacement
836 if ( HasFlag(wxNB_MULTILINE
) )
838 // avoid an infinite recursion: we get another notification too!
839 static bool s_isInOnSize
= false;
844 SendMessage(GetHwnd(), WM_SIZE
, SIZE_RESTORED
,
845 MAKELPARAM(rc
.right
, rc
.bottom
));
846 s_isInOnSize
= false;
851 // background bitmap size has changed, update the brush using it too
853 #endif // wxUSE_UXTHEME
855 TabCtrl_AdjustRect(m_hwnd
, false, &rc
);
857 int width
= rc
.right
- rc
.left
,
858 height
= rc
.bottom
- rc
.top
;
859 size_t nCount
= m_pages
.Count();
860 for ( size_t nPage
= 0; nPage
< nCount
; nPage
++ ) {
861 wxNotebookPage
*pPage
= m_pages
[nPage
];
862 pPage
->SetSize(rc
.left
, rc
.top
, width
, height
);
866 // unless we had already repainted everything, we now need to refresh
867 if ( !HasFlag(wxFULL_REPAINT_ON_RESIZE
) )
869 // invalidate areas not covered by pages
870 RefreshRect(wxRect(0, 0, widthNbook
, rc
.top
), false);
871 RefreshRect(wxRect(0, rc
.top
, rc
.left
, height
), false);
872 RefreshRect(wxRect(0, rc
.bottom
, widthNbook
, heightNbook
- rc
.bottom
),
874 RefreshRect(wxRect(rc
.right
, rc
.top
, widthNbook
- rc
.bottom
, height
),
881 void wxNotebook::OnSelChange(wxNotebookEvent
& event
)
883 // is it our tab control?
884 if ( event
.GetEventObject() == this )
886 int sel
= event
.GetOldSelection();
888 m_pages
[sel
]->Show(false);
890 sel
= event
.GetSelection();
893 wxNotebookPage
*pPage
= m_pages
[sel
];
897 // If the newly focused window is not a child of the new page,
898 // SetFocus was not successful and the notebook itself should be
900 wxWindow
*currentFocus
= FindFocus();
901 wxWindow
*startFocus
= currentFocus
;
902 while ( currentFocus
&& currentFocus
!= pPage
&& currentFocus
!= this )
903 currentFocus
= currentFocus
->GetParent();
905 if ( startFocus
== pPage
|| currentFocus
!= pPage
)
909 else // no pages in the notebook, give the focus to itself
917 // we want to give others a chance to process this message as well
921 bool wxNotebook::MSWTranslateMessage(WXMSG
*wxmsg
)
923 const MSG
* const msg
= (MSG
*)wxmsg
;
925 // intercept TAB, CTRL+TAB and CTRL+SHIFT+TAB for processing by wxNotebook.
926 // TAB will be passed to the currently selected page, CTRL+TAB and
927 // CTRL+SHIFT+TAB will be processed by the notebook itself. do not
928 // intercept SHIFT+TAB. This goes to the parent of the notebook which will
930 if ( msg
->message
== WM_KEYDOWN
&& msg
->wParam
== VK_TAB
&&
931 msg
->hwnd
== m_hwnd
&&
932 (wxIsCtrlDown() || !wxIsShiftDown()) )
934 return MSWProcessMessage(wxmsg
);
940 void wxNotebook::OnNavigationKey(wxNavigationKeyEvent
& event
)
942 if ( event
.IsWindowChange() ) {
944 AdvanceSelection(event
.GetDirection());
947 // we get this event in 3 cases
949 // a) one of our pages might have generated it because the user TABbed
950 // out from it in which case we should propagate the event upwards and
951 // our parent will take care of setting the focus to prev/next sibling
955 // b) the parent panel wants to give the focus to us so that we
956 // forward it to our selected page. We can't deal with this in
957 // OnSetFocus() because we don't know which direction the focus came
958 // from in this case and so can't choose between setting the focus to
959 // first or last panel child
963 // c) we ourselves (see MSWTranslateMessage) generated the event
965 wxWindow
* const parent
= GetParent();
967 // the wxObject* casts are required to avoid MinGW GCC 2.95.3 ICE
968 const bool isFromParent
= event
.GetEventObject() == (wxObject
*) parent
;
969 const bool isFromSelf
= event
.GetEventObject() == (wxObject
*) this;
971 if ( isFromParent
|| isFromSelf
)
973 // no, it doesn't come from child, case (b) or (c): forward to a
974 // page but only if direction is backwards (TAB) or from ourselves,
975 if ( m_nSelection
!= -1 &&
976 (!event
.GetDirection() || isFromSelf
) )
978 // so that the page knows that the event comes from it's parent
979 // and is being propagated downwards
980 event
.SetEventObject(this);
982 wxWindow
*page
= m_pages
[m_nSelection
];
983 if ( !page
->GetEventHandler()->ProcessEvent(event
) )
987 //else: page manages focus inside it itself
989 else // otherwise set the focus to the notebook itself
996 // it comes from our child, case (a), pass to the parent, but only
997 // if the direction is forwards. Otherwise set the focus to the
998 // notebook itself. The notebook is always the 'first' control of a
1000 if ( !event
.GetDirection() )
1006 event
.SetCurrentFocus(this);
1007 parent
->GetEventHandler()->ProcessEvent(event
);
1015 bool wxNotebook::DoDrawBackground(WXHDC hDC
, wxWindow
*child
)
1017 wxUxThemeHandle
theme(child
? child
: this, L
"TAB");
1021 // get the notebook client rect (we're not interested in drawing tabs
1023 wxRect r
= GetPageSize();
1028 wxCopyRectToRECT(r
, rc
);
1030 // map rect to the coords of the window we're drawing in
1032 ::MapWindowPoints(GetHwnd(), GetHwndOf(child
), (POINT
*)&rc
, 2);
1035 // apparently DrawThemeBackground() modifies the rect passed to it and if we
1036 // don't do these adjustments, there are some drawing artifacts which are
1037 // only visible with some non default themes; so modify the rect here using
1038 // the magic numbers below so that it still paints the correct area
1045 wxUxThemeEngine::Get()->DrawThemeBackground
1058 WXHBRUSH
wxNotebook::QueryBgBitmap()
1060 wxRect r
= GetPageSize();
1064 WindowHDC
hDC(GetHwnd());
1065 MemoryHDC
hDCMem(hDC
);
1066 CompatibleBitmap
hBmp(hDC
, r
.x
+ r
.width
, r
.y
+ r
.height
);
1068 SelectInHDC
selectBmp(hDCMem
, hBmp
);
1070 if ( !DoDrawBackground((WXHDC
)(HDC
)hDCMem
) )
1073 return (WXHBRUSH
)::CreatePatternBrush(hBmp
);
1076 void wxNotebook::UpdateBgBrush()
1078 if ( m_hbrBackground
)
1079 ::DeleteObject((HBRUSH
)m_hbrBackground
);
1081 if ( !m_hasBgCol
&& wxUxThemeEngine::GetIfActive() )
1083 m_hbrBackground
= QueryBgBitmap();
1085 else // no themes or we've got user-defined solid colour
1087 m_hbrBackground
= NULL
;
1091 WXHBRUSH
wxNotebook::MSWGetBgBrushForChild(WXHDC hDC
, WXHWND hWnd
)
1093 if ( m_hbrBackground
)
1095 // before drawing with the background brush, we need to position it
1098 ::GetWindowRect((HWND
)hWnd
, &rc
);
1100 ::MapWindowPoints(NULL
, GetHwnd(), (POINT
*)&rc
, 1);
1102 if ( !::SetBrushOrgEx((HDC
)hDC
, -rc
.left
, -rc
.top
, NULL
) )
1104 wxLogLastError(_T("SetBrushOrgEx(notebook bg brush)"));
1107 return m_hbrBackground
;
1110 return wxNotebookBase::MSWGetBgBrushForChild(hDC
, hWnd
);
1113 bool wxNotebook::MSWPrintChild(WXHDC hDC
, wxWindow
*child
)
1115 // solid background colour overrides themed background drawing
1116 if ( !UseBgCol() && DoDrawBackground(hDC
, child
) )
1119 return wxNotebookBase::MSWPrintChild(hDC
, child
);
1122 #endif // wxUSE_UXTHEME
1124 // Windows only: attempts to get colour for UX theme page background
1125 wxColour
wxNotebook::GetThemeBackgroundColour() const
1128 if (wxUxThemeEngine::Get())
1130 wxUxThemeHandle
hTheme((wxNotebook
*) this, L
"TAB");
1133 // This is total guesswork.
1134 // See PlatformSDK\Include\Tmschema.h for values
1135 COLORREF themeColor
;
1136 wxUxThemeEngine::Get()->GetThemeColor(
1140 3821 /* FILLCOLORHINT */,
1144 [DS] Workaround for WindowBlinds:
1145 Some themes return a near black theme color using FILLCOLORHINT,
1146 this makes notebook pages have an ugly black background and makes
1147 text (usually black) unreadable. Retry again with FILLCOLOR.
1149 This workaround potentially breaks appearance of some themes,
1150 but in practice it already fixes some themes.
1152 if (themeColor
== 1)
1154 wxUxThemeEngine::Get()->GetThemeColor(
1158 3802 /* FILLCOLOR */,
1162 return wxRGBToColour(themeColor
);
1165 #endif // wxUSE_UXTHEME
1167 return GetBackgroundColour();
1170 // ----------------------------------------------------------------------------
1171 // wxNotebook base class virtuals
1172 // ----------------------------------------------------------------------------
1174 #if wxUSE_CONSTRAINTS
1176 // override these 2 functions to do nothing: everything is done in OnSize
1178 void wxNotebook::SetConstraintSizes(bool WXUNUSED(recurse
))
1180 // don't set the sizes of the pages - their correct size is not yet known
1181 wxControl::SetConstraintSizes(false);
1184 bool wxNotebook::DoPhase(int WXUNUSED(nPhase
))
1189 #endif // wxUSE_CONSTRAINTS
1191 // ----------------------------------------------------------------------------
1192 // wxNotebook Windows message handlers
1193 // ----------------------------------------------------------------------------
1195 bool wxNotebook::MSWOnScroll(int orientation
, WXWORD nSBCode
,
1196 WXWORD pos
, WXHWND control
)
1198 // don't generate EVT_SCROLLWIN events for the WM_SCROLLs coming from the
1203 return wxNotebookBase::MSWOnScroll(orientation
, nSBCode
, pos
, control
);
1206 bool wxNotebook::MSWOnNotify(int idCtrl
, WXLPARAM lParam
, WXLPARAM
* result
)
1208 wxNotebookEvent
event(wxEVT_NULL
, m_windowId
);
1210 NMHDR
* hdr
= (NMHDR
*)lParam
;
1211 switch ( hdr
->code
) {
1213 event
.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
);
1216 case TCN_SELCHANGING
:
1217 event
.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
);
1221 return wxControl::MSWOnNotify(idCtrl
, lParam
, result
);
1224 event
.SetSelection(TabCtrl_GetCurSel(m_hwnd
));
1225 event
.SetOldSelection(m_nSelection
);
1226 event
.SetEventObject(this);
1227 event
.SetInt(idCtrl
);
1229 bool processed
= GetEventHandler()->ProcessEvent(event
);
1230 *result
= !event
.IsAllowed();
1235 void wxNotebook::OnEraseBackground(wxEraseEvent
& WXUNUSED(event
))
1240 void wxNotebook::OnPaint(wxPaintEvent
& WXUNUSED(event
))
1242 // Better painting behaviour, at the expense of system resources
1243 #if USE_NOTEBOOK_ANTIFLICKER
1247 ::GetClientRect(GetHwnd(), &rc
);
1248 wxBitmap
bmp(rc
.right
, rc
.bottom
);
1249 memdc
.SelectObject(bmp
);
1251 // iterate over all child windows to find spin button
1252 for ( HWND child
= ::GetWindow(GetHwnd(), GW_CHILD
);
1254 child
= ::GetWindow(child
, GW_HWNDNEXT
) )
1256 wxWindow
*childWindow
= wxFindWinFromHandle((WXHWND
)child
);
1258 // see if it exists, if no wxWindow found then assume it's the spin btn
1261 // subclass the spin button to override WM_ERASEBKGND
1262 if ( !s_wndprocNotebookSpinBtn
)
1263 s_wndprocNotebookSpinBtn
= (WXFARPROC
)wxGetWindowProc(child
);
1265 wxSetWindowProc(child
, wxNotebookSpinBtnWndProc
);
1270 HBRUSH hbr
= (HBRUSH
)m_hbrBackground
;
1272 // if there is no special brush just use the solid background colour
1276 brush
= wxBrush(GetBackgroundColour());
1277 hbr
= GetHbrushOf(brush
);
1280 ::FillRect(GetHdcOf(memdc
), &rc
, hbr
);
1282 MSWDefWindowProc(WM_PAINT
, (WPARAM
)memdc
.GetHDC(), 0);
1284 dc
.Blit(0, 0, rc
.right
, rc
.bottom
, &memdc
, 0, 0);
1290 #if USE_NOTEBOOK_ANTIFLICKER
1291 // ---------------------------------------------------------------------------
1292 // window proc for spin button
1293 // ---------------------------------------------------------------------------
1295 LRESULT APIENTRY _EXPORT
wxNotebookSpinBtnWndProc(HWND hwnd
,
1300 if ( message
== WM_ERASEBKGND
)
1303 return ::CallWindowProc(CASTWNDPROC s_wndprocNotebookSpinBtn
, hwnd
, message
, wParam
, lParam
);
1307 // USE_NOTEBOOK_ANTIFLICKER
1309 #endif // wxUSE_NOTEBOOK