1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/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"
21 #include "wx/notebook.h"
24 #include "wx/msw/wrapcctl.h" // include <commctrl.h> "properly"
25 #include "wx/string.h"
30 #include "wx/dcclient.h"
31 #include "wx/dcmemory.h"
32 #include "wx/control.h"
36 #include "wx/imaglist.h"
37 #include "wx/sysopt.h"
39 #include "wx/msw/private.h"
40 #include "wx/msw/dc.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 static bool HasTroubleWithNonTopTabs()
108 const int verComCtl32
= wxApp::GetComCtl32Version();
110 // 600 is XP, 616 is Vista -- and both have a problem with tabs not on top
111 // (but don't just test for >= 600 as Microsoft might decide to fix it in
112 // later versions, who knows...)
113 return verComCtl32
>= 600 && verComCtl32
<= 616;
116 // ----------------------------------------------------------------------------
118 // ----------------------------------------------------------------------------
120 BEGIN_EVENT_TABLE(wxNotebook
, wxBookCtrlBase
)
121 EVT_SIZE(wxNotebook::OnSize
)
122 EVT_NAVIGATION_KEY(wxNotebook::OnNavigationKey
)
124 #if USE_NOTEBOOK_ANTIFLICKER
125 EVT_ERASE_BACKGROUND(wxNotebook::OnEraseBackground
)
126 EVT_PAINT(wxNotebook::OnPaint
)
127 #endif // USE_NOTEBOOK_ANTIFLICKER
130 // ============================================================================
132 // ============================================================================
134 // ----------------------------------------------------------------------------
135 // wxNotebook construction
136 // ----------------------------------------------------------------------------
138 // common part of all ctors
139 void wxNotebook::Init()
142 m_hbrBackground
= NULL
;
143 #endif // wxUSE_UXTHEME
145 #if USE_NOTEBOOK_ANTIFLICKER
146 m_hasSubclassedUpdown
= false;
147 m_doneUpdateHack
= false;
148 #endif // USE_NOTEBOOK_ANTIFLICKER
151 // default for dynamic class
152 wxNotebook::wxNotebook()
157 // the same arguments as for wxControl
158 wxNotebook::wxNotebook(wxWindow
*parent
,
163 const wxString
& name
)
167 Create(parent
, id
, pos
, size
, style
, name
);
171 bool wxNotebook::Create(wxWindow
*parent
,
176 const wxString
& name
)
178 if ( (style
& wxBK_ALIGN_MASK
) == wxBK_DEFAULT
)
180 #if defined(__POCKETPC__)
181 style
|= wxBK_BOTTOM
| wxNB_FLAT
;
188 // Not sure why, but without this style, there is no border
189 // around the notebook tabs.
190 if (style
& wxNB_FLAT
)
191 style
|= wxBORDER_SUNKEN
;
195 // ComCtl32 notebook tabs simply don't work unless they're on top if we
196 // have uxtheme, we can work around it later (after control creation), but
197 // if we have been compiled without uxtheme support, we have to clear those
199 if ( HasTroubleWithNonTopTabs() )
201 style
&= ~(wxBK_BOTTOM
| wxBK_LEFT
| wxBK_RIGHT
);
203 #endif //wxUSE_UXTHEME
205 #if defined(__WINE__) && wxUSE_UNICODE
206 LPCTSTR className
= L
"SysTabControl32";
208 LPCTSTR className
= WC_TABCONTROL
;
211 #if USE_NOTEBOOK_ANTIFLICKER
212 // SysTabCtl32 class has natively CS_HREDRAW and CS_VREDRAW enabled and it
213 // causes horrible flicker when resizing notebook, so get rid of it by
214 // using a class without these styles (but otherwise identical to it)
215 if ( !HasFlag(wxFULL_REPAINT_ON_RESIZE
) )
217 static ClassRegistrar s_clsNotebook
;
218 if ( !s_clsNotebook
.IsInitialized() )
220 // get a copy of standard class and modify it
223 if ( ::GetClassInfo(NULL
, WC_TABCONTROL
, &wc
) )
226 reinterpret_cast<WXFARPROC
>(wc
.lpfnWndProc
);
227 wc
.lpszClassName
= wxT("_wx_SysTabCtl32");
228 wc
.style
&= ~(CS_HREDRAW
| CS_VREDRAW
);
229 wc
.hInstance
= wxGetInstance();
230 wc
.lpfnWndProc
= wxNotebookWndProc
;
231 s_clsNotebook
.Register(wc
);
235 wxLogLastError(wxT("GetClassInfoEx(SysTabCtl32)"));
239 // use our custom class if available but fall back to the standard
240 // notebook if we failed to register it
241 if ( s_clsNotebook
.IsRegistered() )
243 // it's ok to use c_str() here as the static s_clsNotebook object
244 // has sufficiently long lifetime
245 className
= s_clsNotebook
.GetName().c_str();
248 #endif // USE_NOTEBOOK_ANTIFLICKER
250 if ( !CreateControl(parent
, id
, pos
, size
, style
| wxTAB_TRAVERSAL
,
251 wxDefaultValidator
, name
) )
254 if ( !MSWCreateControl(className
, wxEmptyString
, pos
, size
) )
258 if ( HasFlag(wxNB_NOPAGETHEME
) ||
259 wxSystemOptions::IsFalse(wxT("msw.notebook.themed-background")) )
261 SetBackgroundColour(GetThemeBackgroundColour());
263 else // use themed background by default
265 // create backing store
269 // comctl32.dll 6.0 doesn't support non-top tabs with visual styles (the
270 // control is simply not rendered correctly), so we disable themes
271 // if possible, otherwise we simply clear the styles.
272 if ( HasTroubleWithNonTopTabs() &&
273 (style
& (wxBK_BOTTOM
| wxBK_LEFT
| wxBK_RIGHT
)) )
275 // check if we use themes at all -- if we don't, we're still okay
276 if ( wxUxThemeEngine::GetIfActive() )
278 wxUxThemeEngine::GetIfActive()->SetWindowTheme(GetHwnd(), L
"", L
"");
280 // correct the background color for the new non-themed control
281 SetBackgroundColour(GetThemeBackgroundColour());
284 #endif // wxUSE_UXTHEME
286 // Undocumented hack to get flat notebook style
287 // In fact, we should probably only do this in some
288 // curcumstances, i.e. if we know we will have a border
289 // at the bottom (the tab control doesn't draw it itself)
290 #if defined(__POCKETPC__) || defined(__SMARTPHONE__)
291 if (HasFlag(wxNB_FLAT
))
293 SendMessage(GetHwnd(), CCM_SETVERSION
, COMCTL32_VERSION
, 0);
295 SetBackgroundColour(*wxWHITE
);
301 WXDWORD
wxNotebook::MSWGetStyle(long style
, WXDWORD
*exstyle
) const
303 WXDWORD tabStyle
= wxControl::MSWGetStyle(style
, exstyle
);
305 tabStyle
|= WS_TABSTOP
| TCS_TABS
;
307 if ( style
& wxNB_MULTILINE
)
308 tabStyle
|= TCS_MULTILINE
;
309 if ( style
& wxNB_FIXEDWIDTH
)
310 tabStyle
|= TCS_FIXEDWIDTH
;
312 if ( style
& wxBK_BOTTOM
)
313 tabStyle
|= TCS_RIGHT
;
314 else if ( style
& wxBK_LEFT
)
315 tabStyle
|= TCS_VERTICAL
;
316 else if ( style
& wxBK_RIGHT
)
317 tabStyle
|= TCS_VERTICAL
| TCS_RIGHT
;
322 wxNotebook::~wxNotebook()
325 if ( m_hbrBackground
)
326 ::DeleteObject((HBRUSH
)m_hbrBackground
);
327 #endif // wxUSE_UXTHEME
330 // ----------------------------------------------------------------------------
331 // wxNotebook accessors
332 // ----------------------------------------------------------------------------
334 size_t wxNotebook::GetPageCount() const
337 wxASSERT( (int)m_pages
.Count() == TabCtrl_GetItemCount(GetHwnd()) );
339 return m_pages
.Count();
342 int wxNotebook::GetRowCount() const
344 return TabCtrl_GetRowCount(GetHwnd());
347 int wxNotebook::SetSelection(size_t nPage
)
349 wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxNOT_FOUND
, wxT("notebook page out of range") );
351 if ( m_selection
== wxNOT_FOUND
|| nPage
!= (size_t)m_selection
)
353 if ( SendPageChangingEvent(nPage
) )
355 // program allows the page change
356 const int selectionOld
= m_selection
;
358 UpdateSelection(nPage
);
360 TabCtrl_SetCurSel(GetHwnd(), nPage
);
362 SendPageChangedEvent(selectionOld
, nPage
);
369 void wxNotebook::UpdateSelection(int selNew
)
371 if ( m_selection
!= wxNOT_FOUND
)
372 m_pages
[m_selection
]->Show(false);
374 if ( selNew
!= wxNOT_FOUND
)
376 wxNotebookPage
*pPage
= m_pages
[selNew
];
380 // Changing the page should give the focus to it but, as per bug report
381 // http://sf.net/tracker/index.php?func=detail&aid=1150659&group_id=9863&atid=109863,
382 // we should not set the focus to it directly since it erroneously
383 // selects radio buttons and breaks keyboard handling for a notebook's
384 // scroll buttons. So give focus to the notebook and not the page.
386 // but don't do this is the notebook is hidden
387 if ( ::IsWindowVisible(GetHwnd()) )
390 m_selection
= selNew
;
393 int wxNotebook::ChangeSelection(size_t nPage
)
395 wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxNOT_FOUND
, wxT("notebook page out of range") );
397 const int selOld
= m_selection
;
399 if ( m_selection
== wxNOT_FOUND
|| nPage
!= (size_t)m_selection
)
401 TabCtrl_SetCurSel(GetHwnd(), nPage
);
403 UpdateSelection(nPage
);
409 bool wxNotebook::SetPageText(size_t nPage
, const wxString
& strText
)
411 wxCHECK_MSG( IS_VALID_PAGE(nPage
), false, wxT("notebook page out of range") );
414 tcItem
.mask
= TCIF_TEXT
;
415 tcItem
.pszText
= wxMSW_CONV_LPTSTR(strText
);
417 if ( !HasFlag(wxNB_MULTILINE
) )
418 return TabCtrl_SetItem(GetHwnd(), nPage
, &tcItem
) != 0;
420 // multiline - we need to set new page size if a line is added or removed
421 int rows
= GetRowCount();
422 bool ret
= TabCtrl_SetItem(GetHwnd(), nPage
, &tcItem
) != 0;
424 if ( ret
&& rows
!= GetRowCount() )
426 const wxRect r
= GetPageSize();
427 const size_t count
= m_pages
.Count();
428 for ( size_t page
= 0; page
< count
; page
++ )
429 m_pages
[page
]->SetSize(r
);
435 wxString
wxNotebook::GetPageText(size_t nPage
) const
437 wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxEmptyString
, wxT("notebook page out of range") );
441 tcItem
.mask
= TCIF_TEXT
;
442 tcItem
.pszText
= buf
;
443 tcItem
.cchTextMax
= WXSIZEOF(buf
);
446 if ( TabCtrl_GetItem(GetHwnd(), nPage
, &tcItem
) )
447 str
= tcItem
.pszText
;
452 int wxNotebook::GetPageImage(size_t nPage
) const
454 wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxNOT_FOUND
, wxT("notebook page out of range") );
457 tcItem
.mask
= TCIF_IMAGE
;
459 return TabCtrl_GetItem(GetHwnd(), nPage
, &tcItem
) ? tcItem
.iImage
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 (void) TabCtrl_SetImageList(GetHwnd(), GetHimagelistOf(imageList
));
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 either
497 // wxNB_MULTILINE is used or tabs are placed on a side, if the rectangle
500 // The value of 20 is chosen arbitrarily but seems to work
501 if ( 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 // we can't use TabCtrl_AdjustRect here because it only works for wxNB_TOP
541 wxSize sizeTotal
= sizePage
;
544 if ( GetPageCount() > 0 )
547 TabCtrl_GetItemRect(GetHwnd(), 0, &rect
);
548 tabSize
.x
= rect
.right
- rect
.left
;
549 tabSize
.y
= rect
.bottom
- rect
.top
;
552 const int rows
= GetRowCount();
554 // add an extra margin in both directions
555 const int MARGIN
= 8;
558 sizeTotal
.x
+= MARGIN
;
559 sizeTotal
.y
+= tabSize
.y
* rows
+ MARGIN
;
561 else // horizontal layout
563 sizeTotal
.x
+= tabSize
.x
* rows
+ MARGIN
;
564 sizeTotal
.y
+= MARGIN
;
570 void wxNotebook::AdjustPageSize(wxNotebookPage
*page
)
572 wxCHECK_RET( page
, wxT("NULL page in wxNotebook::AdjustPageSize") );
574 const wxRect r
= GetPageSize();
581 // ----------------------------------------------------------------------------
582 // wxNotebook operations
583 // ----------------------------------------------------------------------------
585 // remove one page from the notebook, without deleting
586 wxNotebookPage
*wxNotebook::DoRemovePage(size_t nPage
)
588 wxNotebookPage
*pageRemoved
= wxNotebookBase::DoRemovePage(nPage
);
592 // hide the removed page to maintain the invariant that only the
593 // selected page is visible and others are hidden:
594 pageRemoved
->Show(false);
596 TabCtrl_DeleteItem(GetHwnd(), nPage
);
598 if ( m_pages
.IsEmpty() )
600 // no selection any more, the notebook becamse empty
601 m_selection
= wxNOT_FOUND
;
603 else // notebook still not empty
605 int selNew
= TabCtrl_GetCurSel(GetHwnd());
606 if ( selNew
!= wxNOT_FOUND
)
608 // No selection change, just refresh the current selection.
609 // Because it could be that the slection index changed
610 // we need to update it.
611 // Note: this does not mean the selection it self changed.
612 m_selection
= selNew
;
613 m_pages
[m_selection
]->Refresh();
615 else if (int(nPage
) == m_selection
)
617 // The selection was deleted.
619 // Determine new selection.
620 if (m_selection
== int(GetPageCount()))
621 selNew
= m_selection
- 1;
623 selNew
= m_selection
;
625 // m_selection must be always valid so reset it before calling
627 m_selection
= wxNOT_FOUND
;
628 SetSelection(selNew
);
632 wxFAIL
; // Windows did not behave ok.
640 bool wxNotebook::DeleteAllPages()
642 size_t nPageCount
= GetPageCount();
644 for ( nPage
= 0; nPage
< nPageCount
; nPage
++ )
645 delete m_pages
[nPage
];
649 TabCtrl_DeleteAllItems(GetHwnd());
651 m_selection
= wxNOT_FOUND
;
653 InvalidateBestSize();
657 // same as AddPage() but does it at given position
658 bool wxNotebook::InsertPage(size_t nPage
,
659 wxNotebookPage
*pPage
,
660 const wxString
& strText
,
664 wxCHECK_MSG( pPage
!= NULL
, false, wxT("NULL page in wxNotebook::InsertPage") );
665 wxCHECK_MSG( IS_VALID_PAGE(nPage
) || nPage
== GetPageCount(), false,
666 wxT("invalid index in wxNotebook::InsertPage") );
668 wxASSERT_MSG( pPage
->GetParent() == this,
669 wxT("notebook pages must have notebook as parent") );
671 // add a new tab to the control
672 // ----------------------------
674 // init all fields to 0
676 wxZeroMemory(tcItem
);
678 // set the image, if any
681 tcItem
.mask
|= TCIF_IMAGE
;
682 tcItem
.iImage
= imageId
;
686 if ( !strText
.empty() )
688 tcItem
.mask
|= TCIF_TEXT
;
689 tcItem
.pszText
= wxMSW_CONV_LPTSTR(strText
);
692 // hide the page: unless it is selected, it shouldn't be shown (and if it
693 // is selected it will be shown later)
694 HWND hwnd
= GetWinHwnd(pPage
);
695 SetWindowLong(hwnd
, GWL_STYLE
, GetWindowLong(hwnd
, GWL_STYLE
) & ~WS_VISIBLE
);
697 // this updates internal flag too -- otherwise it would get out of sync
698 // with the real state
702 // fit the notebook page to the tab control's display area: this should be
703 // done before adding it to the notebook or TabCtrl_InsertItem() will
704 // change the notebooks size itself!
705 AdjustPageSize(pPage
);
707 // finally do insert it
708 if ( TabCtrl_InsertItem(GetHwnd(), nPage
, &tcItem
) == -1 )
710 wxLogError(wxT("Can't create the notebook page '%s'."), strText
.c_str());
715 // need to update the bg brush when the first page is added
716 // so the first panel gets the correct themed background
717 if ( m_pages
.empty() )
721 #endif // wxUSE_UXTHEME
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_selection
)
743 // one extra page added
747 DoSetSelectionAfterInsertion(nPage
, bSelect
);
749 InvalidateBestSize();
754 int wxNotebook::HitTest(const wxPoint
& pt
, long *flags
) const
756 TC_HITTESTINFO hitTestInfo
;
757 hitTestInfo
.pt
.x
= pt
.x
;
758 hitTestInfo
.pt
.y
= pt
.y
;
759 int item
= TabCtrl_HitTest(GetHwnd(), &hitTestInfo
);
765 if ((hitTestInfo
.flags
& TCHT_NOWHERE
) == TCHT_NOWHERE
)
766 *flags
|= wxBK_HITTEST_NOWHERE
;
767 if ((hitTestInfo
.flags
& TCHT_ONITEM
) == TCHT_ONITEM
)
768 *flags
|= wxBK_HITTEST_ONITEM
;
769 if ((hitTestInfo
.flags
& TCHT_ONITEMICON
) == TCHT_ONITEMICON
)
770 *flags
|= wxBK_HITTEST_ONICON
;
771 if ((hitTestInfo
.flags
& TCHT_ONITEMLABEL
) == TCHT_ONITEMLABEL
)
772 *flags
|= wxBK_HITTEST_ONLABEL
;
773 if ( item
== wxNOT_FOUND
&& GetPageSize().Contains(pt
) )
774 *flags
|= wxBK_HITTEST_ONPAGE
;
780 // ----------------------------------------------------------------------------
781 // flicker-less notebook redraw
782 // ----------------------------------------------------------------------------
784 #if USE_NOTEBOOK_ANTIFLICKER
786 // wnd proc for the spin button
787 LRESULT APIENTRY _EXPORT
wxNotebookSpinBtnWndProc(HWND hwnd
,
792 if ( message
== WM_ERASEBKGND
)
795 return ::CallWindowProc(CASTWNDPROC gs_wndprocNotebookSpinBtn
,
796 hwnd
, message
, wParam
, lParam
);
799 LRESULT APIENTRY _EXPORT
wxNotebookWndProc(HWND hwnd
,
804 return ::CallWindowProc(CASTWNDPROC gs_wndprocNotebook
,
805 hwnd
, message
, wParam
, lParam
);
808 void wxNotebook::OnEraseBackground(wxEraseEvent
& WXUNUSED(event
))
813 void wxNotebook::OnPaint(wxPaintEvent
& WXUNUSED(event
))
818 ::GetClientRect(GetHwnd(), &rc
);
819 wxBitmap
bmp(rc
.right
, rc
.bottom
);
820 memdc
.SelectObject(bmp
);
822 const wxLayoutDirection dir
= dc
.GetLayoutDirection();
823 memdc
.SetLayoutDirection(dir
);
825 // if there is no special brush just use the solid background colour
827 HBRUSH hbr
= (HBRUSH
)m_hbrBackground
;
834 brush
= wxBrush(GetBackgroundColour());
835 hbr
= GetHbrushOf(brush
);
838 wxMSWDCImpl
*impl
= (wxMSWDCImpl
*) memdc
.GetImpl();
840 ::FillRect(GetHdcOf(*impl
), &rc
, hbr
);
842 MSWDefWindowProc(WM_PAINT
, (WPARAM
)(impl
->GetHDC()), 0);
844 // For some reason in RTL mode, source offset has to be -1, otherwise the
845 // right border (physical) remains unpainted.
846 const wxCoord ofs
= dir
== wxLayout_RightToLeft
? -1 : 0;
847 dc
.Blit(ofs
, 0, rc
.right
, rc
.bottom
, &memdc
, ofs
, 0);
850 #endif // USE_NOTEBOOK_ANTIFLICKER
852 // ----------------------------------------------------------------------------
853 // wxNotebook callbacks
854 // ----------------------------------------------------------------------------
856 void wxNotebook::OnSize(wxSizeEvent
& event
)
858 if ( GetPageCount() == 0 )
860 // Prevents droppings on resize, but does cause some flicker
861 // when there are no pages.
869 // Without this, we can sometimes get droppings at the edges
870 // of a notebook, for example a notebook in a splitter window.
871 // This needs to be reconciled with the RefreshRect calls
872 // at the end of this function, which weren't enough to prevent
875 wxSize sz
= GetClientSize();
877 // Refresh right side
878 wxRect
rect(sz
.x
-4, 0, 4, sz
.y
);
881 // Refresh bottom side
882 rect
= wxRect(0, sz
.y
-4, sz
.x
, 4);
886 rect
= wxRect(0, 0, 4, sz
.y
);
889 #endif // !__WXWINCE__
891 // fit all the notebook pages to the tab control's display area
894 rc
.left
= rc
.top
= 0;
895 GetSize((int *)&rc
.right
, (int *)&rc
.bottom
);
897 // save the total size, we'll use it below
898 int widthNbook
= rc
.right
- rc
.left
,
899 heightNbook
= rc
.bottom
- rc
.top
;
901 // there seems to be a bug in the implementation of TabCtrl_AdjustRect(): it
902 // returns completely false values for multiline tab controls after the tabs
903 // are added but before getting the first WM_SIZE (off by ~50 pixels, see
905 // http://sf.net/tracker/index.php?func=detail&aid=645323&group_id=9863&atid=109863
907 // and the only work around I could find was this ugly hack... without it
908 // simply toggling the "multiline" checkbox in the notebook sample resulted
909 // in a noticeable page displacement
910 if ( HasFlag(wxNB_MULTILINE
) )
912 // avoid an infinite recursion: we get another notification too!
913 static bool s_isInOnSize
= false;
918 SendMessage(GetHwnd(), WM_SIZE
, SIZE_RESTORED
,
919 MAKELPARAM(rc
.right
, rc
.bottom
));
920 s_isInOnSize
= false;
923 // The best size depends on the number of rows of tabs, which can
924 // change when the notepad is resized.
925 InvalidateBestSize();
929 // background bitmap size has changed, update the brush using it too
931 #endif // wxUSE_UXTHEME
933 TabCtrl_AdjustRect(GetHwnd(), false, &rc
);
935 int width
= rc
.right
- rc
.left
,
936 height
= rc
.bottom
- rc
.top
;
937 size_t nCount
= m_pages
.Count();
938 for ( size_t nPage
= 0; nPage
< nCount
; nPage
++ ) {
939 wxNotebookPage
*pPage
= m_pages
[nPage
];
940 pPage
->SetSize(rc
.left
, rc
.top
, width
, height
);
944 // unless we had already repainted everything, we now need to refresh
945 if ( !HasFlag(wxFULL_REPAINT_ON_RESIZE
) )
947 // invalidate areas not covered by pages
948 RefreshRect(wxRect(0, 0, widthNbook
, rc
.top
), false);
949 RefreshRect(wxRect(0, rc
.top
, rc
.left
, height
), false);
950 RefreshRect(wxRect(0, rc
.bottom
, widthNbook
, heightNbook
- rc
.bottom
),
952 RefreshRect(wxRect(rc
.right
, rc
.top
, widthNbook
- rc
.right
, height
),
956 #if USE_NOTEBOOK_ANTIFLICKER
957 // subclass the spin control used by the notebook to scroll pages to
958 // prevent it from flickering on resize
959 if ( !m_hasSubclassedUpdown
)
961 // iterate over all child windows to find spin button
962 for ( HWND child
= ::GetWindow(GetHwnd(), GW_CHILD
);
964 child
= ::GetWindow(child
, GW_HWNDNEXT
) )
966 wxWindow
*childWindow
= wxFindWinFromHandle((WXHWND
)child
);
968 // see if it exists, if no wxWindow found then assume it's the spin
972 // subclass the spin button to override WM_ERASEBKGND
973 if ( !gs_wndprocNotebookSpinBtn
)
974 gs_wndprocNotebookSpinBtn
= (WXFARPROC
)wxGetWindowProc(child
);
976 wxSetWindowProc(child
, wxNotebookSpinBtnWndProc
);
977 m_hasSubclassedUpdown
= true;
983 // Probably because of the games we play above to avoid flicker sometimes
984 // the text controls inside notebook pages are not shown correctly (they
985 // don't have their borders) when the notebook is shown for the first time.
986 // It's not really clear why does this happen and maybe the bug is in
987 // wxTextCtrl itself and not here but updating the page when it's about to
988 // be shown doesn't cost much and works around the problem so do it here
990 if ( !m_doneUpdateHack
&& IsShownOnScreen() )
992 m_doneUpdateHack
= true;
993 wxWindow
* const page
= GetCurrentPage();
997 #endif // USE_NOTEBOOK_ANTIFLICKER
1002 void wxNotebook::OnNavigationKey(wxNavigationKeyEvent
& event
)
1004 if ( event
.IsWindowChange() ) {
1006 AdvanceSelection(event
.GetDirection());
1009 // we get this event in 3 cases
1011 // a) one of our pages might have generated it because the user TABbed
1012 // out from it in which case we should propagate the event upwards and
1013 // our parent will take care of setting the focus to prev/next sibling
1017 // b) the parent panel wants to give the focus to us so that we
1018 // forward it to our selected page. We can't deal with this in
1019 // OnSetFocus() because we don't know which direction the focus came
1020 // from in this case and so can't choose between setting the focus to
1021 // first or last panel child
1025 // c) we ourselves (see MSWTranslateMessage) generated the event
1027 wxWindow
* const parent
= GetParent();
1029 // the wxObject* casts are required to avoid MinGW GCC 2.95.3 ICE
1030 const bool isFromParent
= event
.GetEventObject() == (wxObject
*) parent
;
1031 const bool isFromSelf
= event
.GetEventObject() == (wxObject
*) this;
1032 const bool isForward
= event
.GetDirection();
1034 if ( isFromSelf
&& !isForward
)
1036 // focus is currently on notebook tab and should leave
1037 // it backwards (Shift-TAB)
1038 event
.SetCurrentFocus(this);
1039 parent
->HandleWindowEvent(event
);
1041 else if ( isFromParent
|| isFromSelf
)
1043 // no, it doesn't come from child, case (b) or (c): forward to a
1044 // page but only if entering notebook page (i.e. direction is
1045 // backwards (Shift-TAB) comething from out-of-notebook, or
1046 // direction is forward (TAB) from ourselves),
1047 if ( m_selection
!= wxNOT_FOUND
&&
1048 (!event
.GetDirection() || isFromSelf
) )
1050 // so that the page knows that the event comes from it's parent
1051 // and is being propagated downwards
1052 event
.SetEventObject(this);
1054 wxWindow
*page
= m_pages
[m_selection
];
1055 if ( !page
->HandleWindowEvent(event
) )
1059 //else: page manages focus inside it itself
1061 else // otherwise set the focus to the notebook itself
1068 // it comes from our child, case (a), pass to the parent, but only
1069 // if the direction is forwards. Otherwise set the focus to the
1070 // notebook itself. The notebook is always the 'first' control of a
1078 event
.SetCurrentFocus(this);
1079 parent
->HandleWindowEvent(event
);
1087 bool wxNotebook::DoDrawBackground(WXHDC hDC
, wxWindow
*child
)
1089 wxUxThemeHandle
theme(child
? child
: this, L
"TAB");
1093 // get the notebook client rect (we're not interested in drawing tabs
1095 wxRect r
= GetPageSize();
1100 wxCopyRectToRECT(r
, rc
);
1102 // map rect to the coords of the window we're drawing in
1104 ::MapWindowPoints(GetHwnd(), GetHwndOf(child
), (POINT
*)&rc
, 2);
1106 // we have the content area (page size), but we need to draw all of the
1107 // background for it to be aligned correctly
1108 wxUxThemeEngine::Get()->GetThemeBackgroundExtent
1117 wxUxThemeEngine::Get()->DrawThemeBackground
1130 WXHBRUSH
wxNotebook::QueryBgBitmap()
1132 wxRect r
= GetPageSize();
1136 WindowHDC
hDC(GetHwnd());
1137 MemoryHDC
hDCMem(hDC
);
1138 CompatibleBitmap
hBmp(hDC
, r
.x
+ r
.width
, r
.y
+ r
.height
);
1140 SelectInHDC
selectBmp(hDCMem
, hBmp
);
1142 if ( !DoDrawBackground((WXHDC
)(HDC
)hDCMem
) )
1145 return (WXHBRUSH
)::CreatePatternBrush(hBmp
);
1148 void wxNotebook::UpdateBgBrush()
1150 if ( m_hbrBackground
)
1151 ::DeleteObject((HBRUSH
)m_hbrBackground
);
1153 if ( !m_hasBgCol
&& wxUxThemeEngine::GetIfActive() )
1155 m_hbrBackground
= QueryBgBitmap();
1157 else // no themes or we've got user-defined solid colour
1159 m_hbrBackground
= NULL
;
1163 bool wxNotebook::MSWPrintChild(WXHDC hDC
, wxWindow
*child
)
1165 // solid background colour overrides themed background drawing
1166 if ( !UseBgCol() && DoDrawBackground(hDC
, child
) )
1169 // If we're using a solid colour (for example if we've switched off
1170 // theming for this notebook), paint it
1173 wxRect r
= GetPageSize();
1178 wxCopyRectToRECT(r
, rc
);
1180 // map rect to the coords of the window we're drawing in
1182 ::MapWindowPoints(GetHwnd(), GetHwndOf(child
), (POINT
*)&rc
, 2);
1184 wxBrush
brush(GetBackgroundColour());
1185 HBRUSH hbr
= GetHbrushOf(brush
);
1187 ::FillRect((HDC
) hDC
, &rc
, hbr
);
1192 return wxNotebookBase::MSWPrintChild(hDC
, child
);
1195 #endif // wxUSE_UXTHEME
1197 // Windows only: attempts to get colour for UX theme page background
1198 wxColour
wxNotebook::GetThemeBackgroundColour() const
1201 if (wxUxThemeEngine::Get())
1203 wxUxThemeHandle
hTheme((wxNotebook
*) this, L
"TAB");
1206 // This is total guesswork.
1207 // See PlatformSDK\Include\Tmschema.h for values.
1208 // JACS: can also use 9 (TABP_PANE)
1209 COLORREF themeColor
;
1210 bool success
= (S_OK
== wxUxThemeEngine::Get()->GetThemeColor(
1214 3821 /* FILLCOLORHINT */,
1217 return GetBackgroundColour();
1220 [DS] Workaround for WindowBlinds:
1221 Some themes return a near black theme color using FILLCOLORHINT,
1222 this makes notebook pages have an ugly black background and makes
1223 text (usually black) unreadable. Retry again with FILLCOLOR.
1225 This workaround potentially breaks appearance of some themes,
1226 but in practice it already fixes some themes.
1228 if (themeColor
== 1)
1230 wxUxThemeEngine::Get()->GetThemeColor(
1234 3802 /* FILLCOLOR */,
1238 wxColour colour
= wxRGBToColour(themeColor
);
1240 // Under Vista, the tab background colour is reported incorrectly.
1241 // So for the default theme at least, hard-code the colour to something
1242 // that will blend in.
1244 static int s_AeroStatus
= -1;
1245 if (s_AeroStatus
== -1)
1247 WCHAR szwThemeFile
[1024];
1248 WCHAR szwThemeColor
[256];
1249 if (S_OK
== wxUxThemeEngine::Get()->GetCurrentThemeName(szwThemeFile
, 1024, szwThemeColor
, 256, NULL
, 0))
1251 wxString
themeFile(szwThemeFile
), themeColor(szwThemeColor
);
1252 if (themeFile
.Find(wxT("Aero")) != -1 && themeColor
== wxT("NormalColor"))
1261 if (s_AeroStatus
== 1)
1262 colour
= wxColour(255, 255, 255);
1267 #endif // wxUSE_UXTHEME
1269 return GetBackgroundColour();
1272 // ----------------------------------------------------------------------------
1273 // wxNotebook base class virtuals
1274 // ----------------------------------------------------------------------------
1276 #if wxUSE_CONSTRAINTS
1278 // override these 2 functions to do nothing: everything is done in OnSize
1280 void wxNotebook::SetConstraintSizes(bool WXUNUSED(recurse
))
1282 // don't set the sizes of the pages - their correct size is not yet known
1283 wxControl::SetConstraintSizes(false);
1286 bool wxNotebook::DoPhase(int WXUNUSED(nPhase
))
1291 #endif // wxUSE_CONSTRAINTS
1293 // ----------------------------------------------------------------------------
1294 // wxNotebook Windows message handlers
1295 // ----------------------------------------------------------------------------
1297 bool wxNotebook::MSWOnScroll(int orientation
, WXWORD nSBCode
,
1298 WXWORD pos
, WXHWND control
)
1300 // don't generate EVT_SCROLLWIN events for the WM_SCROLLs coming from the
1305 return wxNotebookBase::MSWOnScroll(orientation
, nSBCode
, pos
, control
);
1308 bool wxNotebook::MSWOnNotify(int idCtrl
, WXLPARAM lParam
, WXLPARAM
* result
)
1310 wxBookCtrlEvent
event(wxEVT_NULL
, m_windowId
);
1312 NMHDR
* hdr
= (NMHDR
*)lParam
;
1313 switch ( hdr
->code
) {
1315 event
.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
);
1318 case TCN_SELCHANGING
:
1319 event
.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
);
1323 return wxControl::MSWOnNotify(idCtrl
, lParam
, result
);
1326 event
.SetSelection(TabCtrl_GetCurSel(GetHwnd()));
1327 event
.SetOldSelection(m_selection
);
1328 event
.SetEventObject(this);
1329 event
.SetInt(idCtrl
);
1331 // Change the selection before generating the event as its handler should
1332 // already see the new page selected.
1333 if ( hdr
->code
== TCN_SELCHANGE
)
1334 UpdateSelection(event
.GetSelection());
1336 bool processed
= HandleWindowEvent(event
);
1337 *result
= !event
.IsAllowed();
1341 #endif // wxUSE_NOTEBOOK