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 #if wxUSE_EXTENDED_RTTI
133 IMPLEMENT_DYNAMIC_CLASS(wxNotebook
, wxBookCtrlBase
)
136 // ============================================================================
138 // ============================================================================
140 // ----------------------------------------------------------------------------
141 // wxNotebook construction
142 // ----------------------------------------------------------------------------
144 // common part of all ctors
145 void wxNotebook::Init()
150 m_hbrBackground
= NULL
;
151 #endif // wxUSE_UXTHEME
153 #if USE_NOTEBOOK_ANTIFLICKER
154 m_hasSubclassedUpdown
= false;
155 #endif // USE_NOTEBOOK_ANTIFLICKER
158 // default for dynamic class
159 wxNotebook::wxNotebook()
164 // the same arguments as for wxControl
165 wxNotebook::wxNotebook(wxWindow
*parent
,
170 const wxString
& name
)
174 Create(parent
, id
, pos
, size
, style
, name
);
178 bool wxNotebook::Create(wxWindow
*parent
,
183 const wxString
& name
)
185 if ( (style
& wxBK_ALIGN_MASK
) == wxBK_DEFAULT
)
187 #if defined(__POCKETPC__)
188 style
|= wxBK_BOTTOM
| wxNB_FLAT
;
195 // Not sure why, but without this style, there is no border
196 // around the notebook tabs.
197 if (style
& wxNB_FLAT
)
198 style
|= wxBORDER_SUNKEN
;
202 // ComCtl32 notebook tabs simply don't work unless they're on top if we
203 // have uxtheme, we can work around it later (after control creation), but
204 // if we have been compiled without uxtheme support, we have to clear those
206 if ( HasTroubleWithNonTopTabs() )
208 style
&= ~(wxBK_BOTTOM
| wxBK_LEFT
| wxBK_RIGHT
);
210 #endif //wxUSE_UXTHEME
212 #if defined(__WINE__) && wxUSE_UNICODE
213 LPCTSTR className
= L
"SysTabControl32";
215 LPCTSTR className
= WC_TABCONTROL
;
218 #if USE_NOTEBOOK_ANTIFLICKER
219 // SysTabCtl32 class has natively CS_HREDRAW and CS_VREDRAW enabled and it
220 // causes horrible flicker when resizing notebook, so get rid of it by
221 // using a class without these styles (but otherwise identical to it)
222 if ( !HasFlag(wxFULL_REPAINT_ON_RESIZE
) )
224 static ClassRegistrar s_clsNotebook
;
225 if ( !s_clsNotebook
.IsInitialized() )
227 // get a copy of standard class and modify it
230 if ( ::GetClassInfo(NULL
, WC_TABCONTROL
, &wc
) )
233 reinterpret_cast<WXFARPROC
>(wc
.lpfnWndProc
);
234 wc
.lpszClassName
= wxT("_wx_SysTabCtl32");
235 wc
.style
&= ~(CS_HREDRAW
| CS_VREDRAW
);
236 wc
.hInstance
= wxGetInstance();
237 wc
.lpfnWndProc
= wxNotebookWndProc
;
238 s_clsNotebook
.Register(wc
);
242 wxLogLastError(wxT("GetClassInfoEx(SysTabCtl32)"));
246 // use our custom class if available but fall back to the standard
247 // notebook if we failed to register it
248 if ( s_clsNotebook
.IsRegistered() )
250 // it's ok to use c_str() here as the static s_clsNotebook object
251 // has sufficiently long lifetime
252 className
= s_clsNotebook
.GetName().c_str();
255 #endif // USE_NOTEBOOK_ANTIFLICKER
257 if ( !CreateControl(parent
, id
, pos
, size
, style
| wxTAB_TRAVERSAL
,
258 wxDefaultValidator
, name
) )
261 if ( !MSWCreateControl(className
, wxEmptyString
, pos
, size
) )
265 if ( HasFlag(wxNB_NOPAGETHEME
) ||
266 wxSystemOptions::IsFalse(wxT("msw.notebook.themed-background")) )
268 SetBackgroundColour(GetThemeBackgroundColour());
270 else // use themed background by default
272 // create backing store
276 // comctl32.dll 6.0 doesn't support non-top tabs with visual styles (the
277 // control is simply not rendered correctly), so we disable themes
278 // if possible, otherwise we simply clear the styles.
279 if ( HasTroubleWithNonTopTabs() &&
280 (style
& (wxBK_BOTTOM
| wxBK_LEFT
| wxBK_RIGHT
)) )
282 // check if we use themes at all -- if we don't, we're still okay
283 if ( wxUxThemeEngine::GetIfActive() )
285 wxUxThemeEngine::GetIfActive()->SetWindowTheme(GetHwnd(), L
"", L
"");
287 // correct the background color for the new non-themed control
288 SetBackgroundColour(GetThemeBackgroundColour());
291 #endif // wxUSE_UXTHEME
293 // Undocumented hack to get flat notebook style
294 // In fact, we should probably only do this in some
295 // curcumstances, i.e. if we know we will have a border
296 // at the bottom (the tab control doesn't draw it itself)
297 #if defined(__POCKETPC__) || defined(__SMARTPHONE__)
298 if (HasFlag(wxNB_FLAT
))
300 SendMessage(GetHwnd(), CCM_SETVERSION
, COMCTL32_VERSION
, 0);
302 SetBackgroundColour(*wxWHITE
);
308 WXDWORD
wxNotebook::MSWGetStyle(long style
, WXDWORD
*exstyle
) const
310 WXDWORD tabStyle
= wxControl::MSWGetStyle(style
, exstyle
);
312 tabStyle
|= WS_TABSTOP
| TCS_TABS
;
314 if ( style
& wxNB_MULTILINE
)
315 tabStyle
|= TCS_MULTILINE
;
316 if ( style
& wxNB_FIXEDWIDTH
)
317 tabStyle
|= TCS_FIXEDWIDTH
;
319 if ( style
& wxBK_BOTTOM
)
320 tabStyle
|= TCS_RIGHT
;
321 else if ( style
& wxBK_LEFT
)
322 tabStyle
|= TCS_VERTICAL
;
323 else if ( style
& wxBK_RIGHT
)
324 tabStyle
|= TCS_VERTICAL
| TCS_RIGHT
;
329 wxNotebook::~wxNotebook()
332 if ( m_hbrBackground
)
333 ::DeleteObject((HBRUSH
)m_hbrBackground
);
334 #endif // wxUSE_UXTHEME
337 // ----------------------------------------------------------------------------
338 // wxNotebook accessors
339 // ----------------------------------------------------------------------------
341 size_t wxNotebook::GetPageCount() const
344 wxASSERT( (int)m_pages
.Count() == TabCtrl_GetItemCount(GetHwnd()) );
346 return m_pages
.Count();
349 int wxNotebook::GetRowCount() const
351 return TabCtrl_GetRowCount(GetHwnd());
354 int wxNotebook::SetSelection(size_t nPage
)
356 wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxNOT_FOUND
, wxT("notebook page out of range") );
358 if ( m_selection
== wxNOT_FOUND
|| nPage
!= (size_t)m_selection
)
360 if ( SendPageChangingEvent(nPage
) )
362 // program allows the page change
363 const int selectionOld
= m_selection
;
365 UpdateSelection(nPage
);
367 TabCtrl_SetCurSel(GetHwnd(), nPage
);
369 SendPageChangedEvent(selectionOld
, nPage
);
376 void wxNotebook::UpdateSelection(int selNew
)
378 if ( m_selection
!= wxNOT_FOUND
)
379 m_pages
[m_selection
]->Show(false);
381 if ( selNew
!= wxNOT_FOUND
)
383 wxNotebookPage
*pPage
= m_pages
[selNew
];
387 // Changing the page should give the focus to it but, as per bug report
388 // http://sf.net/tracker/index.php?func=detail&aid=1150659&group_id=9863&atid=109863,
389 // we should not set the focus to it directly since it erroneously
390 // selects radio buttons and breaks keyboard handling for a notebook's
391 // scroll buttons. So give focus to the notebook and not the page.
393 // but don't do this is the notebook is hidden
394 if ( ::IsWindowVisible(GetHwnd()) )
397 m_selection
= selNew
;
400 int wxNotebook::ChangeSelection(size_t nPage
)
402 wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxNOT_FOUND
, wxT("notebook page out of range") );
404 const int selOld
= m_selection
;
406 if ( m_selection
== wxNOT_FOUND
|| nPage
!= (size_t)m_selection
)
408 TabCtrl_SetCurSel(GetHwnd(), nPage
);
410 UpdateSelection(nPage
);
416 bool wxNotebook::SetPageText(size_t nPage
, const wxString
& strText
)
418 wxCHECK_MSG( IS_VALID_PAGE(nPage
), false, wxT("notebook page out of range") );
421 tcItem
.mask
= TCIF_TEXT
;
422 tcItem
.pszText
= (wxChar
*)strText
.wx_str();
424 if ( !HasFlag(wxNB_MULTILINE
) )
425 return TabCtrl_SetItem(GetHwnd(), nPage
, &tcItem
) != 0;
427 // multiline - we need to set new page size if a line is added or removed
428 int rows
= GetRowCount();
429 bool ret
= TabCtrl_SetItem(GetHwnd(), nPage
, &tcItem
) != 0;
431 if ( ret
&& rows
!= GetRowCount() )
433 const wxRect r
= GetPageSize();
434 const size_t count
= m_pages
.Count();
435 for ( size_t page
= 0; page
< count
; page
++ )
436 m_pages
[page
]->SetSize(r
);
442 wxString
wxNotebook::GetPageText(size_t nPage
) const
444 wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxEmptyString
, wxT("notebook page out of range") );
448 tcItem
.mask
= TCIF_TEXT
;
449 tcItem
.pszText
= buf
;
450 tcItem
.cchTextMax
= WXSIZEOF(buf
);
453 if ( TabCtrl_GetItem(GetHwnd(), nPage
, &tcItem
) )
454 str
= tcItem
.pszText
;
459 int wxNotebook::GetPageImage(size_t nPage
) const
461 wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxNOT_FOUND
, wxT("notebook page out of range") );
464 tcItem
.mask
= TCIF_IMAGE
;
466 return TabCtrl_GetItem(GetHwnd(), nPage
, &tcItem
) ? tcItem
.iImage
470 bool wxNotebook::SetPageImage(size_t nPage
, int nImage
)
472 wxCHECK_MSG( IS_VALID_PAGE(nPage
), false, wxT("notebook page out of range") );
475 tcItem
.mask
= TCIF_IMAGE
;
476 tcItem
.iImage
= nImage
;
478 return TabCtrl_SetItem(GetHwnd(), nPage
, &tcItem
) != 0;
481 void wxNotebook::SetImageList(wxImageList
* imageList
)
483 wxNotebookBase::SetImageList(imageList
);
487 (void) TabCtrl_SetImageList(GetHwnd(), GetHimagelistOf(imageList
));
491 // ----------------------------------------------------------------------------
492 // wxNotebook size settings
493 // ----------------------------------------------------------------------------
495 wxRect
wxNotebook::GetPageSize() const
500 ::GetClientRect(GetHwnd(), &rc
);
502 // This check is to work around a bug in TabCtrl_AdjustRect which will
503 // cause a crash on win2k or on XP with themes disabled if either
504 // wxNB_MULTILINE is used or tabs are placed on a side, if the rectangle
507 // The value of 20 is chosen arbitrarily but seems to work
508 if ( rc
.right
> 20 && rc
.bottom
> 20 )
510 TabCtrl_AdjustRect(GetHwnd(), false, &rc
);
512 wxCopyRECTToRect(rc
, r
);
518 void wxNotebook::SetPageSize(const wxSize
& size
)
520 // transform the page size into the notebook size
527 TabCtrl_AdjustRect(GetHwnd(), true, &rc
);
530 SetSize(rc
.right
- rc
.left
, rc
.bottom
- rc
.top
);
533 void wxNotebook::SetPadding(const wxSize
& padding
)
535 TabCtrl_SetPadding(GetHwnd(), padding
.x
, padding
.y
);
538 // Windows-only at present. Also, you must use the wxNB_FIXEDWIDTH
540 void wxNotebook::SetTabSize(const wxSize
& sz
)
542 ::SendMessage(GetHwnd(), TCM_SETITEMSIZE
, 0, MAKELPARAM(sz
.x
, sz
.y
));
545 wxSize
wxNotebook::CalcSizeFromPage(const wxSize
& sizePage
) const
547 // we can't use TabCtrl_AdjustRect here because it only works for wxNB_TOP
548 wxSize sizeTotal
= sizePage
;
551 if ( GetPageCount() > 0 )
554 TabCtrl_GetItemRect(GetHwnd(), 0, &rect
);
555 tabSize
.x
= rect
.right
- rect
.left
;
556 tabSize
.y
= rect
.bottom
- rect
.top
;
559 const int rows
= GetRowCount();
561 // add an extra margin in both directions
562 const int MARGIN
= 8;
565 sizeTotal
.x
+= MARGIN
;
566 sizeTotal
.y
+= tabSize
.y
* rows
+ MARGIN
;
568 else // horizontal layout
570 sizeTotal
.x
+= tabSize
.x
* rows
+ MARGIN
;
571 sizeTotal
.y
+= MARGIN
;
577 void wxNotebook::AdjustPageSize(wxNotebookPage
*page
)
579 wxCHECK_RET( page
, wxT("NULL page in wxNotebook::AdjustPageSize") );
581 const wxRect r
= GetPageSize();
588 // ----------------------------------------------------------------------------
589 // wxNotebook operations
590 // ----------------------------------------------------------------------------
592 // remove one page from the notebook, without deleting
593 wxNotebookPage
*wxNotebook::DoRemovePage(size_t nPage
)
595 wxNotebookPage
*pageRemoved
= wxNotebookBase::DoRemovePage(nPage
);
599 // hide the removed page to maintain the invariant that only the
600 // selected page is visible and others are hidden:
601 pageRemoved
->Show(false);
603 TabCtrl_DeleteItem(GetHwnd(), nPage
);
605 if ( m_pages
.IsEmpty() )
607 // no selection any more, the notebook becamse empty
608 m_selection
= wxNOT_FOUND
;
610 else // notebook still not empty
612 int selNew
= TabCtrl_GetCurSel(GetHwnd());
613 if ( selNew
!= wxNOT_FOUND
)
615 // No selection change, just refresh the current selection.
616 // Because it could be that the slection index changed
617 // we need to update it.
618 // Note: this does not mean the selection it self changed.
619 m_selection
= selNew
;
620 m_pages
[m_selection
]->Refresh();
622 else if (int(nPage
) == m_selection
)
624 // The selection was deleted.
626 // Determine new selection.
627 if (m_selection
== int(GetPageCount()))
628 selNew
= m_selection
- 1;
630 selNew
= m_selection
;
632 // m_selection must be always valid so reset it before calling
634 m_selection
= wxNOT_FOUND
;
635 SetSelection(selNew
);
639 wxFAIL
; // Windows did not behave ok.
647 bool wxNotebook::DeleteAllPages()
649 size_t nPageCount
= GetPageCount();
651 for ( nPage
= 0; nPage
< nPageCount
; nPage
++ )
652 delete m_pages
[nPage
];
656 TabCtrl_DeleteAllItems(GetHwnd());
658 m_selection
= wxNOT_FOUND
;
660 InvalidateBestSize();
664 // same as AddPage() but does it at given position
665 bool wxNotebook::InsertPage(size_t nPage
,
666 wxNotebookPage
*pPage
,
667 const wxString
& strText
,
671 wxCHECK_MSG( pPage
!= NULL
, false, wxT("NULL page in wxNotebook::InsertPage") );
672 wxCHECK_MSG( IS_VALID_PAGE(nPage
) || nPage
== GetPageCount(), false,
673 wxT("invalid index in wxNotebook::InsertPage") );
675 wxASSERT_MSG( pPage
->GetParent() == this,
676 wxT("notebook pages must have notebook as parent") );
678 // add a new tab to the control
679 // ----------------------------
681 // init all fields to 0
683 wxZeroMemory(tcItem
);
685 // set the image, if any
688 tcItem
.mask
|= TCIF_IMAGE
;
689 tcItem
.iImage
= imageId
;
693 if ( !strText
.empty() )
695 tcItem
.mask
|= TCIF_TEXT
;
696 tcItem
.pszText
= const_cast<wxChar
*>(strText
.wx_str());
699 // hide the page: unless it is selected, it shouldn't be shown (and if it
700 // is selected it will be shown later)
701 HWND hwnd
= GetWinHwnd(pPage
);
702 SetWindowLong(hwnd
, GWL_STYLE
, GetWindowLong(hwnd
, GWL_STYLE
) & ~WS_VISIBLE
);
704 // this updates internal flag too -- otherwise it would get out of sync
705 // with the real state
709 // fit the notebook page to the tab control's display area: this should be
710 // done before adding it to the notebook or TabCtrl_InsertItem() will
711 // change the notebooks size itself!
712 AdjustPageSize(pPage
);
714 // finally do insert it
715 if ( TabCtrl_InsertItem(GetHwnd(), nPage
, &tcItem
) == -1 )
717 wxLogError(wxT("Can't create the notebook page '%s'."), strText
.c_str());
722 // need to update the bg brush when the first page is added
723 // so the first panel gets the correct themed background
724 if ( m_pages
.empty() )
728 #endif // wxUSE_UXTHEME
731 // succeeded: save the pointer to the page
732 m_pages
.Insert(pPage
, nPage
);
734 // we may need to adjust the size again if the notebook size changed:
735 // normally this only happens for the first page we add (the tabs which
736 // hadn't been there before are now shown) but for a multiline notebook it
737 // can happen for any page at all as a new row could have been started
738 if ( m_pages
.GetCount() == 1 || HasFlag(wxNB_MULTILINE
) )
740 AdjustPageSize(pPage
);
743 // now deal with the selection
744 // ---------------------------
746 // if the inserted page is before the selected one, we must update the
747 // index of the selected page
748 if ( int(nPage
) <= m_selection
)
750 // one extra page added
754 DoSetSelectionAfterInsertion(nPage
, bSelect
);
756 InvalidateBestSize();
761 int wxNotebook::HitTest(const wxPoint
& pt
, long *flags
) const
763 TC_HITTESTINFO hitTestInfo
;
764 hitTestInfo
.pt
.x
= pt
.x
;
765 hitTestInfo
.pt
.y
= pt
.y
;
766 int item
= TabCtrl_HitTest(GetHwnd(), &hitTestInfo
);
772 if ((hitTestInfo
.flags
& TCHT_NOWHERE
) == TCHT_NOWHERE
)
773 *flags
|= wxBK_HITTEST_NOWHERE
;
774 if ((hitTestInfo
.flags
& TCHT_ONITEM
) == TCHT_ONITEM
)
775 *flags
|= wxBK_HITTEST_ONITEM
;
776 if ((hitTestInfo
.flags
& TCHT_ONITEMICON
) == TCHT_ONITEMICON
)
777 *flags
|= wxBK_HITTEST_ONICON
;
778 if ((hitTestInfo
.flags
& TCHT_ONITEMLABEL
) == TCHT_ONITEMLABEL
)
779 *flags
|= wxBK_HITTEST_ONLABEL
;
780 if ( item
== wxNOT_FOUND
&& GetPageSize().Contains(pt
) )
781 *flags
|= wxBK_HITTEST_ONPAGE
;
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 const wxLayoutDirection dir
= dc
.GetLayoutDirection();
830 memdc
.SetLayoutDirection(dir
);
832 // if there is no special brush just use the solid background colour
834 HBRUSH hbr
= (HBRUSH
)m_hbrBackground
;
841 brush
= wxBrush(GetBackgroundColour());
842 hbr
= GetHbrushOf(brush
);
845 wxMSWDCImpl
*impl
= (wxMSWDCImpl
*) memdc
.GetImpl();
847 ::FillRect(GetHdcOf(*impl
), &rc
, hbr
);
849 MSWDefWindowProc(WM_PAINT
, (WPARAM
)(impl
->GetHDC()), 0);
851 // For some reason in RTL mode, source offset has to be -1, otherwise the
852 // right border (physical) remains unpainted.
853 const wxCoord ofs
= dir
== wxLayout_RightToLeft
? -1 : 0;
854 dc
.Blit(ofs
, 0, rc
.right
, rc
.bottom
, &memdc
, ofs
, 0);
857 #endif // USE_NOTEBOOK_ANTIFLICKER
859 // ----------------------------------------------------------------------------
860 // wxNotebook callbacks
861 // ----------------------------------------------------------------------------
863 void wxNotebook::OnSize(wxSizeEvent
& event
)
865 if ( GetPageCount() == 0 )
867 // Prevents droppings on resize, but does cause some flicker
868 // when there are no pages.
876 // Without this, we can sometimes get droppings at the edges
877 // of a notebook, for example a notebook in a splitter window.
878 // This needs to be reconciled with the RefreshRect calls
879 // at the end of this function, which weren't enough to prevent
882 wxSize sz
= GetClientSize();
884 // Refresh right side
885 wxRect
rect(sz
.x
-4, 0, 4, sz
.y
);
888 // Refresh bottom side
889 rect
= wxRect(0, sz
.y
-4, sz
.x
, 4);
893 rect
= wxRect(0, 0, 4, sz
.y
);
896 #endif // !__WXWINCE__
898 // fit all the notebook pages to the tab control's display area
901 rc
.left
= rc
.top
= 0;
902 GetSize((int *)&rc
.right
, (int *)&rc
.bottom
);
904 // save the total size, we'll use it below
905 int widthNbook
= rc
.right
- rc
.left
,
906 heightNbook
= rc
.bottom
- rc
.top
;
908 // there seems to be a bug in the implementation of TabCtrl_AdjustRect(): it
909 // returns completely false values for multiline tab controls after the tabs
910 // are added but before getting the first WM_SIZE (off by ~50 pixels, see
912 // http://sf.net/tracker/index.php?func=detail&aid=645323&group_id=9863&atid=109863
914 // and the only work around I could find was this ugly hack... without it
915 // simply toggling the "multiline" checkbox in the notebook sample resulted
916 // in a noticeable page displacement
917 if ( HasFlag(wxNB_MULTILINE
) )
919 // avoid an infinite recursion: we get another notification too!
920 static bool s_isInOnSize
= false;
925 SendMessage(GetHwnd(), WM_SIZE
, SIZE_RESTORED
,
926 MAKELPARAM(rc
.right
, rc
.bottom
));
927 s_isInOnSize
= false;
930 // The best size depends on the number of rows of tabs, which can
931 // change when the notepad is resized.
932 InvalidateBestSize();
936 // background bitmap size has changed, update the brush using it too
938 #endif // wxUSE_UXTHEME
940 TabCtrl_AdjustRect(GetHwnd(), false, &rc
);
942 int width
= rc
.right
- rc
.left
,
943 height
= rc
.bottom
- rc
.top
;
944 size_t nCount
= m_pages
.Count();
945 for ( size_t nPage
= 0; nPage
< nCount
; nPage
++ ) {
946 wxNotebookPage
*pPage
= m_pages
[nPage
];
947 pPage
->SetSize(rc
.left
, rc
.top
, width
, height
);
951 // unless we had already repainted everything, we now need to refresh
952 if ( !HasFlag(wxFULL_REPAINT_ON_RESIZE
) )
954 // invalidate areas not covered by pages
955 RefreshRect(wxRect(0, 0, widthNbook
, rc
.top
), false);
956 RefreshRect(wxRect(0, rc
.top
, rc
.left
, height
), false);
957 RefreshRect(wxRect(0, rc
.bottom
, widthNbook
, heightNbook
- rc
.bottom
),
959 RefreshRect(wxRect(rc
.right
, rc
.top
, widthNbook
- rc
.right
, height
),
963 #if USE_NOTEBOOK_ANTIFLICKER
964 // subclass the spin control used by the notebook to scroll pages to
965 // prevent it from flickering on resize
966 if ( !m_hasSubclassedUpdown
)
968 // iterate over all child windows to find spin button
969 for ( HWND child
= ::GetWindow(GetHwnd(), GW_CHILD
);
971 child
= ::GetWindow(child
, GW_HWNDNEXT
) )
973 wxWindow
*childWindow
= wxFindWinFromHandle((WXHWND
)child
);
975 // see if it exists, if no wxWindow found then assume it's the spin
979 // subclass the spin button to override WM_ERASEBKGND
980 if ( !gs_wndprocNotebookSpinBtn
)
981 gs_wndprocNotebookSpinBtn
= (WXFARPROC
)wxGetWindowProc(child
);
983 wxSetWindowProc(child
, wxNotebookSpinBtnWndProc
);
984 m_hasSubclassedUpdown
= true;
989 #endif // USE_NOTEBOOK_ANTIFLICKER
994 void wxNotebook::OnNavigationKey(wxNavigationKeyEvent
& event
)
996 if ( event
.IsWindowChange() ) {
998 AdvanceSelection(event
.GetDirection());
1001 // we get this event in 3 cases
1003 // a) one of our pages might have generated it because the user TABbed
1004 // out from it in which case we should propagate the event upwards and
1005 // our parent will take care of setting the focus to prev/next sibling
1009 // b) the parent panel wants to give the focus to us so that we
1010 // forward it to our selected page. We can't deal with this in
1011 // OnSetFocus() because we don't know which direction the focus came
1012 // from in this case and so can't choose between setting the focus to
1013 // first or last panel child
1017 // c) we ourselves (see MSWTranslateMessage) generated the event
1019 wxWindow
* const parent
= GetParent();
1021 // the wxObject* casts are required to avoid MinGW GCC 2.95.3 ICE
1022 const bool isFromParent
= event
.GetEventObject() == (wxObject
*) parent
;
1023 const bool isFromSelf
= event
.GetEventObject() == (wxObject
*) this;
1024 const bool isForward
= event
.GetDirection();
1026 if ( isFromSelf
&& !isForward
)
1028 // focus is currently on notebook tab and should leave
1029 // it backwards (Shift-TAB)
1030 event
.SetCurrentFocus(this);
1031 parent
->HandleWindowEvent(event
);
1033 else if ( isFromParent
|| isFromSelf
)
1035 // no, it doesn't come from child, case (b) or (c): forward to a
1036 // page but only if entering notebook page (i.e. direction is
1037 // backwards (Shift-TAB) comething from out-of-notebook, or
1038 // direction is forward (TAB) from ourselves),
1039 if ( m_selection
!= wxNOT_FOUND
&&
1040 (!event
.GetDirection() || isFromSelf
) )
1042 // so that the page knows that the event comes from it's parent
1043 // and is being propagated downwards
1044 event
.SetEventObject(this);
1046 wxWindow
*page
= m_pages
[m_selection
];
1047 if ( !page
->HandleWindowEvent(event
) )
1051 //else: page manages focus inside it itself
1053 else // otherwise set the focus to the notebook itself
1060 // it comes from our child, case (a), pass to the parent, but only
1061 // if the direction is forwards. Otherwise set the focus to the
1062 // notebook itself. The notebook is always the 'first' control of a
1070 event
.SetCurrentFocus(this);
1071 parent
->HandleWindowEvent(event
);
1079 bool wxNotebook::DoDrawBackground(WXHDC hDC
, wxWindow
*child
)
1081 wxUxThemeHandle
theme(child
? child
: this, L
"TAB");
1085 // get the notebook client rect (we're not interested in drawing tabs
1087 wxRect r
= GetPageSize();
1092 wxCopyRectToRECT(r
, rc
);
1094 // map rect to the coords of the window we're drawing in
1096 ::MapWindowPoints(GetHwnd(), GetHwndOf(child
), (POINT
*)&rc
, 2);
1098 // we have the content area (page size), but we need to draw all of the
1099 // background for it to be aligned correctly
1100 wxUxThemeEngine::Get()->GetThemeBackgroundExtent
1109 wxUxThemeEngine::Get()->DrawThemeBackground
1122 WXHBRUSH
wxNotebook::QueryBgBitmap()
1124 wxRect r
= GetPageSize();
1128 WindowHDC
hDC(GetHwnd());
1129 MemoryHDC
hDCMem(hDC
);
1130 CompatibleBitmap
hBmp(hDC
, r
.x
+ r
.width
, r
.y
+ r
.height
);
1132 SelectInHDC
selectBmp(hDCMem
, hBmp
);
1134 if ( !DoDrawBackground((WXHDC
)(HDC
)hDCMem
) )
1137 return (WXHBRUSH
)::CreatePatternBrush(hBmp
);
1140 void wxNotebook::UpdateBgBrush()
1142 if ( m_hbrBackground
)
1143 ::DeleteObject((HBRUSH
)m_hbrBackground
);
1145 if ( !m_hasBgCol
&& wxUxThemeEngine::GetIfActive() )
1147 m_hbrBackground
= QueryBgBitmap();
1149 else // no themes or we've got user-defined solid colour
1151 m_hbrBackground
= NULL
;
1155 WXHBRUSH
wxNotebook::MSWGetBgBrushForChild(WXHDC hDC
, wxWindow
*child
)
1157 if ( m_hbrBackground
)
1159 // before drawing with the background brush, we need to position it
1162 ::GetWindowRect(GetHwndOf(child
), &rc
);
1164 ::MapWindowPoints(NULL
, GetHwnd(), (POINT
*)&rc
, 1);
1166 if ( !::SetBrushOrgEx((HDC
)hDC
, -rc
.left
, -rc
.top
, NULL
) )
1168 wxLogLastError(wxT("SetBrushOrgEx(notebook bg brush)"));
1171 return m_hbrBackground
;
1174 return wxNotebookBase::MSWGetBgBrushForChild(hDC
, child
);
1177 bool wxNotebook::MSWPrintChild(WXHDC hDC
, wxWindow
*child
)
1179 // solid background colour overrides themed background drawing
1180 if ( !UseBgCol() && DoDrawBackground(hDC
, child
) )
1183 // If we're using a solid colour (for example if we've switched off
1184 // theming for this notebook), paint it
1187 wxRect r
= GetPageSize();
1192 wxCopyRectToRECT(r
, rc
);
1194 // map rect to the coords of the window we're drawing in
1196 ::MapWindowPoints(GetHwnd(), GetHwndOf(child
), (POINT
*)&rc
, 2);
1198 wxBrush
brush(GetBackgroundColour());
1199 HBRUSH hbr
= GetHbrushOf(brush
);
1201 ::FillRect((HDC
) hDC
, &rc
, hbr
);
1206 return wxNotebookBase::MSWPrintChild(hDC
, child
);
1209 #endif // wxUSE_UXTHEME
1211 // Windows only: attempts to get colour for UX theme page background
1212 wxColour
wxNotebook::GetThemeBackgroundColour() const
1215 if (wxUxThemeEngine::Get())
1217 wxUxThemeHandle
hTheme((wxNotebook
*) this, L
"TAB");
1220 // This is total guesswork.
1221 // See PlatformSDK\Include\Tmschema.h for values.
1222 // JACS: can also use 9 (TABP_PANE)
1223 COLORREF themeColor
;
1224 bool success
= (S_OK
== wxUxThemeEngine::Get()->GetThemeColor(
1228 3821 /* FILLCOLORHINT */,
1231 return GetBackgroundColour();
1234 [DS] Workaround for WindowBlinds:
1235 Some themes return a near black theme color using FILLCOLORHINT,
1236 this makes notebook pages have an ugly black background and makes
1237 text (usually black) unreadable. Retry again with FILLCOLOR.
1239 This workaround potentially breaks appearance of some themes,
1240 but in practice it already fixes some themes.
1242 if (themeColor
== 1)
1244 wxUxThemeEngine::Get()->GetThemeColor(
1248 3802 /* FILLCOLOR */,
1252 wxColour colour
= wxRGBToColour(themeColor
);
1254 // Under Vista, the tab background colour is reported incorrectly.
1255 // So for the default theme at least, hard-code the colour to something
1256 // that will blend in.
1258 static int s_AeroStatus
= -1;
1259 if (s_AeroStatus
== -1)
1261 WCHAR szwThemeFile
[1024];
1262 WCHAR szwThemeColor
[256];
1263 if (S_OK
== wxUxThemeEngine::Get()->GetCurrentThemeName(szwThemeFile
, 1024, szwThemeColor
, 256, NULL
, 0))
1265 wxString
themeFile(szwThemeFile
), themeColor(szwThemeColor
);
1266 if (themeFile
.Find(wxT("Aero")) != -1 && themeColor
== wxT("NormalColor"))
1275 if (s_AeroStatus
== 1)
1276 colour
= wxColour(255, 255, 255);
1281 #endif // wxUSE_UXTHEME
1283 return GetBackgroundColour();
1286 // ----------------------------------------------------------------------------
1287 // wxNotebook base class virtuals
1288 // ----------------------------------------------------------------------------
1290 #if wxUSE_CONSTRAINTS
1292 // override these 2 functions to do nothing: everything is done in OnSize
1294 void wxNotebook::SetConstraintSizes(bool WXUNUSED(recurse
))
1296 // don't set the sizes of the pages - their correct size is not yet known
1297 wxControl::SetConstraintSizes(false);
1300 bool wxNotebook::DoPhase(int WXUNUSED(nPhase
))
1305 #endif // wxUSE_CONSTRAINTS
1307 // ----------------------------------------------------------------------------
1308 // wxNotebook Windows message handlers
1309 // ----------------------------------------------------------------------------
1311 bool wxNotebook::MSWOnScroll(int orientation
, WXWORD nSBCode
,
1312 WXWORD pos
, WXHWND control
)
1314 // don't generate EVT_SCROLLWIN events for the WM_SCROLLs coming from the
1319 return wxNotebookBase::MSWOnScroll(orientation
, nSBCode
, pos
, control
);
1322 bool wxNotebook::MSWOnNotify(int idCtrl
, WXLPARAM lParam
, WXLPARAM
* result
)
1324 wxBookCtrlEvent
event(wxEVT_NULL
, m_windowId
);
1326 NMHDR
* hdr
= (NMHDR
*)lParam
;
1327 switch ( hdr
->code
) {
1329 event
.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
);
1332 case TCN_SELCHANGING
:
1333 event
.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
);
1337 return wxControl::MSWOnNotify(idCtrl
, lParam
, result
);
1340 event
.SetSelection(TabCtrl_GetCurSel(GetHwnd()));
1341 event
.SetOldSelection(m_selection
);
1342 event
.SetEventObject(this);
1343 event
.SetInt(idCtrl
);
1345 bool processed
= HandleWindowEvent(event
);
1346 if ( hdr
->code
== TCN_SELCHANGE
)
1347 UpdateSelection(event
.GetSelection());
1349 *result
= !event
.IsAllowed();
1353 #endif // wxUSE_NOTEBOOK