1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: msw/notebook.cpp
3 // Purpose: implementation of wxNotebook
4 // Author: Vadim Zeitlin
8 // Copyright: (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
9 // Licence: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
12 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
13 #pragma implementation "notebook.h"
16 // For compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
27 #include "wx/string.h"
32 #include "wx/imaglist.h"
34 #include "wx/control.h"
35 #include "wx/notebook.h"
37 #include "wx/sysopt.h"
38 #include "wx/dcclient.h"
39 #include "wx/dcmemory.h"
41 #include "wx/msw/private.h"
47 #include "wx/msw/winundef.h"
50 #include "wx/msw/uxtheme.h"
53 // ----------------------------------------------------------------------------
55 // ----------------------------------------------------------------------------
57 // check that the page index is valid
58 #define IS_VALID_PAGE(nPage) ((nPage) < GetPageCount())
60 // you can set USE_NOTEBOOK_ANTIFLICKER to 0 for desktop Windows versions too
61 // to disable code whih results in flicker-less notebook redrawing at the
62 // expense of some extra GDI resource consumption
64 // notebooks are never resized under CE anyhow
65 #define USE_NOTEBOOK_ANTIFLICKER 0
67 #define USE_NOTEBOOK_ANTIFLICKER 1
70 // ----------------------------------------------------------------------------
72 // ----------------------------------------------------------------------------
74 // This is a work-around for missing defines in gcc-2.95 headers
76 #define TCS_RIGHT 0x0002
80 #define TCS_VERTICAL 0x0080
84 #define TCS_BOTTOM TCS_RIGHT
87 // ----------------------------------------------------------------------------
89 // ----------------------------------------------------------------------------
91 #if USE_NOTEBOOK_ANTIFLICKER
93 // the pointer to standard spin button wnd proc
94 static WXFARPROC gs_wndprocNotebookSpinBtn
= (WXFARPROC
)NULL
;
96 #endif // USE_NOTEBOOK_ANTIFLICKER
98 // ----------------------------------------------------------------------------
100 // ----------------------------------------------------------------------------
102 #include <wx/listimpl.cpp>
104 WX_DEFINE_LIST( wxNotebookPageInfoList
) ;
106 DEFINE_EVENT_TYPE(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
)
107 DEFINE_EVENT_TYPE(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
)
109 BEGIN_EVENT_TABLE(wxNotebook
, wxControl
)
110 EVT_NOTEBOOK_PAGE_CHANGED(-1, wxNotebook::OnSelChange
)
111 EVT_SIZE(wxNotebook::OnSize
)
112 EVT_NAVIGATION_KEY(wxNotebook::OnNavigationKey
)
114 #if USE_NOTEBOOK_ANTIFLICKER
115 EVT_ERASE_BACKGROUND(wxNotebook::OnEraseBackground
)
116 EVT_PAINT(wxNotebook::OnPaint
)
117 #endif // USE_NOTEBOOK_ANTIFLICKER
120 #if wxUSE_EXTENDED_RTTI
121 WX_DEFINE_FLAGS( wxNotebookStyle
)
123 wxBEGIN_FLAGS( wxNotebookStyle
)
124 // new style border flags, we put them first to
125 // use them for streaming out
126 wxFLAGS_MEMBER(wxBORDER_SIMPLE
)
127 wxFLAGS_MEMBER(wxBORDER_SUNKEN
)
128 wxFLAGS_MEMBER(wxBORDER_DOUBLE
)
129 wxFLAGS_MEMBER(wxBORDER_RAISED
)
130 wxFLAGS_MEMBER(wxBORDER_STATIC
)
131 wxFLAGS_MEMBER(wxBORDER_NONE
)
133 // old style border flags
134 wxFLAGS_MEMBER(wxSIMPLE_BORDER
)
135 wxFLAGS_MEMBER(wxSUNKEN_BORDER
)
136 wxFLAGS_MEMBER(wxDOUBLE_BORDER
)
137 wxFLAGS_MEMBER(wxRAISED_BORDER
)
138 wxFLAGS_MEMBER(wxSTATIC_BORDER
)
139 wxFLAGS_MEMBER(wxBORDER
)
141 // standard window styles
142 wxFLAGS_MEMBER(wxTAB_TRAVERSAL
)
143 wxFLAGS_MEMBER(wxCLIP_CHILDREN
)
144 wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW
)
145 wxFLAGS_MEMBER(wxWANTS_CHARS
)
146 wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE
)
147 wxFLAGS_MEMBER(wxALWAYS_SHOW_SB
)
148 wxFLAGS_MEMBER(wxVSCROLL
)
149 wxFLAGS_MEMBER(wxHSCROLL
)
151 wxFLAGS_MEMBER(wxNB_FIXEDWIDTH
)
152 wxFLAGS_MEMBER(wxNB_LEFT
)
153 wxFLAGS_MEMBER(wxNB_RIGHT
)
154 wxFLAGS_MEMBER(wxNB_BOTTOM
)
155 wxFLAGS_MEMBER(wxNB_NOPAGETHEME
)
156 wxFLAGS_MEMBER(wxNB_FLAT
)
158 wxEND_FLAGS( wxNotebookStyle
)
160 IMPLEMENT_DYNAMIC_CLASS_XTI(wxNotebook
, wxControl
,"wx/notebook.h")
161 IMPLEMENT_DYNAMIC_CLASS_XTI(wxNotebookPageInfo
, wxObject
, "wx/notebook.h" )
163 wxCOLLECTION_TYPE_INFO( wxNotebookPageInfo
* , wxNotebookPageInfoList
) ;
165 template<> void wxCollectionToVariantArray( wxNotebookPageInfoList
const &theList
, wxxVariantArray
&value
)
167 wxListCollectionToVariantArray
<wxNotebookPageInfoList::compatibility_iterator
>( theList
, value
) ;
170 wxBEGIN_PROPERTIES_TABLE(wxNotebook
)
171 wxEVENT_PROPERTY( PageChanging
, wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
, wxNotebookEvent
)
172 wxEVENT_PROPERTY( PageChanged
, wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
, wxNotebookEvent
)
174 wxPROPERTY_COLLECTION( PageInfos
, wxNotebookPageInfoList
, wxNotebookPageInfo
* , AddPageInfo
, GetPageInfos
, 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
175 wxPROPERTY_FLAGS( WindowStyle
, wxNotebookStyle
, long , SetWindowStyleFlag
, GetWindowStyleFlag
, EMPTY_MACROVALUE
, 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
176 wxEND_PROPERTIES_TABLE()
178 wxBEGIN_HANDLERS_TABLE(wxNotebook
)
179 wxEND_HANDLERS_TABLE()
181 wxCONSTRUCTOR_5( wxNotebook
, wxWindow
* , Parent
, wxWindowID
, Id
, wxPoint
, Position
, wxSize
, Size
, long , WindowStyle
)
184 wxBEGIN_PROPERTIES_TABLE(wxNotebookPageInfo
)
185 wxREADONLY_PROPERTY( Page
, wxNotebookPage
* , GetPage
, EMPTY_MACROVALUE
, 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
186 wxREADONLY_PROPERTY( Text
, wxString
, GetText
, wxString() , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
187 wxREADONLY_PROPERTY( Selected
, bool , GetSelected
, false, 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
188 wxREADONLY_PROPERTY( ImageId
, int , GetImageId
, -1 , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
189 wxEND_PROPERTIES_TABLE()
191 wxBEGIN_HANDLERS_TABLE(wxNotebookPageInfo
)
192 wxEND_HANDLERS_TABLE()
194 wxCONSTRUCTOR_4( wxNotebookPageInfo
, wxNotebookPage
* , Page
, wxString
, Text
, bool , Selected
, int , ImageId
)
197 IMPLEMENT_DYNAMIC_CLASS(wxNotebook
, wxControl
)
198 IMPLEMENT_DYNAMIC_CLASS(wxNotebookPageInfo
, wxObject
)
200 IMPLEMENT_DYNAMIC_CLASS(wxNotebookEvent
, wxNotifyEvent
)
202 // ============================================================================
204 // ============================================================================
206 // ----------------------------------------------------------------------------
207 // wxNotebook construction
208 // ----------------------------------------------------------------------------
210 const wxNotebookPageInfoList
& wxNotebook::GetPageInfos() const
212 wxNotebookPageInfoList
* list
= const_cast< wxNotebookPageInfoList
* >( &m_pageInfos
) ;
213 WX_CLEAR_LIST( wxNotebookPageInfoList
, *list
) ;
214 for( size_t i
= 0 ; i
< GetPageCount() ; ++i
)
216 wxNotebookPageInfo
*info
= new wxNotebookPageInfo() ;
217 info
->Create( const_cast<wxNotebook
*>(this)->GetPage(i
) , GetPageText(i
) , GetSelection() == int(i
) , GetPageImage(i
) ) ;
218 list
->Append( info
) ;
223 // common part of all ctors
224 void wxNotebook::Init()
230 m_hbrBackground
= NULL
;
231 #endif // wxUSE_UXTHEME
233 #if USE_NOTEBOOK_ANTIFLICKER
234 m_hasSubclassedUpdown
= false;
235 #endif // USE_NOTEBOOK_ANTIFLICKER
238 // default for dynamic class
239 wxNotebook::wxNotebook()
244 // the same arguments as for wxControl
245 wxNotebook::wxNotebook(wxWindow
*parent
,
250 const wxString
& name
)
254 Create(parent
, id
, pos
, size
, style
, name
);
258 bool wxNotebook::Create(wxWindow
*parent
,
263 const wxString
& name
)
266 // Not sure why, but without this style, there is no border
267 // around the notebook tabs.
268 if (style
& wxNB_FLAT
)
269 style
|= wxBORDER_SUNKEN
;
272 // comctl32.dll 6.0 doesn't support non-top tabs with visual styles (the
273 // control is simply not rendered correctly), so disable them in this case
274 const int verComCtl32
= wxApp::GetComCtl32Version();
275 if ( verComCtl32
== 600 )
277 // check if we use themes at all -- if we don't, we're still ok
279 if ( wxUxThemeEngine::GetIfActive() )
282 style
&= ~(wxNB_BOTTOM
| wxNB_LEFT
| wxNB_RIGHT
);
286 LPCTSTR className
= WC_TABCONTROL
;
288 // SysTabCtl32 class has natively CS_HREDRAW and CS_VREDRAW enabled and it
289 // causes horrible flicker when resizing notebook, so get rid of it by
290 // using a class without these styles (but otherwise identical to it)
291 if ( !HasFlag(wxFULL_REPAINT_ON_RESIZE
) )
293 static ClassRegistrar s_clsNotebook
;
294 if ( !s_clsNotebook
.IsInitialized() )
296 // get a copy of standard class and modify it
299 if ( ::GetClassInfo(::GetModuleHandle(NULL
), WC_TABCONTROL
, &wc
) )
301 wc
.lpszClassName
= wxT("_wx_SysTabCtl32");
302 wc
.style
&= ~(CS_HREDRAW
| CS_VREDRAW
);
304 s_clsNotebook
.Register(wc
);
308 wxLogLastError(_T("GetClassInfoEx(SysTabCtl32)"));
312 // use our custom class if available but fall back to the standard
313 // notebook if we failed to register it
314 if ( s_clsNotebook
.IsRegistered() )
316 // it's ok to use c_str() here as the static s_clsNotebook object
317 // has sufficiently long lifetime
318 className
= s_clsNotebook
.GetName().c_str();
322 if ( !CreateControl(parent
, id
, pos
, size
, style
| wxTAB_TRAVERSAL
,
323 wxDefaultValidator
, name
) )
326 if ( !MSWCreateControl(className
, wxEmptyString
, pos
, size
) )
330 if ( HasFlag(wxNB_NOPAGETHEME
) ||
331 wxSystemOptions::IsFalse(wxT("msw.notebook.themed-background")) )
333 SetBackgroundColour(GetThemeBackgroundColour());
335 #endif // wxUSE_UXTHEME
337 // Undocumented hack to get flat notebook style
338 // In fact, we should probably only do this in some
339 // curcumstances, i.e. if we know we will have a border
340 // at the bottom (the tab control doesn't draw it itself)
341 #if defined(__POCKETPC__) || defined(__SMARTPHONE__)
342 if (HasFlag(wxNB_FLAT
))
344 SendMessage(GetHwnd(), CCM_SETVERSION
, COMCTL32_VERSION
, 0);
346 SetBackgroundColour(*wxWHITE
);
352 WXDWORD
wxNotebook::MSWGetStyle(long style
, WXDWORD
*exstyle
) const
354 WXDWORD tabStyle
= wxControl::MSWGetStyle(style
, exstyle
);
356 tabStyle
|= WS_TABSTOP
| TCS_TABS
;
358 if ( style
& wxNB_MULTILINE
)
359 tabStyle
|= TCS_MULTILINE
;
360 if ( style
& wxNB_FIXEDWIDTH
)
361 tabStyle
|= TCS_FIXEDWIDTH
;
363 if ( style
& wxNB_BOTTOM
)
364 tabStyle
|= TCS_RIGHT
;
365 else if ( style
& wxNB_LEFT
)
366 tabStyle
|= TCS_VERTICAL
;
367 else if ( style
& wxNB_RIGHT
)
368 tabStyle
|= TCS_VERTICAL
| TCS_RIGHT
;
373 // note that we never want to have the default WS_EX_CLIENTEDGE style
374 // as it looks too ugly for the notebooks
381 wxNotebook::~wxNotebook()
384 if ( m_hbrBackground
)
385 ::DeleteObject((HBRUSH
)m_hbrBackground
);
386 #endif // wxUSE_UXTHEME
389 // ----------------------------------------------------------------------------
390 // wxNotebook accessors
391 // ----------------------------------------------------------------------------
393 size_t wxNotebook::GetPageCount() const
396 wxASSERT( (int)m_pages
.Count() == TabCtrl_GetItemCount(GetHwnd()) );
398 return m_pages
.Count();
401 int wxNotebook::GetRowCount() const
403 return TabCtrl_GetRowCount(GetHwnd());
406 int wxNotebook::SetSelection(size_t nPage
)
408 wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxNOT_FOUND
, wxT("notebook page out of range") );
410 if ( int(nPage
) != m_nSelection
)
412 wxNotebookEvent
event(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
, m_windowId
);
413 event
.SetSelection(nPage
);
414 event
.SetOldSelection(m_nSelection
);
415 event
.SetEventObject(this);
416 if ( !GetEventHandler()->ProcessEvent(event
) || event
.IsAllowed() )
418 // program allows the page change
419 event
.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
);
420 (void)GetEventHandler()->ProcessEvent(event
);
422 TabCtrl_SetCurSel(GetHwnd(), nPage
);
429 bool wxNotebook::SetPageText(size_t nPage
, const wxString
& strText
)
431 wxCHECK_MSG( IS_VALID_PAGE(nPage
), false, wxT("notebook page out of range") );
434 tcItem
.mask
= TCIF_TEXT
;
435 tcItem
.pszText
= (wxChar
*)strText
.c_str();
437 return TabCtrl_SetItem(GetHwnd(), nPage
, &tcItem
) != 0;
440 wxString
wxNotebook::GetPageText(size_t nPage
) const
442 wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxEmptyString
, wxT("notebook page out of range") );
446 tcItem
.mask
= TCIF_TEXT
;
447 tcItem
.pszText
= buf
;
448 tcItem
.cchTextMax
= WXSIZEOF(buf
);
451 if ( TabCtrl_GetItem(GetHwnd(), nPage
, &tcItem
) )
452 str
= tcItem
.pszText
;
457 int wxNotebook::GetPageImage(size_t nPage
) const
459 wxCHECK_MSG( IS_VALID_PAGE(nPage
), -1, wxT("notebook page out of range") );
462 tcItem
.mask
= TCIF_IMAGE
;
464 return TabCtrl_GetItem(GetHwnd(), nPage
, &tcItem
) ? tcItem
.iImage
: -1;
467 bool wxNotebook::SetPageImage(size_t nPage
, int nImage
)
469 wxCHECK_MSG( IS_VALID_PAGE(nPage
), false, wxT("notebook page out of range") );
472 tcItem
.mask
= TCIF_IMAGE
;
473 tcItem
.iImage
= nImage
;
475 return TabCtrl_SetItem(GetHwnd(), nPage
, &tcItem
) != 0;
478 void wxNotebook::SetImageList(wxImageList
* imageList
)
480 wxNotebookBase::SetImageList(imageList
);
484 TabCtrl_SetImageList(GetHwnd(), (HIMAGELIST
)imageList
->GetHIMAGELIST());
488 // ----------------------------------------------------------------------------
489 // wxNotebook size settings
490 // ----------------------------------------------------------------------------
492 wxRect
wxNotebook::GetPageSize() const
497 ::GetClientRect(GetHwnd(), &rc
);
499 // This check is to work around a bug in TabCtrl_AdjustRect which will
500 // cause a crash on win2k or on XP with themes disabled if either
501 // wxNB_MULTILINE is used or tabs are placed on a side, if the rectangle
504 // The value of 20 is chosen arbitrarily but seems to work
505 if ( rc
.right
> 20 && rc
.bottom
> 20 )
507 TabCtrl_AdjustRect(GetHwnd(), false, &rc
);
509 wxCopyRECTToRect(rc
, r
);
515 void wxNotebook::SetPageSize(const wxSize
& size
)
517 // transform the page size into the notebook size
524 TabCtrl_AdjustRect(GetHwnd(), true, &rc
);
527 SetSize(rc
.right
- rc
.left
, rc
.bottom
- rc
.top
);
530 void wxNotebook::SetPadding(const wxSize
& padding
)
532 TabCtrl_SetPadding(GetHwnd(), padding
.x
, padding
.y
);
535 // Windows-only at present. Also, you must use the wxNB_FIXEDWIDTH
537 void wxNotebook::SetTabSize(const wxSize
& sz
)
539 ::SendMessage(GetHwnd(), TCM_SETITEMSIZE
, 0, MAKELPARAM(sz
.x
, sz
.y
));
542 wxSize
wxNotebook::CalcSizeFromPage(const wxSize
& sizePage
) const
544 wxSize sizeTotal
= sizePage
;
546 // We need to make getting tab size part of the wxWidgets API.
548 if (GetPageCount() > 0)
551 TabCtrl_GetItemRect((HWND
) GetHWND(), 0, & rect
);
552 tabSize
.x
= rect
.right
- rect
.left
;
553 tabSize
.y
= rect
.bottom
- rect
.top
;
555 if ( HasFlag(wxNB_LEFT
) || HasFlag(wxNB_RIGHT
) )
557 sizeTotal
.x
+= tabSize
.x
+ 7;
563 sizeTotal
.y
+= tabSize
.y
+ 7;
569 void wxNotebook::AdjustPageSize(wxNotebookPage
*page
)
571 wxCHECK_RET( page
, _T("NULL page in wxNotebook::AdjustPageSize") );
573 const wxRect r
= GetPageSize();
580 // ----------------------------------------------------------------------------
581 // wxNotebook operations
582 // ----------------------------------------------------------------------------
584 // remove one page from the notebook, without deleting
585 wxNotebookPage
*wxNotebook::DoRemovePage(size_t nPage
)
587 wxNotebookPage
*pageRemoved
= wxNotebookBase::DoRemovePage(nPage
);
591 TabCtrl_DeleteItem(GetHwnd(), nPage
);
593 if ( m_pages
.IsEmpty() )
595 // no selection any more, the notebook becamse empty
598 else // notebook still not empty
600 int selNew
= TabCtrl_GetCurSel(GetHwnd());
603 // No selection change, just refresh the current selection.
604 // Because it could be that the slection index changed
605 // we need to update it.
606 // Note: this does not mean the selection it self changed.
607 m_nSelection
= selNew
;
608 m_pages
[m_nSelection
]->Refresh();
610 else if (int(nPage
) == m_nSelection
)
612 // The selection was deleted.
614 // Determine new selection.
615 if (m_nSelection
== int(GetPageCount()))
616 selNew
= m_nSelection
- 1;
618 selNew
= m_nSelection
;
620 // m_nSelection must be always valid so reset it before calling
623 SetSelection(selNew
);
627 wxFAIL
; // Windows did not behave ok.
635 bool wxNotebook::DeleteAllPages()
637 size_t nPageCount
= GetPageCount();
639 for ( nPage
= 0; nPage
< nPageCount
; nPage
++ )
640 delete m_pages
[nPage
];
644 TabCtrl_DeleteAllItems(GetHwnd());
648 InvalidateBestSize();
652 // same as AddPage() but does it at given position
653 bool wxNotebook::InsertPage(size_t nPage
,
654 wxNotebookPage
*pPage
,
655 const wxString
& strText
,
659 wxCHECK_MSG( pPage
!= NULL
, false, _T("NULL page in wxNotebook::InsertPage") );
660 wxCHECK_MSG( IS_VALID_PAGE(nPage
) || nPage
== GetPageCount(), false,
661 _T("invalid index in wxNotebook::InsertPage") );
663 wxASSERT_MSG( pPage
->GetParent() == this,
664 _T("notebook pages must have notebook as parent") );
666 // add a new tab to the control
667 // ----------------------------
669 // init all fields to 0
671 wxZeroMemory(tcItem
);
673 // set the image, if any
676 tcItem
.mask
|= TCIF_IMAGE
;
677 tcItem
.iImage
= imageId
;
681 if ( !strText
.empty() )
683 tcItem
.mask
|= TCIF_TEXT
;
684 tcItem
.pszText
= (wxChar
*)strText
.c_str(); // const_cast
687 // hide the page: unless it is selected, it shouldn't be shown (and if it
688 // is selected it will be shown later)
689 HWND hwnd
= GetWinHwnd(pPage
);
690 SetWindowLong(hwnd
, GWL_STYLE
, GetWindowLong(hwnd
, GWL_STYLE
) & ~WS_VISIBLE
);
692 // this updates internal flag too -- otherwise it would get out of sync
693 // with the real state
697 // fit the notebook page to the tab control's display area: this should be
698 // done before adding it to the notebook or TabCtrl_InsertItem() will
699 // change the notebooks size itself!
700 AdjustPageSize(pPage
);
702 // finally do insert it
703 if ( TabCtrl_InsertItem(GetHwnd(), nPage
, &tcItem
) == -1 )
705 wxLogError(wxT("Can't create the notebook page '%s'."), strText
.c_str());
710 // succeeded: save the pointer to the page
711 m_pages
.Insert(pPage
, nPage
);
713 // we may need to adjust the size again if the notebook size changed:
714 // normally this only happens for the first page we add (the tabs which
715 // hadn't been there before are now shown) but for a multiline notebook it
716 // can happen for any page at all as a new row could have been started
717 if ( m_pages
.GetCount() == 1 || HasFlag(wxNB_MULTILINE
) )
719 AdjustPageSize(pPage
);
722 // now deal with the selection
723 // ---------------------------
725 // if the inserted page is before the selected one, we must update the
726 // index of the selected page
727 if ( int(nPage
) <= m_nSelection
)
729 // one extra page added
733 // some page should be selected: either this one or the first one if there
734 // is still no selection
738 else if ( m_nSelection
== -1 )
742 SetSelection(selNew
);
744 InvalidateBestSize();
749 int wxNotebook::HitTest(const wxPoint
& pt
, long *flags
) const
751 TC_HITTESTINFO hitTestInfo
;
752 hitTestInfo
.pt
.x
= pt
.x
;
753 hitTestInfo
.pt
.y
= pt
.y
;
754 int item
= TabCtrl_HitTest(GetHwnd(), &hitTestInfo
);
760 if ((hitTestInfo
.flags
& TCHT_NOWHERE
) == TCHT_NOWHERE
)
761 *flags
|= wxNB_HITTEST_NOWHERE
;
762 if ((hitTestInfo
.flags
& TCHT_ONITEM
) == TCHT_ONITEM
)
763 *flags
|= wxNB_HITTEST_ONITEM
;
764 if ((hitTestInfo
.flags
& TCHT_ONITEMICON
) == TCHT_ONITEMICON
)
765 *flags
|= wxNB_HITTEST_ONICON
;
766 if ((hitTestInfo
.flags
& TCHT_ONITEMLABEL
) == TCHT_ONITEMLABEL
)
767 *flags
|= wxNB_HITTEST_ONLABEL
;
773 // ----------------------------------------------------------------------------
774 // flicker-less notebook redraw
775 // ----------------------------------------------------------------------------
777 #if USE_NOTEBOOK_ANTIFLICKER
779 // wnd proc for the spin button
780 LRESULT APIENTRY _EXPORT
wxNotebookSpinBtnWndProc(HWND hwnd
,
785 if ( message
== WM_ERASEBKGND
)
788 return ::CallWindowProc(CASTWNDPROC gs_wndprocNotebookSpinBtn
,
789 hwnd
, message
, wParam
, lParam
);
793 void wxNotebook::OnEraseBackground(wxEraseEvent
& WXUNUSED(event
))
798 void wxNotebook::OnPaint(wxPaintEvent
& WXUNUSED(event
))
803 ::GetClientRect(GetHwnd(), &rc
);
804 wxBitmap
bmp(rc
.right
, rc
.bottom
);
805 memdc
.SelectObject(bmp
);
807 // if there is no special brush just use the solid background colour
808 HBRUSH hbr
= (HBRUSH
)m_hbrBackground
;
812 brush
= wxBrush(GetBackgroundColour());
813 hbr
= GetHbrushOf(brush
);
816 ::FillRect(GetHdcOf(memdc
), &rc
, hbr
);
818 MSWDefWindowProc(WM_PAINT
, (WPARAM
)memdc
.GetHDC(), 0);
820 dc
.Blit(0, 0, rc
.right
, rc
.bottom
, &memdc
, 0, 0);
823 #endif // USE_NOTEBOOK_ANTIFLICKER
825 // ----------------------------------------------------------------------------
826 // wxNotebook callbacks
827 // ----------------------------------------------------------------------------
829 void wxNotebook::OnSize(wxSizeEvent
& event
)
831 if ( GetPageCount() == 0 )
833 // Prevents droppings on resize, but does cause some flicker
834 // when there are no pages.
842 // Without this, we can sometimes get droppings at the edges
843 // of a notebook, for example a notebook in a splitter window.
844 // This needs to be reconciled with the RefreshRect calls
845 // at the end of this function, which weren't enough to prevent
848 wxSize sz
= GetClientSize();
850 // Refresh right side
851 wxRect
rect(sz
.x
-4, 0, 4, sz
.y
);
854 // Refresh bottom side
855 rect
= wxRect(0, sz
.y
-4, sz
.x
, 4);
859 rect
= wxRect(0, 0, 4, sz
.y
);
862 #endif // !__WXWINCE__
864 // fit all the notebook pages to the tab control's display area
867 rc
.left
= rc
.top
= 0;
868 GetSize((int *)&rc
.right
, (int *)&rc
.bottom
);
870 // save the total size, we'll use it below
871 int widthNbook
= rc
.right
- rc
.left
,
872 heightNbook
= rc
.bottom
- rc
.top
;
874 // there seems to be a bug in the implementation of TabCtrl_AdjustRect(): it
875 // returns completely false values for multiline tab controls after the tabs
876 // are added but before getting the first WM_SIZE (off by ~50 pixels, see
878 // http://sf.net/tracker/index.php?func=detail&aid=645323&group_id=9863&atid=109863
880 // and the only work around I could find was this ugly hack... without it
881 // simply toggling the "multiline" checkbox in the notebook sample resulted
882 // in a noticeable page displacement
883 if ( HasFlag(wxNB_MULTILINE
) )
885 // avoid an infinite recursion: we get another notification too!
886 static bool s_isInOnSize
= false;
891 SendMessage(GetHwnd(), WM_SIZE
, SIZE_RESTORED
,
892 MAKELPARAM(rc
.right
, rc
.bottom
));
893 s_isInOnSize
= false;
898 // background bitmap size has changed, update the brush using it too
900 #endif // wxUSE_UXTHEME
902 TabCtrl_AdjustRect(GetHwnd(), false, &rc
);
904 int width
= rc
.right
- rc
.left
,
905 height
= rc
.bottom
- rc
.top
;
906 size_t nCount
= m_pages
.Count();
907 for ( size_t nPage
= 0; nPage
< nCount
; nPage
++ ) {
908 wxNotebookPage
*pPage
= m_pages
[nPage
];
909 pPage
->SetSize(rc
.left
, rc
.top
, width
, height
);
913 // unless we had already repainted everything, we now need to refresh
914 if ( !HasFlag(wxFULL_REPAINT_ON_RESIZE
) )
916 // invalidate areas not covered by pages
917 RefreshRect(wxRect(0, 0, widthNbook
, rc
.top
), false);
918 RefreshRect(wxRect(0, rc
.top
, rc
.left
, height
), false);
919 RefreshRect(wxRect(0, rc
.bottom
, widthNbook
, heightNbook
- rc
.bottom
),
921 RefreshRect(wxRect(rc
.right
, rc
.top
, widthNbook
- rc
.bottom
, height
),
925 #if USE_NOTEBOOK_ANTIFLICKER
926 // subclass the spin control used by the notebook to scroll pages to
927 // prevent it from flickering on resize
928 if ( !m_hasSubclassedUpdown
)
930 // iterate over all child windows to find spin button
931 for ( HWND child
= ::GetWindow(GetHwnd(), GW_CHILD
);
933 child
= ::GetWindow(child
, GW_HWNDNEXT
) )
935 wxWindow
*childWindow
= wxFindWinFromHandle((WXHWND
)child
);
937 // see if it exists, if no wxWindow found then assume it's the spin
941 // subclass the spin button to override WM_ERASEBKGND
942 if ( !gs_wndprocNotebookSpinBtn
)
943 gs_wndprocNotebookSpinBtn
= (WXFARPROC
)wxGetWindowProc(child
);
945 wxSetWindowProc(child
, wxNotebookSpinBtnWndProc
);
946 m_hasSubclassedUpdown
= true;
951 #endif // USE_NOTEBOOK_ANTIFLICKER
956 void wxNotebook::OnSelChange(wxNotebookEvent
& event
)
958 // is it our tab control?
959 if ( event
.GetEventObject() == this )
961 int sel
= event
.GetOldSelection();
963 m_pages
[sel
]->Show(false);
965 sel
= event
.GetSelection();
968 wxNotebookPage
*pPage
= m_pages
[sel
];
972 // If the newly focused window is not a child of the new page,
973 // SetFocus was not successful and the notebook itself should be
975 wxWindow
*currentFocus
= FindFocus();
976 wxWindow
*startFocus
= currentFocus
;
977 while ( currentFocus
&& currentFocus
!= pPage
&& currentFocus
!= this )
978 currentFocus
= currentFocus
->GetParent();
980 if ( startFocus
== pPage
|| currentFocus
!= pPage
)
984 else // no pages in the notebook, give the focus to itself
992 // we want to give others a chance to process this message as well
996 bool wxNotebook::MSWTranslateMessage(WXMSG
*wxmsg
)
998 const MSG
* const msg
= (MSG
*)wxmsg
;
1000 // intercept TAB, CTRL+TAB and CTRL+SHIFT+TAB for processing by wxNotebook.
1001 // TAB will be passed to the currently selected page, CTRL+TAB and
1002 // CTRL+SHIFT+TAB will be processed by the notebook itself. do not
1003 // intercept SHIFT+TAB. This goes to the parent of the notebook which will
1005 if ( msg
->message
== WM_KEYDOWN
&& msg
->wParam
== VK_TAB
&&
1006 msg
->hwnd
== GetHwnd() &&
1007 (wxIsCtrlDown() || !wxIsShiftDown()) )
1009 return MSWProcessMessage(wxmsg
);
1015 void wxNotebook::OnNavigationKey(wxNavigationKeyEvent
& event
)
1017 if ( event
.IsWindowChange() ) {
1019 AdvanceSelection(event
.GetDirection());
1022 // we get this event in 3 cases
1024 // a) one of our pages might have generated it because the user TABbed
1025 // out from it in which case we should propagate the event upwards and
1026 // our parent will take care of setting the focus to prev/next sibling
1030 // b) the parent panel wants to give the focus to us so that we
1031 // forward it to our selected page. We can't deal with this in
1032 // OnSetFocus() because we don't know which direction the focus came
1033 // from in this case and so can't choose between setting the focus to
1034 // first or last panel child
1038 // c) we ourselves (see MSWTranslateMessage) generated the event
1040 wxWindow
* const parent
= GetParent();
1042 // the wxObject* casts are required to avoid MinGW GCC 2.95.3 ICE
1043 const bool isFromParent
= event
.GetEventObject() == (wxObject
*) parent
;
1044 const bool isFromSelf
= event
.GetEventObject() == (wxObject
*) this;
1046 if ( isFromParent
|| isFromSelf
)
1048 // no, it doesn't come from child, case (b) or (c): forward to a
1049 // page but only if direction is backwards (TAB) or from ourselves,
1050 if ( m_nSelection
!= -1 &&
1051 (!event
.GetDirection() || isFromSelf
) )
1053 // so that the page knows that the event comes from it's parent
1054 // and is being propagated downwards
1055 event
.SetEventObject(this);
1057 wxWindow
*page
= m_pages
[m_nSelection
];
1058 if ( !page
->GetEventHandler()->ProcessEvent(event
) )
1062 //else: page manages focus inside it itself
1064 else // otherwise set the focus to the notebook itself
1071 // it comes from our child, case (a), pass to the parent, but only
1072 // if the direction is forwards. Otherwise set the focus to the
1073 // notebook itself. The notebook is always the 'first' control of a
1075 if ( !event
.GetDirection() )
1081 event
.SetCurrentFocus(this);
1082 parent
->GetEventHandler()->ProcessEvent(event
);
1090 bool wxNotebook::DoDrawBackground(WXHDC hDC
, wxWindow
*child
)
1092 wxUxThemeHandle
theme(child
? child
: this, L
"TAB");
1096 // get the notebook client rect (we're not interested in drawing tabs
1098 wxRect r
= GetPageSize();
1103 wxCopyRectToRECT(r
, rc
);
1105 // map rect to the coords of the window we're drawing in
1107 ::MapWindowPoints(GetHwnd(), GetHwndOf(child
), (POINT
*)&rc
, 2);
1110 // apparently DrawThemeBackground() modifies the rect passed to it and if we
1111 // don't do these adjustments, there are some drawing artifacts which are
1112 // only visible with some non default themes; so modify the rect here using
1113 // the magic numbers below so that it still paints the correct area
1120 wxUxThemeEngine::Get()->DrawThemeBackground
1133 WXHBRUSH
wxNotebook::QueryBgBitmap()
1135 wxRect r
= GetPageSize();
1139 WindowHDC
hDC(GetHwnd());
1140 MemoryHDC
hDCMem(hDC
);
1141 CompatibleBitmap
hBmp(hDC
, r
.x
+ r
.width
, r
.y
+ r
.height
);
1143 SelectInHDC
selectBmp(hDCMem
, hBmp
);
1145 if ( !DoDrawBackground((WXHDC
)(HDC
)hDCMem
) )
1148 return (WXHBRUSH
)::CreatePatternBrush(hBmp
);
1151 void wxNotebook::UpdateBgBrush()
1153 if ( m_hbrBackground
)
1154 ::DeleteObject((HBRUSH
)m_hbrBackground
);
1156 if ( !m_hasBgCol
&& wxUxThemeEngine::GetIfActive() )
1158 m_hbrBackground
= QueryBgBitmap();
1160 else // no themes or we've got user-defined solid colour
1162 m_hbrBackground
= NULL
;
1166 WXHBRUSH
wxNotebook::MSWGetBgBrushForChild(WXHDC hDC
, WXHWND hWnd
)
1168 if ( m_hbrBackground
)
1170 // before drawing with the background brush, we need to position it
1173 ::GetWindowRect((HWND
)hWnd
, &rc
);
1175 ::MapWindowPoints(NULL
, GetHwnd(), (POINT
*)&rc
, 1);
1177 if ( !::SetBrushOrgEx((HDC
)hDC
, -rc
.left
, -rc
.top
, NULL
) )
1179 wxLogLastError(_T("SetBrushOrgEx(notebook bg brush)"));
1182 return m_hbrBackground
;
1185 return wxNotebookBase::MSWGetBgBrushForChild(hDC
, hWnd
);
1188 bool wxNotebook::MSWPrintChild(WXHDC hDC
, wxWindow
*child
)
1190 // solid background colour overrides themed background drawing
1191 if ( !UseBgCol() && DoDrawBackground(hDC
, child
) )
1194 // If we're using a solid colour (for example if we've switched off
1195 // theming for this notebook), paint it
1198 wxRect r
= GetPageSize();
1203 wxCopyRectToRECT(r
, rc
);
1205 // map rect to the coords of the window we're drawing in
1207 ::MapWindowPoints(GetHwnd(), GetHwndOf(child
), (POINT
*)&rc
, 2);
1209 wxBrush
brush(GetBackgroundColour());
1210 HBRUSH hbr
= GetHbrushOf(brush
);
1212 ::FillRect((HDC
) hDC
, &rc
, hbr
);
1217 return wxNotebookBase::MSWPrintChild(hDC
, child
);
1220 #endif // wxUSE_UXTHEME
1222 // Windows only: attempts to get colour for UX theme page background
1223 wxColour
wxNotebook::GetThemeBackgroundColour() const
1226 if (wxUxThemeEngine::Get())
1228 wxUxThemeHandle
hTheme((wxNotebook
*) this, L
"TAB");
1231 // This is total guesswork.
1232 // See PlatformSDK\Include\Tmschema.h for values
1233 COLORREF themeColor
;
1234 wxUxThemeEngine::Get()->GetThemeColor(
1238 3821 /* FILLCOLORHINT */,
1242 [DS] Workaround for WindowBlinds:
1243 Some themes return a near black theme color using FILLCOLORHINT,
1244 this makes notebook pages have an ugly black background and makes
1245 text (usually black) unreadable. Retry again with FILLCOLOR.
1247 This workaround potentially breaks appearance of some themes,
1248 but in practice it already fixes some themes.
1250 if (themeColor
== 1)
1252 wxUxThemeEngine::Get()->GetThemeColor(
1256 3802 /* FILLCOLOR */,
1260 return wxRGBToColour(themeColor
);
1263 #endif // wxUSE_UXTHEME
1265 return GetBackgroundColour();
1268 // ----------------------------------------------------------------------------
1269 // wxNotebook base class virtuals
1270 // ----------------------------------------------------------------------------
1272 #if wxUSE_CONSTRAINTS
1274 // override these 2 functions to do nothing: everything is done in OnSize
1276 void wxNotebook::SetConstraintSizes(bool WXUNUSED(recurse
))
1278 // don't set the sizes of the pages - their correct size is not yet known
1279 wxControl::SetConstraintSizes(false);
1282 bool wxNotebook::DoPhase(int WXUNUSED(nPhase
))
1287 #endif // wxUSE_CONSTRAINTS
1289 // ----------------------------------------------------------------------------
1290 // wxNotebook Windows message handlers
1291 // ----------------------------------------------------------------------------
1293 bool wxNotebook::MSWOnScroll(int orientation
, WXWORD nSBCode
,
1294 WXWORD pos
, WXHWND control
)
1296 // don't generate EVT_SCROLLWIN events for the WM_SCROLLs coming from the
1301 return wxNotebookBase::MSWOnScroll(orientation
, nSBCode
, pos
, control
);
1304 bool wxNotebook::MSWOnNotify(int idCtrl
, WXLPARAM lParam
, WXLPARAM
* result
)
1306 wxNotebookEvent
event(wxEVT_NULL
, m_windowId
);
1308 NMHDR
* hdr
= (NMHDR
*)lParam
;
1309 switch ( hdr
->code
) {
1311 event
.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
);
1314 case TCN_SELCHANGING
:
1315 event
.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
);
1319 return wxControl::MSWOnNotify(idCtrl
, lParam
, result
);
1322 event
.SetSelection(TabCtrl_GetCurSel(GetHwnd()));
1323 event
.SetOldSelection(m_nSelection
);
1324 event
.SetEventObject(this);
1325 event
.SetInt(idCtrl
);
1327 bool processed
= GetEventHandler()->ProcessEvent(event
);
1328 *result
= !event
.IsAllowed();
1332 #endif // wxUSE_NOTEBOOK