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 // For compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
23 #include "wx/string.h"
28 #include "wx/imaglist.h"
30 #include "wx/control.h"
31 #include "wx/notebook.h"
33 #include "wx/sysopt.h"
34 #include "wx/dcclient.h"
35 #include "wx/dcmemory.h"
37 #include "wx/msw/private.h"
43 #include "wx/msw/winundef.h"
46 #include "wx/msw/uxtheme.h"
49 // ----------------------------------------------------------------------------
51 // ----------------------------------------------------------------------------
53 // check that the page index is valid
54 #define IS_VALID_PAGE(nPage) ((nPage) < GetPageCount())
56 // you can set USE_NOTEBOOK_ANTIFLICKER to 0 for desktop Windows versions too
57 // to disable code whih results in flicker-less notebook redrawing at the
58 // expense of some extra GDI resource consumption
60 // notebooks are never resized under CE anyhow
61 #define USE_NOTEBOOK_ANTIFLICKER 0
63 #define USE_NOTEBOOK_ANTIFLICKER 1
66 // ----------------------------------------------------------------------------
68 // ----------------------------------------------------------------------------
70 // This is a work-around for missing defines in gcc-2.95 headers
72 #define TCS_RIGHT 0x0002
76 #define TCS_VERTICAL 0x0080
80 #define TCS_BOTTOM TCS_RIGHT
83 // ----------------------------------------------------------------------------
85 // ----------------------------------------------------------------------------
87 #if USE_NOTEBOOK_ANTIFLICKER
89 // the pointer to standard spin button wnd proc
90 static WXFARPROC gs_wndprocNotebookSpinBtn
= (WXFARPROC
)NULL
;
92 // the pointer to standard tab control wnd proc
93 static WXFARPROC gs_wndprocNotebook
= (WXFARPROC
)NULL
;
95 LRESULT APIENTRY _EXPORT
wxNotebookWndProc(HWND hwnd
,
100 #endif // USE_NOTEBOOK_ANTIFLICKER
102 // ----------------------------------------------------------------------------
104 // ----------------------------------------------------------------------------
106 #include <wx/listimpl.cpp>
108 WX_DEFINE_LIST( wxNotebookPageInfoList
) ;
110 DEFINE_EVENT_TYPE(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
)
111 DEFINE_EVENT_TYPE(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
)
113 BEGIN_EVENT_TABLE(wxNotebook
, wxControl
)
114 EVT_NOTEBOOK_PAGE_CHANGED(-1, wxNotebook::OnSelChange
)
115 EVT_SIZE(wxNotebook::OnSize
)
116 EVT_NAVIGATION_KEY(wxNotebook::OnNavigationKey
)
118 #if USE_NOTEBOOK_ANTIFLICKER
119 EVT_ERASE_BACKGROUND(wxNotebook::OnEraseBackground
)
120 EVT_PAINT(wxNotebook::OnPaint
)
121 #endif // USE_NOTEBOOK_ANTIFLICKER
124 #if wxUSE_EXTENDED_RTTI
125 WX_DEFINE_FLAGS( wxNotebookStyle
)
127 wxBEGIN_FLAGS( wxNotebookStyle
)
128 // new style border flags, we put them first to
129 // use them for streaming out
130 wxFLAGS_MEMBER(wxBORDER_SIMPLE
)
131 wxFLAGS_MEMBER(wxBORDER_SUNKEN
)
132 wxFLAGS_MEMBER(wxBORDER_DOUBLE
)
133 wxFLAGS_MEMBER(wxBORDER_RAISED
)
134 wxFLAGS_MEMBER(wxBORDER_STATIC
)
135 wxFLAGS_MEMBER(wxBORDER_NONE
)
137 // old style border flags
138 wxFLAGS_MEMBER(wxSIMPLE_BORDER
)
139 wxFLAGS_MEMBER(wxSUNKEN_BORDER
)
140 wxFLAGS_MEMBER(wxDOUBLE_BORDER
)
141 wxFLAGS_MEMBER(wxRAISED_BORDER
)
142 wxFLAGS_MEMBER(wxSTATIC_BORDER
)
143 wxFLAGS_MEMBER(wxBORDER
)
145 // standard window styles
146 wxFLAGS_MEMBER(wxTAB_TRAVERSAL
)
147 wxFLAGS_MEMBER(wxCLIP_CHILDREN
)
148 wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW
)
149 wxFLAGS_MEMBER(wxWANTS_CHARS
)
150 wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE
)
151 wxFLAGS_MEMBER(wxALWAYS_SHOW_SB
)
152 wxFLAGS_MEMBER(wxVSCROLL
)
153 wxFLAGS_MEMBER(wxHSCROLL
)
155 wxFLAGS_MEMBER(wxNB_FIXEDWIDTH
)
156 wxFLAGS_MEMBER(wxNB_LEFT
)
157 wxFLAGS_MEMBER(wxNB_RIGHT
)
158 wxFLAGS_MEMBER(wxNB_BOTTOM
)
159 wxFLAGS_MEMBER(wxNB_NOPAGETHEME
)
160 wxFLAGS_MEMBER(wxNB_FLAT
)
162 wxEND_FLAGS( wxNotebookStyle
)
164 IMPLEMENT_DYNAMIC_CLASS_XTI(wxNotebook
, wxControl
,"wx/notebook.h")
165 IMPLEMENT_DYNAMIC_CLASS_XTI(wxNotebookPageInfo
, wxObject
, "wx/notebook.h" )
167 wxCOLLECTION_TYPE_INFO( wxNotebookPageInfo
* , wxNotebookPageInfoList
) ;
169 template<> void wxCollectionToVariantArray( wxNotebookPageInfoList
const &theList
, wxxVariantArray
&value
)
171 wxListCollectionToVariantArray
<wxNotebookPageInfoList::compatibility_iterator
>( theList
, value
) ;
174 wxBEGIN_PROPERTIES_TABLE(wxNotebook
)
175 wxEVENT_PROPERTY( PageChanging
, wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
, wxNotebookEvent
)
176 wxEVENT_PROPERTY( PageChanged
, wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
, wxNotebookEvent
)
178 wxPROPERTY_COLLECTION( PageInfos
, wxNotebookPageInfoList
, wxNotebookPageInfo
* , AddPageInfo
, GetPageInfos
, 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
179 wxPROPERTY_FLAGS( WindowStyle
, wxNotebookStyle
, long , SetWindowStyleFlag
, GetWindowStyleFlag
, EMPTY_MACROVALUE
, 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
180 wxEND_PROPERTIES_TABLE()
182 wxBEGIN_HANDLERS_TABLE(wxNotebook
)
183 wxEND_HANDLERS_TABLE()
185 wxCONSTRUCTOR_5( wxNotebook
, wxWindow
* , Parent
, wxWindowID
, Id
, wxPoint
, Position
, wxSize
, Size
, long , WindowStyle
)
188 wxBEGIN_PROPERTIES_TABLE(wxNotebookPageInfo
)
189 wxREADONLY_PROPERTY( Page
, wxNotebookPage
* , GetPage
, EMPTY_MACROVALUE
, 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
190 wxREADONLY_PROPERTY( Text
, wxString
, GetText
, wxString() , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
191 wxREADONLY_PROPERTY( Selected
, bool , GetSelected
, false, 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
192 wxREADONLY_PROPERTY( ImageId
, int , GetImageId
, -1 , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
193 wxEND_PROPERTIES_TABLE()
195 wxBEGIN_HANDLERS_TABLE(wxNotebookPageInfo
)
196 wxEND_HANDLERS_TABLE()
198 wxCONSTRUCTOR_4( wxNotebookPageInfo
, wxNotebookPage
* , Page
, wxString
, Text
, bool , Selected
, int , ImageId
)
201 IMPLEMENT_DYNAMIC_CLASS(wxNotebook
, wxControl
)
202 IMPLEMENT_DYNAMIC_CLASS(wxNotebookPageInfo
, wxObject
)
204 IMPLEMENT_DYNAMIC_CLASS(wxNotebookEvent
, wxNotifyEvent
)
206 // ============================================================================
208 // ============================================================================
210 // ----------------------------------------------------------------------------
211 // wxNotebook construction
212 // ----------------------------------------------------------------------------
214 const wxNotebookPageInfoList
& wxNotebook::GetPageInfos() const
216 wxNotebookPageInfoList
* list
= const_cast< wxNotebookPageInfoList
* >( &m_pageInfos
) ;
217 WX_CLEAR_LIST( wxNotebookPageInfoList
, *list
) ;
218 for( size_t i
= 0 ; i
< GetPageCount() ; ++i
)
220 wxNotebookPageInfo
*info
= new wxNotebookPageInfo() ;
221 info
->Create( const_cast<wxNotebook
*>(this)->GetPage(i
) , GetPageText(i
) , GetSelection() == int(i
) , GetPageImage(i
) ) ;
222 list
->Append( info
) ;
227 // common part of all ctors
228 void wxNotebook::Init()
234 m_hbrBackground
= NULL
;
235 #endif // wxUSE_UXTHEME
237 #if USE_NOTEBOOK_ANTIFLICKER
238 m_hasSubclassedUpdown
= false;
239 #endif // USE_NOTEBOOK_ANTIFLICKER
242 // default for dynamic class
243 wxNotebook::wxNotebook()
248 // the same arguments as for wxControl
249 wxNotebook::wxNotebook(wxWindow
*parent
,
254 const wxString
& name
)
258 Create(parent
, id
, pos
, size
, style
, name
);
262 bool wxNotebook::Create(wxWindow
*parent
,
267 const wxString
& name
)
270 // Not sure why, but without this style, there is no border
271 // around the notebook tabs.
272 if (style
& wxNB_FLAT
)
273 style
|= wxBORDER_SUNKEN
;
276 // comctl32.dll 6.0 doesn't support non-top tabs with visual styles (the
277 // control is simply not rendered correctly), so disable them in this case
278 const int verComCtl32
= wxApp::GetComCtl32Version();
279 if ( verComCtl32
== 600 )
281 // check if we use themes at all -- if we don't, we're still ok
283 if ( wxUxThemeEngine::GetIfActive() )
286 style
&= ~(wxNB_BOTTOM
| wxNB_LEFT
| wxNB_RIGHT
);
290 LPCTSTR className
= WC_TABCONTROL
;
292 #if USE_NOTEBOOK_ANTIFLICKER
293 // SysTabCtl32 class has natively CS_HREDRAW and CS_VREDRAW enabled and it
294 // causes horrible flicker when resizing notebook, so get rid of it by
295 // using a class without these styles (but otherwise identical to it)
296 if ( !HasFlag(wxFULL_REPAINT_ON_RESIZE
) )
298 static ClassRegistrar s_clsNotebook
;
299 if ( !s_clsNotebook
.IsInitialized() )
301 // get a copy of standard class and modify it
304 if ( ::GetClassInfo(NULL
, WC_TABCONTROL
, &wc
) )
307 wx_reinterpret_cast(WXFARPROC
, wc
.lpfnWndProc
);
308 wc
.lpszClassName
= wxT("_wx_SysTabCtl32");
309 wc
.style
&= ~(CS_HREDRAW
| CS_VREDRAW
);
310 wc
.hInstance
= wxGetInstance();
311 wc
.lpfnWndProc
= wxNotebookWndProc
;
312 s_clsNotebook
.Register(wc
);
316 wxLogLastError(_T("GetClassInfoEx(SysTabCtl32)"));
320 // use our custom class if available but fall back to the standard
321 // notebook if we failed to register it
322 if ( s_clsNotebook
.IsRegistered() )
324 // it's ok to use c_str() here as the static s_clsNotebook object
325 // has sufficiently long lifetime
326 className
= s_clsNotebook
.GetName().c_str();
329 #endif // USE_NOTEBOOK_ANTIFLICKER
331 if ( !CreateControl(parent
, id
, pos
, size
, style
| wxTAB_TRAVERSAL
,
332 wxDefaultValidator
, name
) )
335 if ( !MSWCreateControl(className
, wxEmptyString
, pos
, size
) )
339 if ( HasFlag(wxNB_NOPAGETHEME
) ||
340 wxSystemOptions::IsFalse(wxT("msw.notebook.themed-background")) )
342 SetBackgroundColour(GetThemeBackgroundColour());
344 else // use themed background by default
346 // create backing store
349 #endif // wxUSE_UXTHEME
351 // Undocumented hack to get flat notebook style
352 // In fact, we should probably only do this in some
353 // curcumstances, i.e. if we know we will have a border
354 // at the bottom (the tab control doesn't draw it itself)
355 #if defined(__POCKETPC__) || defined(__SMARTPHONE__)
356 if (HasFlag(wxNB_FLAT
))
358 SendMessage(GetHwnd(), CCM_SETVERSION
, COMCTL32_VERSION
, 0);
360 SetBackgroundColour(*wxWHITE
);
366 WXDWORD
wxNotebook::MSWGetStyle(long style
, WXDWORD
*exstyle
) const
368 WXDWORD tabStyle
= wxControl::MSWGetStyle(style
, exstyle
);
370 tabStyle
|= WS_TABSTOP
| TCS_TABS
;
372 if ( style
& wxNB_MULTILINE
)
373 tabStyle
|= TCS_MULTILINE
;
374 if ( style
& wxNB_FIXEDWIDTH
)
375 tabStyle
|= TCS_FIXEDWIDTH
;
377 if ( style
& wxNB_BOTTOM
)
378 tabStyle
|= TCS_RIGHT
;
379 else if ( style
& wxNB_LEFT
)
380 tabStyle
|= TCS_VERTICAL
;
381 else if ( style
& wxNB_RIGHT
)
382 tabStyle
|= TCS_VERTICAL
| TCS_RIGHT
;
387 // note that we never want to have the default WS_EX_CLIENTEDGE style
388 // as it looks too ugly for the notebooks
395 wxNotebook::~wxNotebook()
398 if ( m_hbrBackground
)
399 ::DeleteObject((HBRUSH
)m_hbrBackground
);
400 #endif // wxUSE_UXTHEME
403 // ----------------------------------------------------------------------------
404 // wxNotebook accessors
405 // ----------------------------------------------------------------------------
407 size_t wxNotebook::GetPageCount() const
410 wxASSERT( (int)m_pages
.Count() == TabCtrl_GetItemCount(GetHwnd()) );
412 return m_pages
.Count();
415 int wxNotebook::GetRowCount() const
417 return TabCtrl_GetRowCount(GetHwnd());
420 int wxNotebook::SetSelection(size_t nPage
)
422 wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxNOT_FOUND
, wxT("notebook page out of range") );
424 if ( int(nPage
) != m_nSelection
)
426 wxNotebookEvent
event(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
, m_windowId
);
427 event
.SetSelection(nPage
);
428 event
.SetOldSelection(m_nSelection
);
429 event
.SetEventObject(this);
430 if ( !GetEventHandler()->ProcessEvent(event
) || event
.IsAllowed() )
432 // program allows the page change
433 event
.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
);
434 (void)GetEventHandler()->ProcessEvent(event
);
436 TabCtrl_SetCurSel(GetHwnd(), nPage
);
443 bool wxNotebook::SetPageText(size_t nPage
, const wxString
& strText
)
445 wxCHECK_MSG( IS_VALID_PAGE(nPage
), false, wxT("notebook page out of range") );
448 tcItem
.mask
= TCIF_TEXT
;
449 tcItem
.pszText
= (wxChar
*)strText
.c_str();
451 return TabCtrl_SetItem(GetHwnd(), nPage
, &tcItem
) != 0;
454 wxString
wxNotebook::GetPageText(size_t nPage
) const
456 wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxEmptyString
, wxT("notebook page out of range") );
460 tcItem
.mask
= TCIF_TEXT
;
461 tcItem
.pszText
= buf
;
462 tcItem
.cchTextMax
= WXSIZEOF(buf
);
465 if ( TabCtrl_GetItem(GetHwnd(), nPage
, &tcItem
) )
466 str
= tcItem
.pszText
;
471 int wxNotebook::GetPageImage(size_t nPage
) const
473 wxCHECK_MSG( IS_VALID_PAGE(nPage
), -1, wxT("notebook page out of range") );
476 tcItem
.mask
= TCIF_IMAGE
;
478 return TabCtrl_GetItem(GetHwnd(), nPage
, &tcItem
) ? tcItem
.iImage
: -1;
481 bool wxNotebook::SetPageImage(size_t nPage
, int nImage
)
483 wxCHECK_MSG( IS_VALID_PAGE(nPage
), false, wxT("notebook page out of range") );
486 tcItem
.mask
= TCIF_IMAGE
;
487 tcItem
.iImage
= nImage
;
489 return TabCtrl_SetItem(GetHwnd(), nPage
, &tcItem
) != 0;
492 void wxNotebook::SetImageList(wxImageList
* imageList
)
494 wxNotebookBase::SetImageList(imageList
);
498 TabCtrl_SetImageList(GetHwnd(), (HIMAGELIST
)imageList
->GetHIMAGELIST());
502 // ----------------------------------------------------------------------------
503 // wxNotebook size settings
504 // ----------------------------------------------------------------------------
506 wxRect
wxNotebook::GetPageSize() const
511 ::GetClientRect(GetHwnd(), &rc
);
513 // This check is to work around a bug in TabCtrl_AdjustRect which will
514 // cause a crash on win2k or on XP with themes disabled if either
515 // wxNB_MULTILINE is used or tabs are placed on a side, if the rectangle
518 // The value of 20 is chosen arbitrarily but seems to work
519 if ( rc
.right
> 20 && rc
.bottom
> 20 )
521 TabCtrl_AdjustRect(GetHwnd(), false, &rc
);
523 wxCopyRECTToRect(rc
, r
);
529 void wxNotebook::SetPageSize(const wxSize
& size
)
531 // transform the page size into the notebook size
538 TabCtrl_AdjustRect(GetHwnd(), true, &rc
);
541 SetSize(rc
.right
- rc
.left
, rc
.bottom
- rc
.top
);
544 void wxNotebook::SetPadding(const wxSize
& padding
)
546 TabCtrl_SetPadding(GetHwnd(), padding
.x
, padding
.y
);
549 // Windows-only at present. Also, you must use the wxNB_FIXEDWIDTH
551 void wxNotebook::SetTabSize(const wxSize
& sz
)
553 ::SendMessage(GetHwnd(), TCM_SETITEMSIZE
, 0, MAKELPARAM(sz
.x
, sz
.y
));
556 wxSize
wxNotebook::CalcSizeFromPage(const wxSize
& sizePage
) const
558 wxSize sizeTotal
= sizePage
;
560 // We need to make getting tab size part of the wxWidgets API.
562 if (GetPageCount() > 0)
565 TabCtrl_GetItemRect((HWND
) GetHWND(), 0, & rect
);
566 tabSize
.x
= rect
.right
- rect
.left
;
567 tabSize
.y
= rect
.bottom
- rect
.top
;
569 if ( HasFlag(wxNB_LEFT
) || HasFlag(wxNB_RIGHT
) )
571 sizeTotal
.x
+= tabSize
.x
+ 7;
577 sizeTotal
.y
+= tabSize
.y
+ 7;
583 void wxNotebook::AdjustPageSize(wxNotebookPage
*page
)
585 wxCHECK_RET( page
, _T("NULL page in wxNotebook::AdjustPageSize") );
587 const wxRect r
= GetPageSize();
594 // ----------------------------------------------------------------------------
595 // wxNotebook operations
596 // ----------------------------------------------------------------------------
598 // remove one page from the notebook, without deleting
599 wxNotebookPage
*wxNotebook::DoRemovePage(size_t nPage
)
601 wxNotebookPage
*pageRemoved
= wxNotebookBase::DoRemovePage(nPage
);
605 TabCtrl_DeleteItem(GetHwnd(), nPage
);
607 if ( m_pages
.IsEmpty() )
609 // no selection any more, the notebook becamse empty
612 else // notebook still not empty
614 int selNew
= TabCtrl_GetCurSel(GetHwnd());
617 // No selection change, just refresh the current selection.
618 // Because it could be that the slection index changed
619 // we need to update it.
620 // Note: this does not mean the selection it self changed.
621 m_nSelection
= selNew
;
622 m_pages
[m_nSelection
]->Refresh();
624 else if (int(nPage
) == m_nSelection
)
626 // The selection was deleted.
628 // Determine new selection.
629 if (m_nSelection
== int(GetPageCount()))
630 selNew
= m_nSelection
- 1;
632 selNew
= m_nSelection
;
634 // m_nSelection must be always valid so reset it before calling
637 SetSelection(selNew
);
641 wxFAIL
; // Windows did not behave ok.
649 bool wxNotebook::DeleteAllPages()
651 size_t nPageCount
= GetPageCount();
653 for ( nPage
= 0; nPage
< nPageCount
; nPage
++ )
654 delete m_pages
[nPage
];
658 TabCtrl_DeleteAllItems(GetHwnd());
662 InvalidateBestSize();
666 // same as AddPage() but does it at given position
667 bool wxNotebook::InsertPage(size_t nPage
,
668 wxNotebookPage
*pPage
,
669 const wxString
& strText
,
673 wxCHECK_MSG( pPage
!= NULL
, false, _T("NULL page in wxNotebook::InsertPage") );
674 wxCHECK_MSG( IS_VALID_PAGE(nPage
) || nPage
== GetPageCount(), false,
675 _T("invalid index in wxNotebook::InsertPage") );
677 wxASSERT_MSG( pPage
->GetParent() == this,
678 _T("notebook pages must have notebook as parent") );
680 // add a new tab to the control
681 // ----------------------------
683 // init all fields to 0
685 wxZeroMemory(tcItem
);
687 // set the image, if any
690 tcItem
.mask
|= TCIF_IMAGE
;
691 tcItem
.iImage
= imageId
;
695 if ( !strText
.empty() )
697 tcItem
.mask
|= TCIF_TEXT
;
698 tcItem
.pszText
= (wxChar
*)strText
.c_str(); // const_cast
701 // hide the page: unless it is selected, it shouldn't be shown (and if it
702 // is selected it will be shown later)
703 HWND hwnd
= GetWinHwnd(pPage
);
704 SetWindowLong(hwnd
, GWL_STYLE
, GetWindowLong(hwnd
, GWL_STYLE
) & ~WS_VISIBLE
);
706 // this updates internal flag too -- otherwise it would get out of sync
707 // with the real state
711 // fit the notebook page to the tab control's display area: this should be
712 // done before adding it to the notebook or TabCtrl_InsertItem() will
713 // change the notebooks size itself!
714 AdjustPageSize(pPage
);
716 // finally do insert it
717 if ( TabCtrl_InsertItem(GetHwnd(), nPage
, &tcItem
) == -1 )
719 wxLogError(wxT("Can't create the notebook page '%s'."), strText
.c_str());
724 // succeeded: save the pointer to the page
725 m_pages
.Insert(pPage
, nPage
);
727 // we may need to adjust the size again if the notebook size changed:
728 // normally this only happens for the first page we add (the tabs which
729 // hadn't been there before are now shown) but for a multiline notebook it
730 // can happen for any page at all as a new row could have been started
731 if ( m_pages
.GetCount() == 1 || HasFlag(wxNB_MULTILINE
) )
733 AdjustPageSize(pPage
);
736 // now deal with the selection
737 // ---------------------------
739 // if the inserted page is before the selected one, we must update the
740 // index of the selected page
741 if ( int(nPage
) <= m_nSelection
)
743 // one extra page added
747 // some page should be selected: either this one or the first one if there
748 // is still no selection
752 else if ( m_nSelection
== -1 )
756 SetSelection(selNew
);
758 InvalidateBestSize();
763 int wxNotebook::HitTest(const wxPoint
& pt
, long *flags
) const
765 TC_HITTESTINFO hitTestInfo
;
766 hitTestInfo
.pt
.x
= pt
.x
;
767 hitTestInfo
.pt
.y
= pt
.y
;
768 int item
= TabCtrl_HitTest(GetHwnd(), &hitTestInfo
);
774 if ((hitTestInfo
.flags
& TCHT_NOWHERE
) == TCHT_NOWHERE
)
775 *flags
|= wxNB_HITTEST_NOWHERE
;
776 if ((hitTestInfo
.flags
& TCHT_ONITEM
) == TCHT_ONITEM
)
777 *flags
|= wxNB_HITTEST_ONITEM
;
778 if ((hitTestInfo
.flags
& TCHT_ONITEMICON
) == TCHT_ONITEMICON
)
779 *flags
|= wxNB_HITTEST_ONICON
;
780 if ((hitTestInfo
.flags
& TCHT_ONITEMLABEL
) == TCHT_ONITEMLABEL
)
781 *flags
|= wxNB_HITTEST_ONLABEL
;
787 // ----------------------------------------------------------------------------
788 // flicker-less notebook redraw
789 // ----------------------------------------------------------------------------
791 #if USE_NOTEBOOK_ANTIFLICKER
793 // wnd proc for the spin button
794 LRESULT APIENTRY _EXPORT
wxNotebookSpinBtnWndProc(HWND hwnd
,
799 if ( message
== WM_ERASEBKGND
)
802 return ::CallWindowProc(CASTWNDPROC gs_wndprocNotebookSpinBtn
,
803 hwnd
, message
, wParam
, lParam
);
806 LRESULT APIENTRY _EXPORT
wxNotebookWndProc(HWND hwnd
,
811 return ::CallWindowProc(CASTWNDPROC gs_wndprocNotebook
,
812 hwnd
, message
, wParam
, lParam
);
815 void wxNotebook::OnEraseBackground(wxEraseEvent
& WXUNUSED(event
))
820 void wxNotebook::OnPaint(wxPaintEvent
& WXUNUSED(event
))
825 ::GetClientRect(GetHwnd(), &rc
);
826 wxBitmap
bmp(rc
.right
, rc
.bottom
);
827 memdc
.SelectObject(bmp
);
829 // if there is no special brush just use the solid background colour
830 HBRUSH hbr
= (HBRUSH
)m_hbrBackground
;
834 brush
= wxBrush(GetBackgroundColour());
835 hbr
= GetHbrushOf(brush
);
838 ::FillRect(GetHdcOf(memdc
), &rc
, hbr
);
840 MSWDefWindowProc(WM_PAINT
, (WPARAM
)memdc
.GetHDC(), 0);
842 dc
.Blit(0, 0, rc
.right
, rc
.bottom
, &memdc
, 0, 0);
845 #endif // USE_NOTEBOOK_ANTIFLICKER
847 // ----------------------------------------------------------------------------
848 // wxNotebook callbacks
849 // ----------------------------------------------------------------------------
851 void wxNotebook::OnSize(wxSizeEvent
& event
)
853 if ( GetPageCount() == 0 )
855 // Prevents droppings on resize, but does cause some flicker
856 // when there are no pages.
864 // Without this, we can sometimes get droppings at the edges
865 // of a notebook, for example a notebook in a splitter window.
866 // This needs to be reconciled with the RefreshRect calls
867 // at the end of this function, which weren't enough to prevent
870 wxSize sz
= GetClientSize();
872 // Refresh right side
873 wxRect
rect(sz
.x
-4, 0, 4, sz
.y
);
876 // Refresh bottom side
877 rect
= wxRect(0, sz
.y
-4, sz
.x
, 4);
881 rect
= wxRect(0, 0, 4, sz
.y
);
884 #endif // !__WXWINCE__
886 // fit all the notebook pages to the tab control's display area
889 rc
.left
= rc
.top
= 0;
890 GetSize((int *)&rc
.right
, (int *)&rc
.bottom
);
892 // save the total size, we'll use it below
893 int widthNbook
= rc
.right
- rc
.left
,
894 heightNbook
= rc
.bottom
- rc
.top
;
896 // there seems to be a bug in the implementation of TabCtrl_AdjustRect(): it
897 // returns completely false values for multiline tab controls after the tabs
898 // are added but before getting the first WM_SIZE (off by ~50 pixels, see
900 // http://sf.net/tracker/index.php?func=detail&aid=645323&group_id=9863&atid=109863
902 // and the only work around I could find was this ugly hack... without it
903 // simply toggling the "multiline" checkbox in the notebook sample resulted
904 // in a noticeable page displacement
905 if ( HasFlag(wxNB_MULTILINE
) )
907 // avoid an infinite recursion: we get another notification too!
908 static bool s_isInOnSize
= false;
913 SendMessage(GetHwnd(), WM_SIZE
, SIZE_RESTORED
,
914 MAKELPARAM(rc
.right
, rc
.bottom
));
915 s_isInOnSize
= false;
920 // background bitmap size has changed, update the brush using it too
922 #endif // wxUSE_UXTHEME
924 TabCtrl_AdjustRect(GetHwnd(), false, &rc
);
926 int width
= rc
.right
- rc
.left
,
927 height
= rc
.bottom
- rc
.top
;
928 size_t nCount
= m_pages
.Count();
929 for ( size_t nPage
= 0; nPage
< nCount
; nPage
++ ) {
930 wxNotebookPage
*pPage
= m_pages
[nPage
];
931 pPage
->SetSize(rc
.left
, rc
.top
, width
, height
);
935 // unless we had already repainted everything, we now need to refresh
936 if ( !HasFlag(wxFULL_REPAINT_ON_RESIZE
) )
938 // invalidate areas not covered by pages
939 RefreshRect(wxRect(0, 0, widthNbook
, rc
.top
), false);
940 RefreshRect(wxRect(0, rc
.top
, rc
.left
, height
), false);
941 RefreshRect(wxRect(0, rc
.bottom
, widthNbook
, heightNbook
- rc
.bottom
),
943 RefreshRect(wxRect(rc
.right
, rc
.top
, widthNbook
- rc
.right
, height
),
947 #if USE_NOTEBOOK_ANTIFLICKER
948 // subclass the spin control used by the notebook to scroll pages to
949 // prevent it from flickering on resize
950 if ( !m_hasSubclassedUpdown
)
952 // iterate over all child windows to find spin button
953 for ( HWND child
= ::GetWindow(GetHwnd(), GW_CHILD
);
955 child
= ::GetWindow(child
, GW_HWNDNEXT
) )
957 wxWindow
*childWindow
= wxFindWinFromHandle((WXHWND
)child
);
959 // see if it exists, if no wxWindow found then assume it's the spin
963 // subclass the spin button to override WM_ERASEBKGND
964 if ( !gs_wndprocNotebookSpinBtn
)
965 gs_wndprocNotebookSpinBtn
= (WXFARPROC
)wxGetWindowProc(child
);
967 wxSetWindowProc(child
, wxNotebookSpinBtnWndProc
);
968 m_hasSubclassedUpdown
= true;
973 #endif // USE_NOTEBOOK_ANTIFLICKER
978 void wxNotebook::OnSelChange(wxNotebookEvent
& event
)
980 // is it our tab control?
981 if ( event
.GetEventObject() == this )
983 int sel
= event
.GetOldSelection();
985 m_pages
[sel
]->Show(false);
987 sel
= event
.GetSelection();
990 wxNotebookPage
*pPage
= m_pages
[sel
];
994 // Changing the page should give the focus to it but, as per bug report
995 // http://sf.net/tracker/index.php?func=detail&aid=1150659&group_id=9863&atid=109863,
996 // we should not set the focus to it directly since it erroneously
997 // selects radio buttons and breaks keyboard handling for a notebook's
998 // scroll buttons. So give focus to the notebook and not the page.
1000 // but don't do this is the notebook is hidden
1001 if ( ::IsWindowVisible(GetHwnd()) )
1007 // we want to give others a chance to process this message as well
1011 bool wxNotebook::MSWTranslateMessage(WXMSG
*wxmsg
)
1013 const MSG
* const msg
= (MSG
*)wxmsg
;
1015 // intercept TAB, CTRL+TAB and CTRL+SHIFT+TAB for processing by wxNotebook.
1016 // TAB will be passed to the currently selected page, CTRL+TAB and
1017 // CTRL+SHIFT+TAB will be processed by the notebook itself. do not
1018 // intercept SHIFT+TAB. This goes to the parent of the notebook which will
1020 if ( msg
->message
== WM_KEYDOWN
&& msg
->wParam
== VK_TAB
&&
1021 msg
->hwnd
== GetHwnd() &&
1022 (wxIsCtrlDown() || !wxIsShiftDown()) )
1024 return MSWProcessMessage(wxmsg
);
1030 void wxNotebook::OnNavigationKey(wxNavigationKeyEvent
& event
)
1032 if ( event
.IsWindowChange() ) {
1034 AdvanceSelection(event
.GetDirection());
1037 // we get this event in 3 cases
1039 // a) one of our pages might have generated it because the user TABbed
1040 // out from it in which case we should propagate the event upwards and
1041 // our parent will take care of setting the focus to prev/next sibling
1045 // b) the parent panel wants to give the focus to us so that we
1046 // forward it to our selected page. We can't deal with this in
1047 // OnSetFocus() because we don't know which direction the focus came
1048 // from in this case and so can't choose between setting the focus to
1049 // first or last panel child
1053 // c) we ourselves (see MSWTranslateMessage) generated the event
1055 wxWindow
* const parent
= GetParent();
1057 // the wxObject* casts are required to avoid MinGW GCC 2.95.3 ICE
1058 const bool isFromParent
= event
.GetEventObject() == (wxObject
*) parent
;
1059 const bool isFromSelf
= event
.GetEventObject() == (wxObject
*) this;
1061 if ( isFromParent
|| isFromSelf
)
1063 // no, it doesn't come from child, case (b) or (c): forward to a
1064 // page but only if direction is backwards (TAB) or from ourselves,
1065 if ( m_nSelection
!= -1 &&
1066 (!event
.GetDirection() || isFromSelf
) )
1068 // so that the page knows that the event comes from it's parent
1069 // and is being propagated downwards
1070 event
.SetEventObject(this);
1072 wxWindow
*page
= m_pages
[m_nSelection
];
1073 if ( !page
->GetEventHandler()->ProcessEvent(event
) )
1077 //else: page manages focus inside it itself
1079 else // otherwise set the focus to the notebook itself
1086 // it comes from our child, case (a), pass to the parent, but only
1087 // if the direction is forwards. Otherwise set the focus to the
1088 // notebook itself. The notebook is always the 'first' control of a
1090 if ( !event
.GetDirection() )
1096 event
.SetCurrentFocus(this);
1097 parent
->GetEventHandler()->ProcessEvent(event
);
1105 bool wxNotebook::DoDrawBackground(WXHDC hDC
, wxWindow
*child
)
1107 wxUxThemeHandle
theme(child
? child
: this, L
"TAB");
1111 // get the notebook client rect (we're not interested in drawing tabs
1113 wxRect r
= GetPageSize();
1118 wxCopyRectToRECT(r
, rc
);
1120 // map rect to the coords of the window we're drawing in
1122 ::MapWindowPoints(GetHwnd(), GetHwndOf(child
), (POINT
*)&rc
, 2);
1124 // we have the content area (page size), but we need to draw all of the
1125 // background for it to be aligned correctly
1126 wxUxThemeEngine::Get()->GetThemeBackgroundExtent
1135 wxUxThemeEngine::Get()->DrawThemeBackground
1148 WXHBRUSH
wxNotebook::QueryBgBitmap()
1150 wxRect r
= GetPageSize();
1154 WindowHDC
hDC(GetHwnd());
1155 MemoryHDC
hDCMem(hDC
);
1156 CompatibleBitmap
hBmp(hDC
, r
.x
+ r
.width
, r
.y
+ r
.height
);
1158 SelectInHDC
selectBmp(hDCMem
, hBmp
);
1160 if ( !DoDrawBackground((WXHDC
)(HDC
)hDCMem
) )
1163 return (WXHBRUSH
)::CreatePatternBrush(hBmp
);
1166 void wxNotebook::UpdateBgBrush()
1168 if ( m_hbrBackground
)
1169 ::DeleteObject((HBRUSH
)m_hbrBackground
);
1171 if ( !m_hasBgCol
&& wxUxThemeEngine::GetIfActive() )
1173 m_hbrBackground
= QueryBgBitmap();
1175 else // no themes or we've got user-defined solid colour
1177 m_hbrBackground
= NULL
;
1181 WXHBRUSH
wxNotebook::MSWGetBgBrushForChild(WXHDC hDC
, WXHWND hWnd
)
1183 if ( m_hbrBackground
)
1185 // before drawing with the background brush, we need to position it
1188 ::GetWindowRect((HWND
)hWnd
, &rc
);
1190 ::MapWindowPoints(NULL
, GetHwnd(), (POINT
*)&rc
, 1);
1192 if ( !::SetBrushOrgEx((HDC
)hDC
, -rc
.left
, -rc
.top
, NULL
) )
1194 wxLogLastError(_T("SetBrushOrgEx(notebook bg brush)"));
1197 return m_hbrBackground
;
1200 return wxNotebookBase::MSWGetBgBrushForChild(hDC
, hWnd
);
1203 bool wxNotebook::MSWPrintChild(WXHDC hDC
, wxWindow
*child
)
1205 // solid background colour overrides themed background drawing
1206 if ( !UseBgCol() && DoDrawBackground(hDC
, child
) )
1209 // If we're using a solid colour (for example if we've switched off
1210 // theming for this notebook), paint it
1213 wxRect r
= GetPageSize();
1218 wxCopyRectToRECT(r
, rc
);
1220 // map rect to the coords of the window we're drawing in
1222 ::MapWindowPoints(GetHwnd(), GetHwndOf(child
), (POINT
*)&rc
, 2);
1224 wxBrush
brush(GetBackgroundColour());
1225 HBRUSH hbr
= GetHbrushOf(brush
);
1227 ::FillRect((HDC
) hDC
, &rc
, hbr
);
1232 return wxNotebookBase::MSWPrintChild(hDC
, child
);
1235 #endif // wxUSE_UXTHEME
1237 // Windows only: attempts to get colour for UX theme page background
1238 wxColour
wxNotebook::GetThemeBackgroundColour() const
1241 if (wxUxThemeEngine::Get())
1243 wxUxThemeHandle
hTheme((wxNotebook
*) this, L
"TAB");
1246 // This is total guesswork.
1247 // See PlatformSDK\Include\Tmschema.h for values
1248 COLORREF themeColor
;
1249 wxUxThemeEngine::Get()->GetThemeColor(
1253 3821 /* FILLCOLORHINT */,
1257 [DS] Workaround for WindowBlinds:
1258 Some themes return a near black theme color using FILLCOLORHINT,
1259 this makes notebook pages have an ugly black background and makes
1260 text (usually black) unreadable. Retry again with FILLCOLOR.
1262 This workaround potentially breaks appearance of some themes,
1263 but in practice it already fixes some themes.
1265 if (themeColor
== 1)
1267 wxUxThemeEngine::Get()->GetThemeColor(
1271 3802 /* FILLCOLOR */,
1275 return wxRGBToColour(themeColor
);
1278 #endif // wxUSE_UXTHEME
1280 return GetBackgroundColour();
1283 // ----------------------------------------------------------------------------
1284 // wxNotebook base class virtuals
1285 // ----------------------------------------------------------------------------
1287 #if wxUSE_CONSTRAINTS
1289 // override these 2 functions to do nothing: everything is done in OnSize
1291 void wxNotebook::SetConstraintSizes(bool WXUNUSED(recurse
))
1293 // don't set the sizes of the pages - their correct size is not yet known
1294 wxControl::SetConstraintSizes(false);
1297 bool wxNotebook::DoPhase(int WXUNUSED(nPhase
))
1302 #endif // wxUSE_CONSTRAINTS
1304 // ----------------------------------------------------------------------------
1305 // wxNotebook Windows message handlers
1306 // ----------------------------------------------------------------------------
1308 bool wxNotebook::MSWOnScroll(int orientation
, WXWORD nSBCode
,
1309 WXWORD pos
, WXHWND control
)
1311 // don't generate EVT_SCROLLWIN events for the WM_SCROLLs coming from the
1316 return wxNotebookBase::MSWOnScroll(orientation
, nSBCode
, pos
, control
);
1319 bool wxNotebook::MSWOnNotify(int idCtrl
, WXLPARAM lParam
, WXLPARAM
* result
)
1321 wxNotebookEvent
event(wxEVT_NULL
, m_windowId
);
1323 NMHDR
* hdr
= (NMHDR
*)lParam
;
1324 switch ( hdr
->code
) {
1326 event
.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
);
1329 case TCN_SELCHANGING
:
1330 event
.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
);
1334 return wxControl::MSWOnNotify(idCtrl
, lParam
, result
);
1337 event
.SetSelection(TabCtrl_GetCurSel(GetHwnd()));
1338 event
.SetOldSelection(m_nSelection
);
1339 event
.SetEventObject(this);
1340 event
.SetInt(idCtrl
);
1342 bool processed
= GetEventHandler()->ProcessEvent(event
);
1343 *result
= !event
.IsAllowed();
1347 #endif // wxUSE_NOTEBOOK