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"
45 #ifdef __GNUWIN32_OLD__
46 #include "wx/msw/gnuwin32/extra.h"
49 #if !(defined(__GNUWIN32_OLD__) && !defined(__CYGWIN10__))
53 #include "wx/msw/winundef.h"
56 #include "wx/msw/uxtheme.h"
59 // ----------------------------------------------------------------------------
61 // ----------------------------------------------------------------------------
63 // check that the page index is valid
64 #define IS_VALID_PAGE(nPage) ((nPage) < GetPageCount())
67 #define m_hwnd (HWND)GetHWND()
70 #define USE_NOTEBOOK_ANTIFLICKER 0
72 // Set this to 1 to compile anti-flicker code, which creates a potentially
73 // large bitmap for every paint event
74 #define USE_NOTEBOOK_ANTIFLICKER 0
77 // ----------------------------------------------------------------------------
79 // ----------------------------------------------------------------------------
81 // This is a work-around for missing defines in gcc-2.95 headers
83 #define TCS_RIGHT 0x0002
87 #define TCS_VERTICAL 0x0080
91 #define TCS_BOTTOM TCS_RIGHT
94 // ----------------------------------------------------------------------------
96 // ----------------------------------------------------------------------------
98 #include <wx/listimpl.cpp>
100 WX_DEFINE_LIST( wxNotebookPageInfoList
) ;
102 DEFINE_EVENT_TYPE(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
)
103 DEFINE_EVENT_TYPE(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
)
105 BEGIN_EVENT_TABLE(wxNotebook
, wxControl
)
106 #if USE_NOTEBOOK_ANTIFLICKER
107 EVT_ERASE_BACKGROUND(wxNotebook::OnEraseBackground
)
108 EVT_PAINT(wxNotebook::OnPaint
)
110 EVT_NOTEBOOK_PAGE_CHANGED(-1, wxNotebook::OnSelChange
)
111 EVT_SIZE(wxNotebook::OnSize
)
112 EVT_NAVIGATION_KEY(wxNotebook::OnNavigationKey
)
115 #if wxUSE_EXTENDED_RTTI
116 WX_DEFINE_FLAGS( wxNotebookStyle
)
118 wxBEGIN_FLAGS( wxNotebookStyle
)
119 // new style border flags, we put them first to
120 // use them for streaming out
121 wxFLAGS_MEMBER(wxBORDER_SIMPLE
)
122 wxFLAGS_MEMBER(wxBORDER_SUNKEN
)
123 wxFLAGS_MEMBER(wxBORDER_DOUBLE
)
124 wxFLAGS_MEMBER(wxBORDER_RAISED
)
125 wxFLAGS_MEMBER(wxBORDER_STATIC
)
126 wxFLAGS_MEMBER(wxBORDER_NONE
)
128 // old style border flags
129 wxFLAGS_MEMBER(wxSIMPLE_BORDER
)
130 wxFLAGS_MEMBER(wxSUNKEN_BORDER
)
131 wxFLAGS_MEMBER(wxDOUBLE_BORDER
)
132 wxFLAGS_MEMBER(wxRAISED_BORDER
)
133 wxFLAGS_MEMBER(wxSTATIC_BORDER
)
134 wxFLAGS_MEMBER(wxBORDER
)
136 // standard window styles
137 wxFLAGS_MEMBER(wxTAB_TRAVERSAL
)
138 wxFLAGS_MEMBER(wxCLIP_CHILDREN
)
139 wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW
)
140 wxFLAGS_MEMBER(wxWANTS_CHARS
)
141 wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE
)
142 wxFLAGS_MEMBER(wxALWAYS_SHOW_SB
)
143 wxFLAGS_MEMBER(wxVSCROLL
)
144 wxFLAGS_MEMBER(wxHSCROLL
)
146 wxFLAGS_MEMBER(wxNB_FIXEDWIDTH
)
147 wxFLAGS_MEMBER(wxNB_LEFT
)
148 wxFLAGS_MEMBER(wxNB_RIGHT
)
149 wxFLAGS_MEMBER(wxNB_BOTTOM
)
150 wxFLAGS_MEMBER(wxNB_NOPAGETHEME
)
151 wxFLAGS_MEMBER(wxNB_FLAT
)
153 wxEND_FLAGS( wxNotebookStyle
)
155 IMPLEMENT_DYNAMIC_CLASS_XTI(wxNotebook
, wxControl
,"wx/notebook.h")
156 IMPLEMENT_DYNAMIC_CLASS_XTI(wxNotebookPageInfo
, wxObject
, "wx/notebook.h" )
158 wxCOLLECTION_TYPE_INFO( wxNotebookPageInfo
* , wxNotebookPageInfoList
) ;
160 template<> void wxCollectionToVariantArray( wxNotebookPageInfoList
const &theList
, wxxVariantArray
&value
)
162 wxListCollectionToVariantArray
<wxNotebookPageInfoList::compatibility_iterator
>( theList
, value
) ;
165 wxBEGIN_PROPERTIES_TABLE(wxNotebook
)
166 wxEVENT_PROPERTY( PageChanging
, wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
, wxNotebookEvent
)
167 wxEVENT_PROPERTY( PageChanged
, wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
, wxNotebookEvent
)
169 wxPROPERTY_COLLECTION( PageInfos
, wxNotebookPageInfoList
, wxNotebookPageInfo
* , AddPageInfo
, GetPageInfos
, 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
170 wxPROPERTY_FLAGS( WindowStyle
, wxNotebookStyle
, long , SetWindowStyleFlag
, GetWindowStyleFlag
, EMPTY_MACROVALUE
, 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
171 wxEND_PROPERTIES_TABLE()
173 wxBEGIN_HANDLERS_TABLE(wxNotebook
)
174 wxEND_HANDLERS_TABLE()
176 wxCONSTRUCTOR_5( wxNotebook
, wxWindow
* , Parent
, wxWindowID
, Id
, wxPoint
, Position
, wxSize
, Size
, long , WindowStyle
)
179 wxBEGIN_PROPERTIES_TABLE(wxNotebookPageInfo
)
180 wxREADONLY_PROPERTY( Page
, wxNotebookPage
* , GetPage
, EMPTY_MACROVALUE
, 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
181 wxREADONLY_PROPERTY( Text
, wxString
, GetText
, wxString() , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
182 wxREADONLY_PROPERTY( Selected
, bool , GetSelected
, false, 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
183 wxREADONLY_PROPERTY( ImageId
, int , GetImageId
, -1 , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
184 wxEND_PROPERTIES_TABLE()
186 wxBEGIN_HANDLERS_TABLE(wxNotebookPageInfo
)
187 wxEND_HANDLERS_TABLE()
189 wxCONSTRUCTOR_4( wxNotebookPageInfo
, wxNotebookPage
* , Page
, wxString
, Text
, bool , Selected
, int , ImageId
)
192 IMPLEMENT_DYNAMIC_CLASS(wxNotebook
, wxControl
)
193 IMPLEMENT_DYNAMIC_CLASS(wxNotebookPageInfo
, wxObject
)
195 IMPLEMENT_DYNAMIC_CLASS(wxNotebookEvent
, wxNotifyEvent
)
197 // ---------------------------------------------------------------------------
199 // ---------------------------------------------------------------------------
201 #if USE_NOTEBOOK_ANTIFLICKER
202 // wnd proc for the spin button
203 LRESULT APIENTRY _EXPORT
wxNotebookSpinBtnWndProc(HWND hWnd
,
208 // ---------------------------------------------------------------------------
210 // ---------------------------------------------------------------------------
212 // the pointer to standard spin button wnd proc
213 static WXFARPROC s_wndprocNotebookSpinBtn
= (WXFARPROC
)NULL
;
216 // ============================================================================
218 // ============================================================================
220 // ----------------------------------------------------------------------------
221 // wxNotebook construction
222 // ----------------------------------------------------------------------------
224 const wxNotebookPageInfoList
& wxNotebook::GetPageInfos() const
226 wxNotebookPageInfoList
* list
= const_cast< wxNotebookPageInfoList
* >( &m_pageInfos
) ;
227 WX_CLEAR_LIST( wxNotebookPageInfoList
, *list
) ;
228 for( size_t i
= 0 ; i
< GetPageCount() ; ++i
)
230 wxNotebookPageInfo
*info
= new wxNotebookPageInfo() ;
231 info
->Create( const_cast<wxNotebook
*>(this)->GetPage(i
) , GetPageText(i
) , GetSelection() == int(i
) , GetPageImage(i
) ) ;
232 list
->Append( info
) ;
237 // common part of all ctors
238 void wxNotebook::Init()
244 m_hbrBackground
= NULL
;
245 #endif // wxUSE_UXTHEME
248 // default for dynamic class
249 wxNotebook::wxNotebook()
254 // the same arguments as for wxControl
255 wxNotebook::wxNotebook(wxWindow
*parent
,
260 const wxString
& name
)
264 Create(parent
, id
, pos
, size
, style
, name
);
268 bool wxNotebook::Create(wxWindow
*parent
,
273 const wxString
& name
)
276 // Not sure why, but without this style, there is no border
277 // around the notebook tabs.
278 if (style
& wxNB_FLAT
)
279 style
|= wxBORDER_SUNKEN
;
282 // comctl32.dll 6.0 doesn't support non-top tabs with visual styles (the
283 // control is simply not rendered correctly), so disable them in this case
284 const int verComCtl32
= wxApp::GetComCtl32Version();
285 if ( verComCtl32
== 600 )
287 // check if we use themes at all -- if we don't, we're still ok
289 if ( wxUxThemeEngine::GetIfActive() )
292 style
&= ~(wxNB_BOTTOM
| wxNB_LEFT
| wxNB_RIGHT
);
296 LPCTSTR className
= WC_TABCONTROL
;
298 // SysTabCtl32 class has natively CS_HREDRAW and CS_VREDRAW enabled and it
299 // causes horrible flicker when resizing notebook, so get rid of it by
300 // using a class without these styles (but otherwise identical to it)
301 if ( !HasFlag(wxFULL_REPAINT_ON_RESIZE
) )
303 static ClassRegistrar s_clsNotebook
;
304 if ( !s_clsNotebook
.IsInitialized() )
306 // get a copy of standard class and modify it
309 if ( ::GetClassInfo(::GetModuleHandle(NULL
), WC_TABCONTROL
, &wc
) )
311 wc
.lpszClassName
= wxT("_wx_SysTabCtl32");
312 wc
.style
&= ~(CS_HREDRAW
| CS_VREDRAW
);
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();
332 if ( !CreateControl(parent
, id
, pos
, size
, style
| wxTAB_TRAVERSAL
,
333 wxDefaultValidator
, name
) )
336 if ( !MSWCreateControl(className
, wxEmptyString
, pos
, size
) )
340 if ( HasFlag(wxNB_NOPAGETHEME
) ||
341 wxSystemOptions::IsFalse(wxT("msw.notebook.themed-background")) )
343 SetBackgroundColour(GetThemeBackgroundColour());
345 #endif // wxUSE_UXTHEME
347 // Undocumented hack to get flat notebook style
348 // In fact, we should probably only do this in some
349 // curcumstances, i.e. if we know we will have a border
350 // at the bottom (the tab control doesn't draw it itself)
351 #if defined(__POCKETPC__) || defined(__SMARTPHONE__)
352 if (HasFlag(wxNB_FLAT
))
354 SendMessage(m_hwnd
, CCM_SETVERSION
, COMCTL32_VERSION
, 0);
356 SetBackgroundColour(*wxWHITE
);
362 WXDWORD
wxNotebook::MSWGetStyle(long style
, WXDWORD
*exstyle
) const
364 WXDWORD tabStyle
= wxControl::MSWGetStyle(style
, exstyle
);
366 tabStyle
|= WS_TABSTOP
| TCS_TABS
;
368 if ( style
& wxNB_MULTILINE
)
369 tabStyle
|= TCS_MULTILINE
;
370 if ( style
& wxNB_FIXEDWIDTH
)
371 tabStyle
|= TCS_FIXEDWIDTH
;
373 if ( style
& wxNB_BOTTOM
)
374 tabStyle
|= TCS_RIGHT
;
375 else if ( style
& wxNB_LEFT
)
376 tabStyle
|= TCS_VERTICAL
;
377 else if ( style
& wxNB_RIGHT
)
378 tabStyle
|= TCS_VERTICAL
| TCS_RIGHT
;
383 // note that we never want to have the default WS_EX_CLIENTEDGE style
384 // as it looks too ugly for the notebooks
391 wxNotebook::~wxNotebook()
394 if ( m_hbrBackground
)
395 ::DeleteObject((HBRUSH
)m_hbrBackground
);
396 #endif // wxUSE_UXTHEME
399 // ----------------------------------------------------------------------------
400 // wxNotebook accessors
401 // ----------------------------------------------------------------------------
403 size_t wxNotebook::GetPageCount() const
406 wxASSERT( (int)m_pages
.Count() == TabCtrl_GetItemCount(m_hwnd
) );
408 return m_pages
.Count();
411 int wxNotebook::GetRowCount() const
413 return TabCtrl_GetRowCount(m_hwnd
);
416 int wxNotebook::SetSelection(size_t nPage
)
418 wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxNOT_FOUND
, wxT("notebook page out of range") );
420 if ( int(nPage
) != m_nSelection
)
422 wxNotebookEvent
event(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
, m_windowId
);
423 event
.SetSelection(nPage
);
424 event
.SetOldSelection(m_nSelection
);
425 event
.SetEventObject(this);
426 if ( !GetEventHandler()->ProcessEvent(event
) || event
.IsAllowed() )
428 // program allows the page change
429 event
.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
);
430 (void)GetEventHandler()->ProcessEvent(event
);
432 TabCtrl_SetCurSel(m_hwnd
, nPage
);
439 bool wxNotebook::SetPageText(size_t nPage
, const wxString
& strText
)
441 wxCHECK_MSG( IS_VALID_PAGE(nPage
), false, wxT("notebook page out of range") );
444 tcItem
.mask
= TCIF_TEXT
;
445 tcItem
.pszText
= (wxChar
*)strText
.c_str();
447 return TabCtrl_SetItem(m_hwnd
, nPage
, &tcItem
) != 0;
450 wxString
wxNotebook::GetPageText(size_t nPage
) const
452 wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxEmptyString
, wxT("notebook page out of range") );
456 tcItem
.mask
= TCIF_TEXT
;
457 tcItem
.pszText
= buf
;
458 tcItem
.cchTextMax
= WXSIZEOF(buf
);
461 if ( TabCtrl_GetItem(m_hwnd
, nPage
, &tcItem
) )
462 str
= tcItem
.pszText
;
467 int wxNotebook::GetPageImage(size_t nPage
) const
469 wxCHECK_MSG( IS_VALID_PAGE(nPage
), -1, wxT("notebook page out of range") );
472 tcItem
.mask
= TCIF_IMAGE
;
474 return TabCtrl_GetItem(m_hwnd
, nPage
, &tcItem
) ? tcItem
.iImage
: -1;
477 bool wxNotebook::SetPageImage(size_t nPage
, int nImage
)
479 wxCHECK_MSG( IS_VALID_PAGE(nPage
), false, wxT("notebook page out of range") );
482 tcItem
.mask
= TCIF_IMAGE
;
483 tcItem
.iImage
= nImage
;
485 return TabCtrl_SetItem(m_hwnd
, nPage
, &tcItem
) != 0;
488 void wxNotebook::SetImageList(wxImageList
* imageList
)
490 wxNotebookBase::SetImageList(imageList
);
494 TabCtrl_SetImageList(m_hwnd
, (HIMAGELIST
)imageList
->GetHIMAGELIST());
498 // ----------------------------------------------------------------------------
499 // wxNotebook size settings
500 // ----------------------------------------------------------------------------
502 wxRect
wxNotebook::GetPageSize() const
507 ::GetClientRect(GetHwnd(), &rc
);
509 // This check is to work around a bug in TabCtrl_AdjustRect which will
510 // cause a crash on win2k, or on XP with themes disabled, if the
511 // wxNB_MULTILINE style is used and the rectangle is very small, (such as
512 // when the notebook is first created.) The value of 20 is just
513 // arbitrarily chosen, if there is a better way to determine this value
514 // then please do so. --RD
515 if ( !HasFlag(wxNB_MULTILINE
) || (rc
.right
> 20 && rc
.bottom
> 20) )
517 TabCtrl_AdjustRect(m_hwnd
, false, &rc
);
519 wxCopyRECTToRect(rc
, r
);
525 void wxNotebook::SetPageSize(const wxSize
& size
)
527 // transform the page size into the notebook size
534 TabCtrl_AdjustRect(GetHwnd(), true, &rc
);
537 SetSize(rc
.right
- rc
.left
, rc
.bottom
- rc
.top
);
540 void wxNotebook::SetPadding(const wxSize
& padding
)
542 TabCtrl_SetPadding(GetHwnd(), padding
.x
, padding
.y
);
545 // Windows-only at present. Also, you must use the wxNB_FIXEDWIDTH
547 void wxNotebook::SetTabSize(const wxSize
& sz
)
549 ::SendMessage(GetHwnd(), TCM_SETITEMSIZE
, 0, MAKELPARAM(sz
.x
, sz
.y
));
552 wxSize
wxNotebook::CalcSizeFromPage(const wxSize
& sizePage
) const
554 wxSize sizeTotal
= sizePage
;
556 // We need to make getting tab size part of the wxWidgets API.
558 if (GetPageCount() > 0)
561 TabCtrl_GetItemRect((HWND
) GetHWND(), 0, & rect
);
562 tabSize
.x
= rect
.right
- rect
.left
;
563 tabSize
.y
= rect
.bottom
- rect
.top
;
565 if ( HasFlag(wxNB_LEFT
) || HasFlag(wxNB_RIGHT
) )
567 sizeTotal
.x
+= tabSize
.x
+ 7;
573 sizeTotal
.y
+= tabSize
.y
+ 7;
579 void wxNotebook::AdjustPageSize(wxNotebookPage
*page
)
581 wxCHECK_RET( page
, _T("NULL page in wxNotebook::AdjustPageSize") );
583 const wxRect r
= GetPageSize();
590 // ----------------------------------------------------------------------------
591 // wxNotebook operations
592 // ----------------------------------------------------------------------------
594 // remove one page from the notebook, without deleting
595 wxNotebookPage
*wxNotebook::DoRemovePage(size_t nPage
)
597 wxNotebookPage
*pageRemoved
= wxNotebookBase::DoRemovePage(nPage
);
601 TabCtrl_DeleteItem(m_hwnd
, nPage
);
603 if ( m_pages
.IsEmpty() )
605 // no selection any more, the notebook becamse empty
608 else // notebook still not empty
610 int selNew
= TabCtrl_GetCurSel(m_hwnd
);
613 // No selection change, just refresh the current selection.
614 // Because it could be that the slection index changed
615 // we need to update it.
616 // Note: this does not mean the selection it self changed.
617 m_nSelection
= selNew
;
618 m_pages
[m_nSelection
]->Refresh();
620 else if (int(nPage
) == m_nSelection
)
622 // The selection was deleted.
624 // Determine new selection.
625 if (m_nSelection
== int(GetPageCount()))
626 selNew
= m_nSelection
- 1;
628 selNew
= m_nSelection
;
630 // m_nSelection must be always valid so reset it before calling
633 SetSelection(selNew
);
637 wxFAIL
; // Windows did not behave ok.
645 bool wxNotebook::DeleteAllPages()
647 size_t nPageCount
= GetPageCount();
649 for ( nPage
= 0; nPage
< nPageCount
; nPage
++ )
650 delete m_pages
[nPage
];
654 TabCtrl_DeleteAllItems(m_hwnd
);
658 InvalidateBestSize();
662 // same as AddPage() but does it at given position
663 bool wxNotebook::InsertPage(size_t nPage
,
664 wxNotebookPage
*pPage
,
665 const wxString
& strText
,
669 wxCHECK_MSG( pPage
!= NULL
, false, _T("NULL page in wxNotebook::InsertPage") );
670 wxCHECK_MSG( IS_VALID_PAGE(nPage
) || nPage
== GetPageCount(), false,
671 _T("invalid index in wxNotebook::InsertPage") );
673 wxASSERT_MSG( pPage
->GetParent() == this,
674 _T("notebook pages must have notebook as parent") );
676 // add a new tab to the control
677 // ----------------------------
679 // init all fields to 0
681 wxZeroMemory(tcItem
);
683 // set the image, if any
686 tcItem
.mask
|= TCIF_IMAGE
;
687 tcItem
.iImage
= imageId
;
691 if ( !strText
.empty() )
693 tcItem
.mask
|= TCIF_TEXT
;
694 tcItem
.pszText
= (wxChar
*)strText
.c_str(); // const_cast
697 // hide the page: unless it is selected, it shouldn't be shown (and if it
698 // is selected it will be shown later)
699 HWND hwnd
= GetWinHwnd(pPage
);
700 SetWindowLong(hwnd
, GWL_STYLE
, GetWindowLong(hwnd
, GWL_STYLE
) & ~WS_VISIBLE
);
702 // this updates internal flag too -- otherwise it would get out of sync
703 // with the real state
707 // fit the notebook page to the tab control's display area: this should be
708 // done before adding it to the notebook or TabCtrl_InsertItem() will
709 // change the notebooks size itself!
710 AdjustPageSize(pPage
);
712 // finally do insert it
713 if ( TabCtrl_InsertItem(m_hwnd
, nPage
, &tcItem
) == -1 )
715 wxLogError(wxT("Can't create the notebook page '%s'."), strText
.c_str());
720 // succeeded: save the pointer to the page
721 m_pages
.Insert(pPage
, nPage
);
723 // we may need to adjust the size again if the notebook size changed:
724 // normally this only happens for the first page we add (the tabs which
725 // hadn't been there before are now shown) but for a multiline notebook it
726 // can happen for any page at all as a new row could have been started
727 if ( m_pages
.GetCount() == 1 || HasFlag(wxNB_MULTILINE
) )
729 AdjustPageSize(pPage
);
732 // now deal with the selection
733 // ---------------------------
735 // if the inserted page is before the selected one, we must update the
736 // index of the selected page
737 if ( int(nPage
) <= m_nSelection
)
739 // one extra page added
743 // some page should be selected: either this one or the first one if there
744 // is still no selection
748 else if ( m_nSelection
== -1 )
752 SetSelection(selNew
);
754 InvalidateBestSize();
759 int wxNotebook::HitTest(const wxPoint
& pt
, long *flags
) const
761 TC_HITTESTINFO hitTestInfo
;
762 hitTestInfo
.pt
.x
= pt
.x
;
763 hitTestInfo
.pt
.y
= pt
.y
;
764 int item
= TabCtrl_HitTest(GetHwnd(), &hitTestInfo
);
770 if ((hitTestInfo
.flags
& TCHT_NOWHERE
) == TCHT_NOWHERE
)
771 *flags
|= wxNB_HITTEST_NOWHERE
;
772 if ((hitTestInfo
.flags
& TCHT_ONITEM
) == TCHT_ONITEM
)
773 *flags
|= wxNB_HITTEST_ONITEM
;
774 if ((hitTestInfo
.flags
& TCHT_ONITEMICON
) == TCHT_ONITEMICON
)
775 *flags
|= wxNB_HITTEST_ONICON
;
776 if ((hitTestInfo
.flags
& TCHT_ONITEMLABEL
) == TCHT_ONITEMLABEL
)
777 *flags
|= wxNB_HITTEST_ONLABEL
;
784 // ----------------------------------------------------------------------------
785 // wxNotebook callbacks
786 // ----------------------------------------------------------------------------
788 void wxNotebook::OnSize(wxSizeEvent
& event
)
790 if ( GetPageCount() == 0 )
792 // Prevents droppings on resize, but does cause some flicker
793 // when there are no pages.
801 // Without this, we can sometimes get droppings at the edges
802 // of a notebook, for example a notebook in a splitter window.
803 // This needs to be reconciled with the RefreshRect calls
804 // at the end of this function, which weren't enough to prevent
807 wxSize sz
= GetClientSize();
809 // Refresh right side
810 wxRect
rect(sz
.x
-4, 0, 4, sz
.y
);
813 // Refresh bottom side
814 rect
= wxRect(0, sz
.y
-4, sz
.x
, 4);
818 rect
= wxRect(0, 0, 4, sz
.y
);
823 // fit all the notebook pages to the tab control's display area
826 rc
.left
= rc
.top
= 0;
827 GetSize((int *)&rc
.right
, (int *)&rc
.bottom
);
829 // save the total size, we'll use it below
830 int widthNbook
= rc
.right
- rc
.left
,
831 heightNbook
= rc
.bottom
- rc
.top
;
833 // there seems to be a bug in the implementation of TabCtrl_AdjustRect(): it
834 // returns completely false values for multiline tab controls after the tabs
835 // are added but before getting the first WM_SIZE (off by ~50 pixels, see
837 // http://sf.net/tracker/index.php?func=detail&aid=645323&group_id=9863&atid=109863
839 // and the only work around I could find was this ugly hack... without it
840 // simply toggling the "multiline" checkbox in the notebook sample resulted
841 // in a noticeable page displacement
842 if ( HasFlag(wxNB_MULTILINE
) )
844 // avoid an infinite recursion: we get another notification too!
845 static bool s_isInOnSize
= false;
850 SendMessage(GetHwnd(), WM_SIZE
, SIZE_RESTORED
,
851 MAKELPARAM(rc
.right
, rc
.bottom
));
852 s_isInOnSize
= false;
857 // background bitmap size has changed, update the brush using it too
859 #endif // wxUSE_UXTHEME
861 TabCtrl_AdjustRect(m_hwnd
, false, &rc
);
863 int width
= rc
.right
- rc
.left
,
864 height
= rc
.bottom
- rc
.top
;
865 size_t nCount
= m_pages
.Count();
866 for ( size_t nPage
= 0; nPage
< nCount
; nPage
++ ) {
867 wxNotebookPage
*pPage
= m_pages
[nPage
];
868 pPage
->SetSize(rc
.left
, rc
.top
, width
, height
);
872 // unless we had already repainted everything, we now need to refresh
873 if ( !HasFlag(wxFULL_REPAINT_ON_RESIZE
) )
875 // invalidate areas not covered by pages
876 RefreshRect(wxRect(0, 0, widthNbook
, rc
.top
), false);
877 RefreshRect(wxRect(0, rc
.top
, rc
.left
, height
), false);
878 RefreshRect(wxRect(0, rc
.bottom
, widthNbook
, heightNbook
- rc
.bottom
),
880 RefreshRect(wxRect(rc
.right
, rc
.top
, widthNbook
- rc
.bottom
, height
),
887 void wxNotebook::OnSelChange(wxNotebookEvent
& event
)
889 // is it our tab control?
890 if ( event
.GetEventObject() == this )
892 int sel
= event
.GetOldSelection();
894 m_pages
[sel
]->Show(false);
896 sel
= event
.GetSelection();
899 wxNotebookPage
*pPage
= m_pages
[sel
];
903 // If the newly focused window is not a child of the new page,
904 // SetFocus was not successful and the notebook itself should be
906 wxWindow
*currentFocus
= FindFocus();
907 wxWindow
*startFocus
= currentFocus
;
908 while ( currentFocus
&& currentFocus
!= pPage
&& currentFocus
!= this )
909 currentFocus
= currentFocus
->GetParent();
911 if ( startFocus
== pPage
|| currentFocus
!= pPage
)
915 else // no pages in the notebook, give the focus to itself
923 // we want to give others a chance to process this message as well
927 bool wxNotebook::MSWTranslateMessage(WXMSG
*wxmsg
)
929 const MSG
* const msg
= (MSG
*)wxmsg
;
931 // intercept TAB, CTRL+TAB and CTRL+SHIFT+TAB for processing by wxNotebook.
932 // TAB will be passed to the currently selected page, CTRL+TAB and
933 // CTRL+SHIFT+TAB will be processed by the notebook itself. do not
934 // intercept SHIFT+TAB. This goes to the parent of the notebook which will
936 if ( msg
->message
== WM_KEYDOWN
&& msg
->wParam
== VK_TAB
&&
937 msg
->hwnd
== m_hwnd
&&
938 (wxIsCtrlDown() || !wxIsShiftDown()) )
940 return MSWProcessMessage(wxmsg
);
946 void wxNotebook::OnNavigationKey(wxNavigationKeyEvent
& event
)
948 if ( event
.IsWindowChange() ) {
950 AdvanceSelection(event
.GetDirection());
953 // we get this event in 3 cases
955 // a) one of our pages might have generated it because the user TABbed
956 // out from it in which case we should propagate the event upwards and
957 // our parent will take care of setting the focus to prev/next sibling
961 // b) the parent panel wants to give the focus to us so that we
962 // forward it to our selected page. We can't deal with this in
963 // OnSetFocus() because we don't know which direction the focus came
964 // from in this case and so can't choose between setting the focus to
965 // first or last panel child
969 // c) we ourselves (see MSWTranslateMessage) generated the event
971 wxWindow
* const parent
= GetParent();
973 const bool isFromParent
= event
.GetEventObject() == parent
;
974 const bool isFromSelf
= event
.GetEventObject() == this;
976 if ( isFromParent
|| isFromSelf
)
978 // no, it doesn't come from child, case (b) or (c): forward to a
979 // page but only if direction is backwards (TAB) or from ourselves,
980 if ( m_nSelection
!= -1 &&
981 (!event
.GetDirection() || isFromSelf
) )
983 // so that the page knows that the event comes from it's parent
984 // and is being propagated downwards
985 event
.SetEventObject(this);
987 wxWindow
*page
= m_pages
[m_nSelection
];
988 if ( !page
->GetEventHandler()->ProcessEvent(event
) )
992 //else: page manages focus inside it itself
994 else // otherwise set the focus to the notebook itself
1001 // it comes from our child, case (a), pass to the parent, but only
1002 // if the direction is forwards. Otherwise set the focus to the
1003 // notebook itself. The notebook is always the 'first' control of a
1005 if ( !event
.GetDirection() )
1011 event
.SetCurrentFocus(this);
1012 parent
->GetEventHandler()->ProcessEvent(event
);
1020 bool wxNotebook::DoDrawBackground(WXHDC hDC
, wxWindow
*child
)
1022 wxUxThemeHandle
theme(child
? child
: this, L
"TAB");
1026 // get the notebook client rect (we're not interested in drawing tabs
1028 wxRect r
= GetPageSize();
1033 wxCopyRectToRECT(r
, rc
);
1035 // map rect to the coords of the window we're drawing in
1037 ::MapWindowPoints(GetHwnd(), GetHwndOf(child
), (POINT
*)&rc
, 2);
1040 // apparently DrawThemeBackground() modifies the rect passed to it and if we
1041 // don't do these adjustments, there are some drawing artifacts which are
1042 // only visible with some non default themes; so modify the rect here using
1043 // the magic numbers below so that it still paints the correct area
1050 wxUxThemeEngine::Get()->DrawThemeBackground
1063 WXHBRUSH
wxNotebook::QueryBgBitmap()
1065 wxRect r
= GetPageSize();
1069 WindowHDC
hDC(GetHwnd());
1070 MemoryHDC
hDCMem(hDC
);
1071 CompatibleBitmap
hBmp(hDC
, r
.x
+ r
.width
, r
.y
+ r
.height
);
1073 SelectInHDC
selectBmp(hDCMem
, hBmp
);
1075 if ( !DoDrawBackground((WXHDC
)(HDC
)hDCMem
) )
1078 return (WXHBRUSH
)::CreatePatternBrush(hBmp
);
1081 void wxNotebook::UpdateBgBrush()
1083 if ( m_hbrBackground
)
1084 ::DeleteObject((HBRUSH
)m_hbrBackground
);
1086 if ( !m_hasBgCol
&& wxUxThemeEngine::GetIfActive() )
1088 m_hbrBackground
= QueryBgBitmap();
1090 else // no themes or we've got user-defined solid colour
1092 m_hbrBackground
= NULL
;
1096 WXHBRUSH
wxNotebook::MSWGetBgBrushForChild(WXHDC hDC
, WXHWND hWnd
)
1098 if ( m_hbrBackground
)
1100 // before drawing with the background brush, we need to position it
1103 ::GetWindowRect((HWND
)hWnd
, &rc
);
1105 ::MapWindowPoints(NULL
, GetHwnd(), (POINT
*)&rc
, 1);
1107 if ( !::SetBrushOrgEx((HDC
)hDC
, -rc
.left
, -rc
.top
, NULL
) )
1109 wxLogLastError(_T("SetBrushOrgEx(notebook bg brush)"));
1112 return m_hbrBackground
;
1115 return wxNotebookBase::MSWGetBgBrushForChild(hDC
, hWnd
);
1118 bool wxNotebook::MSWPrintChild(WXHDC hDC
, wxWindow
*child
)
1120 // solid background colour overrides themed background drawing
1121 if ( !UseBgCol() && DoDrawBackground(hDC
, child
) )
1124 return wxNotebookBase::MSWPrintChild(hDC
, child
);
1127 #endif // wxUSE_UXTHEME
1129 // Windows only: attempts to get colour for UX theme page background
1130 wxColour
wxNotebook::GetThemeBackgroundColour() const
1133 if (wxUxThemeEngine::Get())
1135 wxUxThemeHandle
hTheme((wxNotebook
*) this, L
"TAB");
1138 // This is total guesswork.
1139 // See PlatformSDK\Include\Tmschema.h for values
1140 COLORREF themeColor
;
1141 wxUxThemeEngine::Get()->GetThemeColor(
1145 3821 /* FILLCOLORHINT */,
1149 [DS] Workaround for WindowBlinds:
1150 Some themes return a near black theme color using FILLCOLORHINT,
1151 this makes notebook pages have an ugly black background and makes
1152 text (usually black) unreadable. Retry again with FILLCOLOR.
1154 This workaround potentially breaks appearance of some themes,
1155 but in practice it already fixes some themes.
1157 if (themeColor
== 1)
1159 wxUxThemeEngine::Get()->GetThemeColor(
1163 3802 /* FILLCOLOR */,
1167 return wxRGBToColour(themeColor
);
1170 #endif // wxUSE_UXTHEME
1172 return GetBackgroundColour();
1175 // ----------------------------------------------------------------------------
1176 // wxNotebook base class virtuals
1177 // ----------------------------------------------------------------------------
1179 #if wxUSE_CONSTRAINTS
1181 // override these 2 functions to do nothing: everything is done in OnSize
1183 void wxNotebook::SetConstraintSizes(bool WXUNUSED(recurse
))
1185 // don't set the sizes of the pages - their correct size is not yet known
1186 wxControl::SetConstraintSizes(false);
1189 bool wxNotebook::DoPhase(int WXUNUSED(nPhase
))
1194 #endif // wxUSE_CONSTRAINTS
1196 // ----------------------------------------------------------------------------
1197 // wxNotebook Windows message handlers
1198 // ----------------------------------------------------------------------------
1200 bool wxNotebook::MSWOnScroll(int orientation
, WXWORD nSBCode
,
1201 WXWORD pos
, WXHWND control
)
1203 // don't generate EVT_SCROLLWIN events for the WM_SCROLLs coming from the
1208 return wxNotebookBase::MSWOnScroll(orientation
, nSBCode
, pos
, control
);
1211 bool wxNotebook::MSWOnNotify(int idCtrl
, WXLPARAM lParam
, WXLPARAM
* result
)
1213 wxNotebookEvent
event(wxEVT_NULL
, m_windowId
);
1215 NMHDR
* hdr
= (NMHDR
*)lParam
;
1216 switch ( hdr
->code
) {
1218 event
.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
);
1221 case TCN_SELCHANGING
:
1222 event
.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
);
1226 return wxControl::MSWOnNotify(idCtrl
, lParam
, result
);
1229 event
.SetSelection(TabCtrl_GetCurSel(m_hwnd
));
1230 event
.SetOldSelection(m_nSelection
);
1231 event
.SetEventObject(this);
1232 event
.SetInt(idCtrl
);
1234 bool processed
= GetEventHandler()->ProcessEvent(event
);
1235 *result
= !event
.IsAllowed();
1240 void wxNotebook::OnEraseBackground(wxEraseEvent
& WXUNUSED(event
))
1245 void wxNotebook::OnPaint(wxPaintEvent
& WXUNUSED(event
))
1247 // Better painting behaviour, at the expense of system resources
1248 #if USE_NOTEBOOK_ANTIFLICKER
1252 ::GetClientRect(GetHwnd(), &rc
);
1253 wxBitmap
bmp(rc
.right
, rc
.bottom
);
1254 memdc
.SelectObject(bmp
);
1256 // iterate over all child windows to find spin button
1257 for ( HWND child
= ::GetWindow(GetHwnd(), GW_CHILD
);
1259 child
= ::GetWindow(child
, GW_HWNDNEXT
) )
1261 wxWindow
*childWindow
= wxFindWinFromHandle((WXHWND
)child
);
1263 // see if it exists, if no wxWindow found then assume it's the spin btn
1266 // subclass the spin button to override WM_ERASEBKGND
1267 if ( !s_wndprocNotebookSpinBtn
)
1268 s_wndprocNotebookSpinBtn
= (WXFARPROC
)wxGetWindowProc(child
);
1270 wxSetWindowProc(child
, wxNotebookSpinBtnWndProc
);
1275 HBRUSH hbr
= (HBRUSH
)m_hbrBackground
;
1277 // if there is no special brush just use the solid background colour
1281 brush
= wxBrush(GetBackgroundColour());
1282 hbr
= GetHbrushOf(brush
);
1285 ::FillRect(GetHdcOf(memdc
), &rc
, hbr
);
1287 MSWDefWindowProc(WM_PAINT
, (WPARAM
)memdc
.GetHDC(), 0);
1289 dc
.Blit(0, 0, rc
.right
, rc
.bottom
, &memdc
, 0, 0);
1295 #if USE_NOTEBOOK_ANTIFLICKER
1296 // ---------------------------------------------------------------------------
1297 // window proc for spin button
1298 // ---------------------------------------------------------------------------
1300 LRESULT APIENTRY _EXPORT
wxNotebookSpinBtnWndProc(HWND hwnd
,
1305 if ( message
== WM_ERASEBKGND
)
1308 return ::CallWindowProc(CASTWNDPROC s_wndprocNotebookSpinBtn
, hwnd
, message
, wParam
, lParam
);
1312 // USE_NOTEBOOK_ANTIFLICKER
1314 #endif // wxUSE_NOTEBOOK