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
];
971 // As per bug report:
972 // http://sourceforge.net/tracker/index.php?func=detail&aid=1150659&group_id=9863&atid=109863,
973 // we should not set the page focus (and thereby the focus for
974 // a child window) since it erroneously selects radio button controls and also
975 // breaks keyboard handling for a notebook's scroll buttons. So
976 // we always focus the notebook and not the page.
980 else // no pages in the notebook, give the focus to itself
988 // we want to give others a chance to process this message as well
992 bool wxNotebook::MSWTranslateMessage(WXMSG
*wxmsg
)
994 const MSG
* const msg
= (MSG
*)wxmsg
;
996 // intercept TAB, CTRL+TAB and CTRL+SHIFT+TAB for processing by wxNotebook.
997 // TAB will be passed to the currently selected page, CTRL+TAB and
998 // CTRL+SHIFT+TAB will be processed by the notebook itself. do not
999 // intercept SHIFT+TAB. This goes to the parent of the notebook which will
1001 if ( msg
->message
== WM_KEYDOWN
&& msg
->wParam
== VK_TAB
&&
1002 msg
->hwnd
== GetHwnd() &&
1003 (wxIsCtrlDown() || !wxIsShiftDown()) )
1005 return MSWProcessMessage(wxmsg
);
1011 void wxNotebook::OnNavigationKey(wxNavigationKeyEvent
& event
)
1013 if ( event
.IsWindowChange() ) {
1015 AdvanceSelection(event
.GetDirection());
1018 // we get this event in 3 cases
1020 // a) one of our pages might have generated it because the user TABbed
1021 // out from it in which case we should propagate the event upwards and
1022 // our parent will take care of setting the focus to prev/next sibling
1026 // b) the parent panel wants to give the focus to us so that we
1027 // forward it to our selected page. We can't deal with this in
1028 // OnSetFocus() because we don't know which direction the focus came
1029 // from in this case and so can't choose between setting the focus to
1030 // first or last panel child
1034 // c) we ourselves (see MSWTranslateMessage) generated the event
1036 wxWindow
* const parent
= GetParent();
1038 // the wxObject* casts are required to avoid MinGW GCC 2.95.3 ICE
1039 const bool isFromParent
= event
.GetEventObject() == (wxObject
*) parent
;
1040 const bool isFromSelf
= event
.GetEventObject() == (wxObject
*) this;
1042 if ( isFromParent
|| isFromSelf
)
1044 // no, it doesn't come from child, case (b) or (c): forward to a
1045 // page but only if direction is backwards (TAB) or from ourselves,
1046 if ( m_nSelection
!= -1 &&
1047 (!event
.GetDirection() || isFromSelf
) )
1049 // so that the page knows that the event comes from it's parent
1050 // and is being propagated downwards
1051 event
.SetEventObject(this);
1053 wxWindow
*page
= m_pages
[m_nSelection
];
1054 if ( !page
->GetEventHandler()->ProcessEvent(event
) )
1058 //else: page manages focus inside it itself
1060 else // otherwise set the focus to the notebook itself
1067 // it comes from our child, case (a), pass to the parent, but only
1068 // if the direction is forwards. Otherwise set the focus to the
1069 // notebook itself. The notebook is always the 'first' control of a
1071 if ( !event
.GetDirection() )
1077 event
.SetCurrentFocus(this);
1078 parent
->GetEventHandler()->ProcessEvent(event
);
1086 bool wxNotebook::DoDrawBackground(WXHDC hDC
, wxWindow
*child
)
1088 wxUxThemeHandle
theme(child
? child
: this, L
"TAB");
1092 // get the notebook client rect (we're not interested in drawing tabs
1094 wxRect r
= GetPageSize();
1099 wxCopyRectToRECT(r
, rc
);
1101 // map rect to the coords of the window we're drawing in
1103 ::MapWindowPoints(GetHwnd(), GetHwndOf(child
), (POINT
*)&rc
, 2);
1106 // apparently DrawThemeBackground() modifies the rect passed to it and if we
1107 // don't do these adjustments, there are some drawing artifacts which are
1108 // only visible with some non default themes; so modify the rect here using
1109 // the magic numbers below so that it still paints the correct area
1116 wxUxThemeEngine::Get()->DrawThemeBackground
1129 WXHBRUSH
wxNotebook::QueryBgBitmap()
1131 wxRect r
= GetPageSize();
1135 WindowHDC
hDC(GetHwnd());
1136 MemoryHDC
hDCMem(hDC
);
1137 CompatibleBitmap
hBmp(hDC
, r
.x
+ r
.width
, r
.y
+ r
.height
);
1139 SelectInHDC
selectBmp(hDCMem
, hBmp
);
1141 if ( !DoDrawBackground((WXHDC
)(HDC
)hDCMem
) )
1144 return (WXHBRUSH
)::CreatePatternBrush(hBmp
);
1147 void wxNotebook::UpdateBgBrush()
1149 if ( m_hbrBackground
)
1150 ::DeleteObject((HBRUSH
)m_hbrBackground
);
1152 if ( !m_hasBgCol
&& wxUxThemeEngine::GetIfActive() )
1154 m_hbrBackground
= QueryBgBitmap();
1156 else // no themes or we've got user-defined solid colour
1158 m_hbrBackground
= NULL
;
1162 WXHBRUSH
wxNotebook::MSWGetBgBrushForChild(WXHDC hDC
, WXHWND hWnd
)
1164 if ( m_hbrBackground
)
1166 // before drawing with the background brush, we need to position it
1169 ::GetWindowRect((HWND
)hWnd
, &rc
);
1171 ::MapWindowPoints(NULL
, GetHwnd(), (POINT
*)&rc
, 1);
1173 if ( !::SetBrushOrgEx((HDC
)hDC
, -rc
.left
, -rc
.top
, NULL
) )
1175 wxLogLastError(_T("SetBrushOrgEx(notebook bg brush)"));
1178 return m_hbrBackground
;
1181 return wxNotebookBase::MSWGetBgBrushForChild(hDC
, hWnd
);
1184 bool wxNotebook::MSWPrintChild(WXHDC hDC
, wxWindow
*child
)
1186 // solid background colour overrides themed background drawing
1187 if ( !UseBgCol() && DoDrawBackground(hDC
, child
) )
1190 // If we're using a solid colour (for example if we've switched off
1191 // theming for this notebook), paint it
1194 wxRect r
= GetPageSize();
1199 wxCopyRectToRECT(r
, rc
);
1201 // map rect to the coords of the window we're drawing in
1203 ::MapWindowPoints(GetHwnd(), GetHwndOf(child
), (POINT
*)&rc
, 2);
1205 wxBrush
brush(GetBackgroundColour());
1206 HBRUSH hbr
= GetHbrushOf(brush
);
1208 ::FillRect((HDC
) hDC
, &rc
, hbr
);
1213 return wxNotebookBase::MSWPrintChild(hDC
, child
);
1216 #endif // wxUSE_UXTHEME
1218 // Windows only: attempts to get colour for UX theme page background
1219 wxColour
wxNotebook::GetThemeBackgroundColour() const
1222 if (wxUxThemeEngine::Get())
1224 wxUxThemeHandle
hTheme((wxNotebook
*) this, L
"TAB");
1227 // This is total guesswork.
1228 // See PlatformSDK\Include\Tmschema.h for values
1229 COLORREF themeColor
;
1230 wxUxThemeEngine::Get()->GetThemeColor(
1234 3821 /* FILLCOLORHINT */,
1238 [DS] Workaround for WindowBlinds:
1239 Some themes return a near black theme color using FILLCOLORHINT,
1240 this makes notebook pages have an ugly black background and makes
1241 text (usually black) unreadable. Retry again with FILLCOLOR.
1243 This workaround potentially breaks appearance of some themes,
1244 but in practice it already fixes some themes.
1246 if (themeColor
== 1)
1248 wxUxThemeEngine::Get()->GetThemeColor(
1252 3802 /* FILLCOLOR */,
1256 return wxRGBToColour(themeColor
);
1259 #endif // wxUSE_UXTHEME
1261 return GetBackgroundColour();
1264 // ----------------------------------------------------------------------------
1265 // wxNotebook base class virtuals
1266 // ----------------------------------------------------------------------------
1268 #if wxUSE_CONSTRAINTS
1270 // override these 2 functions to do nothing: everything is done in OnSize
1272 void wxNotebook::SetConstraintSizes(bool WXUNUSED(recurse
))
1274 // don't set the sizes of the pages - their correct size is not yet known
1275 wxControl::SetConstraintSizes(false);
1278 bool wxNotebook::DoPhase(int WXUNUSED(nPhase
))
1283 #endif // wxUSE_CONSTRAINTS
1285 // ----------------------------------------------------------------------------
1286 // wxNotebook Windows message handlers
1287 // ----------------------------------------------------------------------------
1289 bool wxNotebook::MSWOnScroll(int orientation
, WXWORD nSBCode
,
1290 WXWORD pos
, WXHWND control
)
1292 // don't generate EVT_SCROLLWIN events for the WM_SCROLLs coming from the
1297 return wxNotebookBase::MSWOnScroll(orientation
, nSBCode
, pos
, control
);
1300 bool wxNotebook::MSWOnNotify(int idCtrl
, WXLPARAM lParam
, WXLPARAM
* result
)
1302 wxNotebookEvent
event(wxEVT_NULL
, m_windowId
);
1304 NMHDR
* hdr
= (NMHDR
*)lParam
;
1305 switch ( hdr
->code
) {
1307 event
.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
);
1310 case TCN_SELCHANGING
:
1311 event
.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
);
1315 return wxControl::MSWOnNotify(idCtrl
, lParam
, result
);
1318 event
.SetSelection(TabCtrl_GetCurSel(GetHwnd()));
1319 event
.SetOldSelection(m_nSelection
);
1320 event
.SetEventObject(this);
1321 event
.SetInt(idCtrl
);
1323 bool processed
= GetEventHandler()->ProcessEvent(event
);
1324 *result
= !event
.IsAllowed();
1328 #endif // wxUSE_NOTEBOOK