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()
144 m_hbrBackground
= NULL
;
145 #endif // wxUSE_UXTHEME
147 #if USE_NOTEBOOK_ANTIFLICKER
148 m_hasSubclassedUpdown
= false;
149 #endif // USE_NOTEBOOK_ANTIFLICKER
152 // default for dynamic class
153 wxNotebook::wxNotebook()
158 // the same arguments as for wxControl
159 wxNotebook::wxNotebook(wxWindow
*parent
,
164 const wxString
& name
)
168 Create(parent
, id
, pos
, size
, style
, name
);
172 bool wxNotebook::Create(wxWindow
*parent
,
177 const wxString
& name
)
179 if ( (style
& wxBK_ALIGN_MASK
) == wxBK_DEFAULT
)
181 #if defined(__POCKETPC__)
182 style
|= wxBK_BOTTOM
| wxNB_FLAT
;
189 // Not sure why, but without this style, there is no border
190 // around the notebook tabs.
191 if (style
& wxNB_FLAT
)
192 style
|= wxBORDER_SUNKEN
;
196 // ComCtl32 notebook tabs simply don't work unless they're on top if we
197 // have uxtheme, we can work around it later (after control creation), but
198 // if we have been compiled without uxtheme support, we have to clear those
200 if ( HasTroubleWithNonTopTabs() )
202 style
&= ~(wxBK_BOTTOM
| wxBK_LEFT
| wxBK_RIGHT
);
204 #endif //wxUSE_UXTHEME
206 #if defined(__WINE__) && wxUSE_UNICODE
207 LPCTSTR className
= L
"SysTabControl32";
209 LPCTSTR className
= WC_TABCONTROL
;
212 #if USE_NOTEBOOK_ANTIFLICKER
213 // SysTabCtl32 class has natively CS_HREDRAW and CS_VREDRAW enabled and it
214 // causes horrible flicker when resizing notebook, so get rid of it by
215 // using a class without these styles (but otherwise identical to it)
216 if ( !HasFlag(wxFULL_REPAINT_ON_RESIZE
) )
218 static ClassRegistrar s_clsNotebook
;
219 if ( !s_clsNotebook
.IsInitialized() )
221 // get a copy of standard class and modify it
224 if ( ::GetClassInfo(NULL
, WC_TABCONTROL
, &wc
) )
227 reinterpret_cast<WXFARPROC
>(wc
.lpfnWndProc
);
228 wc
.lpszClassName
= wxT("_wx_SysTabCtl32");
229 wc
.style
&= ~(CS_HREDRAW
| CS_VREDRAW
);
230 wc
.hInstance
= wxGetInstance();
231 wc
.lpfnWndProc
= wxNotebookWndProc
;
232 s_clsNotebook
.Register(wc
);
236 wxLogLastError(wxT("GetClassInfoEx(SysTabCtl32)"));
240 // use our custom class if available but fall back to the standard
241 // notebook if we failed to register it
242 if ( s_clsNotebook
.IsRegistered() )
244 // it's ok to use c_str() here as the static s_clsNotebook object
245 // has sufficiently long lifetime
246 className
= s_clsNotebook
.GetName().c_str();
249 #endif // USE_NOTEBOOK_ANTIFLICKER
251 if ( !CreateControl(parent
, id
, pos
, size
, style
| wxTAB_TRAVERSAL
,
252 wxDefaultValidator
, name
) )
255 if ( !MSWCreateControl(className
, wxEmptyString
, pos
, size
) )
259 if ( HasFlag(wxNB_NOPAGETHEME
) ||
260 wxSystemOptions::IsFalse(wxT("msw.notebook.themed-background")) )
262 SetBackgroundColour(GetThemeBackgroundColour());
264 else // use themed background by default
266 // create backing store
270 // comctl32.dll 6.0 doesn't support non-top tabs with visual styles (the
271 // control is simply not rendered correctly), so we disable themes
272 // if possible, otherwise we simply clear the styles.
273 if ( HasTroubleWithNonTopTabs() &&
274 (style
& (wxBK_BOTTOM
| wxBK_LEFT
| wxBK_RIGHT
)) )
276 // check if we use themes at all -- if we don't, we're still okay
277 if ( wxUxThemeEngine::GetIfActive() )
279 wxUxThemeEngine::GetIfActive()->SetWindowTheme(GetHwnd(), L
"", L
"");
281 // correct the background color for the new non-themed control
282 SetBackgroundColour(GetThemeBackgroundColour());
285 #endif // wxUSE_UXTHEME
287 // Undocumented hack to get flat notebook style
288 // In fact, we should probably only do this in some
289 // curcumstances, i.e. if we know we will have a border
290 // at the bottom (the tab control doesn't draw it itself)
291 #if defined(__POCKETPC__) || defined(__SMARTPHONE__)
292 if (HasFlag(wxNB_FLAT
))
294 SendMessage(GetHwnd(), CCM_SETVERSION
, COMCTL32_VERSION
, 0);
296 SetBackgroundColour(*wxWHITE
);
302 WXDWORD
wxNotebook::MSWGetStyle(long style
, WXDWORD
*exstyle
) const
304 WXDWORD tabStyle
= wxControl::MSWGetStyle(style
, exstyle
);
306 tabStyle
|= WS_TABSTOP
| TCS_TABS
;
308 if ( style
& wxNB_MULTILINE
)
309 tabStyle
|= TCS_MULTILINE
;
310 if ( style
& wxNB_FIXEDWIDTH
)
311 tabStyle
|= TCS_FIXEDWIDTH
;
313 if ( style
& wxBK_BOTTOM
)
314 tabStyle
|= TCS_RIGHT
;
315 else if ( style
& wxBK_LEFT
)
316 tabStyle
|= TCS_VERTICAL
;
317 else if ( style
& wxBK_RIGHT
)
318 tabStyle
|= TCS_VERTICAL
| TCS_RIGHT
;
323 wxNotebook::~wxNotebook()
326 if ( m_hbrBackground
)
327 ::DeleteObject((HBRUSH
)m_hbrBackground
);
328 #endif // wxUSE_UXTHEME
331 // ----------------------------------------------------------------------------
332 // wxNotebook accessors
333 // ----------------------------------------------------------------------------
335 size_t wxNotebook::GetPageCount() const
338 wxASSERT( (int)m_pages
.Count() == TabCtrl_GetItemCount(GetHwnd()) );
340 return m_pages
.Count();
343 int wxNotebook::GetRowCount() const
345 return TabCtrl_GetRowCount(GetHwnd());
348 int wxNotebook::SetSelection(size_t nPage
)
350 wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxNOT_FOUND
, wxT("notebook page out of range") );
352 if ( m_selection
== wxNOT_FOUND
|| nPage
!= (size_t)m_selection
)
354 if ( SendPageChangingEvent(nPage
) )
356 // program allows the page change
357 const int selectionOld
= m_selection
;
359 UpdateSelection(nPage
);
361 TabCtrl_SetCurSel(GetHwnd(), nPage
);
363 SendPageChangedEvent(selectionOld
, nPage
);
370 void wxNotebook::UpdateSelection(int selNew
)
372 if ( m_selection
!= wxNOT_FOUND
)
373 m_pages
[m_selection
]->Show(false);
375 if ( selNew
!= wxNOT_FOUND
)
377 wxNotebookPage
*pPage
= m_pages
[selNew
];
381 // Changing the page should give the focus to it but, as per bug report
382 // http://sf.net/tracker/index.php?func=detail&aid=1150659&group_id=9863&atid=109863,
383 // we should not set the focus to it directly since it erroneously
384 // selects radio buttons and breaks keyboard handling for a notebook's
385 // scroll buttons. So give focus to the notebook and not the page.
387 // but don't do this is the notebook is hidden
388 if ( ::IsWindowVisible(GetHwnd()) )
391 m_selection
= selNew
;
394 int wxNotebook::ChangeSelection(size_t nPage
)
396 wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxNOT_FOUND
, wxT("notebook page out of range") );
398 const int selOld
= m_selection
;
400 if ( m_selection
== wxNOT_FOUND
|| nPage
!= (size_t)m_selection
)
402 TabCtrl_SetCurSel(GetHwnd(), nPage
);
404 UpdateSelection(nPage
);
410 bool wxNotebook::SetPageText(size_t nPage
, const wxString
& strText
)
412 wxCHECK_MSG( IS_VALID_PAGE(nPage
), false, wxT("notebook page out of range") );
415 tcItem
.mask
= TCIF_TEXT
;
416 tcItem
.pszText
= (wxChar
*)strText
.wx_str();
418 if ( !HasFlag(wxNB_MULTILINE
) )
419 return TabCtrl_SetItem(GetHwnd(), nPage
, &tcItem
) != 0;
421 // multiline - we need to set new page size if a line is added or removed
422 int rows
= GetRowCount();
423 bool ret
= TabCtrl_SetItem(GetHwnd(), nPage
, &tcItem
) != 0;
425 if ( ret
&& rows
!= GetRowCount() )
427 const wxRect r
= GetPageSize();
428 const size_t count
= m_pages
.Count();
429 for ( size_t page
= 0; page
< count
; page
++ )
430 m_pages
[page
]->SetSize(r
);
436 wxString
wxNotebook::GetPageText(size_t nPage
) const
438 wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxEmptyString
, wxT("notebook page out of range") );
442 tcItem
.mask
= TCIF_TEXT
;
443 tcItem
.pszText
= buf
;
444 tcItem
.cchTextMax
= WXSIZEOF(buf
);
447 if ( TabCtrl_GetItem(GetHwnd(), nPage
, &tcItem
) )
448 str
= tcItem
.pszText
;
453 int wxNotebook::GetPageImage(size_t nPage
) const
455 wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxNOT_FOUND
, wxT("notebook page out of range") );
458 tcItem
.mask
= TCIF_IMAGE
;
460 return TabCtrl_GetItem(GetHwnd(), nPage
, &tcItem
) ? tcItem
.iImage
464 bool wxNotebook::SetPageImage(size_t nPage
, int nImage
)
466 wxCHECK_MSG( IS_VALID_PAGE(nPage
), false, wxT("notebook page out of range") );
469 tcItem
.mask
= TCIF_IMAGE
;
470 tcItem
.iImage
= nImage
;
472 return TabCtrl_SetItem(GetHwnd(), nPage
, &tcItem
) != 0;
475 void wxNotebook::SetImageList(wxImageList
* imageList
)
477 wxNotebookBase::SetImageList(imageList
);
481 (void) TabCtrl_SetImageList(GetHwnd(), GetHimagelistOf(imageList
));
485 // ----------------------------------------------------------------------------
486 // wxNotebook size settings
487 // ----------------------------------------------------------------------------
489 wxRect
wxNotebook::GetPageSize() const
494 ::GetClientRect(GetHwnd(), &rc
);
496 // This check is to work around a bug in TabCtrl_AdjustRect which will
497 // cause a crash on win2k or on XP with themes disabled if either
498 // wxNB_MULTILINE is used or tabs are placed on a side, if the rectangle
501 // The value of 20 is chosen arbitrarily but seems to work
502 if ( rc
.right
> 20 && rc
.bottom
> 20 )
504 TabCtrl_AdjustRect(GetHwnd(), false, &rc
);
506 wxCopyRECTToRect(rc
, r
);
512 void wxNotebook::SetPageSize(const wxSize
& size
)
514 // transform the page size into the notebook size
521 TabCtrl_AdjustRect(GetHwnd(), true, &rc
);
524 SetSize(rc
.right
- rc
.left
, rc
.bottom
- rc
.top
);
527 void wxNotebook::SetPadding(const wxSize
& padding
)
529 TabCtrl_SetPadding(GetHwnd(), padding
.x
, padding
.y
);
532 // Windows-only at present. Also, you must use the wxNB_FIXEDWIDTH
534 void wxNotebook::SetTabSize(const wxSize
& sz
)
536 ::SendMessage(GetHwnd(), TCM_SETITEMSIZE
, 0, MAKELPARAM(sz
.x
, sz
.y
));
539 wxSize
wxNotebook::CalcSizeFromPage(const wxSize
& sizePage
) const
541 // we can't use TabCtrl_AdjustRect here because it only works for wxNB_TOP
542 wxSize sizeTotal
= sizePage
;
545 if ( GetPageCount() > 0 )
548 TabCtrl_GetItemRect(GetHwnd(), 0, &rect
);
549 tabSize
.x
= rect
.right
- rect
.left
;
550 tabSize
.y
= rect
.bottom
- rect
.top
;
553 const int rows
= GetRowCount();
555 // add an extra margin in both directions
556 const int MARGIN
= 8;
559 sizeTotal
.x
+= MARGIN
;
560 sizeTotal
.y
+= tabSize
.y
* rows
+ MARGIN
;
562 else // horizontal layout
564 sizeTotal
.x
+= tabSize
.x
* rows
+ MARGIN
;
565 sizeTotal
.y
+= MARGIN
;
571 void wxNotebook::AdjustPageSize(wxNotebookPage
*page
)
573 wxCHECK_RET( page
, wxT("NULL page in wxNotebook::AdjustPageSize") );
575 const wxRect r
= GetPageSize();
582 // ----------------------------------------------------------------------------
583 // wxNotebook operations
584 // ----------------------------------------------------------------------------
586 // remove one page from the notebook, without deleting
587 wxNotebookPage
*wxNotebook::DoRemovePage(size_t nPage
)
589 wxNotebookPage
*pageRemoved
= wxNotebookBase::DoRemovePage(nPage
);
593 // hide the removed page to maintain the invariant that only the
594 // selected page is visible and others are hidden:
595 pageRemoved
->Show(false);
597 TabCtrl_DeleteItem(GetHwnd(), nPage
);
599 if ( m_pages
.IsEmpty() )
601 // no selection any more, the notebook becamse empty
602 m_selection
= wxNOT_FOUND
;
604 else // notebook still not empty
606 int selNew
= TabCtrl_GetCurSel(GetHwnd());
607 if ( selNew
!= wxNOT_FOUND
)
609 // No selection change, just refresh the current selection.
610 // Because it could be that the slection index changed
611 // we need to update it.
612 // Note: this does not mean the selection it self changed.
613 m_selection
= selNew
;
614 m_pages
[m_selection
]->Refresh();
616 else if (int(nPage
) == m_selection
)
618 // The selection was deleted.
620 // Determine new selection.
621 if (m_selection
== int(GetPageCount()))
622 selNew
= m_selection
- 1;
624 selNew
= m_selection
;
626 // m_selection must be always valid so reset it before calling
628 m_selection
= wxNOT_FOUND
;
629 SetSelection(selNew
);
633 wxFAIL
; // Windows did not behave ok.
641 bool wxNotebook::DeleteAllPages()
643 size_t nPageCount
= GetPageCount();
645 for ( nPage
= 0; nPage
< nPageCount
; nPage
++ )
646 delete m_pages
[nPage
];
650 TabCtrl_DeleteAllItems(GetHwnd());
652 m_selection
= wxNOT_FOUND
;
654 InvalidateBestSize();
658 // same as AddPage() but does it at given position
659 bool wxNotebook::InsertPage(size_t nPage
,
660 wxNotebookPage
*pPage
,
661 const wxString
& strText
,
665 wxCHECK_MSG( pPage
!= NULL
, false, wxT("NULL page in wxNotebook::InsertPage") );
666 wxCHECK_MSG( IS_VALID_PAGE(nPage
) || nPage
== GetPageCount(), false,
667 wxT("invalid index in wxNotebook::InsertPage") );
669 wxASSERT_MSG( pPage
->GetParent() == this,
670 wxT("notebook pages must have notebook as parent") );
672 // add a new tab to the control
673 // ----------------------------
675 // init all fields to 0
677 wxZeroMemory(tcItem
);
679 // set the image, if any
682 tcItem
.mask
|= TCIF_IMAGE
;
683 tcItem
.iImage
= imageId
;
687 if ( !strText
.empty() )
689 tcItem
.mask
|= TCIF_TEXT
;
690 tcItem
.pszText
= const_cast<wxChar
*>(strText
.wx_str());
693 // hide the page: unless it is selected, it shouldn't be shown (and if it
694 // is selected it will be shown later)
695 HWND hwnd
= GetWinHwnd(pPage
);
696 SetWindowLong(hwnd
, GWL_STYLE
, GetWindowLong(hwnd
, GWL_STYLE
) & ~WS_VISIBLE
);
698 // this updates internal flag too -- otherwise it would get out of sync
699 // with the real state
703 // fit the notebook page to the tab control's display area: this should be
704 // done before adding it to the notebook or TabCtrl_InsertItem() will
705 // change the notebooks size itself!
706 AdjustPageSize(pPage
);
708 // finally do insert it
709 if ( TabCtrl_InsertItem(GetHwnd(), nPage
, &tcItem
) == -1 )
711 wxLogError(wxT("Can't create the notebook page '%s'."), strText
.c_str());
716 // need to update the bg brush when the first page is added
717 // so the first panel gets the correct themed background
718 if ( m_pages
.empty() )
722 #endif // wxUSE_UXTHEME
725 // succeeded: save the pointer to the page
726 m_pages
.Insert(pPage
, nPage
);
728 // we may need to adjust the size again if the notebook size changed:
729 // normally this only happens for the first page we add (the tabs which
730 // hadn't been there before are now shown) but for a multiline notebook it
731 // can happen for any page at all as a new row could have been started
732 if ( m_pages
.GetCount() == 1 || HasFlag(wxNB_MULTILINE
) )
734 AdjustPageSize(pPage
);
737 // now deal with the selection
738 // ---------------------------
740 // if the inserted page is before the selected one, we must update the
741 // index of the selected page
742 if ( int(nPage
) <= m_selection
)
744 // one extra page added
748 DoSetSelectionAfterInsertion(nPage
, bSelect
);
750 InvalidateBestSize();
755 int wxNotebook::HitTest(const wxPoint
& pt
, long *flags
) const
757 TC_HITTESTINFO hitTestInfo
;
758 hitTestInfo
.pt
.x
= pt
.x
;
759 hitTestInfo
.pt
.y
= pt
.y
;
760 int item
= TabCtrl_HitTest(GetHwnd(), &hitTestInfo
);
766 if ((hitTestInfo
.flags
& TCHT_NOWHERE
) == TCHT_NOWHERE
)
767 *flags
|= wxBK_HITTEST_NOWHERE
;
768 if ((hitTestInfo
.flags
& TCHT_ONITEM
) == TCHT_ONITEM
)
769 *flags
|= wxBK_HITTEST_ONITEM
;
770 if ((hitTestInfo
.flags
& TCHT_ONITEMICON
) == TCHT_ONITEMICON
)
771 *flags
|= wxBK_HITTEST_ONICON
;
772 if ((hitTestInfo
.flags
& TCHT_ONITEMLABEL
) == TCHT_ONITEMLABEL
)
773 *flags
|= wxBK_HITTEST_ONLABEL
;
774 if ( item
== wxNOT_FOUND
&& GetPageSize().Contains(pt
) )
775 *flags
|= wxBK_HITTEST_ONPAGE
;
781 // ----------------------------------------------------------------------------
782 // flicker-less notebook redraw
783 // ----------------------------------------------------------------------------
785 #if USE_NOTEBOOK_ANTIFLICKER
787 // wnd proc for the spin button
788 LRESULT APIENTRY _EXPORT
wxNotebookSpinBtnWndProc(HWND hwnd
,
793 if ( message
== WM_ERASEBKGND
)
796 return ::CallWindowProc(CASTWNDPROC gs_wndprocNotebookSpinBtn
,
797 hwnd
, message
, wParam
, lParam
);
800 LRESULT APIENTRY _EXPORT
wxNotebookWndProc(HWND hwnd
,
805 return ::CallWindowProc(CASTWNDPROC gs_wndprocNotebook
,
806 hwnd
, message
, wParam
, lParam
);
809 void wxNotebook::OnEraseBackground(wxEraseEvent
& WXUNUSED(event
))
814 void wxNotebook::OnPaint(wxPaintEvent
& WXUNUSED(event
))
819 ::GetClientRect(GetHwnd(), &rc
);
820 wxBitmap
bmp(rc
.right
, rc
.bottom
);
821 memdc
.SelectObject(bmp
);
823 const wxLayoutDirection dir
= dc
.GetLayoutDirection();
824 memdc
.SetLayoutDirection(dir
);
826 // if there is no special brush just use the solid background colour
828 HBRUSH hbr
= (HBRUSH
)m_hbrBackground
;
835 brush
= wxBrush(GetBackgroundColour());
836 hbr
= GetHbrushOf(brush
);
839 wxMSWDCImpl
*impl
= (wxMSWDCImpl
*) memdc
.GetImpl();
841 ::FillRect(GetHdcOf(*impl
), &rc
, hbr
);
843 MSWDefWindowProc(WM_PAINT
, (WPARAM
)(impl
->GetHDC()), 0);
845 // For some reason in RTL mode, source offset has to be -1, otherwise the
846 // right border (physical) remains unpainted.
847 const wxCoord ofs
= dir
== wxLayout_RightToLeft
? -1 : 0;
848 dc
.Blit(ofs
, 0, rc
.right
, rc
.bottom
, &memdc
, ofs
, 0);
851 #endif // USE_NOTEBOOK_ANTIFLICKER
853 // ----------------------------------------------------------------------------
854 // wxNotebook callbacks
855 // ----------------------------------------------------------------------------
857 void wxNotebook::OnSize(wxSizeEvent
& event
)
859 if ( GetPageCount() == 0 )
861 // Prevents droppings on resize, but does cause some flicker
862 // when there are no pages.
870 // Without this, we can sometimes get droppings at the edges
871 // of a notebook, for example a notebook in a splitter window.
872 // This needs to be reconciled with the RefreshRect calls
873 // at the end of this function, which weren't enough to prevent
876 wxSize sz
= GetClientSize();
878 // Refresh right side
879 wxRect
rect(sz
.x
-4, 0, 4, sz
.y
);
882 // Refresh bottom side
883 rect
= wxRect(0, sz
.y
-4, sz
.x
, 4);
887 rect
= wxRect(0, 0, 4, sz
.y
);
890 #endif // !__WXWINCE__
892 // fit all the notebook pages to the tab control's display area
895 rc
.left
= rc
.top
= 0;
896 GetSize((int *)&rc
.right
, (int *)&rc
.bottom
);
898 // save the total size, we'll use it below
899 int widthNbook
= rc
.right
- rc
.left
,
900 heightNbook
= rc
.bottom
- rc
.top
;
902 // there seems to be a bug in the implementation of TabCtrl_AdjustRect(): it
903 // returns completely false values for multiline tab controls after the tabs
904 // are added but before getting the first WM_SIZE (off by ~50 pixels, see
906 // http://sf.net/tracker/index.php?func=detail&aid=645323&group_id=9863&atid=109863
908 // and the only work around I could find was this ugly hack... without it
909 // simply toggling the "multiline" checkbox in the notebook sample resulted
910 // in a noticeable page displacement
911 if ( HasFlag(wxNB_MULTILINE
) )
913 // avoid an infinite recursion: we get another notification too!
914 static bool s_isInOnSize
= false;
919 SendMessage(GetHwnd(), WM_SIZE
, SIZE_RESTORED
,
920 MAKELPARAM(rc
.right
, rc
.bottom
));
921 s_isInOnSize
= false;
924 // The best size depends on the number of rows of tabs, which can
925 // change when the notepad is resized.
926 InvalidateBestSize();
930 // background bitmap size has changed, update the brush using it too
932 #endif // wxUSE_UXTHEME
934 TabCtrl_AdjustRect(GetHwnd(), false, &rc
);
936 int width
= rc
.right
- rc
.left
,
937 height
= rc
.bottom
- rc
.top
;
938 size_t nCount
= m_pages
.Count();
939 for ( size_t nPage
= 0; nPage
< nCount
; nPage
++ ) {
940 wxNotebookPage
*pPage
= m_pages
[nPage
];
941 pPage
->SetSize(rc
.left
, rc
.top
, width
, height
);
945 // unless we had already repainted everything, we now need to refresh
946 if ( !HasFlag(wxFULL_REPAINT_ON_RESIZE
) )
948 // invalidate areas not covered by pages
949 RefreshRect(wxRect(0, 0, widthNbook
, rc
.top
), false);
950 RefreshRect(wxRect(0, rc
.top
, rc
.left
, height
), false);
951 RefreshRect(wxRect(0, rc
.bottom
, widthNbook
, heightNbook
- rc
.bottom
),
953 RefreshRect(wxRect(rc
.right
, rc
.top
, widthNbook
- rc
.right
, height
),
957 #if USE_NOTEBOOK_ANTIFLICKER
958 // subclass the spin control used by the notebook to scroll pages to
959 // prevent it from flickering on resize
960 if ( !m_hasSubclassedUpdown
)
962 // iterate over all child windows to find spin button
963 for ( HWND child
= ::GetWindow(GetHwnd(), GW_CHILD
);
965 child
= ::GetWindow(child
, GW_HWNDNEXT
) )
967 wxWindow
*childWindow
= wxFindWinFromHandle((WXHWND
)child
);
969 // see if it exists, if no wxWindow found then assume it's the spin
973 // subclass the spin button to override WM_ERASEBKGND
974 if ( !gs_wndprocNotebookSpinBtn
)
975 gs_wndprocNotebookSpinBtn
= (WXFARPROC
)wxGetWindowProc(child
);
977 wxSetWindowProc(child
, wxNotebookSpinBtnWndProc
);
978 m_hasSubclassedUpdown
= true;
983 #endif // USE_NOTEBOOK_ANTIFLICKER
988 void wxNotebook::OnNavigationKey(wxNavigationKeyEvent
& event
)
990 if ( event
.IsWindowChange() ) {
992 AdvanceSelection(event
.GetDirection());
995 // we get this event in 3 cases
997 // a) one of our pages might have generated it because the user TABbed
998 // out from it in which case we should propagate the event upwards and
999 // our parent will take care of setting the focus to prev/next sibling
1003 // b) the parent panel wants to give the focus to us so that we
1004 // forward it to our selected page. We can't deal with this in
1005 // OnSetFocus() because we don't know which direction the focus came
1006 // from in this case and so can't choose between setting the focus to
1007 // first or last panel child
1011 // c) we ourselves (see MSWTranslateMessage) generated the event
1013 wxWindow
* const parent
= GetParent();
1015 // the wxObject* casts are required to avoid MinGW GCC 2.95.3 ICE
1016 const bool isFromParent
= event
.GetEventObject() == (wxObject
*) parent
;
1017 const bool isFromSelf
= event
.GetEventObject() == (wxObject
*) this;
1018 const bool isForward
= event
.GetDirection();
1020 if ( isFromSelf
&& !isForward
)
1022 // focus is currently on notebook tab and should leave
1023 // it backwards (Shift-TAB)
1024 event
.SetCurrentFocus(this);
1025 parent
->HandleWindowEvent(event
);
1027 else if ( isFromParent
|| isFromSelf
)
1029 // no, it doesn't come from child, case (b) or (c): forward to a
1030 // page but only if entering notebook page (i.e. direction is
1031 // backwards (Shift-TAB) comething from out-of-notebook, or
1032 // direction is forward (TAB) from ourselves),
1033 if ( m_selection
!= wxNOT_FOUND
&&
1034 (!event
.GetDirection() || isFromSelf
) )
1036 // so that the page knows that the event comes from it's parent
1037 // and is being propagated downwards
1038 event
.SetEventObject(this);
1040 wxWindow
*page
= m_pages
[m_selection
];
1041 if ( !page
->HandleWindowEvent(event
) )
1045 //else: page manages focus inside it itself
1047 else // otherwise set the focus to the notebook itself
1054 // it comes from our child, case (a), pass to the parent, but only
1055 // if the direction is forwards. Otherwise set the focus to the
1056 // notebook itself. The notebook is always the 'first' control of a
1064 event
.SetCurrentFocus(this);
1065 parent
->HandleWindowEvent(event
);
1073 bool wxNotebook::DoDrawBackground(WXHDC hDC
, wxWindow
*child
)
1075 wxUxThemeHandle
theme(child
? child
: this, L
"TAB");
1079 // get the notebook client rect (we're not interested in drawing tabs
1081 wxRect r
= GetPageSize();
1086 wxCopyRectToRECT(r
, rc
);
1088 // map rect to the coords of the window we're drawing in
1090 ::MapWindowPoints(GetHwnd(), GetHwndOf(child
), (POINT
*)&rc
, 2);
1092 // we have the content area (page size), but we need to draw all of the
1093 // background for it to be aligned correctly
1094 wxUxThemeEngine::Get()->GetThemeBackgroundExtent
1103 wxUxThemeEngine::Get()->DrawThemeBackground
1116 WXHBRUSH
wxNotebook::QueryBgBitmap()
1118 wxRect r
= GetPageSize();
1122 WindowHDC
hDC(GetHwnd());
1123 MemoryHDC
hDCMem(hDC
);
1124 CompatibleBitmap
hBmp(hDC
, r
.x
+ r
.width
, r
.y
+ r
.height
);
1126 SelectInHDC
selectBmp(hDCMem
, hBmp
);
1128 if ( !DoDrawBackground((WXHDC
)(HDC
)hDCMem
) )
1131 return (WXHBRUSH
)::CreatePatternBrush(hBmp
);
1134 void wxNotebook::UpdateBgBrush()
1136 if ( m_hbrBackground
)
1137 ::DeleteObject((HBRUSH
)m_hbrBackground
);
1139 if ( !m_hasBgCol
&& wxUxThemeEngine::GetIfActive() )
1141 m_hbrBackground
= QueryBgBitmap();
1143 else // no themes or we've got user-defined solid colour
1145 m_hbrBackground
= NULL
;
1149 bool wxNotebook::MSWPrintChild(WXHDC hDC
, wxWindow
*child
)
1151 // solid background colour overrides themed background drawing
1152 if ( !UseBgCol() && DoDrawBackground(hDC
, child
) )
1155 // If we're using a solid colour (for example if we've switched off
1156 // theming for this notebook), paint it
1159 wxRect r
= GetPageSize();
1164 wxCopyRectToRECT(r
, rc
);
1166 // map rect to the coords of the window we're drawing in
1168 ::MapWindowPoints(GetHwnd(), GetHwndOf(child
), (POINT
*)&rc
, 2);
1170 wxBrush
brush(GetBackgroundColour());
1171 HBRUSH hbr
= GetHbrushOf(brush
);
1173 ::FillRect((HDC
) hDC
, &rc
, hbr
);
1178 return wxNotebookBase::MSWPrintChild(hDC
, child
);
1181 #endif // wxUSE_UXTHEME
1183 // Windows only: attempts to get colour for UX theme page background
1184 wxColour
wxNotebook::GetThemeBackgroundColour() const
1187 if (wxUxThemeEngine::Get())
1189 wxUxThemeHandle
hTheme((wxNotebook
*) this, L
"TAB");
1192 // This is total guesswork.
1193 // See PlatformSDK\Include\Tmschema.h for values.
1194 // JACS: can also use 9 (TABP_PANE)
1195 COLORREF themeColor
;
1196 bool success
= (S_OK
== wxUxThemeEngine::Get()->GetThemeColor(
1200 3821 /* FILLCOLORHINT */,
1203 return GetBackgroundColour();
1206 [DS] Workaround for WindowBlinds:
1207 Some themes return a near black theme color using FILLCOLORHINT,
1208 this makes notebook pages have an ugly black background and makes
1209 text (usually black) unreadable. Retry again with FILLCOLOR.
1211 This workaround potentially breaks appearance of some themes,
1212 but in practice it already fixes some themes.
1214 if (themeColor
== 1)
1216 wxUxThemeEngine::Get()->GetThemeColor(
1220 3802 /* FILLCOLOR */,
1224 wxColour colour
= wxRGBToColour(themeColor
);
1226 // Under Vista, the tab background colour is reported incorrectly.
1227 // So for the default theme at least, hard-code the colour to something
1228 // that will blend in.
1230 static int s_AeroStatus
= -1;
1231 if (s_AeroStatus
== -1)
1233 WCHAR szwThemeFile
[1024];
1234 WCHAR szwThemeColor
[256];
1235 if (S_OK
== wxUxThemeEngine::Get()->GetCurrentThemeName(szwThemeFile
, 1024, szwThemeColor
, 256, NULL
, 0))
1237 wxString
themeFile(szwThemeFile
), themeColor(szwThemeColor
);
1238 if (themeFile
.Find(wxT("Aero")) != -1 && themeColor
== wxT("NormalColor"))
1247 if (s_AeroStatus
== 1)
1248 colour
= wxColour(255, 255, 255);
1253 #endif // wxUSE_UXTHEME
1255 return GetBackgroundColour();
1258 // ----------------------------------------------------------------------------
1259 // wxNotebook base class virtuals
1260 // ----------------------------------------------------------------------------
1262 #if wxUSE_CONSTRAINTS
1264 // override these 2 functions to do nothing: everything is done in OnSize
1266 void wxNotebook::SetConstraintSizes(bool WXUNUSED(recurse
))
1268 // don't set the sizes of the pages - their correct size is not yet known
1269 wxControl::SetConstraintSizes(false);
1272 bool wxNotebook::DoPhase(int WXUNUSED(nPhase
))
1277 #endif // wxUSE_CONSTRAINTS
1279 // ----------------------------------------------------------------------------
1280 // wxNotebook Windows message handlers
1281 // ----------------------------------------------------------------------------
1283 bool wxNotebook::MSWOnScroll(int orientation
, WXWORD nSBCode
,
1284 WXWORD pos
, WXHWND control
)
1286 // don't generate EVT_SCROLLWIN events for the WM_SCROLLs coming from the
1291 return wxNotebookBase::MSWOnScroll(orientation
, nSBCode
, pos
, control
);
1294 bool wxNotebook::MSWOnNotify(int idCtrl
, WXLPARAM lParam
, WXLPARAM
* result
)
1296 wxBookCtrlEvent
event(wxEVT_NULL
, m_windowId
);
1298 NMHDR
* hdr
= (NMHDR
*)lParam
;
1299 switch ( hdr
->code
) {
1301 event
.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
);
1304 case TCN_SELCHANGING
:
1305 event
.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
);
1309 return wxControl::MSWOnNotify(idCtrl
, lParam
, result
);
1312 event
.SetSelection(TabCtrl_GetCurSel(GetHwnd()));
1313 event
.SetOldSelection(m_selection
);
1314 event
.SetEventObject(this);
1315 event
.SetInt(idCtrl
);
1317 // Change the selection before generating the event as its handler should
1318 // already see the new page selected.
1319 if ( hdr
->code
== TCN_SELCHANGE
)
1320 UpdateSelection(event
.GetSelection());
1322 bool processed
= HandleWindowEvent(event
);
1323 *result
= !event
.IsAllowed();
1327 #endif // wxUSE_NOTEBOOK