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 #endif // USE_NOTEBOOK_ANTIFLICKER
150 // default for dynamic class
151 wxNotebook::wxNotebook()
156 // the same arguments as for wxControl
157 wxNotebook::wxNotebook(wxWindow
*parent
,
162 const wxString
& name
)
166 Create(parent
, id
, pos
, size
, style
, name
);
170 bool wxNotebook::Create(wxWindow
*parent
,
175 const wxString
& name
)
177 if ( (style
& wxBK_ALIGN_MASK
) == wxBK_DEFAULT
)
179 #if defined(__POCKETPC__)
180 style
|= wxBK_BOTTOM
| wxNB_FLAT
;
187 // Not sure why, but without this style, there is no border
188 // around the notebook tabs.
189 if (style
& wxNB_FLAT
)
190 style
|= wxBORDER_SUNKEN
;
194 // ComCtl32 notebook tabs simply don't work unless they're on top if we
195 // have uxtheme, we can work around it later (after control creation), but
196 // if we have been compiled without uxtheme support, we have to clear those
198 if ( HasTroubleWithNonTopTabs() )
200 style
&= ~(wxBK_BOTTOM
| wxBK_LEFT
| wxBK_RIGHT
);
202 #endif //wxUSE_UXTHEME
204 #if defined(__WINE__) && wxUSE_UNICODE
205 LPCTSTR className
= L
"SysTabControl32";
207 LPCTSTR className
= WC_TABCONTROL
;
210 #if USE_NOTEBOOK_ANTIFLICKER
211 // SysTabCtl32 class has natively CS_HREDRAW and CS_VREDRAW enabled and it
212 // causes horrible flicker when resizing notebook, so get rid of it by
213 // using a class without these styles (but otherwise identical to it)
214 if ( !HasFlag(wxFULL_REPAINT_ON_RESIZE
) )
216 static ClassRegistrar s_clsNotebook
;
217 if ( !s_clsNotebook
.IsInitialized() )
219 // get a copy of standard class and modify it
222 if ( ::GetClassInfo(NULL
, WC_TABCONTROL
, &wc
) )
225 reinterpret_cast<WXFARPROC
>(wc
.lpfnWndProc
);
226 wc
.lpszClassName
= wxT("_wx_SysTabCtl32");
227 wc
.style
&= ~(CS_HREDRAW
| CS_VREDRAW
);
228 wc
.hInstance
= wxGetInstance();
229 wc
.lpfnWndProc
= wxNotebookWndProc
;
230 s_clsNotebook
.Register(wc
);
234 wxLogLastError(wxT("GetClassInfoEx(SysTabCtl32)"));
238 // use our custom class if available but fall back to the standard
239 // notebook if we failed to register it
240 if ( s_clsNotebook
.IsRegistered() )
242 // it's ok to use c_str() here as the static s_clsNotebook object
243 // has sufficiently long lifetime
244 className
= s_clsNotebook
.GetName().c_str();
247 #endif // USE_NOTEBOOK_ANTIFLICKER
249 if ( !CreateControl(parent
, id
, pos
, size
, style
| wxTAB_TRAVERSAL
,
250 wxDefaultValidator
, name
) )
253 if ( !MSWCreateControl(className
, wxEmptyString
, pos
, size
) )
257 if ( HasFlag(wxNB_NOPAGETHEME
) ||
258 wxSystemOptions::IsFalse(wxT("msw.notebook.themed-background")) )
260 SetBackgroundColour(GetThemeBackgroundColour());
262 else // use themed background by default
264 // create backing store
268 // comctl32.dll 6.0 doesn't support non-top tabs with visual styles (the
269 // control is simply not rendered correctly), so we disable themes
270 // if possible, otherwise we simply clear the styles.
271 if ( HasTroubleWithNonTopTabs() &&
272 (style
& (wxBK_BOTTOM
| wxBK_LEFT
| wxBK_RIGHT
)) )
274 // check if we use themes at all -- if we don't, we're still okay
275 if ( wxUxThemeEngine::GetIfActive() )
277 wxUxThemeEngine::GetIfActive()->SetWindowTheme(GetHwnd(), L
"", L
"");
279 // correct the background color for the new non-themed control
280 SetBackgroundColour(GetThemeBackgroundColour());
283 #endif // wxUSE_UXTHEME
285 // Undocumented hack to get flat notebook style
286 // In fact, we should probably only do this in some
287 // curcumstances, i.e. if we know we will have a border
288 // at the bottom (the tab control doesn't draw it itself)
289 #if defined(__POCKETPC__) || defined(__SMARTPHONE__)
290 if (HasFlag(wxNB_FLAT
))
292 SendMessage(GetHwnd(), CCM_SETVERSION
, COMCTL32_VERSION
, 0);
294 SetBackgroundColour(*wxWHITE
);
300 WXDWORD
wxNotebook::MSWGetStyle(long style
, WXDWORD
*exstyle
) const
302 WXDWORD tabStyle
= wxControl::MSWGetStyle(style
, exstyle
);
304 tabStyle
|= WS_TABSTOP
| TCS_TABS
;
306 if ( style
& wxNB_MULTILINE
)
307 tabStyle
|= TCS_MULTILINE
;
308 if ( style
& wxNB_FIXEDWIDTH
)
309 tabStyle
|= TCS_FIXEDWIDTH
;
311 if ( style
& wxBK_BOTTOM
)
312 tabStyle
|= TCS_RIGHT
;
313 else if ( style
& wxBK_LEFT
)
314 tabStyle
|= TCS_VERTICAL
;
315 else if ( style
& wxBK_RIGHT
)
316 tabStyle
|= TCS_VERTICAL
| TCS_RIGHT
;
321 wxNotebook::~wxNotebook()
324 if ( m_hbrBackground
)
325 ::DeleteObject((HBRUSH
)m_hbrBackground
);
326 #endif // wxUSE_UXTHEME
329 // ----------------------------------------------------------------------------
330 // wxNotebook accessors
331 // ----------------------------------------------------------------------------
333 size_t wxNotebook::GetPageCount() const
336 wxASSERT( (int)m_pages
.Count() == TabCtrl_GetItemCount(GetHwnd()) );
338 return m_pages
.Count();
341 int wxNotebook::GetRowCount() const
343 return TabCtrl_GetRowCount(GetHwnd());
346 int wxNotebook::SetSelection(size_t nPage
)
348 wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxNOT_FOUND
, wxT("notebook page out of range") );
350 if ( m_selection
== wxNOT_FOUND
|| nPage
!= (size_t)m_selection
)
352 if ( SendPageChangingEvent(nPage
) )
354 // program allows the page change
355 const int selectionOld
= m_selection
;
357 UpdateSelection(nPage
);
359 TabCtrl_SetCurSel(GetHwnd(), nPage
);
361 SendPageChangedEvent(selectionOld
, nPage
);
368 void wxNotebook::UpdateSelection(int selNew
)
370 if ( m_selection
!= wxNOT_FOUND
)
371 m_pages
[m_selection
]->Show(false);
373 if ( selNew
!= wxNOT_FOUND
)
375 wxNotebookPage
*pPage
= m_pages
[selNew
];
379 // Changing the page should give the focus to it but, as per bug report
380 // http://sf.net/tracker/index.php?func=detail&aid=1150659&group_id=9863&atid=109863,
381 // we should not set the focus to it directly since it erroneously
382 // selects radio buttons and breaks keyboard handling for a notebook's
383 // scroll buttons. So give focus to the notebook and not the page.
385 // but don't do this is the notebook is hidden
386 if ( ::IsWindowVisible(GetHwnd()) )
389 m_selection
= selNew
;
392 int wxNotebook::ChangeSelection(size_t nPage
)
394 wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxNOT_FOUND
, wxT("notebook page out of range") );
396 const int selOld
= m_selection
;
398 if ( m_selection
== wxNOT_FOUND
|| nPage
!= (size_t)m_selection
)
400 TabCtrl_SetCurSel(GetHwnd(), nPage
);
402 UpdateSelection(nPage
);
408 bool wxNotebook::SetPageText(size_t nPage
, const wxString
& strText
)
410 wxCHECK_MSG( IS_VALID_PAGE(nPage
), false, wxT("notebook page out of range") );
413 tcItem
.mask
= TCIF_TEXT
;
414 tcItem
.pszText
= (wxChar
*)strText
.wx_str();
416 if ( !HasFlag(wxNB_MULTILINE
) )
417 return TabCtrl_SetItem(GetHwnd(), nPage
, &tcItem
) != 0;
419 // multiline - we need to set new page size if a line is added or removed
420 int rows
= GetRowCount();
421 bool ret
= TabCtrl_SetItem(GetHwnd(), nPage
, &tcItem
) != 0;
423 if ( ret
&& rows
!= GetRowCount() )
425 const wxRect r
= GetPageSize();
426 const size_t count
= m_pages
.Count();
427 for ( size_t page
= 0; page
< count
; page
++ )
428 m_pages
[page
]->SetSize(r
);
434 wxString
wxNotebook::GetPageText(size_t nPage
) const
436 wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxEmptyString
, wxT("notebook page out of range") );
440 tcItem
.mask
= TCIF_TEXT
;
441 tcItem
.pszText
= buf
;
442 tcItem
.cchTextMax
= WXSIZEOF(buf
);
445 if ( TabCtrl_GetItem(GetHwnd(), nPage
, &tcItem
) )
446 str
= tcItem
.pszText
;
451 int wxNotebook::GetPageImage(size_t nPage
) const
453 wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxNOT_FOUND
, wxT("notebook page out of range") );
456 tcItem
.mask
= TCIF_IMAGE
;
458 return TabCtrl_GetItem(GetHwnd(), nPage
, &tcItem
) ? tcItem
.iImage
462 bool wxNotebook::SetPageImage(size_t nPage
, int nImage
)
464 wxCHECK_MSG( IS_VALID_PAGE(nPage
), false, wxT("notebook page out of range") );
467 tcItem
.mask
= TCIF_IMAGE
;
468 tcItem
.iImage
= nImage
;
470 return TabCtrl_SetItem(GetHwnd(), nPage
, &tcItem
) != 0;
473 void wxNotebook::SetImageList(wxImageList
* imageList
)
475 wxNotebookBase::SetImageList(imageList
);
479 (void) TabCtrl_SetImageList(GetHwnd(), GetHimagelistOf(imageList
));
483 // ----------------------------------------------------------------------------
484 // wxNotebook size settings
485 // ----------------------------------------------------------------------------
487 wxRect
wxNotebook::GetPageSize() const
492 ::GetClientRect(GetHwnd(), &rc
);
494 // This check is to work around a bug in TabCtrl_AdjustRect which will
495 // cause a crash on win2k or on XP with themes disabled if either
496 // wxNB_MULTILINE is used or tabs are placed on a side, if the rectangle
499 // The value of 20 is chosen arbitrarily but seems to work
500 if ( rc
.right
> 20 && rc
.bottom
> 20 )
502 TabCtrl_AdjustRect(GetHwnd(), false, &rc
);
504 wxCopyRECTToRect(rc
, r
);
510 void wxNotebook::SetPageSize(const wxSize
& size
)
512 // transform the page size into the notebook size
519 TabCtrl_AdjustRect(GetHwnd(), true, &rc
);
522 SetSize(rc
.right
- rc
.left
, rc
.bottom
- rc
.top
);
525 void wxNotebook::SetPadding(const wxSize
& padding
)
527 TabCtrl_SetPadding(GetHwnd(), padding
.x
, padding
.y
);
530 // Windows-only at present. Also, you must use the wxNB_FIXEDWIDTH
532 void wxNotebook::SetTabSize(const wxSize
& sz
)
534 ::SendMessage(GetHwnd(), TCM_SETITEMSIZE
, 0, MAKELPARAM(sz
.x
, sz
.y
));
537 wxSize
wxNotebook::CalcSizeFromPage(const wxSize
& sizePage
) const
539 // we can't use TabCtrl_AdjustRect here because it only works for wxNB_TOP
540 wxSize sizeTotal
= sizePage
;
543 if ( GetPageCount() > 0 )
546 TabCtrl_GetItemRect(GetHwnd(), 0, &rect
);
547 tabSize
.x
= rect
.right
- rect
.left
;
548 tabSize
.y
= rect
.bottom
- rect
.top
;
551 const int rows
= GetRowCount();
553 // add an extra margin in both directions
554 const int MARGIN
= 8;
557 sizeTotal
.x
+= MARGIN
;
558 sizeTotal
.y
+= tabSize
.y
* rows
+ MARGIN
;
560 else // horizontal layout
562 sizeTotal
.x
+= tabSize
.x
* rows
+ MARGIN
;
563 sizeTotal
.y
+= MARGIN
;
569 void wxNotebook::AdjustPageSize(wxNotebookPage
*page
)
571 wxCHECK_RET( page
, wxT("NULL page in wxNotebook::AdjustPageSize") );
573 const wxRect r
= GetPageSize();
580 // ----------------------------------------------------------------------------
581 // wxNotebook operations
582 // ----------------------------------------------------------------------------
584 // remove one page from the notebook, without deleting
585 wxNotebookPage
*wxNotebook::DoRemovePage(size_t nPage
)
587 wxNotebookPage
*pageRemoved
= wxNotebookBase::DoRemovePage(nPage
);
591 // hide the removed page to maintain the invariant that only the
592 // selected page is visible and others are hidden:
593 pageRemoved
->Show(false);
595 TabCtrl_DeleteItem(GetHwnd(), nPage
);
597 if ( m_pages
.IsEmpty() )
599 // no selection any more, the notebook becamse empty
600 m_selection
= wxNOT_FOUND
;
602 else // notebook still not empty
604 int selNew
= TabCtrl_GetCurSel(GetHwnd());
605 if ( selNew
!= wxNOT_FOUND
)
607 // No selection change, just refresh the current selection.
608 // Because it could be that the slection index changed
609 // we need to update it.
610 // Note: this does not mean the selection it self changed.
611 m_selection
= selNew
;
612 m_pages
[m_selection
]->Refresh();
614 else if (int(nPage
) == m_selection
)
616 // The selection was deleted.
618 // Determine new selection.
619 if (m_selection
== int(GetPageCount()))
620 selNew
= m_selection
- 1;
622 selNew
= m_selection
;
624 // m_selection must be always valid so reset it before calling
626 m_selection
= wxNOT_FOUND
;
627 SetSelection(selNew
);
631 wxFAIL
; // Windows did not behave ok.
639 bool wxNotebook::DeleteAllPages()
641 size_t nPageCount
= GetPageCount();
643 for ( nPage
= 0; nPage
< nPageCount
; nPage
++ )
644 delete m_pages
[nPage
];
648 TabCtrl_DeleteAllItems(GetHwnd());
650 m_selection
= wxNOT_FOUND
;
652 InvalidateBestSize();
656 // same as AddPage() but does it at given position
657 bool wxNotebook::InsertPage(size_t nPage
,
658 wxNotebookPage
*pPage
,
659 const wxString
& strText
,
663 wxCHECK_MSG( pPage
!= NULL
, false, wxT("NULL page in wxNotebook::InsertPage") );
664 wxCHECK_MSG( IS_VALID_PAGE(nPage
) || nPage
== GetPageCount(), false,
665 wxT("invalid index in wxNotebook::InsertPage") );
667 wxASSERT_MSG( pPage
->GetParent() == this,
668 wxT("notebook pages must have notebook as parent") );
670 // add a new tab to the control
671 // ----------------------------
673 // init all fields to 0
675 wxZeroMemory(tcItem
);
677 // set the image, if any
680 tcItem
.mask
|= TCIF_IMAGE
;
681 tcItem
.iImage
= imageId
;
685 if ( !strText
.empty() )
687 tcItem
.mask
|= TCIF_TEXT
;
688 tcItem
.pszText
= const_cast<wxChar
*>(strText
.wx_str());
691 // hide the page: unless it is selected, it shouldn't be shown (and if it
692 // is selected it will be shown later)
693 HWND hwnd
= GetWinHwnd(pPage
);
694 SetWindowLong(hwnd
, GWL_STYLE
, GetWindowLong(hwnd
, GWL_STYLE
) & ~WS_VISIBLE
);
696 // this updates internal flag too -- otherwise it would get out of sync
697 // with the real state
701 // fit the notebook page to the tab control's display area: this should be
702 // done before adding it to the notebook or TabCtrl_InsertItem() will
703 // change the notebooks size itself!
704 AdjustPageSize(pPage
);
706 // finally do insert it
707 if ( TabCtrl_InsertItem(GetHwnd(), nPage
, &tcItem
) == -1 )
709 wxLogError(wxT("Can't create the notebook page '%s'."), strText
.c_str());
714 // need to update the bg brush when the first page is added
715 // so the first panel gets the correct themed background
716 if ( m_pages
.empty() )
720 #endif // wxUSE_UXTHEME
723 // succeeded: save the pointer to the page
724 m_pages
.Insert(pPage
, nPage
);
726 // we may need to adjust the size again if the notebook size changed:
727 // normally this only happens for the first page we add (the tabs which
728 // hadn't been there before are now shown) but for a multiline notebook it
729 // can happen for any page at all as a new row could have been started
730 if ( m_pages
.GetCount() == 1 || HasFlag(wxNB_MULTILINE
) )
732 AdjustPageSize(pPage
);
735 // now deal with the selection
736 // ---------------------------
738 // if the inserted page is before the selected one, we must update the
739 // index of the selected page
740 if ( int(nPage
) <= m_selection
)
742 // one extra page added
746 DoSetSelectionAfterInsertion(nPage
, bSelect
);
748 InvalidateBestSize();
753 int wxNotebook::HitTest(const wxPoint
& pt
, long *flags
) const
755 TC_HITTESTINFO hitTestInfo
;
756 hitTestInfo
.pt
.x
= pt
.x
;
757 hitTestInfo
.pt
.y
= pt
.y
;
758 int item
= TabCtrl_HitTest(GetHwnd(), &hitTestInfo
);
764 if ((hitTestInfo
.flags
& TCHT_NOWHERE
) == TCHT_NOWHERE
)
765 *flags
|= wxBK_HITTEST_NOWHERE
;
766 if ((hitTestInfo
.flags
& TCHT_ONITEM
) == TCHT_ONITEM
)
767 *flags
|= wxBK_HITTEST_ONITEM
;
768 if ((hitTestInfo
.flags
& TCHT_ONITEMICON
) == TCHT_ONITEMICON
)
769 *flags
|= wxBK_HITTEST_ONICON
;
770 if ((hitTestInfo
.flags
& TCHT_ONITEMLABEL
) == TCHT_ONITEMLABEL
)
771 *flags
|= wxBK_HITTEST_ONLABEL
;
772 if ( item
== wxNOT_FOUND
&& GetPageSize().Contains(pt
) )
773 *flags
|= wxBK_HITTEST_ONPAGE
;
779 // ----------------------------------------------------------------------------
780 // flicker-less notebook redraw
781 // ----------------------------------------------------------------------------
783 #if USE_NOTEBOOK_ANTIFLICKER
785 // wnd proc for the spin button
786 LRESULT APIENTRY _EXPORT
wxNotebookSpinBtnWndProc(HWND hwnd
,
791 if ( message
== WM_ERASEBKGND
)
794 return ::CallWindowProc(CASTWNDPROC gs_wndprocNotebookSpinBtn
,
795 hwnd
, message
, wParam
, lParam
);
798 LRESULT APIENTRY _EXPORT
wxNotebookWndProc(HWND hwnd
,
803 return ::CallWindowProc(CASTWNDPROC gs_wndprocNotebook
,
804 hwnd
, message
, wParam
, lParam
);
807 void wxNotebook::OnEraseBackground(wxEraseEvent
& WXUNUSED(event
))
812 void wxNotebook::OnPaint(wxPaintEvent
& WXUNUSED(event
))
817 ::GetClientRect(GetHwnd(), &rc
);
818 wxBitmap
bmp(rc
.right
, rc
.bottom
);
819 memdc
.SelectObject(bmp
);
821 const wxLayoutDirection dir
= dc
.GetLayoutDirection();
822 memdc
.SetLayoutDirection(dir
);
824 // if there is no special brush just use the solid background colour
826 HBRUSH hbr
= (HBRUSH
)m_hbrBackground
;
833 brush
= wxBrush(GetBackgroundColour());
834 hbr
= GetHbrushOf(brush
);
837 wxMSWDCImpl
*impl
= (wxMSWDCImpl
*) memdc
.GetImpl();
839 ::FillRect(GetHdcOf(*impl
), &rc
, hbr
);
841 MSWDefWindowProc(WM_PAINT
, (WPARAM
)(impl
->GetHDC()), 0);
843 // For some reason in RTL mode, source offset has to be -1, otherwise the
844 // right border (physical) remains unpainted.
845 const wxCoord ofs
= dir
== wxLayout_RightToLeft
? -1 : 0;
846 dc
.Blit(ofs
, 0, rc
.right
, rc
.bottom
, &memdc
, ofs
, 0);
849 #endif // USE_NOTEBOOK_ANTIFLICKER
851 // ----------------------------------------------------------------------------
852 // wxNotebook callbacks
853 // ----------------------------------------------------------------------------
855 void wxNotebook::OnSize(wxSizeEvent
& event
)
857 if ( GetPageCount() == 0 )
859 // Prevents droppings on resize, but does cause some flicker
860 // when there are no pages.
868 // Without this, we can sometimes get droppings at the edges
869 // of a notebook, for example a notebook in a splitter window.
870 // This needs to be reconciled with the RefreshRect calls
871 // at the end of this function, which weren't enough to prevent
874 wxSize sz
= GetClientSize();
876 // Refresh right side
877 wxRect
rect(sz
.x
-4, 0, 4, sz
.y
);
880 // Refresh bottom side
881 rect
= wxRect(0, sz
.y
-4, sz
.x
, 4);
885 rect
= wxRect(0, 0, 4, sz
.y
);
888 #endif // !__WXWINCE__
890 // fit all the notebook pages to the tab control's display area
893 rc
.left
= rc
.top
= 0;
894 GetSize((int *)&rc
.right
, (int *)&rc
.bottom
);
896 // save the total size, we'll use it below
897 int widthNbook
= rc
.right
- rc
.left
,
898 heightNbook
= rc
.bottom
- rc
.top
;
900 // there seems to be a bug in the implementation of TabCtrl_AdjustRect(): it
901 // returns completely false values for multiline tab controls after the tabs
902 // are added but before getting the first WM_SIZE (off by ~50 pixels, see
904 // http://sf.net/tracker/index.php?func=detail&aid=645323&group_id=9863&atid=109863
906 // and the only work around I could find was this ugly hack... without it
907 // simply toggling the "multiline" checkbox in the notebook sample resulted
908 // in a noticeable page displacement
909 if ( HasFlag(wxNB_MULTILINE
) )
911 // avoid an infinite recursion: we get another notification too!
912 static bool s_isInOnSize
= false;
917 SendMessage(GetHwnd(), WM_SIZE
, SIZE_RESTORED
,
918 MAKELPARAM(rc
.right
, rc
.bottom
));
919 s_isInOnSize
= false;
922 // The best size depends on the number of rows of tabs, which can
923 // change when the notepad is resized.
924 InvalidateBestSize();
928 // background bitmap size has changed, update the brush using it too
930 #endif // wxUSE_UXTHEME
932 TabCtrl_AdjustRect(GetHwnd(), false, &rc
);
934 int width
= rc
.right
- rc
.left
,
935 height
= rc
.bottom
- rc
.top
;
936 size_t nCount
= m_pages
.Count();
937 for ( size_t nPage
= 0; nPage
< nCount
; nPage
++ ) {
938 wxNotebookPage
*pPage
= m_pages
[nPage
];
939 pPage
->SetSize(rc
.left
, rc
.top
, width
, height
);
943 // unless we had already repainted everything, we now need to refresh
944 if ( !HasFlag(wxFULL_REPAINT_ON_RESIZE
) )
946 // invalidate areas not covered by pages
947 RefreshRect(wxRect(0, 0, widthNbook
, rc
.top
), false);
948 RefreshRect(wxRect(0, rc
.top
, rc
.left
, height
), false);
949 RefreshRect(wxRect(0, rc
.bottom
, widthNbook
, heightNbook
- rc
.bottom
),
951 RefreshRect(wxRect(rc
.right
, rc
.top
, widthNbook
- rc
.right
, height
),
955 #if USE_NOTEBOOK_ANTIFLICKER
956 // subclass the spin control used by the notebook to scroll pages to
957 // prevent it from flickering on resize
958 if ( !m_hasSubclassedUpdown
)
960 // iterate over all child windows to find spin button
961 for ( HWND child
= ::GetWindow(GetHwnd(), GW_CHILD
);
963 child
= ::GetWindow(child
, GW_HWNDNEXT
) )
965 wxWindow
*childWindow
= wxFindWinFromHandle((WXHWND
)child
);
967 // see if it exists, if no wxWindow found then assume it's the spin
971 // subclass the spin button to override WM_ERASEBKGND
972 if ( !gs_wndprocNotebookSpinBtn
)
973 gs_wndprocNotebookSpinBtn
= (WXFARPROC
)wxGetWindowProc(child
);
975 wxSetWindowProc(child
, wxNotebookSpinBtnWndProc
);
976 m_hasSubclassedUpdown
= true;
981 #endif // USE_NOTEBOOK_ANTIFLICKER
986 void wxNotebook::OnNavigationKey(wxNavigationKeyEvent
& event
)
988 if ( event
.IsWindowChange() ) {
990 AdvanceSelection(event
.GetDirection());
993 // we get this event in 3 cases
995 // a) one of our pages might have generated it because the user TABbed
996 // out from it in which case we should propagate the event upwards and
997 // our parent will take care of setting the focus to prev/next sibling
1001 // b) the parent panel wants to give the focus to us so that we
1002 // forward it to our selected page. We can't deal with this in
1003 // OnSetFocus() because we don't know which direction the focus came
1004 // from in this case and so can't choose between setting the focus to
1005 // first or last panel child
1009 // c) we ourselves (see MSWTranslateMessage) generated the event
1011 wxWindow
* const parent
= GetParent();
1013 // the wxObject* casts are required to avoid MinGW GCC 2.95.3 ICE
1014 const bool isFromParent
= event
.GetEventObject() == (wxObject
*) parent
;
1015 const bool isFromSelf
= event
.GetEventObject() == (wxObject
*) this;
1016 const bool isForward
= event
.GetDirection();
1018 if ( isFromSelf
&& !isForward
)
1020 // focus is currently on notebook tab and should leave
1021 // it backwards (Shift-TAB)
1022 event
.SetCurrentFocus(this);
1023 parent
->HandleWindowEvent(event
);
1025 else if ( isFromParent
|| isFromSelf
)
1027 // no, it doesn't come from child, case (b) or (c): forward to a
1028 // page but only if entering notebook page (i.e. direction is
1029 // backwards (Shift-TAB) comething from out-of-notebook, or
1030 // direction is forward (TAB) from ourselves),
1031 if ( m_selection
!= wxNOT_FOUND
&&
1032 (!event
.GetDirection() || isFromSelf
) )
1034 // so that the page knows that the event comes from it's parent
1035 // and is being propagated downwards
1036 event
.SetEventObject(this);
1038 wxWindow
*page
= m_pages
[m_selection
];
1039 if ( !page
->HandleWindowEvent(event
) )
1043 //else: page manages focus inside it itself
1045 else // otherwise set the focus to the notebook itself
1052 // it comes from our child, case (a), pass to the parent, but only
1053 // if the direction is forwards. Otherwise set the focus to the
1054 // notebook itself. The notebook is always the 'first' control of a
1062 event
.SetCurrentFocus(this);
1063 parent
->HandleWindowEvent(event
);
1071 bool wxNotebook::DoDrawBackground(WXHDC hDC
, wxWindow
*child
)
1073 wxUxThemeHandle
theme(child
? child
: this, L
"TAB");
1077 // get the notebook client rect (we're not interested in drawing tabs
1079 wxRect r
= GetPageSize();
1084 wxCopyRectToRECT(r
, rc
);
1086 // map rect to the coords of the window we're drawing in
1088 ::MapWindowPoints(GetHwnd(), GetHwndOf(child
), (POINT
*)&rc
, 2);
1090 // we have the content area (page size), but we need to draw all of the
1091 // background for it to be aligned correctly
1092 wxUxThemeEngine::Get()->GetThemeBackgroundExtent
1101 wxUxThemeEngine::Get()->DrawThemeBackground
1114 WXHBRUSH
wxNotebook::QueryBgBitmap()
1116 wxRect r
= GetPageSize();
1120 WindowHDC
hDC(GetHwnd());
1121 MemoryHDC
hDCMem(hDC
);
1122 CompatibleBitmap
hBmp(hDC
, r
.x
+ r
.width
, r
.y
+ r
.height
);
1124 SelectInHDC
selectBmp(hDCMem
, hBmp
);
1126 if ( !DoDrawBackground((WXHDC
)(HDC
)hDCMem
) )
1129 return (WXHBRUSH
)::CreatePatternBrush(hBmp
);
1132 void wxNotebook::UpdateBgBrush()
1134 if ( m_hbrBackground
)
1135 ::DeleteObject((HBRUSH
)m_hbrBackground
);
1137 if ( !m_hasBgCol
&& wxUxThemeEngine::GetIfActive() )
1139 m_hbrBackground
= QueryBgBitmap();
1141 else // no themes or we've got user-defined solid colour
1143 m_hbrBackground
= NULL
;
1147 bool wxNotebook::MSWPrintChild(WXHDC hDC
, wxWindow
*child
)
1149 // solid background colour overrides themed background drawing
1150 if ( !UseBgCol() && DoDrawBackground(hDC
, child
) )
1153 // If we're using a solid colour (for example if we've switched off
1154 // theming for this notebook), paint it
1157 wxRect r
= GetPageSize();
1162 wxCopyRectToRECT(r
, rc
);
1164 // map rect to the coords of the window we're drawing in
1166 ::MapWindowPoints(GetHwnd(), GetHwndOf(child
), (POINT
*)&rc
, 2);
1168 wxBrush
brush(GetBackgroundColour());
1169 HBRUSH hbr
= GetHbrushOf(brush
);
1171 ::FillRect((HDC
) hDC
, &rc
, hbr
);
1176 return wxNotebookBase::MSWPrintChild(hDC
, child
);
1179 #endif // wxUSE_UXTHEME
1181 // Windows only: attempts to get colour for UX theme page background
1182 wxColour
wxNotebook::GetThemeBackgroundColour() const
1185 if (wxUxThemeEngine::Get())
1187 wxUxThemeHandle
hTheme((wxNotebook
*) this, L
"TAB");
1190 // This is total guesswork.
1191 // See PlatformSDK\Include\Tmschema.h for values.
1192 // JACS: can also use 9 (TABP_PANE)
1193 COLORREF themeColor
;
1194 bool success
= (S_OK
== wxUxThemeEngine::Get()->GetThemeColor(
1198 3821 /* FILLCOLORHINT */,
1201 return GetBackgroundColour();
1204 [DS] Workaround for WindowBlinds:
1205 Some themes return a near black theme color using FILLCOLORHINT,
1206 this makes notebook pages have an ugly black background and makes
1207 text (usually black) unreadable. Retry again with FILLCOLOR.
1209 This workaround potentially breaks appearance of some themes,
1210 but in practice it already fixes some themes.
1212 if (themeColor
== 1)
1214 wxUxThemeEngine::Get()->GetThemeColor(
1218 3802 /* FILLCOLOR */,
1222 wxColour colour
= wxRGBToColour(themeColor
);
1224 // Under Vista, the tab background colour is reported incorrectly.
1225 // So for the default theme at least, hard-code the colour to something
1226 // that will blend in.
1228 static int s_AeroStatus
= -1;
1229 if (s_AeroStatus
== -1)
1231 WCHAR szwThemeFile
[1024];
1232 WCHAR szwThemeColor
[256];
1233 if (S_OK
== wxUxThemeEngine::Get()->GetCurrentThemeName(szwThemeFile
, 1024, szwThemeColor
, 256, NULL
, 0))
1235 wxString
themeFile(szwThemeFile
), themeColor(szwThemeColor
);
1236 if (themeFile
.Find(wxT("Aero")) != -1 && themeColor
== wxT("NormalColor"))
1245 if (s_AeroStatus
== 1)
1246 colour
= wxColour(255, 255, 255);
1251 #endif // wxUSE_UXTHEME
1253 return GetBackgroundColour();
1256 // ----------------------------------------------------------------------------
1257 // wxNotebook base class virtuals
1258 // ----------------------------------------------------------------------------
1260 #if wxUSE_CONSTRAINTS
1262 // override these 2 functions to do nothing: everything is done in OnSize
1264 void wxNotebook::SetConstraintSizes(bool WXUNUSED(recurse
))
1266 // don't set the sizes of the pages - their correct size is not yet known
1267 wxControl::SetConstraintSizes(false);
1270 bool wxNotebook::DoPhase(int WXUNUSED(nPhase
))
1275 #endif // wxUSE_CONSTRAINTS
1277 // ----------------------------------------------------------------------------
1278 // wxNotebook Windows message handlers
1279 // ----------------------------------------------------------------------------
1281 bool wxNotebook::MSWOnScroll(int orientation
, WXWORD nSBCode
,
1282 WXWORD pos
, WXHWND control
)
1284 // don't generate EVT_SCROLLWIN events for the WM_SCROLLs coming from the
1289 return wxNotebookBase::MSWOnScroll(orientation
, nSBCode
, pos
, control
);
1292 bool wxNotebook::MSWOnNotify(int idCtrl
, WXLPARAM lParam
, WXLPARAM
* result
)
1294 wxBookCtrlEvent
event(wxEVT_NULL
, m_windowId
);
1296 NMHDR
* hdr
= (NMHDR
*)lParam
;
1297 switch ( hdr
->code
) {
1299 event
.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
);
1302 case TCN_SELCHANGING
:
1303 event
.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
);
1307 return wxControl::MSWOnNotify(idCtrl
, lParam
, result
);
1310 event
.SetSelection(TabCtrl_GetCurSel(GetHwnd()));
1311 event
.SetOldSelection(m_selection
);
1312 event
.SetEventObject(this);
1313 event
.SetInt(idCtrl
);
1315 // Change the selection before generating the event as its handler should
1316 // already see the new page selected.
1317 if ( hdr
->code
== TCN_SELCHANGE
)
1318 UpdateSelection(event
.GetSelection());
1320 bool processed
= HandleWindowEvent(event
);
1321 *result
= !event
.IsAllowed();
1325 #endif // wxUSE_NOTEBOOK