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 USE_NOTEBOOK_ANTIFLICKER 0
63 // Set this to 1 to compile anti-flicker code, which creates a potentially
64 // large bitmap for every paint event
65 #define USE_NOTEBOOK_ANTIFLICKER 0
68 // ----------------------------------------------------------------------------
70 // ----------------------------------------------------------------------------
72 // This is a work-around for missing defines in gcc-2.95 headers
74 #define TCS_RIGHT 0x0002
78 #define TCS_VERTICAL 0x0080
82 #define TCS_BOTTOM TCS_RIGHT
85 // ----------------------------------------------------------------------------
87 // ----------------------------------------------------------------------------
89 #include <wx/listimpl.cpp>
91 WX_DEFINE_LIST( wxNotebookPageInfoList
) ;
93 DEFINE_EVENT_TYPE(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
)
94 DEFINE_EVENT_TYPE(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
)
96 BEGIN_EVENT_TABLE(wxNotebook
, wxControl
)
97 #if USE_NOTEBOOK_ANTIFLICKER
98 EVT_ERASE_BACKGROUND(wxNotebook::OnEraseBackground
)
99 EVT_PAINT(wxNotebook::OnPaint
)
101 EVT_NOTEBOOK_PAGE_CHANGED(-1, wxNotebook::OnSelChange
)
102 EVT_SIZE(wxNotebook::OnSize
)
103 EVT_NAVIGATION_KEY(wxNotebook::OnNavigationKey
)
106 #if wxUSE_EXTENDED_RTTI
107 WX_DEFINE_FLAGS( wxNotebookStyle
)
109 wxBEGIN_FLAGS( wxNotebookStyle
)
110 // new style border flags, we put them first to
111 // use them for streaming out
112 wxFLAGS_MEMBER(wxBORDER_SIMPLE
)
113 wxFLAGS_MEMBER(wxBORDER_SUNKEN
)
114 wxFLAGS_MEMBER(wxBORDER_DOUBLE
)
115 wxFLAGS_MEMBER(wxBORDER_RAISED
)
116 wxFLAGS_MEMBER(wxBORDER_STATIC
)
117 wxFLAGS_MEMBER(wxBORDER_NONE
)
119 // old style border flags
120 wxFLAGS_MEMBER(wxSIMPLE_BORDER
)
121 wxFLAGS_MEMBER(wxSUNKEN_BORDER
)
122 wxFLAGS_MEMBER(wxDOUBLE_BORDER
)
123 wxFLAGS_MEMBER(wxRAISED_BORDER
)
124 wxFLAGS_MEMBER(wxSTATIC_BORDER
)
125 wxFLAGS_MEMBER(wxBORDER
)
127 // standard window styles
128 wxFLAGS_MEMBER(wxTAB_TRAVERSAL
)
129 wxFLAGS_MEMBER(wxCLIP_CHILDREN
)
130 wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW
)
131 wxFLAGS_MEMBER(wxWANTS_CHARS
)
132 wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE
)
133 wxFLAGS_MEMBER(wxALWAYS_SHOW_SB
)
134 wxFLAGS_MEMBER(wxVSCROLL
)
135 wxFLAGS_MEMBER(wxHSCROLL
)
137 wxFLAGS_MEMBER(wxNB_FIXEDWIDTH
)
138 wxFLAGS_MEMBER(wxNB_LEFT
)
139 wxFLAGS_MEMBER(wxNB_RIGHT
)
140 wxFLAGS_MEMBER(wxNB_BOTTOM
)
141 wxFLAGS_MEMBER(wxNB_NOPAGETHEME
)
142 wxFLAGS_MEMBER(wxNB_FLAT
)
144 wxEND_FLAGS( wxNotebookStyle
)
146 IMPLEMENT_DYNAMIC_CLASS_XTI(wxNotebook
, wxControl
,"wx/notebook.h")
147 IMPLEMENT_DYNAMIC_CLASS_XTI(wxNotebookPageInfo
, wxObject
, "wx/notebook.h" )
149 wxCOLLECTION_TYPE_INFO( wxNotebookPageInfo
* , wxNotebookPageInfoList
) ;
151 template<> void wxCollectionToVariantArray( wxNotebookPageInfoList
const &theList
, wxxVariantArray
&value
)
153 wxListCollectionToVariantArray
<wxNotebookPageInfoList::compatibility_iterator
>( theList
, value
) ;
156 wxBEGIN_PROPERTIES_TABLE(wxNotebook
)
157 wxEVENT_PROPERTY( PageChanging
, wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
, wxNotebookEvent
)
158 wxEVENT_PROPERTY( PageChanged
, wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
, wxNotebookEvent
)
160 wxPROPERTY_COLLECTION( PageInfos
, wxNotebookPageInfoList
, wxNotebookPageInfo
* , AddPageInfo
, GetPageInfos
, 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
161 wxPROPERTY_FLAGS( WindowStyle
, wxNotebookStyle
, long , SetWindowStyleFlag
, GetWindowStyleFlag
, EMPTY_MACROVALUE
, 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
162 wxEND_PROPERTIES_TABLE()
164 wxBEGIN_HANDLERS_TABLE(wxNotebook
)
165 wxEND_HANDLERS_TABLE()
167 wxCONSTRUCTOR_5( wxNotebook
, wxWindow
* , Parent
, wxWindowID
, Id
, wxPoint
, Position
, wxSize
, Size
, long , WindowStyle
)
170 wxBEGIN_PROPERTIES_TABLE(wxNotebookPageInfo
)
171 wxREADONLY_PROPERTY( Page
, wxNotebookPage
* , GetPage
, EMPTY_MACROVALUE
, 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
172 wxREADONLY_PROPERTY( Text
, wxString
, GetText
, wxString() , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
173 wxREADONLY_PROPERTY( Selected
, bool , GetSelected
, false, 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
174 wxREADONLY_PROPERTY( ImageId
, int , GetImageId
, -1 , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
175 wxEND_PROPERTIES_TABLE()
177 wxBEGIN_HANDLERS_TABLE(wxNotebookPageInfo
)
178 wxEND_HANDLERS_TABLE()
180 wxCONSTRUCTOR_4( wxNotebookPageInfo
, wxNotebookPage
* , Page
, wxString
, Text
, bool , Selected
, int , ImageId
)
183 IMPLEMENT_DYNAMIC_CLASS(wxNotebook
, wxControl
)
184 IMPLEMENT_DYNAMIC_CLASS(wxNotebookPageInfo
, wxObject
)
186 IMPLEMENT_DYNAMIC_CLASS(wxNotebookEvent
, wxNotifyEvent
)
188 // ---------------------------------------------------------------------------
190 // ---------------------------------------------------------------------------
192 #if USE_NOTEBOOK_ANTIFLICKER
193 // wnd proc for the spin button
194 LRESULT APIENTRY _EXPORT
wxNotebookSpinBtnWndProc(HWND hWnd
,
199 // ---------------------------------------------------------------------------
201 // ---------------------------------------------------------------------------
203 // the pointer to standard spin button wnd proc
204 static WXFARPROC s_wndprocNotebookSpinBtn
= (WXFARPROC
)NULL
;
207 // ============================================================================
209 // ============================================================================
211 // ----------------------------------------------------------------------------
212 // wxNotebook construction
213 // ----------------------------------------------------------------------------
215 const wxNotebookPageInfoList
& wxNotebook::GetPageInfos() const
217 wxNotebookPageInfoList
* list
= const_cast< wxNotebookPageInfoList
* >( &m_pageInfos
) ;
218 WX_CLEAR_LIST( wxNotebookPageInfoList
, *list
) ;
219 for( size_t i
= 0 ; i
< GetPageCount() ; ++i
)
221 wxNotebookPageInfo
*info
= new wxNotebookPageInfo() ;
222 info
->Create( const_cast<wxNotebook
*>(this)->GetPage(i
) , GetPageText(i
) , GetSelection() == int(i
) , GetPageImage(i
) ) ;
223 list
->Append( info
) ;
228 // common part of all ctors
229 void wxNotebook::Init()
235 m_hbrBackground
= NULL
;
236 #endif // wxUSE_UXTHEME
239 // default for dynamic class
240 wxNotebook::wxNotebook()
245 // the same arguments as for wxControl
246 wxNotebook::wxNotebook(wxWindow
*parent
,
251 const wxString
& name
)
255 Create(parent
, id
, pos
, size
, style
, name
);
259 bool wxNotebook::Create(wxWindow
*parent
,
264 const wxString
& name
)
267 // Not sure why, but without this style, there is no border
268 // around the notebook tabs.
269 if (style
& wxNB_FLAT
)
270 style
|= wxBORDER_SUNKEN
;
273 // comctl32.dll 6.0 doesn't support non-top tabs with visual styles (the
274 // control is simply not rendered correctly), so disable them in this case
275 const int verComCtl32
= wxApp::GetComCtl32Version();
276 if ( verComCtl32
== 600 )
278 // check if we use themes at all -- if we don't, we're still ok
280 if ( wxUxThemeEngine::GetIfActive() )
283 style
&= ~(wxNB_BOTTOM
| wxNB_LEFT
| wxNB_RIGHT
);
287 LPCTSTR className
= WC_TABCONTROL
;
289 // SysTabCtl32 class has natively CS_HREDRAW and CS_VREDRAW enabled and it
290 // causes horrible flicker when resizing notebook, so get rid of it by
291 // using a class without these styles (but otherwise identical to it)
292 if ( !HasFlag(wxFULL_REPAINT_ON_RESIZE
) )
294 static ClassRegistrar s_clsNotebook
;
295 if ( !s_clsNotebook
.IsInitialized() )
297 // get a copy of standard class and modify it
300 if ( ::GetClassInfo(::GetModuleHandle(NULL
), WC_TABCONTROL
, &wc
) )
302 wc
.lpszClassName
= wxT("_wx_SysTabCtl32");
303 wc
.style
&= ~(CS_HREDRAW
| CS_VREDRAW
);
305 s_clsNotebook
.Register(wc
);
309 wxLogLastError(_T("GetClassInfoEx(SysTabCtl32)"));
313 // use our custom class if available but fall back to the standard
314 // notebook if we failed to register it
315 if ( s_clsNotebook
.IsRegistered() )
317 // it's ok to use c_str() here as the static s_clsNotebook object
318 // has sufficiently long lifetime
319 className
= s_clsNotebook
.GetName().c_str();
323 if ( !CreateControl(parent
, id
, pos
, size
, style
| wxTAB_TRAVERSAL
,
324 wxDefaultValidator
, name
) )
327 if ( !MSWCreateControl(className
, wxEmptyString
, pos
, size
) )
331 if ( HasFlag(wxNB_NOPAGETHEME
) ||
332 wxSystemOptions::IsFalse(wxT("msw.notebook.themed-background")) )
334 SetBackgroundColour(GetThemeBackgroundColour());
336 #endif // wxUSE_UXTHEME
338 // Undocumented hack to get flat notebook style
339 // In fact, we should probably only do this in some
340 // curcumstances, i.e. if we know we will have a border
341 // at the bottom (the tab control doesn't draw it itself)
342 #if defined(__POCKETPC__) || defined(__SMARTPHONE__)
343 if (HasFlag(wxNB_FLAT
))
345 SendMessage(GetHwnd(), CCM_SETVERSION
, COMCTL32_VERSION
, 0);
347 SetBackgroundColour(*wxWHITE
);
353 WXDWORD
wxNotebook::MSWGetStyle(long style
, WXDWORD
*exstyle
) const
355 WXDWORD tabStyle
= wxControl::MSWGetStyle(style
, exstyle
);
357 tabStyle
|= WS_TABSTOP
| TCS_TABS
;
359 if ( style
& wxNB_MULTILINE
)
360 tabStyle
|= TCS_MULTILINE
;
361 if ( style
& wxNB_FIXEDWIDTH
)
362 tabStyle
|= TCS_FIXEDWIDTH
;
364 if ( style
& wxNB_BOTTOM
)
365 tabStyle
|= TCS_RIGHT
;
366 else if ( style
& wxNB_LEFT
)
367 tabStyle
|= TCS_VERTICAL
;
368 else if ( style
& wxNB_RIGHT
)
369 tabStyle
|= TCS_VERTICAL
| TCS_RIGHT
;
374 // note that we never want to have the default WS_EX_CLIENTEDGE style
375 // as it looks too ugly for the notebooks
382 wxNotebook::~wxNotebook()
385 if ( m_hbrBackground
)
386 ::DeleteObject((HBRUSH
)m_hbrBackground
);
387 #endif // wxUSE_UXTHEME
390 // ----------------------------------------------------------------------------
391 // wxNotebook accessors
392 // ----------------------------------------------------------------------------
394 size_t wxNotebook::GetPageCount() const
397 wxASSERT( (int)m_pages
.Count() == TabCtrl_GetItemCount(GetHwnd()) );
399 return m_pages
.Count();
402 int wxNotebook::GetRowCount() const
404 return TabCtrl_GetRowCount(GetHwnd());
407 int wxNotebook::SetSelection(size_t nPage
)
409 wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxNOT_FOUND
, wxT("notebook page out of range") );
411 if ( int(nPage
) != m_nSelection
)
413 wxNotebookEvent
event(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
, m_windowId
);
414 event
.SetSelection(nPage
);
415 event
.SetOldSelection(m_nSelection
);
416 event
.SetEventObject(this);
417 if ( !GetEventHandler()->ProcessEvent(event
) || event
.IsAllowed() )
419 // program allows the page change
420 event
.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
);
421 (void)GetEventHandler()->ProcessEvent(event
);
423 TabCtrl_SetCurSel(GetHwnd(), nPage
);
430 bool wxNotebook::SetPageText(size_t nPage
, const wxString
& strText
)
432 wxCHECK_MSG( IS_VALID_PAGE(nPage
), false, wxT("notebook page out of range") );
435 tcItem
.mask
= TCIF_TEXT
;
436 tcItem
.pszText
= (wxChar
*)strText
.c_str();
438 return TabCtrl_SetItem(GetHwnd(), nPage
, &tcItem
) != 0;
441 wxString
wxNotebook::GetPageText(size_t nPage
) const
443 wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxEmptyString
, wxT("notebook page out of range") );
447 tcItem
.mask
= TCIF_TEXT
;
448 tcItem
.pszText
= buf
;
449 tcItem
.cchTextMax
= WXSIZEOF(buf
);
452 if ( TabCtrl_GetItem(GetHwnd(), nPage
, &tcItem
) )
453 str
= tcItem
.pszText
;
458 int wxNotebook::GetPageImage(size_t nPage
) const
460 wxCHECK_MSG( IS_VALID_PAGE(nPage
), -1, wxT("notebook page out of range") );
463 tcItem
.mask
= TCIF_IMAGE
;
465 return TabCtrl_GetItem(GetHwnd(), nPage
, &tcItem
) ? tcItem
.iImage
: -1;
468 bool wxNotebook::SetPageImage(size_t nPage
, int nImage
)
470 wxCHECK_MSG( IS_VALID_PAGE(nPage
), false, wxT("notebook page out of range") );
473 tcItem
.mask
= TCIF_IMAGE
;
474 tcItem
.iImage
= nImage
;
476 return TabCtrl_SetItem(GetHwnd(), nPage
, &tcItem
) != 0;
479 void wxNotebook::SetImageList(wxImageList
* imageList
)
481 wxNotebookBase::SetImageList(imageList
);
485 TabCtrl_SetImageList(GetHwnd(), (HIMAGELIST
)imageList
->GetHIMAGELIST());
489 // ----------------------------------------------------------------------------
490 // wxNotebook size settings
491 // ----------------------------------------------------------------------------
493 wxRect
wxNotebook::GetPageSize() const
498 ::GetClientRect(GetHwnd(), &rc
);
500 // This check is to work around a bug in TabCtrl_AdjustRect which will
501 // cause a crash on win2k, or on XP with themes disabled, if the
502 // wxNB_MULTILINE style is used and the rectangle is very small, (such as
503 // when the notebook is first created.) The value of 20 is just
504 // arbitrarily chosen, if there is a better way to determine this value
505 // then please do so. --RD
506 if ( !HasFlag(wxNB_MULTILINE
) || (rc
.right
> 20 && rc
.bottom
> 20) )
508 TabCtrl_AdjustRect(GetHwnd(), false, &rc
);
510 wxCopyRECTToRect(rc
, r
);
516 void wxNotebook::SetPageSize(const wxSize
& size
)
518 // transform the page size into the notebook size
525 TabCtrl_AdjustRect(GetHwnd(), true, &rc
);
528 SetSize(rc
.right
- rc
.left
, rc
.bottom
- rc
.top
);
531 void wxNotebook::SetPadding(const wxSize
& padding
)
533 TabCtrl_SetPadding(GetHwnd(), padding
.x
, padding
.y
);
536 // Windows-only at present. Also, you must use the wxNB_FIXEDWIDTH
538 void wxNotebook::SetTabSize(const wxSize
& sz
)
540 ::SendMessage(GetHwnd(), TCM_SETITEMSIZE
, 0, MAKELPARAM(sz
.x
, sz
.y
));
543 wxSize
wxNotebook::CalcSizeFromPage(const wxSize
& sizePage
) const
545 wxSize sizeTotal
= sizePage
;
547 // We need to make getting tab size part of the wxWidgets API.
549 if (GetPageCount() > 0)
552 TabCtrl_GetItemRect((HWND
) GetHWND(), 0, & rect
);
553 tabSize
.x
= rect
.right
- rect
.left
;
554 tabSize
.y
= rect
.bottom
- rect
.top
;
556 if ( HasFlag(wxNB_LEFT
) || HasFlag(wxNB_RIGHT
) )
558 sizeTotal
.x
+= tabSize
.x
+ 7;
564 sizeTotal
.y
+= tabSize
.y
+ 7;
570 void wxNotebook::AdjustPageSize(wxNotebookPage
*page
)
572 wxCHECK_RET( page
, _T("NULL page in wxNotebook::AdjustPageSize") );
574 const wxRect r
= GetPageSize();
581 // ----------------------------------------------------------------------------
582 // wxNotebook operations
583 // ----------------------------------------------------------------------------
585 // remove one page from the notebook, without deleting
586 wxNotebookPage
*wxNotebook::DoRemovePage(size_t nPage
)
588 wxNotebookPage
*pageRemoved
= wxNotebookBase::DoRemovePage(nPage
);
592 TabCtrl_DeleteItem(GetHwnd(), nPage
);
594 if ( m_pages
.IsEmpty() )
596 // no selection any more, the notebook becamse empty
599 else // notebook still not empty
601 int selNew
= TabCtrl_GetCurSel(GetHwnd());
604 // No selection change, just refresh the current selection.
605 // Because it could be that the slection index changed
606 // we need to update it.
607 // Note: this does not mean the selection it self changed.
608 m_nSelection
= selNew
;
609 m_pages
[m_nSelection
]->Refresh();
611 else if (int(nPage
) == m_nSelection
)
613 // The selection was deleted.
615 // Determine new selection.
616 if (m_nSelection
== int(GetPageCount()))
617 selNew
= m_nSelection
- 1;
619 selNew
= m_nSelection
;
621 // m_nSelection must be always valid so reset it before calling
624 SetSelection(selNew
);
628 wxFAIL
; // Windows did not behave ok.
636 bool wxNotebook::DeleteAllPages()
638 size_t nPageCount
= GetPageCount();
640 for ( nPage
= 0; nPage
< nPageCount
; nPage
++ )
641 delete m_pages
[nPage
];
645 TabCtrl_DeleteAllItems(GetHwnd());
649 InvalidateBestSize();
653 // same as AddPage() but does it at given position
654 bool wxNotebook::InsertPage(size_t nPage
,
655 wxNotebookPage
*pPage
,
656 const wxString
& strText
,
660 wxCHECK_MSG( pPage
!= NULL
, false, _T("NULL page in wxNotebook::InsertPage") );
661 wxCHECK_MSG( IS_VALID_PAGE(nPage
) || nPage
== GetPageCount(), false,
662 _T("invalid index in wxNotebook::InsertPage") );
664 wxASSERT_MSG( pPage
->GetParent() == this,
665 _T("notebook pages must have notebook as parent") );
667 // add a new tab to the control
668 // ----------------------------
670 // init all fields to 0
672 wxZeroMemory(tcItem
);
674 // set the image, if any
677 tcItem
.mask
|= TCIF_IMAGE
;
678 tcItem
.iImage
= imageId
;
682 if ( !strText
.empty() )
684 tcItem
.mask
|= TCIF_TEXT
;
685 tcItem
.pszText
= (wxChar
*)strText
.c_str(); // const_cast
688 // hide the page: unless it is selected, it shouldn't be shown (and if it
689 // is selected it will be shown later)
690 HWND hwnd
= GetWinHwnd(pPage
);
691 SetWindowLong(hwnd
, GWL_STYLE
, GetWindowLong(hwnd
, GWL_STYLE
) & ~WS_VISIBLE
);
693 // this updates internal flag too -- otherwise it would get out of sync
694 // with the real state
698 // fit the notebook page to the tab control's display area: this should be
699 // done before adding it to the notebook or TabCtrl_InsertItem() will
700 // change the notebooks size itself!
701 AdjustPageSize(pPage
);
703 // finally do insert it
704 if ( TabCtrl_InsertItem(GetHwnd(), nPage
, &tcItem
) == -1 )
706 wxLogError(wxT("Can't create the notebook page '%s'."), strText
.c_str());
711 // succeeded: save the pointer to the page
712 m_pages
.Insert(pPage
, nPage
);
714 // we may need to adjust the size again if the notebook size changed:
715 // normally this only happens for the first page we add (the tabs which
716 // hadn't been there before are now shown) but for a multiline notebook it
717 // can happen for any page at all as a new row could have been started
718 if ( m_pages
.GetCount() == 1 || HasFlag(wxNB_MULTILINE
) )
720 AdjustPageSize(pPage
);
723 // now deal with the selection
724 // ---------------------------
726 // if the inserted page is before the selected one, we must update the
727 // index of the selected page
728 if ( int(nPage
) <= m_nSelection
)
730 // one extra page added
734 // some page should be selected: either this one or the first one if there
735 // is still no selection
739 else if ( m_nSelection
== -1 )
743 SetSelection(selNew
);
745 InvalidateBestSize();
750 int wxNotebook::HitTest(const wxPoint
& pt
, long *flags
) const
752 TC_HITTESTINFO hitTestInfo
;
753 hitTestInfo
.pt
.x
= pt
.x
;
754 hitTestInfo
.pt
.y
= pt
.y
;
755 int item
= TabCtrl_HitTest(GetHwnd(), &hitTestInfo
);
761 if ((hitTestInfo
.flags
& TCHT_NOWHERE
) == TCHT_NOWHERE
)
762 *flags
|= wxNB_HITTEST_NOWHERE
;
763 if ((hitTestInfo
.flags
& TCHT_ONITEM
) == TCHT_ONITEM
)
764 *flags
|= wxNB_HITTEST_ONITEM
;
765 if ((hitTestInfo
.flags
& TCHT_ONITEMICON
) == TCHT_ONITEMICON
)
766 *flags
|= wxNB_HITTEST_ONICON
;
767 if ((hitTestInfo
.flags
& TCHT_ONITEMLABEL
) == TCHT_ONITEMLABEL
)
768 *flags
|= wxNB_HITTEST_ONLABEL
;
775 // ----------------------------------------------------------------------------
776 // wxNotebook callbacks
777 // ----------------------------------------------------------------------------
779 void wxNotebook::OnSize(wxSizeEvent
& event
)
781 if ( GetPageCount() == 0 )
783 // Prevents droppings on resize, but does cause some flicker
784 // when there are no pages.
792 // Without this, we can sometimes get droppings at the edges
793 // of a notebook, for example a notebook in a splitter window.
794 // This needs to be reconciled with the RefreshRect calls
795 // at the end of this function, which weren't enough to prevent
798 wxSize sz
= GetClientSize();
800 // Refresh right side
801 wxRect
rect(sz
.x
-4, 0, 4, sz
.y
);
804 // Refresh bottom side
805 rect
= wxRect(0, sz
.y
-4, sz
.x
, 4);
809 rect
= wxRect(0, 0, 4, sz
.y
);
814 // fit all the notebook pages to the tab control's display area
817 rc
.left
= rc
.top
= 0;
818 GetSize((int *)&rc
.right
, (int *)&rc
.bottom
);
820 // save the total size, we'll use it below
821 int widthNbook
= rc
.right
- rc
.left
,
822 heightNbook
= rc
.bottom
- rc
.top
;
824 // there seems to be a bug in the implementation of TabCtrl_AdjustRect(): it
825 // returns completely false values for multiline tab controls after the tabs
826 // are added but before getting the first WM_SIZE (off by ~50 pixels, see
828 // http://sf.net/tracker/index.php?func=detail&aid=645323&group_id=9863&atid=109863
830 // and the only work around I could find was this ugly hack... without it
831 // simply toggling the "multiline" checkbox in the notebook sample resulted
832 // in a noticeable page displacement
833 if ( HasFlag(wxNB_MULTILINE
) )
835 // avoid an infinite recursion: we get another notification too!
836 static bool s_isInOnSize
= false;
841 SendMessage(GetHwnd(), WM_SIZE
, SIZE_RESTORED
,
842 MAKELPARAM(rc
.right
, rc
.bottom
));
843 s_isInOnSize
= false;
848 // background bitmap size has changed, update the brush using it too
850 #endif // wxUSE_UXTHEME
852 TabCtrl_AdjustRect(GetHwnd(), false, &rc
);
854 int width
= rc
.right
- rc
.left
,
855 height
= rc
.bottom
- rc
.top
;
856 size_t nCount
= m_pages
.Count();
857 for ( size_t nPage
= 0; nPage
< nCount
; nPage
++ ) {
858 wxNotebookPage
*pPage
= m_pages
[nPage
];
859 pPage
->SetSize(rc
.left
, rc
.top
, width
, height
);
863 // unless we had already repainted everything, we now need to refresh
864 if ( !HasFlag(wxFULL_REPAINT_ON_RESIZE
) )
866 // invalidate areas not covered by pages
867 RefreshRect(wxRect(0, 0, widthNbook
, rc
.top
), false);
868 RefreshRect(wxRect(0, rc
.top
, rc
.left
, height
), false);
869 RefreshRect(wxRect(0, rc
.bottom
, widthNbook
, heightNbook
- rc
.bottom
),
871 RefreshRect(wxRect(rc
.right
, rc
.top
, widthNbook
- rc
.bottom
, height
),
878 void wxNotebook::OnSelChange(wxNotebookEvent
& event
)
880 // is it our tab control?
881 if ( event
.GetEventObject() == this )
883 int sel
= event
.GetOldSelection();
885 m_pages
[sel
]->Show(false);
887 sel
= event
.GetSelection();
890 wxNotebookPage
*pPage
= m_pages
[sel
];
894 // If the newly focused window is not a child of the new page,
895 // SetFocus was not successful and the notebook itself should be
897 wxWindow
*currentFocus
= FindFocus();
898 wxWindow
*startFocus
= currentFocus
;
899 while ( currentFocus
&& currentFocus
!= pPage
&& currentFocus
!= this )
900 currentFocus
= currentFocus
->GetParent();
902 if ( startFocus
== pPage
|| currentFocus
!= pPage
)
906 else // no pages in the notebook, give the focus to itself
914 // we want to give others a chance to process this message as well
918 bool wxNotebook::MSWTranslateMessage(WXMSG
*wxmsg
)
920 const MSG
* const msg
= (MSG
*)wxmsg
;
922 // intercept TAB, CTRL+TAB and CTRL+SHIFT+TAB for processing by wxNotebook.
923 // TAB will be passed to the currently selected page, CTRL+TAB and
924 // CTRL+SHIFT+TAB will be processed by the notebook itself. do not
925 // intercept SHIFT+TAB. This goes to the parent of the notebook which will
927 if ( msg
->message
== WM_KEYDOWN
&& msg
->wParam
== VK_TAB
&&
928 msg
->hwnd
== GetHwnd() &&
929 (wxIsCtrlDown() || !wxIsShiftDown()) )
931 return MSWProcessMessage(wxmsg
);
937 void wxNotebook::OnNavigationKey(wxNavigationKeyEvent
& event
)
939 if ( event
.IsWindowChange() ) {
941 AdvanceSelection(event
.GetDirection());
944 // we get this event in 3 cases
946 // a) one of our pages might have generated it because the user TABbed
947 // out from it in which case we should propagate the event upwards and
948 // our parent will take care of setting the focus to prev/next sibling
952 // b) the parent panel wants to give the focus to us so that we
953 // forward it to our selected page. We can't deal with this in
954 // OnSetFocus() because we don't know which direction the focus came
955 // from in this case and so can't choose between setting the focus to
956 // first or last panel child
960 // c) we ourselves (see MSWTranslateMessage) generated the event
962 wxWindow
* const parent
= GetParent();
964 // the wxObject* casts are required to avoid MinGW GCC 2.95.3 ICE
965 const bool isFromParent
= event
.GetEventObject() == (wxObject
*) parent
;
966 const bool isFromSelf
= event
.GetEventObject() == (wxObject
*) this;
968 if ( isFromParent
|| isFromSelf
)
970 // no, it doesn't come from child, case (b) or (c): forward to a
971 // page but only if direction is backwards (TAB) or from ourselves,
972 if ( m_nSelection
!= -1 &&
973 (!event
.GetDirection() || isFromSelf
) )
975 // so that the page knows that the event comes from it's parent
976 // and is being propagated downwards
977 event
.SetEventObject(this);
979 wxWindow
*page
= m_pages
[m_nSelection
];
980 if ( !page
->GetEventHandler()->ProcessEvent(event
) )
984 //else: page manages focus inside it itself
986 else // otherwise set the focus to the notebook itself
993 // it comes from our child, case (a), pass to the parent, but only
994 // if the direction is forwards. Otherwise set the focus to the
995 // notebook itself. The notebook is always the 'first' control of a
997 if ( !event
.GetDirection() )
1003 event
.SetCurrentFocus(this);
1004 parent
->GetEventHandler()->ProcessEvent(event
);
1012 bool wxNotebook::DoDrawBackground(WXHDC hDC
, wxWindow
*child
)
1014 wxUxThemeHandle
theme(child
? child
: this, L
"TAB");
1018 // get the notebook client rect (we're not interested in drawing tabs
1020 wxRect r
= GetPageSize();
1025 wxCopyRectToRECT(r
, rc
);
1027 // map rect to the coords of the window we're drawing in
1029 ::MapWindowPoints(GetHwnd(), GetHwndOf(child
), (POINT
*)&rc
, 2);
1032 // apparently DrawThemeBackground() modifies the rect passed to it and if we
1033 // don't do these adjustments, there are some drawing artifacts which are
1034 // only visible with some non default themes; so modify the rect here using
1035 // the magic numbers below so that it still paints the correct area
1042 wxUxThemeEngine::Get()->DrawThemeBackground
1055 WXHBRUSH
wxNotebook::QueryBgBitmap()
1057 wxRect r
= GetPageSize();
1061 WindowHDC
hDC(GetHwnd());
1062 MemoryHDC
hDCMem(hDC
);
1063 CompatibleBitmap
hBmp(hDC
, r
.x
+ r
.width
, r
.y
+ r
.height
);
1065 SelectInHDC
selectBmp(hDCMem
, hBmp
);
1067 if ( !DoDrawBackground((WXHDC
)(HDC
)hDCMem
) )
1070 return (WXHBRUSH
)::CreatePatternBrush(hBmp
);
1073 void wxNotebook::UpdateBgBrush()
1075 if ( m_hbrBackground
)
1076 ::DeleteObject((HBRUSH
)m_hbrBackground
);
1078 if ( !m_hasBgCol
&& wxUxThemeEngine::GetIfActive() )
1080 m_hbrBackground
= QueryBgBitmap();
1082 else // no themes or we've got user-defined solid colour
1084 m_hbrBackground
= NULL
;
1088 WXHBRUSH
wxNotebook::MSWGetBgBrushForChild(WXHDC hDC
, WXHWND hWnd
)
1090 if ( m_hbrBackground
)
1092 // before drawing with the background brush, we need to position it
1095 ::GetWindowRect((HWND
)hWnd
, &rc
);
1097 ::MapWindowPoints(NULL
, GetHwnd(), (POINT
*)&rc
, 1);
1099 if ( !::SetBrushOrgEx((HDC
)hDC
, -rc
.left
, -rc
.top
, NULL
) )
1101 wxLogLastError(_T("SetBrushOrgEx(notebook bg brush)"));
1104 return m_hbrBackground
;
1107 return wxNotebookBase::MSWGetBgBrushForChild(hDC
, hWnd
);
1110 bool wxNotebook::MSWPrintChild(WXHDC hDC
, wxWindow
*child
)
1112 // solid background colour overrides themed background drawing
1113 if ( !UseBgCol() && DoDrawBackground(hDC
, child
) )
1116 // If we're using a solid colour (for example if we've switched off
1117 // theming for this notebook), paint it
1120 wxRect r
= GetPageSize();
1125 wxCopyRectToRECT(r
, rc
);
1127 // map rect to the coords of the window we're drawing in
1129 ::MapWindowPoints(GetHwnd(), GetHwndOf(child
), (POINT
*)&rc
, 2);
1131 wxBrush
brush(GetBackgroundColour());
1132 HBRUSH hbr
= GetHbrushOf(brush
);
1134 ::FillRect((HDC
) hDC
, &rc
, hbr
);
1139 return wxNotebookBase::MSWPrintChild(hDC
, child
);
1142 #endif // wxUSE_UXTHEME
1144 // Windows only: attempts to get colour for UX theme page background
1145 wxColour
wxNotebook::GetThemeBackgroundColour() const
1148 if (wxUxThemeEngine::Get())
1150 wxUxThemeHandle
hTheme((wxNotebook
*) this, L
"TAB");
1153 // This is total guesswork.
1154 // See PlatformSDK\Include\Tmschema.h for values
1155 COLORREF themeColor
;
1156 wxUxThemeEngine::Get()->GetThemeColor(
1160 3821 /* FILLCOLORHINT */,
1164 [DS] Workaround for WindowBlinds:
1165 Some themes return a near black theme color using FILLCOLORHINT,
1166 this makes notebook pages have an ugly black background and makes
1167 text (usually black) unreadable. Retry again with FILLCOLOR.
1169 This workaround potentially breaks appearance of some themes,
1170 but in practice it already fixes some themes.
1172 if (themeColor
== 1)
1174 wxUxThemeEngine::Get()->GetThemeColor(
1178 3802 /* FILLCOLOR */,
1182 return wxRGBToColour(themeColor
);
1185 #endif // wxUSE_UXTHEME
1187 return GetBackgroundColour();
1190 // ----------------------------------------------------------------------------
1191 // wxNotebook base class virtuals
1192 // ----------------------------------------------------------------------------
1194 #if wxUSE_CONSTRAINTS
1196 // override these 2 functions to do nothing: everything is done in OnSize
1198 void wxNotebook::SetConstraintSizes(bool WXUNUSED(recurse
))
1200 // don't set the sizes of the pages - their correct size is not yet known
1201 wxControl::SetConstraintSizes(false);
1204 bool wxNotebook::DoPhase(int WXUNUSED(nPhase
))
1209 #endif // wxUSE_CONSTRAINTS
1211 // ----------------------------------------------------------------------------
1212 // wxNotebook Windows message handlers
1213 // ----------------------------------------------------------------------------
1215 bool wxNotebook::MSWOnScroll(int orientation
, WXWORD nSBCode
,
1216 WXWORD pos
, WXHWND control
)
1218 // don't generate EVT_SCROLLWIN events for the WM_SCROLLs coming from the
1223 return wxNotebookBase::MSWOnScroll(orientation
, nSBCode
, pos
, control
);
1226 bool wxNotebook::MSWOnNotify(int idCtrl
, WXLPARAM lParam
, WXLPARAM
* result
)
1228 wxNotebookEvent
event(wxEVT_NULL
, m_windowId
);
1230 NMHDR
* hdr
= (NMHDR
*)lParam
;
1231 switch ( hdr
->code
) {
1233 event
.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
);
1236 case TCN_SELCHANGING
:
1237 event
.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
);
1241 return wxControl::MSWOnNotify(idCtrl
, lParam
, result
);
1244 event
.SetSelection(TabCtrl_GetCurSel(GetHwnd()));
1245 event
.SetOldSelection(m_nSelection
);
1246 event
.SetEventObject(this);
1247 event
.SetInt(idCtrl
);
1249 bool processed
= GetEventHandler()->ProcessEvent(event
);
1250 *result
= !event
.IsAllowed();
1255 void wxNotebook::OnEraseBackground(wxEraseEvent
& WXUNUSED(event
))
1260 void wxNotebook::OnPaint(wxPaintEvent
& WXUNUSED(event
))
1262 // Better painting behaviour, at the expense of system resources
1263 #if USE_NOTEBOOK_ANTIFLICKER
1267 ::GetClientRect(GetHwnd(), &rc
);
1268 wxBitmap
bmp(rc
.right
, rc
.bottom
);
1269 memdc
.SelectObject(bmp
);
1271 // iterate over all child windows to find spin button
1272 for ( HWND child
= ::GetWindow(GetHwnd(), GW_CHILD
);
1274 child
= ::GetWindow(child
, GW_HWNDNEXT
) )
1276 wxWindow
*childWindow
= wxFindWinFromHandle((WXHWND
)child
);
1278 // see if it exists, if no wxWindow found then assume it's the spin btn
1281 // subclass the spin button to override WM_ERASEBKGND
1282 if ( !s_wndprocNotebookSpinBtn
)
1283 s_wndprocNotebookSpinBtn
= (WXFARPROC
)wxGetWindowProc(child
);
1285 wxSetWindowProc(child
, wxNotebookSpinBtnWndProc
);
1290 HBRUSH hbr
= (HBRUSH
)m_hbrBackground
;
1292 // if there is no special brush just use the solid background colour
1296 brush
= wxBrush(GetBackgroundColour());
1297 hbr
= GetHbrushOf(brush
);
1300 ::FillRect(GetHdcOf(memdc
), &rc
, hbr
);
1302 MSWDefWindowProc(WM_PAINT
, (WPARAM
)memdc
.GetHDC(), 0);
1304 dc
.Blit(0, 0, rc
.right
, rc
.bottom
, &memdc
, 0, 0);
1310 #if USE_NOTEBOOK_ANTIFLICKER
1311 // ---------------------------------------------------------------------------
1312 // window proc for spin button
1313 // ---------------------------------------------------------------------------
1315 LRESULT APIENTRY _EXPORT
wxNotebookSpinBtnWndProc(HWND hwnd
,
1320 if ( message
== WM_ERASEBKGND
)
1323 return ::CallWindowProc(CASTWNDPROC s_wndprocNotebookSpinBtn
, hwnd
, message
, wParam
, lParam
);
1326 #endif // USE_NOTEBOOK_ANTIFLICKER
1328 #endif // wxUSE_NOTEBOOK