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
;
806 hbr
= GetHbrushOf(wxBrush(GetBackgroundColour()));
808 ::FillRect(GetHdcOf(memdc
), &rc
, hbr
);
810 MSWDefWindowProc(WM_PAINT
, (WPARAM
)memdc
.GetHDC(), 0);
812 dc
.Blit(0, 0, rc
.right
, rc
.bottom
, &memdc
, 0, 0);
815 #endif // USE_NOTEBOOK_ANTIFLICKER
817 // ----------------------------------------------------------------------------
818 // wxNotebook callbacks
819 // ----------------------------------------------------------------------------
821 void wxNotebook::OnSize(wxSizeEvent
& event
)
823 if ( GetPageCount() == 0 )
825 // Prevents droppings on resize, but does cause some flicker
826 // when there are no pages.
834 // Without this, we can sometimes get droppings at the edges
835 // of a notebook, for example a notebook in a splitter window.
836 // This needs to be reconciled with the RefreshRect calls
837 // at the end of this function, which weren't enough to prevent
840 wxSize sz
= GetClientSize();
842 // Refresh right side
843 wxRect
rect(sz
.x
-4, 0, 4, sz
.y
);
846 // Refresh bottom side
847 rect
= wxRect(0, sz
.y
-4, sz
.x
, 4);
851 rect
= wxRect(0, 0, 4, sz
.y
);
854 #endif // !__WXWINCE__
856 // fit all the notebook pages to the tab control's display area
859 rc
.left
= rc
.top
= 0;
860 GetSize((int *)&rc
.right
, (int *)&rc
.bottom
);
862 // save the total size, we'll use it below
863 int widthNbook
= rc
.right
- rc
.left
,
864 heightNbook
= rc
.bottom
- rc
.top
;
866 // there seems to be a bug in the implementation of TabCtrl_AdjustRect(): it
867 // returns completely false values for multiline tab controls after the tabs
868 // are added but before getting the first WM_SIZE (off by ~50 pixels, see
870 // http://sf.net/tracker/index.php?func=detail&aid=645323&group_id=9863&atid=109863
872 // and the only work around I could find was this ugly hack... without it
873 // simply toggling the "multiline" checkbox in the notebook sample resulted
874 // in a noticeable page displacement
875 if ( HasFlag(wxNB_MULTILINE
) )
877 // avoid an infinite recursion: we get another notification too!
878 static bool s_isInOnSize
= false;
883 SendMessage(GetHwnd(), WM_SIZE
, SIZE_RESTORED
,
884 MAKELPARAM(rc
.right
, rc
.bottom
));
885 s_isInOnSize
= false;
890 // background bitmap size has changed, update the brush using it too
892 #endif // wxUSE_UXTHEME
894 TabCtrl_AdjustRect(GetHwnd(), false, &rc
);
896 int width
= rc
.right
- rc
.left
,
897 height
= rc
.bottom
- rc
.top
;
898 size_t nCount
= m_pages
.Count();
899 for ( size_t nPage
= 0; nPage
< nCount
; nPage
++ ) {
900 wxNotebookPage
*pPage
= m_pages
[nPage
];
901 pPage
->SetSize(rc
.left
, rc
.top
, width
, height
);
905 // unless we had already repainted everything, we now need to refresh
906 if ( !HasFlag(wxFULL_REPAINT_ON_RESIZE
) )
908 // invalidate areas not covered by pages
909 RefreshRect(wxRect(0, 0, widthNbook
, rc
.top
), false);
910 RefreshRect(wxRect(0, rc
.top
, rc
.left
, height
), false);
911 RefreshRect(wxRect(0, rc
.bottom
, widthNbook
, heightNbook
- rc
.bottom
),
913 RefreshRect(wxRect(rc
.right
, rc
.top
, widthNbook
- rc
.bottom
, height
),
917 #if USE_NOTEBOOK_ANTIFLICKER
918 // subclass the spin control used by the notebook to scroll pages to
919 // prevent it from flickering on resize
920 if ( !gs_wndprocNotebookSpinBtn
)
922 // iterate over all child windows to find spin button
923 for ( HWND child
= ::GetWindow(GetHwnd(), GW_CHILD
);
925 child
= ::GetWindow(child
, GW_HWNDNEXT
) )
927 wxWindow
*childWindow
= wxFindWinFromHandle((WXHWND
)child
);
929 // see if it exists, if no wxWindow found then assume it's the spin
933 // subclass the spin button to override WM_ERASEBKGND
934 gs_wndprocNotebookSpinBtn
= (WXFARPROC
)wxGetWindowProc(child
);
936 wxSetWindowProc(child
, wxNotebookSpinBtnWndProc
);
941 #endif // USE_NOTEBOOK_ANTIFLICKER
946 void wxNotebook::OnSelChange(wxNotebookEvent
& event
)
948 // is it our tab control?
949 if ( event
.GetEventObject() == this )
951 int sel
= event
.GetOldSelection();
953 m_pages
[sel
]->Show(false);
955 sel
= event
.GetSelection();
958 wxNotebookPage
*pPage
= m_pages
[sel
];
962 // If the newly focused window is not a child of the new page,
963 // SetFocus was not successful and the notebook itself should be
965 wxWindow
*currentFocus
= FindFocus();
966 wxWindow
*startFocus
= currentFocus
;
967 while ( currentFocus
&& currentFocus
!= pPage
&& currentFocus
!= this )
968 currentFocus
= currentFocus
->GetParent();
970 if ( startFocus
== pPage
|| currentFocus
!= pPage
)
974 else // no pages in the notebook, give the focus to itself
982 // we want to give others a chance to process this message as well
986 bool wxNotebook::MSWTranslateMessage(WXMSG
*wxmsg
)
988 const MSG
* const msg
= (MSG
*)wxmsg
;
990 // intercept TAB, CTRL+TAB and CTRL+SHIFT+TAB for processing by wxNotebook.
991 // TAB will be passed to the currently selected page, CTRL+TAB and
992 // CTRL+SHIFT+TAB will be processed by the notebook itself. do not
993 // intercept SHIFT+TAB. This goes to the parent of the notebook which will
995 if ( msg
->message
== WM_KEYDOWN
&& msg
->wParam
== VK_TAB
&&
996 msg
->hwnd
== GetHwnd() &&
997 (wxIsCtrlDown() || !wxIsShiftDown()) )
999 return MSWProcessMessage(wxmsg
);
1005 void wxNotebook::OnNavigationKey(wxNavigationKeyEvent
& event
)
1007 if ( event
.IsWindowChange() ) {
1009 AdvanceSelection(event
.GetDirection());
1012 // we get this event in 3 cases
1014 // a) one of our pages might have generated it because the user TABbed
1015 // out from it in which case we should propagate the event upwards and
1016 // our parent will take care of setting the focus to prev/next sibling
1020 // b) the parent panel wants to give the focus to us so that we
1021 // forward it to our selected page. We can't deal with this in
1022 // OnSetFocus() because we don't know which direction the focus came
1023 // from in this case and so can't choose between setting the focus to
1024 // first or last panel child
1028 // c) we ourselves (see MSWTranslateMessage) generated the event
1030 wxWindow
* const parent
= GetParent();
1032 // the wxObject* casts are required to avoid MinGW GCC 2.95.3 ICE
1033 const bool isFromParent
= event
.GetEventObject() == (wxObject
*) parent
;
1034 const bool isFromSelf
= event
.GetEventObject() == (wxObject
*) this;
1036 if ( isFromParent
|| isFromSelf
)
1038 // no, it doesn't come from child, case (b) or (c): forward to a
1039 // page but only if direction is backwards (TAB) or from ourselves,
1040 if ( m_nSelection
!= -1 &&
1041 (!event
.GetDirection() || isFromSelf
) )
1043 // so that the page knows that the event comes from it's parent
1044 // and is being propagated downwards
1045 event
.SetEventObject(this);
1047 wxWindow
*page
= m_pages
[m_nSelection
];
1048 if ( !page
->GetEventHandler()->ProcessEvent(event
) )
1052 //else: page manages focus inside it itself
1054 else // otherwise set the focus to the notebook itself
1061 // it comes from our child, case (a), pass to the parent, but only
1062 // if the direction is forwards. Otherwise set the focus to the
1063 // notebook itself. The notebook is always the 'first' control of a
1065 if ( !event
.GetDirection() )
1071 event
.SetCurrentFocus(this);
1072 parent
->GetEventHandler()->ProcessEvent(event
);
1080 bool wxNotebook::DoDrawBackground(WXHDC hDC
, wxWindow
*child
)
1082 wxUxThemeHandle
theme(child
? child
: this, L
"TAB");
1086 // get the notebook client rect (we're not interested in drawing tabs
1088 wxRect r
= GetPageSize();
1093 wxCopyRectToRECT(r
, rc
);
1095 // map rect to the coords of the window we're drawing in
1097 ::MapWindowPoints(GetHwnd(), GetHwndOf(child
), (POINT
*)&rc
, 2);
1100 // apparently DrawThemeBackground() modifies the rect passed to it and if we
1101 // don't do these adjustments, there are some drawing artifacts which are
1102 // only visible with some non default themes; so modify the rect here using
1103 // the magic numbers below so that it still paints the correct area
1110 wxUxThemeEngine::Get()->DrawThemeBackground
1123 WXHBRUSH
wxNotebook::QueryBgBitmap()
1125 wxRect r
= GetPageSize();
1129 WindowHDC
hDC(GetHwnd());
1130 MemoryHDC
hDCMem(hDC
);
1131 CompatibleBitmap
hBmp(hDC
, r
.x
+ r
.width
, r
.y
+ r
.height
);
1133 SelectInHDC
selectBmp(hDCMem
, hBmp
);
1135 if ( !DoDrawBackground((WXHDC
)(HDC
)hDCMem
) )
1138 return (WXHBRUSH
)::CreatePatternBrush(hBmp
);
1141 void wxNotebook::UpdateBgBrush()
1143 if ( m_hbrBackground
)
1144 ::DeleteObject((HBRUSH
)m_hbrBackground
);
1146 if ( !m_hasBgCol
&& wxUxThemeEngine::GetIfActive() )
1148 m_hbrBackground
= QueryBgBitmap();
1150 else // no themes or we've got user-defined solid colour
1152 m_hbrBackground
= NULL
;
1156 WXHBRUSH
wxNotebook::MSWGetBgBrushForChild(WXHDC hDC
, WXHWND hWnd
)
1158 if ( m_hbrBackground
)
1160 // before drawing with the background brush, we need to position it
1163 ::GetWindowRect((HWND
)hWnd
, &rc
);
1165 ::MapWindowPoints(NULL
, GetHwnd(), (POINT
*)&rc
, 1);
1167 if ( !::SetBrushOrgEx((HDC
)hDC
, -rc
.left
, -rc
.top
, NULL
) )
1169 wxLogLastError(_T("SetBrushOrgEx(notebook bg brush)"));
1172 return m_hbrBackground
;
1175 return wxNotebookBase::MSWGetBgBrushForChild(hDC
, hWnd
);
1178 bool wxNotebook::MSWPrintChild(WXHDC hDC
, wxWindow
*child
)
1180 // solid background colour overrides themed background drawing
1181 if ( !UseBgCol() && DoDrawBackground(hDC
, child
) )
1184 // If we're using a solid colour (for example if we've switched off
1185 // theming for this notebook), paint it
1188 wxRect r
= GetPageSize();
1193 wxCopyRectToRECT(r
, rc
);
1195 // map rect to the coords of the window we're drawing in
1197 ::MapWindowPoints(GetHwnd(), GetHwndOf(child
), (POINT
*)&rc
, 2);
1199 wxBrush
brush(GetBackgroundColour());
1200 HBRUSH hbr
= GetHbrushOf(brush
);
1202 ::FillRect((HDC
) hDC
, &rc
, hbr
);
1207 return wxNotebookBase::MSWPrintChild(hDC
, child
);
1210 #endif // wxUSE_UXTHEME
1212 // Windows only: attempts to get colour for UX theme page background
1213 wxColour
wxNotebook::GetThemeBackgroundColour() const
1216 if (wxUxThemeEngine::Get())
1218 wxUxThemeHandle
hTheme((wxNotebook
*) this, L
"TAB");
1221 // This is total guesswork.
1222 // See PlatformSDK\Include\Tmschema.h for values
1223 COLORREF themeColor
;
1224 wxUxThemeEngine::Get()->GetThemeColor(
1228 3821 /* FILLCOLORHINT */,
1232 [DS] Workaround for WindowBlinds:
1233 Some themes return a near black theme color using FILLCOLORHINT,
1234 this makes notebook pages have an ugly black background and makes
1235 text (usually black) unreadable. Retry again with FILLCOLOR.
1237 This workaround potentially breaks appearance of some themes,
1238 but in practice it already fixes some themes.
1240 if (themeColor
== 1)
1242 wxUxThemeEngine::Get()->GetThemeColor(
1246 3802 /* FILLCOLOR */,
1250 return wxRGBToColour(themeColor
);
1253 #endif // wxUSE_UXTHEME
1255 return GetBackgroundColour();
1258 // ----------------------------------------------------------------------------
1259 // wxNotebook base class virtuals
1260 // ----------------------------------------------------------------------------
1262 #if wxUSE_CONSTRAINTS
1264 // override these 2 functions to do nothing: everything is done in OnSize
1266 void wxNotebook::SetConstraintSizes(bool WXUNUSED(recurse
))
1268 // don't set the sizes of the pages - their correct size is not yet known
1269 wxControl::SetConstraintSizes(false);
1272 bool wxNotebook::DoPhase(int WXUNUSED(nPhase
))
1277 #endif // wxUSE_CONSTRAINTS
1279 // ----------------------------------------------------------------------------
1280 // wxNotebook Windows message handlers
1281 // ----------------------------------------------------------------------------
1283 bool wxNotebook::MSWOnScroll(int orientation
, WXWORD nSBCode
,
1284 WXWORD pos
, WXHWND control
)
1286 // don't generate EVT_SCROLLWIN events for the WM_SCROLLs coming from the
1291 return wxNotebookBase::MSWOnScroll(orientation
, nSBCode
, pos
, control
);
1294 bool wxNotebook::MSWOnNotify(int idCtrl
, WXLPARAM lParam
, WXLPARAM
* result
)
1296 wxNotebookEvent
event(wxEVT_NULL
, m_windowId
);
1298 NMHDR
* hdr
= (NMHDR
*)lParam
;
1299 switch ( hdr
->code
) {
1301 event
.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
);
1304 case TCN_SELCHANGING
:
1305 event
.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
);
1309 return wxControl::MSWOnNotify(idCtrl
, lParam
, result
);
1312 event
.SetSelection(TabCtrl_GetCurSel(GetHwnd()));
1313 event
.SetOldSelection(m_nSelection
);
1314 event
.SetEventObject(this);
1315 event
.SetInt(idCtrl
);
1317 bool processed
= GetEventHandler()->ProcessEvent(event
);
1318 *result
= !event
.IsAllowed();
1322 #endif // wxUSE_NOTEBOOK