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
234 // default for dynamic class
235 wxNotebook::wxNotebook()
240 // the same arguments as for wxControl
241 wxNotebook::wxNotebook(wxWindow
*parent
,
246 const wxString
& name
)
250 Create(parent
, id
, pos
, size
, style
, name
);
254 bool wxNotebook::Create(wxWindow
*parent
,
259 const wxString
& name
)
262 // Not sure why, but without this style, there is no border
263 // around the notebook tabs.
264 if (style
& wxNB_FLAT
)
265 style
|= wxBORDER_SUNKEN
;
268 // comctl32.dll 6.0 doesn't support non-top tabs with visual styles (the
269 // control is simply not rendered correctly), so disable them in this case
270 const int verComCtl32
= wxApp::GetComCtl32Version();
271 if ( verComCtl32
== 600 )
273 // check if we use themes at all -- if we don't, we're still ok
275 if ( wxUxThemeEngine::GetIfActive() )
278 style
&= ~(wxNB_BOTTOM
| wxNB_LEFT
| wxNB_RIGHT
);
282 LPCTSTR className
= WC_TABCONTROL
;
284 // SysTabCtl32 class has natively CS_HREDRAW and CS_VREDRAW enabled and it
285 // causes horrible flicker when resizing notebook, so get rid of it by
286 // using a class without these styles (but otherwise identical to it)
287 if ( !HasFlag(wxFULL_REPAINT_ON_RESIZE
) )
289 static ClassRegistrar s_clsNotebook
;
290 if ( !s_clsNotebook
.IsInitialized() )
292 // get a copy of standard class and modify it
295 if ( ::GetClassInfo(::GetModuleHandle(NULL
), WC_TABCONTROL
, &wc
) )
297 wc
.lpszClassName
= wxT("_wx_SysTabCtl32");
298 wc
.style
&= ~(CS_HREDRAW
| CS_VREDRAW
);
300 s_clsNotebook
.Register(wc
);
304 wxLogLastError(_T("GetClassInfoEx(SysTabCtl32)"));
308 // use our custom class if available but fall back to the standard
309 // notebook if we failed to register it
310 if ( s_clsNotebook
.IsRegistered() )
312 // it's ok to use c_str() here as the static s_clsNotebook object
313 // has sufficiently long lifetime
314 className
= s_clsNotebook
.GetName().c_str();
318 if ( !CreateControl(parent
, id
, pos
, size
, style
| wxTAB_TRAVERSAL
,
319 wxDefaultValidator
, name
) )
322 if ( !MSWCreateControl(className
, wxEmptyString
, pos
, size
) )
326 if ( HasFlag(wxNB_NOPAGETHEME
) ||
327 wxSystemOptions::IsFalse(wxT("msw.notebook.themed-background")) )
329 SetBackgroundColour(GetThemeBackgroundColour());
331 #endif // wxUSE_UXTHEME
333 // Undocumented hack to get flat notebook style
334 // In fact, we should probably only do this in some
335 // curcumstances, i.e. if we know we will have a border
336 // at the bottom (the tab control doesn't draw it itself)
337 #if defined(__POCKETPC__) || defined(__SMARTPHONE__)
338 if (HasFlag(wxNB_FLAT
))
340 SendMessage(GetHwnd(), CCM_SETVERSION
, COMCTL32_VERSION
, 0);
342 SetBackgroundColour(*wxWHITE
);
348 WXDWORD
wxNotebook::MSWGetStyle(long style
, WXDWORD
*exstyle
) const
350 WXDWORD tabStyle
= wxControl::MSWGetStyle(style
, exstyle
);
352 tabStyle
|= WS_TABSTOP
| TCS_TABS
;
354 if ( style
& wxNB_MULTILINE
)
355 tabStyle
|= TCS_MULTILINE
;
356 if ( style
& wxNB_FIXEDWIDTH
)
357 tabStyle
|= TCS_FIXEDWIDTH
;
359 if ( style
& wxNB_BOTTOM
)
360 tabStyle
|= TCS_RIGHT
;
361 else if ( style
& wxNB_LEFT
)
362 tabStyle
|= TCS_VERTICAL
;
363 else if ( style
& wxNB_RIGHT
)
364 tabStyle
|= TCS_VERTICAL
| TCS_RIGHT
;
369 // note that we never want to have the default WS_EX_CLIENTEDGE style
370 // as it looks too ugly for the notebooks
377 wxNotebook::~wxNotebook()
380 if ( m_hbrBackground
)
381 ::DeleteObject((HBRUSH
)m_hbrBackground
);
382 #endif // wxUSE_UXTHEME
385 // ----------------------------------------------------------------------------
386 // wxNotebook accessors
387 // ----------------------------------------------------------------------------
389 size_t wxNotebook::GetPageCount() const
392 wxASSERT( (int)m_pages
.Count() == TabCtrl_GetItemCount(GetHwnd()) );
394 return m_pages
.Count();
397 int wxNotebook::GetRowCount() const
399 return TabCtrl_GetRowCount(GetHwnd());
402 int wxNotebook::SetSelection(size_t nPage
)
404 wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxNOT_FOUND
, wxT("notebook page out of range") );
406 if ( int(nPage
) != m_nSelection
)
408 wxNotebookEvent
event(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
, m_windowId
);
409 event
.SetSelection(nPage
);
410 event
.SetOldSelection(m_nSelection
);
411 event
.SetEventObject(this);
412 if ( !GetEventHandler()->ProcessEvent(event
) || event
.IsAllowed() )
414 // program allows the page change
415 event
.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
);
416 (void)GetEventHandler()->ProcessEvent(event
);
418 TabCtrl_SetCurSel(GetHwnd(), nPage
);
425 bool wxNotebook::SetPageText(size_t nPage
, const wxString
& strText
)
427 wxCHECK_MSG( IS_VALID_PAGE(nPage
), false, wxT("notebook page out of range") );
430 tcItem
.mask
= TCIF_TEXT
;
431 tcItem
.pszText
= (wxChar
*)strText
.c_str();
433 return TabCtrl_SetItem(GetHwnd(), nPage
, &tcItem
) != 0;
436 wxString
wxNotebook::GetPageText(size_t nPage
) const
438 wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxEmptyString
, wxT("notebook page out of range") );
442 tcItem
.mask
= TCIF_TEXT
;
443 tcItem
.pszText
= buf
;
444 tcItem
.cchTextMax
= WXSIZEOF(buf
);
447 if ( TabCtrl_GetItem(GetHwnd(), nPage
, &tcItem
) )
448 str
= tcItem
.pszText
;
453 int wxNotebook::GetPageImage(size_t nPage
) const
455 wxCHECK_MSG( IS_VALID_PAGE(nPage
), -1, wxT("notebook page out of range") );
458 tcItem
.mask
= TCIF_IMAGE
;
460 return TabCtrl_GetItem(GetHwnd(), nPage
, &tcItem
) ? tcItem
.iImage
: -1;
463 bool wxNotebook::SetPageImage(size_t nPage
, int nImage
)
465 wxCHECK_MSG( IS_VALID_PAGE(nPage
), false, wxT("notebook page out of range") );
468 tcItem
.mask
= TCIF_IMAGE
;
469 tcItem
.iImage
= nImage
;
471 return TabCtrl_SetItem(GetHwnd(), nPage
, &tcItem
) != 0;
474 void wxNotebook::SetImageList(wxImageList
* imageList
)
476 wxNotebookBase::SetImageList(imageList
);
480 TabCtrl_SetImageList(GetHwnd(), (HIMAGELIST
)imageList
->GetHIMAGELIST());
484 // ----------------------------------------------------------------------------
485 // wxNotebook size settings
486 // ----------------------------------------------------------------------------
488 wxRect
wxNotebook::GetPageSize() const
493 ::GetClientRect(GetHwnd(), &rc
);
495 // This check is to work around a bug in TabCtrl_AdjustRect which will
496 // cause a crash on win2k, or on XP with themes disabled, if the
497 // wxNB_MULTILINE style is used and the rectangle is very small, (such as
498 // when the notebook is first created.) The value of 20 is just
499 // arbitrarily chosen, if there is a better way to determine this value
500 // then please do so. --RD
501 if ( !HasFlag(wxNB_MULTILINE
) || (rc
.right
> 20 && rc
.bottom
> 20) )
503 TabCtrl_AdjustRect(GetHwnd(), false, &rc
);
505 wxCopyRECTToRect(rc
, r
);
511 void wxNotebook::SetPageSize(const wxSize
& size
)
513 // transform the page size into the notebook size
520 TabCtrl_AdjustRect(GetHwnd(), true, &rc
);
523 SetSize(rc
.right
- rc
.left
, rc
.bottom
- rc
.top
);
526 void wxNotebook::SetPadding(const wxSize
& padding
)
528 TabCtrl_SetPadding(GetHwnd(), padding
.x
, padding
.y
);
531 // Windows-only at present. Also, you must use the wxNB_FIXEDWIDTH
533 void wxNotebook::SetTabSize(const wxSize
& sz
)
535 ::SendMessage(GetHwnd(), TCM_SETITEMSIZE
, 0, MAKELPARAM(sz
.x
, sz
.y
));
538 wxSize
wxNotebook::CalcSizeFromPage(const wxSize
& sizePage
) const
540 wxSize sizeTotal
= sizePage
;
542 // We need to make getting tab size part of the wxWidgets API.
544 if (GetPageCount() > 0)
547 TabCtrl_GetItemRect((HWND
) GetHWND(), 0, & rect
);
548 tabSize
.x
= rect
.right
- rect
.left
;
549 tabSize
.y
= rect
.bottom
- rect
.top
;
551 if ( HasFlag(wxNB_LEFT
) || HasFlag(wxNB_RIGHT
) )
553 sizeTotal
.x
+= tabSize
.x
+ 7;
559 sizeTotal
.y
+= tabSize
.y
+ 7;
565 void wxNotebook::AdjustPageSize(wxNotebookPage
*page
)
567 wxCHECK_RET( page
, _T("NULL page in wxNotebook::AdjustPageSize") );
569 const wxRect r
= GetPageSize();
576 // ----------------------------------------------------------------------------
577 // wxNotebook operations
578 // ----------------------------------------------------------------------------
580 // remove one page from the notebook, without deleting
581 wxNotebookPage
*wxNotebook::DoRemovePage(size_t nPage
)
583 wxNotebookPage
*pageRemoved
= wxNotebookBase::DoRemovePage(nPage
);
587 TabCtrl_DeleteItem(GetHwnd(), nPage
);
589 if ( m_pages
.IsEmpty() )
591 // no selection any more, the notebook becamse empty
594 else // notebook still not empty
596 int selNew
= TabCtrl_GetCurSel(GetHwnd());
599 // No selection change, just refresh the current selection.
600 // Because it could be that the slection index changed
601 // we need to update it.
602 // Note: this does not mean the selection it self changed.
603 m_nSelection
= selNew
;
604 m_pages
[m_nSelection
]->Refresh();
606 else if (int(nPage
) == m_nSelection
)
608 // The selection was deleted.
610 // Determine new selection.
611 if (m_nSelection
== int(GetPageCount()))
612 selNew
= m_nSelection
- 1;
614 selNew
= m_nSelection
;
616 // m_nSelection must be always valid so reset it before calling
619 SetSelection(selNew
);
623 wxFAIL
; // Windows did not behave ok.
631 bool wxNotebook::DeleteAllPages()
633 size_t nPageCount
= GetPageCount();
635 for ( nPage
= 0; nPage
< nPageCount
; nPage
++ )
636 delete m_pages
[nPage
];
640 TabCtrl_DeleteAllItems(GetHwnd());
644 InvalidateBestSize();
648 // same as AddPage() but does it at given position
649 bool wxNotebook::InsertPage(size_t nPage
,
650 wxNotebookPage
*pPage
,
651 const wxString
& strText
,
655 wxCHECK_MSG( pPage
!= NULL
, false, _T("NULL page in wxNotebook::InsertPage") );
656 wxCHECK_MSG( IS_VALID_PAGE(nPage
) || nPage
== GetPageCount(), false,
657 _T("invalid index in wxNotebook::InsertPage") );
659 wxASSERT_MSG( pPage
->GetParent() == this,
660 _T("notebook pages must have notebook as parent") );
662 // add a new tab to the control
663 // ----------------------------
665 // init all fields to 0
667 wxZeroMemory(tcItem
);
669 // set the image, if any
672 tcItem
.mask
|= TCIF_IMAGE
;
673 tcItem
.iImage
= imageId
;
677 if ( !strText
.empty() )
679 tcItem
.mask
|= TCIF_TEXT
;
680 tcItem
.pszText
= (wxChar
*)strText
.c_str(); // const_cast
683 // hide the page: unless it is selected, it shouldn't be shown (and if it
684 // is selected it will be shown later)
685 HWND hwnd
= GetWinHwnd(pPage
);
686 SetWindowLong(hwnd
, GWL_STYLE
, GetWindowLong(hwnd
, GWL_STYLE
) & ~WS_VISIBLE
);
688 // this updates internal flag too -- otherwise it would get out of sync
689 // with the real state
693 // fit the notebook page to the tab control's display area: this should be
694 // done before adding it to the notebook or TabCtrl_InsertItem() will
695 // change the notebooks size itself!
696 AdjustPageSize(pPage
);
698 // finally do insert it
699 if ( TabCtrl_InsertItem(GetHwnd(), nPage
, &tcItem
) == -1 )
701 wxLogError(wxT("Can't create the notebook page '%s'."), strText
.c_str());
706 // succeeded: save the pointer to the page
707 m_pages
.Insert(pPage
, nPage
);
709 // we may need to adjust the size again if the notebook size changed:
710 // normally this only happens for the first page we add (the tabs which
711 // hadn't been there before are now shown) but for a multiline notebook it
712 // can happen for any page at all as a new row could have been started
713 if ( m_pages
.GetCount() == 1 || HasFlag(wxNB_MULTILINE
) )
715 AdjustPageSize(pPage
);
718 // now deal with the selection
719 // ---------------------------
721 // if the inserted page is before the selected one, we must update the
722 // index of the selected page
723 if ( int(nPage
) <= m_nSelection
)
725 // one extra page added
729 // some page should be selected: either this one or the first one if there
730 // is still no selection
734 else if ( m_nSelection
== -1 )
738 SetSelection(selNew
);
740 InvalidateBestSize();
745 int wxNotebook::HitTest(const wxPoint
& pt
, long *flags
) const
747 TC_HITTESTINFO hitTestInfo
;
748 hitTestInfo
.pt
.x
= pt
.x
;
749 hitTestInfo
.pt
.y
= pt
.y
;
750 int item
= TabCtrl_HitTest(GetHwnd(), &hitTestInfo
);
756 if ((hitTestInfo
.flags
& TCHT_NOWHERE
) == TCHT_NOWHERE
)
757 *flags
|= wxNB_HITTEST_NOWHERE
;
758 if ((hitTestInfo
.flags
& TCHT_ONITEM
) == TCHT_ONITEM
)
759 *flags
|= wxNB_HITTEST_ONITEM
;
760 if ((hitTestInfo
.flags
& TCHT_ONITEMICON
) == TCHT_ONITEMICON
)
761 *flags
|= wxNB_HITTEST_ONICON
;
762 if ((hitTestInfo
.flags
& TCHT_ONITEMLABEL
) == TCHT_ONITEMLABEL
)
763 *flags
|= wxNB_HITTEST_ONLABEL
;
769 // ----------------------------------------------------------------------------
770 // flicker-less notebook redraw
771 // ----------------------------------------------------------------------------
773 #if USE_NOTEBOOK_ANTIFLICKER
775 // wnd proc for the spin button
776 LRESULT APIENTRY _EXPORT
wxNotebookSpinBtnWndProc(HWND hwnd
,
781 if ( message
== WM_ERASEBKGND
)
784 return ::CallWindowProc(CASTWNDPROC gs_wndprocNotebookSpinBtn
,
785 hwnd
, message
, wParam
, lParam
);
789 void wxNotebook::OnEraseBackground(wxEraseEvent
& WXUNUSED(event
))
794 void wxNotebook::OnPaint(wxPaintEvent
& WXUNUSED(event
))
799 ::GetClientRect(GetHwnd(), &rc
);
800 wxBitmap
bmp(rc
.right
, rc
.bottom
);
801 memdc
.SelectObject(bmp
);
803 // if there is no special brush just use the solid background colour
804 HBRUSH hbr
= (HBRUSH
)m_hbrBackground
;
808 brush
= wxBrush(GetBackgroundColour());
809 hbr
= GetHbrushOf(brush
);
812 ::FillRect(GetHdcOf(memdc
), &rc
, hbr
);
814 MSWDefWindowProc(WM_PAINT
, (WPARAM
)memdc
.GetHDC(), 0);
816 dc
.Blit(0, 0, rc
.right
, rc
.bottom
, &memdc
, 0, 0);
819 #endif // USE_NOTEBOOK_ANTIFLICKER
821 // ----------------------------------------------------------------------------
822 // wxNotebook callbacks
823 // ----------------------------------------------------------------------------
825 void wxNotebook::OnSize(wxSizeEvent
& event
)
827 if ( GetPageCount() == 0 )
829 // Prevents droppings on resize, but does cause some flicker
830 // when there are no pages.
838 // Without this, we can sometimes get droppings at the edges
839 // of a notebook, for example a notebook in a splitter window.
840 // This needs to be reconciled with the RefreshRect calls
841 // at the end of this function, which weren't enough to prevent
844 wxSize sz
= GetClientSize();
846 // Refresh right side
847 wxRect
rect(sz
.x
-4, 0, 4, sz
.y
);
850 // Refresh bottom side
851 rect
= wxRect(0, sz
.y
-4, sz
.x
, 4);
855 rect
= wxRect(0, 0, 4, sz
.y
);
858 #endif // !__WXWINCE__
860 // fit all the notebook pages to the tab control's display area
863 rc
.left
= rc
.top
= 0;
864 GetSize((int *)&rc
.right
, (int *)&rc
.bottom
);
866 // save the total size, we'll use it below
867 int widthNbook
= rc
.right
- rc
.left
,
868 heightNbook
= rc
.bottom
- rc
.top
;
870 // there seems to be a bug in the implementation of TabCtrl_AdjustRect(): it
871 // returns completely false values for multiline tab controls after the tabs
872 // are added but before getting the first WM_SIZE (off by ~50 pixels, see
874 // http://sf.net/tracker/index.php?func=detail&aid=645323&group_id=9863&atid=109863
876 // and the only work around I could find was this ugly hack... without it
877 // simply toggling the "multiline" checkbox in the notebook sample resulted
878 // in a noticeable page displacement
879 if ( HasFlag(wxNB_MULTILINE
) )
881 // avoid an infinite recursion: we get another notification too!
882 static bool s_isInOnSize
= false;
887 SendMessage(GetHwnd(), WM_SIZE
, SIZE_RESTORED
,
888 MAKELPARAM(rc
.right
, rc
.bottom
));
889 s_isInOnSize
= false;
894 // background bitmap size has changed, update the brush using it too
896 #endif // wxUSE_UXTHEME
898 TabCtrl_AdjustRect(GetHwnd(), false, &rc
);
900 int width
= rc
.right
- rc
.left
,
901 height
= rc
.bottom
- rc
.top
;
902 size_t nCount
= m_pages
.Count();
903 for ( size_t nPage
= 0; nPage
< nCount
; nPage
++ ) {
904 wxNotebookPage
*pPage
= m_pages
[nPage
];
905 pPage
->SetSize(rc
.left
, rc
.top
, width
, height
);
909 // unless we had already repainted everything, we now need to refresh
910 if ( !HasFlag(wxFULL_REPAINT_ON_RESIZE
) )
912 // invalidate areas not covered by pages
913 RefreshRect(wxRect(0, 0, widthNbook
, rc
.top
), false);
914 RefreshRect(wxRect(0, rc
.top
, rc
.left
, height
), false);
915 RefreshRect(wxRect(0, rc
.bottom
, widthNbook
, heightNbook
- rc
.bottom
),
917 RefreshRect(wxRect(rc
.right
, rc
.top
, widthNbook
- rc
.bottom
, height
),
921 #if USE_NOTEBOOK_ANTIFLICKER
922 // subclass the spin control used by the notebook to scroll pages to
923 // prevent it from flickering on resize
924 if ( !gs_wndprocNotebookSpinBtn
)
926 // iterate over all child windows to find spin button
927 for ( HWND child
= ::GetWindow(GetHwnd(), GW_CHILD
);
929 child
= ::GetWindow(child
, GW_HWNDNEXT
) )
931 wxWindow
*childWindow
= wxFindWinFromHandle((WXHWND
)child
);
933 // see if it exists, if no wxWindow found then assume it's the spin
937 // subclass the spin button to override WM_ERASEBKGND
938 gs_wndprocNotebookSpinBtn
= (WXFARPROC
)wxGetWindowProc(child
);
940 wxSetWindowProc(child
, wxNotebookSpinBtnWndProc
);
945 #endif // USE_NOTEBOOK_ANTIFLICKER
950 void wxNotebook::OnSelChange(wxNotebookEvent
& event
)
952 // is it our tab control?
953 if ( event
.GetEventObject() == this )
955 int sel
= event
.GetOldSelection();
957 m_pages
[sel
]->Show(false);
959 sel
= event
.GetSelection();
962 wxNotebookPage
*pPage
= m_pages
[sel
];
966 // If the newly focused window is not a child of the new page,
967 // SetFocus was not successful and the notebook itself should be
969 wxWindow
*currentFocus
= FindFocus();
970 wxWindow
*startFocus
= currentFocus
;
971 while ( currentFocus
&& currentFocus
!= pPage
&& currentFocus
!= this )
972 currentFocus
= currentFocus
->GetParent();
974 if ( startFocus
== pPage
|| currentFocus
!= pPage
)
978 else // no pages in the notebook, give the focus to itself
986 // we want to give others a chance to process this message as well
990 bool wxNotebook::MSWTranslateMessage(WXMSG
*wxmsg
)
992 const MSG
* const msg
= (MSG
*)wxmsg
;
994 // intercept TAB, CTRL+TAB and CTRL+SHIFT+TAB for processing by wxNotebook.
995 // TAB will be passed to the currently selected page, CTRL+TAB and
996 // CTRL+SHIFT+TAB will be processed by the notebook itself. do not
997 // intercept SHIFT+TAB. This goes to the parent of the notebook which will
999 if ( msg
->message
== WM_KEYDOWN
&& msg
->wParam
== VK_TAB
&&
1000 msg
->hwnd
== GetHwnd() &&
1001 (wxIsCtrlDown() || !wxIsShiftDown()) )
1003 return MSWProcessMessage(wxmsg
);
1009 void wxNotebook::OnNavigationKey(wxNavigationKeyEvent
& event
)
1011 if ( event
.IsWindowChange() ) {
1013 AdvanceSelection(event
.GetDirection());
1016 // we get this event in 3 cases
1018 // a) one of our pages might have generated it because the user TABbed
1019 // out from it in which case we should propagate the event upwards and
1020 // our parent will take care of setting the focus to prev/next sibling
1024 // b) the parent panel wants to give the focus to us so that we
1025 // forward it to our selected page. We can't deal with this in
1026 // OnSetFocus() because we don't know which direction the focus came
1027 // from in this case and so can't choose between setting the focus to
1028 // first or last panel child
1032 // c) we ourselves (see MSWTranslateMessage) generated the event
1034 wxWindow
* const parent
= GetParent();
1036 // the wxObject* casts are required to avoid MinGW GCC 2.95.3 ICE
1037 const bool isFromParent
= event
.GetEventObject() == (wxObject
*) parent
;
1038 const bool isFromSelf
= event
.GetEventObject() == (wxObject
*) this;
1040 if ( isFromParent
|| isFromSelf
)
1042 // no, it doesn't come from child, case (b) or (c): forward to a
1043 // page but only if direction is backwards (TAB) or from ourselves,
1044 if ( m_nSelection
!= -1 &&
1045 (!event
.GetDirection() || isFromSelf
) )
1047 // so that the page knows that the event comes from it's parent
1048 // and is being propagated downwards
1049 event
.SetEventObject(this);
1051 wxWindow
*page
= m_pages
[m_nSelection
];
1052 if ( !page
->GetEventHandler()->ProcessEvent(event
) )
1056 //else: page manages focus inside it itself
1058 else // otherwise set the focus to the notebook itself
1065 // it comes from our child, case (a), pass to the parent, but only
1066 // if the direction is forwards. Otherwise set the focus to the
1067 // notebook itself. The notebook is always the 'first' control of a
1069 if ( !event
.GetDirection() )
1075 event
.SetCurrentFocus(this);
1076 parent
->GetEventHandler()->ProcessEvent(event
);
1084 bool wxNotebook::DoDrawBackground(WXHDC hDC
, wxWindow
*child
)
1086 wxUxThemeHandle
theme(child
? child
: this, L
"TAB");
1090 // get the notebook client rect (we're not interested in drawing tabs
1092 wxRect r
= GetPageSize();
1097 wxCopyRectToRECT(r
, rc
);
1099 // map rect to the coords of the window we're drawing in
1101 ::MapWindowPoints(GetHwnd(), GetHwndOf(child
), (POINT
*)&rc
, 2);
1104 // apparently DrawThemeBackground() modifies the rect passed to it and if we
1105 // don't do these adjustments, there are some drawing artifacts which are
1106 // only visible with some non default themes; so modify the rect here using
1107 // the magic numbers below so that it still paints the correct area
1114 wxUxThemeEngine::Get()->DrawThemeBackground
1127 WXHBRUSH
wxNotebook::QueryBgBitmap()
1129 wxRect r
= GetPageSize();
1133 WindowHDC
hDC(GetHwnd());
1134 MemoryHDC
hDCMem(hDC
);
1135 CompatibleBitmap
hBmp(hDC
, r
.x
+ r
.width
, r
.y
+ r
.height
);
1137 SelectInHDC
selectBmp(hDCMem
, hBmp
);
1139 if ( !DoDrawBackground((WXHDC
)(HDC
)hDCMem
) )
1142 return (WXHBRUSH
)::CreatePatternBrush(hBmp
);
1145 void wxNotebook::UpdateBgBrush()
1147 if ( m_hbrBackground
)
1148 ::DeleteObject((HBRUSH
)m_hbrBackground
);
1150 if ( !m_hasBgCol
&& wxUxThemeEngine::GetIfActive() )
1152 m_hbrBackground
= QueryBgBitmap();
1154 else // no themes or we've got user-defined solid colour
1156 m_hbrBackground
= NULL
;
1160 WXHBRUSH
wxNotebook::MSWGetBgBrushForChild(WXHDC hDC
, WXHWND hWnd
)
1162 if ( m_hbrBackground
)
1164 // before drawing with the background brush, we need to position it
1167 ::GetWindowRect((HWND
)hWnd
, &rc
);
1169 ::MapWindowPoints(NULL
, GetHwnd(), (POINT
*)&rc
, 1);
1171 if ( !::SetBrushOrgEx((HDC
)hDC
, -rc
.left
, -rc
.top
, NULL
) )
1173 wxLogLastError(_T("SetBrushOrgEx(notebook bg brush)"));
1176 return m_hbrBackground
;
1179 return wxNotebookBase::MSWGetBgBrushForChild(hDC
, hWnd
);
1182 bool wxNotebook::MSWPrintChild(WXHDC hDC
, wxWindow
*child
)
1184 // solid background colour overrides themed background drawing
1185 if ( !UseBgCol() && DoDrawBackground(hDC
, child
) )
1188 // If we're using a solid colour (for example if we've switched off
1189 // theming for this notebook), paint it
1192 wxRect r
= GetPageSize();
1197 wxCopyRectToRECT(r
, rc
);
1199 // map rect to the coords of the window we're drawing in
1201 ::MapWindowPoints(GetHwnd(), GetHwndOf(child
), (POINT
*)&rc
, 2);
1203 wxBrush
brush(GetBackgroundColour());
1204 HBRUSH hbr
= GetHbrushOf(brush
);
1206 ::FillRect((HDC
) hDC
, &rc
, hbr
);
1211 return wxNotebookBase::MSWPrintChild(hDC
, child
);
1214 #endif // wxUSE_UXTHEME
1216 // Windows only: attempts to get colour for UX theme page background
1217 wxColour
wxNotebook::GetThemeBackgroundColour() const
1220 if (wxUxThemeEngine::Get())
1222 wxUxThemeHandle
hTheme((wxNotebook
*) this, L
"TAB");
1225 // This is total guesswork.
1226 // See PlatformSDK\Include\Tmschema.h for values
1227 COLORREF themeColor
;
1228 wxUxThemeEngine::Get()->GetThemeColor(
1232 3821 /* FILLCOLORHINT */,
1236 [DS] Workaround for WindowBlinds:
1237 Some themes return a near black theme color using FILLCOLORHINT,
1238 this makes notebook pages have an ugly black background and makes
1239 text (usually black) unreadable. Retry again with FILLCOLOR.
1241 This workaround potentially breaks appearance of some themes,
1242 but in practice it already fixes some themes.
1244 if (themeColor
== 1)
1246 wxUxThemeEngine::Get()->GetThemeColor(
1250 3802 /* FILLCOLOR */,
1254 return wxRGBToColour(themeColor
);
1257 #endif // wxUSE_UXTHEME
1259 return GetBackgroundColour();
1262 // ----------------------------------------------------------------------------
1263 // wxNotebook base class virtuals
1264 // ----------------------------------------------------------------------------
1266 #if wxUSE_CONSTRAINTS
1268 // override these 2 functions to do nothing: everything is done in OnSize
1270 void wxNotebook::SetConstraintSizes(bool WXUNUSED(recurse
))
1272 // don't set the sizes of the pages - their correct size is not yet known
1273 wxControl::SetConstraintSizes(false);
1276 bool wxNotebook::DoPhase(int WXUNUSED(nPhase
))
1281 #endif // wxUSE_CONSTRAINTS
1283 // ----------------------------------------------------------------------------
1284 // wxNotebook Windows message handlers
1285 // ----------------------------------------------------------------------------
1287 bool wxNotebook::MSWOnScroll(int orientation
, WXWORD nSBCode
,
1288 WXWORD pos
, WXHWND control
)
1290 // don't generate EVT_SCROLLWIN events for the WM_SCROLLs coming from the
1295 return wxNotebookBase::MSWOnScroll(orientation
, nSBCode
, pos
, control
);
1298 bool wxNotebook::MSWOnNotify(int idCtrl
, WXLPARAM lParam
, WXLPARAM
* result
)
1300 wxNotebookEvent
event(wxEVT_NULL
, m_windowId
);
1302 NMHDR
* hdr
= (NMHDR
*)lParam
;
1303 switch ( hdr
->code
) {
1305 event
.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
);
1308 case TCN_SELCHANGING
:
1309 event
.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
);
1313 return wxControl::MSWOnNotify(idCtrl
, lParam
, result
);
1316 event
.SetSelection(TabCtrl_GetCurSel(GetHwnd()));
1317 event
.SetOldSelection(m_nSelection
);
1318 event
.SetEventObject(this);
1319 event
.SetInt(idCtrl
);
1321 bool processed
= GetEventHandler()->ProcessEvent(event
);
1322 *result
= !event
.IsAllowed();
1326 #endif // wxUSE_NOTEBOOK