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"
35 #include "wx/imaglist.h"
36 #include "wx/sysopt.h"
38 #include "wx/msw/private.h"
39 #include "wx/msw/dc.h"
42 #include "wx/msw/winundef.h"
45 #include "wx/msw/uxtheme.h"
48 // ----------------------------------------------------------------------------
50 // ----------------------------------------------------------------------------
52 // check that the page index is valid
53 #define IS_VALID_PAGE(nPage) ((nPage) < GetPageCount())
55 // you can set USE_NOTEBOOK_ANTIFLICKER to 0 for desktop Windows versions too
56 // to disable code whih results in flicker-less notebook redrawing at the
57 // expense of some extra GDI resource consumption
59 // notebooks are never resized under CE anyhow
60 #define USE_NOTEBOOK_ANTIFLICKER 0
62 #define USE_NOTEBOOK_ANTIFLICKER 1
65 // ----------------------------------------------------------------------------
67 // ----------------------------------------------------------------------------
69 // This is a work-around for missing defines in gcc-2.95 headers
71 #define TCS_RIGHT 0x0002
75 #define TCS_VERTICAL 0x0080
79 #define TCS_BOTTOM TCS_RIGHT
82 // ----------------------------------------------------------------------------
84 // ----------------------------------------------------------------------------
86 #if USE_NOTEBOOK_ANTIFLICKER
88 // the pointer to standard spin button wnd proc
89 static WXFARPROC gs_wndprocNotebookSpinBtn
= (WXFARPROC
)NULL
;
91 // the pointer to standard tab control wnd proc
92 static WXFARPROC gs_wndprocNotebook
= (WXFARPROC
)NULL
;
94 LRESULT APIENTRY _EXPORT
wxNotebookWndProc(HWND hwnd
,
99 #endif // USE_NOTEBOOK_ANTIFLICKER
101 // ----------------------------------------------------------------------------
103 // ----------------------------------------------------------------------------
105 static bool HasTroubleWithNonTopTabs()
107 const int verComCtl32
= wxApp::GetComCtl32Version();
109 // 600 is XP, 616 is Vista -- and both have a problem with tabs not on top
110 // (but don't just test for >= 600 as Microsoft might decide to fix it in
111 // later versions, who knows...)
112 return verComCtl32
>= 600 && verComCtl32
<= 616;
115 // ----------------------------------------------------------------------------
117 // ----------------------------------------------------------------------------
119 #include "wx/listimpl.cpp"
121 WX_DEFINE_LIST( wxNotebookPageInfoList
)
123 DEFINE_EVENT_TYPE(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
)
124 DEFINE_EVENT_TYPE(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
)
126 BEGIN_EVENT_TABLE(wxNotebook
, wxBookCtrlBase
)
127 EVT_NOTEBOOK_PAGE_CHANGED(wxID_ANY
, wxNotebook::OnSelChange
)
128 EVT_SIZE(wxNotebook::OnSize
)
129 EVT_NAVIGATION_KEY(wxNotebook::OnNavigationKey
)
131 #if USE_NOTEBOOK_ANTIFLICKER
132 EVT_ERASE_BACKGROUND(wxNotebook::OnEraseBackground
)
133 EVT_PAINT(wxNotebook::OnPaint
)
134 #endif // USE_NOTEBOOK_ANTIFLICKER
137 #if wxUSE_EXTENDED_RTTI
138 WX_DEFINE_FLAGS( wxNotebookStyle
)
140 wxBEGIN_FLAGS( wxNotebookStyle
)
141 // new style border flags, we put them first to
142 // use them for streaming out
143 wxFLAGS_MEMBER(wxBORDER_SIMPLE
)
144 wxFLAGS_MEMBER(wxBORDER_SUNKEN
)
145 wxFLAGS_MEMBER(wxBORDER_DOUBLE
)
146 wxFLAGS_MEMBER(wxBORDER_RAISED
)
147 wxFLAGS_MEMBER(wxBORDER_STATIC
)
148 wxFLAGS_MEMBER(wxBORDER_NONE
)
150 // old style border flags
151 wxFLAGS_MEMBER(wxSIMPLE_BORDER
)
152 wxFLAGS_MEMBER(wxSUNKEN_BORDER
)
153 wxFLAGS_MEMBER(wxDOUBLE_BORDER
)
154 wxFLAGS_MEMBER(wxRAISED_BORDER
)
155 wxFLAGS_MEMBER(wxSTATIC_BORDER
)
156 wxFLAGS_MEMBER(wxBORDER
)
158 // standard window styles
159 wxFLAGS_MEMBER(wxTAB_TRAVERSAL
)
160 wxFLAGS_MEMBER(wxCLIP_CHILDREN
)
161 wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW
)
162 wxFLAGS_MEMBER(wxWANTS_CHARS
)
163 wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE
)
164 wxFLAGS_MEMBER(wxALWAYS_SHOW_SB
)
165 wxFLAGS_MEMBER(wxVSCROLL
)
166 wxFLAGS_MEMBER(wxHSCROLL
)
168 wxFLAGS_MEMBER(wxNB_FIXEDWIDTH
)
169 wxFLAGS_MEMBER(wxBK_DEFAULT
)
170 wxFLAGS_MEMBER(wxBK_TOP
)
171 wxFLAGS_MEMBER(wxBK_LEFT
)
172 wxFLAGS_MEMBER(wxBK_RIGHT
)
173 wxFLAGS_MEMBER(wxBK_BOTTOM
)
174 wxFLAGS_MEMBER(wxNB_NOPAGETHEME
)
175 wxFLAGS_MEMBER(wxNB_FLAT
)
177 wxEND_FLAGS( wxNotebookStyle
)
179 IMPLEMENT_DYNAMIC_CLASS_XTI(wxNotebook
, wxBookCtrlBase
,"wx/notebook.h")
180 IMPLEMENT_DYNAMIC_CLASS_XTI(wxNotebookPageInfo
, wxObject
, "wx/notebook.h" )
182 wxCOLLECTION_TYPE_INFO( wxNotebookPageInfo
* , wxNotebookPageInfoList
) ;
184 template<> void wxCollectionToVariantArray( wxNotebookPageInfoList
const &theList
, wxxVariantArray
&value
)
186 wxListCollectionToVariantArray
<wxNotebookPageInfoList::compatibility_iterator
>( theList
, value
) ;
189 wxBEGIN_PROPERTIES_TABLE(wxNotebook
)
190 wxEVENT_PROPERTY( PageChanging
, wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
, wxNotebookEvent
)
191 wxEVENT_PROPERTY( PageChanged
, wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
, wxNotebookEvent
)
193 wxPROPERTY_COLLECTION( PageInfos
, wxNotebookPageInfoList
, wxNotebookPageInfo
* , AddPageInfo
, GetPageInfos
, 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
194 wxPROPERTY_FLAGS( WindowStyle
, wxNotebookStyle
, long , SetWindowStyleFlag
, GetWindowStyleFlag
, EMPTY_MACROVALUE
, 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
195 wxEND_PROPERTIES_TABLE()
197 wxBEGIN_HANDLERS_TABLE(wxNotebook
)
198 wxEND_HANDLERS_TABLE()
200 wxCONSTRUCTOR_5( wxNotebook
, wxWindow
* , Parent
, wxWindowID
, Id
, wxPoint
, Position
, wxSize
, Size
, long , WindowStyle
)
203 wxBEGIN_PROPERTIES_TABLE(wxNotebookPageInfo
)
204 wxREADONLY_PROPERTY( Page
, wxNotebookPage
* , GetPage
, EMPTY_MACROVALUE
, 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
205 wxREADONLY_PROPERTY( Text
, wxString
, GetText
, wxString() , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
206 wxREADONLY_PROPERTY( Selected
, bool , GetSelected
, false, 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
207 wxREADONLY_PROPERTY( ImageId
, int , GetImageId
, -1 , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
208 wxEND_PROPERTIES_TABLE()
210 wxBEGIN_HANDLERS_TABLE(wxNotebookPageInfo
)
211 wxEND_HANDLERS_TABLE()
213 wxCONSTRUCTOR_4( wxNotebookPageInfo
, wxNotebookPage
* , Page
, wxString
, Text
, bool , Selected
, int , ImageId
)
216 IMPLEMENT_DYNAMIC_CLASS(wxNotebook
, wxBookCtrlBase
)
217 IMPLEMENT_DYNAMIC_CLASS(wxNotebookPageInfo
, wxObject
)
219 IMPLEMENT_DYNAMIC_CLASS(wxNotebookEvent
, wxNotifyEvent
)
221 // ============================================================================
223 // ============================================================================
225 // ----------------------------------------------------------------------------
226 // wxNotebook construction
227 // ----------------------------------------------------------------------------
229 const wxNotebookPageInfoList
& wxNotebook::GetPageInfos() const
231 wxNotebookPageInfoList
* list
= const_cast< wxNotebookPageInfoList
* >( &m_pageInfos
) ;
232 WX_CLEAR_LIST( wxNotebookPageInfoList
, *list
) ;
233 for( size_t i
= 0 ; i
< GetPageCount() ; ++i
)
235 wxNotebookPageInfo
*info
= new wxNotebookPageInfo() ;
236 info
->Create( const_cast<wxNotebook
*>(this)->GetPage(i
) , GetPageText(i
) , GetSelection() == int(i
) , GetPageImage(i
) ) ;
237 list
->Append( info
) ;
242 // common part of all ctors
243 void wxNotebook::Init()
246 m_nSelection
= wxNOT_FOUND
;
249 m_hbrBackground
= NULL
;
250 #endif // wxUSE_UXTHEME
252 #if USE_NOTEBOOK_ANTIFLICKER
253 m_hasSubclassedUpdown
= false;
254 #endif // USE_NOTEBOOK_ANTIFLICKER
257 // default for dynamic class
258 wxNotebook::wxNotebook()
263 // the same arguments as for wxControl
264 wxNotebook::wxNotebook(wxWindow
*parent
,
269 const wxString
& name
)
273 Create(parent
, id
, pos
, size
, style
, name
);
277 bool wxNotebook::Create(wxWindow
*parent
,
282 const wxString
& name
)
284 if ( (style
& wxBK_ALIGN_MASK
) == wxBK_DEFAULT
)
286 #if defined(__POCKETPC__)
287 style
|= wxBK_BOTTOM
| wxNB_FLAT
;
294 // Not sure why, but without this style, there is no border
295 // around the notebook tabs.
296 if (style
& wxNB_FLAT
)
297 style
|= wxBORDER_SUNKEN
;
301 // ComCtl32 notebook tabs simply don't work unless they're on top if we
302 // have uxtheme, we can work around it later (after control creation), but
303 // if we have been compiled without uxtheme support, we have to clear those
305 if ( HasTroubleWithNonTopTabs() )
307 style
&= ~(wxBK_BOTTOM
| wxBK_LEFT
| wxBK_RIGHT
);
309 #endif //wxUSE_UXTHEME
311 #if defined(__WINE__) && wxUSE_UNICODE
312 LPCTSTR className
= L
"SysTabControl32";
314 LPCTSTR className
= WC_TABCONTROL
;
317 #if USE_NOTEBOOK_ANTIFLICKER
318 // SysTabCtl32 class has natively CS_HREDRAW and CS_VREDRAW enabled and it
319 // causes horrible flicker when resizing notebook, so get rid of it by
320 // using a class without these styles (but otherwise identical to it)
321 if ( !HasFlag(wxFULL_REPAINT_ON_RESIZE
) )
323 static ClassRegistrar s_clsNotebook
;
324 if ( !s_clsNotebook
.IsInitialized() )
326 // get a copy of standard class and modify it
329 if ( ::GetClassInfo(NULL
, WC_TABCONTROL
, &wc
) )
332 wx_reinterpret_cast(WXFARPROC
, wc
.lpfnWndProc
);
333 wc
.lpszClassName
= wxT("_wx_SysTabCtl32");
334 wc
.style
&= ~(CS_HREDRAW
| CS_VREDRAW
);
335 wc
.hInstance
= wxGetInstance();
336 wc
.lpfnWndProc
= wxNotebookWndProc
;
337 s_clsNotebook
.Register(wc
);
341 wxLogLastError(_T("GetClassInfoEx(SysTabCtl32)"));
345 // use our custom class if available but fall back to the standard
346 // notebook if we failed to register it
347 if ( s_clsNotebook
.IsRegistered() )
349 // it's ok to use c_str() here as the static s_clsNotebook object
350 // has sufficiently long lifetime
351 className
= s_clsNotebook
.GetName().c_str();
354 #endif // USE_NOTEBOOK_ANTIFLICKER
356 if ( !CreateControl(parent
, id
, pos
, size
, style
| wxTAB_TRAVERSAL
,
357 wxDefaultValidator
, name
) )
360 if ( !MSWCreateControl(className
, wxEmptyString
, pos
, size
) )
364 if ( HasFlag(wxNB_NOPAGETHEME
) ||
365 wxSystemOptions::IsFalse(wxT("msw.notebook.themed-background")) )
367 SetBackgroundColour(GetThemeBackgroundColour());
369 else // use themed background by default
371 // create backing store
375 // comctl32.dll 6.0 doesn't support non-top tabs with visual styles (the
376 // control is simply not rendered correctly), so we disable themes
377 // if possible, otherwise we simply clear the styles.
378 if ( HasTroubleWithNonTopTabs() &&
379 (style
& (wxBK_BOTTOM
| wxBK_LEFT
| wxBK_RIGHT
)) )
381 // check if we use themes at all -- if we don't, we're still okay
382 if ( wxUxThemeEngine::GetIfActive() )
384 wxUxThemeEngine::GetIfActive()->SetWindowTheme(GetHwnd(), L
"", L
"");
386 // correct the background color for the new non-themed control
387 SetBackgroundColour(GetThemeBackgroundColour());
390 #endif // wxUSE_UXTHEME
392 // Undocumented hack to get flat notebook style
393 // In fact, we should probably only do this in some
394 // curcumstances, i.e. if we know we will have a border
395 // at the bottom (the tab control doesn't draw it itself)
396 #if defined(__POCKETPC__) || defined(__SMARTPHONE__)
397 if (HasFlag(wxNB_FLAT
))
399 SendMessage(GetHwnd(), CCM_SETVERSION
, COMCTL32_VERSION
, 0);
401 SetBackgroundColour(*wxWHITE
);
407 WXDWORD
wxNotebook::MSWGetStyle(long style
, WXDWORD
*exstyle
) const
409 WXDWORD tabStyle
= wxControl::MSWGetStyle(style
, exstyle
);
411 tabStyle
|= WS_TABSTOP
| TCS_TABS
;
413 if ( style
& wxNB_MULTILINE
)
414 tabStyle
|= TCS_MULTILINE
;
415 if ( style
& wxNB_FIXEDWIDTH
)
416 tabStyle
|= TCS_FIXEDWIDTH
;
418 if ( style
& wxBK_BOTTOM
)
419 tabStyle
|= TCS_RIGHT
;
420 else if ( style
& wxBK_LEFT
)
421 tabStyle
|= TCS_VERTICAL
;
422 else if ( style
& wxBK_RIGHT
)
423 tabStyle
|= TCS_VERTICAL
| TCS_RIGHT
;
428 // note that we never want to have the default WS_EX_CLIENTEDGE style
429 // as it looks too ugly for the notebooks
436 wxNotebook::~wxNotebook()
439 if ( m_hbrBackground
)
440 ::DeleteObject((HBRUSH
)m_hbrBackground
);
441 #endif // wxUSE_UXTHEME
444 // ----------------------------------------------------------------------------
445 // wxNotebook accessors
446 // ----------------------------------------------------------------------------
448 size_t wxNotebook::GetPageCount() const
451 wxASSERT( (int)m_pages
.Count() == TabCtrl_GetItemCount(GetHwnd()) );
453 return m_pages
.Count();
456 int wxNotebook::GetRowCount() const
458 return TabCtrl_GetRowCount(GetHwnd());
461 int wxNotebook::SetSelection(size_t nPage
)
463 wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxNOT_FOUND
, wxT("notebook page out of range") );
465 if ( m_nSelection
== wxNOT_FOUND
|| nPage
!= (size_t)m_nSelection
)
467 if ( SendPageChangingEvent(nPage
) )
469 // program allows the page change
470 SendPageChangedEvent(m_nSelection
, nPage
);
472 TabCtrl_SetCurSel(GetHwnd(), nPage
);
479 void wxNotebook::UpdateSelection(int selNew
)
481 if ( m_nSelection
!= wxNOT_FOUND
)
482 m_pages
[m_nSelection
]->Show(false);
484 if ( selNew
!= wxNOT_FOUND
)
486 wxNotebookPage
*pPage
= m_pages
[selNew
];
490 // Changing the page should give the focus to it but, as per bug report
491 // http://sf.net/tracker/index.php?func=detail&aid=1150659&group_id=9863&atid=109863,
492 // we should not set the focus to it directly since it erroneously
493 // selects radio buttons and breaks keyboard handling for a notebook's
494 // scroll buttons. So give focus to the notebook and not the page.
496 // but don't do this is the notebook is hidden
497 if ( ::IsWindowVisible(GetHwnd()) )
500 m_nSelection
= selNew
;
503 int wxNotebook::ChangeSelection(size_t nPage
)
505 wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxNOT_FOUND
, wxT("notebook page out of range") );
507 if ( m_nSelection
== wxNOT_FOUND
|| nPage
!= (size_t)m_nSelection
)
509 TabCtrl_SetCurSel(GetHwnd(), nPage
);
511 UpdateSelection(nPage
);
517 bool wxNotebook::SetPageText(size_t nPage
, const wxString
& strText
)
519 wxCHECK_MSG( IS_VALID_PAGE(nPage
), false, wxT("notebook page out of range") );
522 tcItem
.mask
= TCIF_TEXT
;
523 tcItem
.pszText
= (wxChar
*)strText
.wx_str();
525 if ( !HasFlag(wxNB_MULTILINE
) )
526 return TabCtrl_SetItem(GetHwnd(), nPage
, &tcItem
) != 0;
528 // multiline - we need to set new page size if a line is added or removed
529 int rows
= GetRowCount();
530 bool ret
= TabCtrl_SetItem(GetHwnd(), nPage
, &tcItem
) != 0;
532 if ( ret
&& rows
!= GetRowCount() )
534 const wxRect r
= GetPageSize();
535 const size_t count
= m_pages
.Count();
536 for ( size_t page
= 0; page
< count
; page
++ )
537 m_pages
[page
]->SetSize(r
);
543 wxString
wxNotebook::GetPageText(size_t nPage
) const
545 wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxEmptyString
, wxT("notebook page out of range") );
549 tcItem
.mask
= TCIF_TEXT
;
550 tcItem
.pszText
= buf
;
551 tcItem
.cchTextMax
= WXSIZEOF(buf
);
554 if ( TabCtrl_GetItem(GetHwnd(), nPage
, &tcItem
) )
555 str
= tcItem
.pszText
;
560 int wxNotebook::GetPageImage(size_t nPage
) const
562 wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxNOT_FOUND
, wxT("notebook page out of range") );
565 tcItem
.mask
= TCIF_IMAGE
;
567 return TabCtrl_GetItem(GetHwnd(), nPage
, &tcItem
) ? tcItem
.iImage
571 bool wxNotebook::SetPageImage(size_t nPage
, int nImage
)
573 wxCHECK_MSG( IS_VALID_PAGE(nPage
), false, wxT("notebook page out of range") );
576 tcItem
.mask
= TCIF_IMAGE
;
577 tcItem
.iImage
= nImage
;
579 return TabCtrl_SetItem(GetHwnd(), nPage
, &tcItem
) != 0;
582 void wxNotebook::SetImageList(wxImageList
* imageList
)
584 wxNotebookBase::SetImageList(imageList
);
588 (void) TabCtrl_SetImageList(GetHwnd(), GetHimagelistOf(imageList
));
592 // ----------------------------------------------------------------------------
593 // wxNotebook size settings
594 // ----------------------------------------------------------------------------
596 wxRect
wxNotebook::GetPageSize() const
601 ::GetClientRect(GetHwnd(), &rc
);
603 // This check is to work around a bug in TabCtrl_AdjustRect which will
604 // cause a crash on win2k or on XP with themes disabled if either
605 // wxNB_MULTILINE is used or tabs are placed on a side, if the rectangle
608 // The value of 20 is chosen arbitrarily but seems to work
609 if ( rc
.right
> 20 && rc
.bottom
> 20 )
611 TabCtrl_AdjustRect(GetHwnd(), false, &rc
);
613 wxCopyRECTToRect(rc
, r
);
619 void wxNotebook::SetPageSize(const wxSize
& size
)
621 // transform the page size into the notebook size
628 TabCtrl_AdjustRect(GetHwnd(), true, &rc
);
631 SetSize(rc
.right
- rc
.left
, rc
.bottom
- rc
.top
);
634 void wxNotebook::SetPadding(const wxSize
& padding
)
636 TabCtrl_SetPadding(GetHwnd(), padding
.x
, padding
.y
);
639 // Windows-only at present. Also, you must use the wxNB_FIXEDWIDTH
641 void wxNotebook::SetTabSize(const wxSize
& sz
)
643 ::SendMessage(GetHwnd(), TCM_SETITEMSIZE
, 0, MAKELPARAM(sz
.x
, sz
.y
));
646 wxSize
wxNotebook::CalcSizeFromPage(const wxSize
& sizePage
) const
648 // we can't use TabCtrl_AdjustRect here because it only works for wxNB_TOP
649 wxSize sizeTotal
= sizePage
;
652 if ( GetPageCount() > 0 )
655 TabCtrl_GetItemRect(GetHwnd(), 0, &rect
);
656 tabSize
.x
= rect
.right
- rect
.left
;
657 tabSize
.y
= rect
.bottom
- rect
.top
;
660 // add an extra margin in both directions
661 const int MARGIN
= 8;
664 sizeTotal
.x
+= MARGIN
;
665 sizeTotal
.y
+= tabSize
.y
+ MARGIN
;
667 else // horizontal layout
669 sizeTotal
.x
+= tabSize
.x
+ MARGIN
;
670 sizeTotal
.y
+= MARGIN
;
676 void wxNotebook::AdjustPageSize(wxNotebookPage
*page
)
678 wxCHECK_RET( page
, _T("NULL page in wxNotebook::AdjustPageSize") );
680 const wxRect r
= GetPageSize();
687 // ----------------------------------------------------------------------------
688 // wxNotebook operations
689 // ----------------------------------------------------------------------------
691 // remove one page from the notebook, without deleting
692 wxNotebookPage
*wxNotebook::DoRemovePage(size_t nPage
)
694 wxNotebookPage
*pageRemoved
= wxNotebookBase::DoRemovePage(nPage
);
698 TabCtrl_DeleteItem(GetHwnd(), nPage
);
700 if ( m_pages
.IsEmpty() )
702 // no selection any more, the notebook becamse empty
703 m_nSelection
= wxNOT_FOUND
;
705 else // notebook still not empty
707 int selNew
= TabCtrl_GetCurSel(GetHwnd());
708 if ( selNew
!= wxNOT_FOUND
)
710 // No selection change, just refresh the current selection.
711 // Because it could be that the slection index changed
712 // we need to update it.
713 // Note: this does not mean the selection it self changed.
714 m_nSelection
= selNew
;
715 m_pages
[m_nSelection
]->Refresh();
717 else if (int(nPage
) == m_nSelection
)
719 // The selection was deleted.
721 // Determine new selection.
722 if (m_nSelection
== int(GetPageCount()))
723 selNew
= m_nSelection
- 1;
725 selNew
= m_nSelection
;
727 // m_nSelection must be always valid so reset it before calling
729 m_nSelection
= wxNOT_FOUND
;
730 SetSelection(selNew
);
734 wxFAIL
; // Windows did not behave ok.
742 bool wxNotebook::DeleteAllPages()
744 size_t nPageCount
= GetPageCount();
746 for ( nPage
= 0; nPage
< nPageCount
; nPage
++ )
747 delete m_pages
[nPage
];
751 TabCtrl_DeleteAllItems(GetHwnd());
753 m_nSelection
= wxNOT_FOUND
;
755 InvalidateBestSize();
759 // same as AddPage() but does it at given position
760 bool wxNotebook::InsertPage(size_t nPage
,
761 wxNotebookPage
*pPage
,
762 const wxString
& strText
,
766 wxCHECK_MSG( pPage
!= NULL
, false, _T("NULL page in wxNotebook::InsertPage") );
767 wxCHECK_MSG( IS_VALID_PAGE(nPage
) || nPage
== GetPageCount(), false,
768 _T("invalid index in wxNotebook::InsertPage") );
770 wxASSERT_MSG( pPage
->GetParent() == this,
771 _T("notebook pages must have notebook as parent") );
773 // add a new tab to the control
774 // ----------------------------
776 // init all fields to 0
778 wxZeroMemory(tcItem
);
780 // set the image, if any
783 tcItem
.mask
|= TCIF_IMAGE
;
784 tcItem
.iImage
= imageId
;
788 if ( !strText
.empty() )
790 tcItem
.mask
|= TCIF_TEXT
;
791 tcItem
.pszText
= (wxChar
*)strText
.wx_str(); // const_cast
794 // hide the page: unless it is selected, it shouldn't be shown (and if it
795 // is selected it will be shown later)
796 HWND hwnd
= GetWinHwnd(pPage
);
797 SetWindowLong(hwnd
, GWL_STYLE
, GetWindowLong(hwnd
, GWL_STYLE
) & ~WS_VISIBLE
);
799 // this updates internal flag too -- otherwise it would get out of sync
800 // with the real state
804 // fit the notebook page to the tab control's display area: this should be
805 // done before adding it to the notebook or TabCtrl_InsertItem() will
806 // change the notebooks size itself!
807 AdjustPageSize(pPage
);
809 // finally do insert it
810 if ( TabCtrl_InsertItem(GetHwnd(), nPage
, &tcItem
) == -1 )
812 wxLogError(wxT("Can't create the notebook page '%s'."), strText
.c_str());
817 // need to update the bg brush when the first page is added
818 // so the first panel gets the correct themed background
819 if ( m_pages
.empty() )
823 #endif // wxUSE_UXTHEME
826 // succeeded: save the pointer to the page
827 m_pages
.Insert(pPage
, nPage
);
829 // we may need to adjust the size again if the notebook size changed:
830 // normally this only happens for the first page we add (the tabs which
831 // hadn't been there before are now shown) but for a multiline notebook it
832 // can happen for any page at all as a new row could have been started
833 if ( m_pages
.GetCount() == 1 || HasFlag(wxNB_MULTILINE
) )
835 AdjustPageSize(pPage
);
838 // now deal with the selection
839 // ---------------------------
841 // if the inserted page is before the selected one, we must update the
842 // index of the selected page
843 if ( int(nPage
) <= m_nSelection
)
845 // one extra page added
849 // some page should be selected: either this one or the first one if there
850 // is still no selection
851 int selNew
= wxNOT_FOUND
;
854 else if ( m_nSelection
== wxNOT_FOUND
)
857 if ( selNew
!= wxNOT_FOUND
)
858 SetSelection(selNew
);
860 InvalidateBestSize();
865 int wxNotebook::HitTest(const wxPoint
& pt
, long *flags
) const
867 TC_HITTESTINFO hitTestInfo
;
868 hitTestInfo
.pt
.x
= pt
.x
;
869 hitTestInfo
.pt
.y
= pt
.y
;
870 int item
= TabCtrl_HitTest(GetHwnd(), &hitTestInfo
);
876 if ((hitTestInfo
.flags
& TCHT_NOWHERE
) == TCHT_NOWHERE
)
877 *flags
|= wxBK_HITTEST_NOWHERE
;
878 if ((hitTestInfo
.flags
& TCHT_ONITEM
) == TCHT_ONITEM
)
879 *flags
|= wxBK_HITTEST_ONITEM
;
880 if ((hitTestInfo
.flags
& TCHT_ONITEMICON
) == TCHT_ONITEMICON
)
881 *flags
|= wxBK_HITTEST_ONICON
;
882 if ((hitTestInfo
.flags
& TCHT_ONITEMLABEL
) == TCHT_ONITEMLABEL
)
883 *flags
|= wxBK_HITTEST_ONLABEL
;
884 if ( item
== wxNOT_FOUND
&& GetPageSize().Contains(pt
) )
885 *flags
|= wxBK_HITTEST_ONPAGE
;
891 // ----------------------------------------------------------------------------
892 // flicker-less notebook redraw
893 // ----------------------------------------------------------------------------
895 #if USE_NOTEBOOK_ANTIFLICKER
897 // wnd proc for the spin button
898 LRESULT APIENTRY _EXPORT
wxNotebookSpinBtnWndProc(HWND hwnd
,
903 if ( message
== WM_ERASEBKGND
)
906 return ::CallWindowProc(CASTWNDPROC gs_wndprocNotebookSpinBtn
,
907 hwnd
, message
, wParam
, lParam
);
910 LRESULT APIENTRY _EXPORT
wxNotebookWndProc(HWND hwnd
,
915 return ::CallWindowProc(CASTWNDPROC gs_wndprocNotebook
,
916 hwnd
, message
, wParam
, lParam
);
919 void wxNotebook::OnEraseBackground(wxEraseEvent
& WXUNUSED(event
))
924 void wxNotebook::OnPaint(wxPaintEvent
& WXUNUSED(event
))
929 ::GetClientRect(GetHwnd(), &rc
);
930 wxBitmap
bmp(rc
.right
, rc
.bottom
);
931 memdc
.SelectObject(bmp
);
933 const wxLayoutDirection dir
= dc
.GetLayoutDirection();
934 memdc
.SetLayoutDirection(dir
);
936 // if there is no special brush just use the solid background colour
938 HBRUSH hbr
= (HBRUSH
)m_hbrBackground
;
945 brush
= wxBrush(GetBackgroundColour());
946 hbr
= GetHbrushOf(brush
);
949 wxMSWDCImpl
*impl
= (wxMSWDCImpl
*) memdc
.GetImpl();
951 ::FillRect(GetHdcOf(*impl
), &rc
, hbr
);
953 MSWDefWindowProc(WM_PAINT
, (WPARAM
)(impl
->GetHDC()), 0);
955 // For some reason in RTL mode, source offset has to be -1, otherwise the
956 // right border (physical) remains unpainted.
957 const wxCoord ofs
= dir
== wxLayout_RightToLeft
? -1 : 0;
958 dc
.Blit(ofs
, 0, rc
.right
, rc
.bottom
, &memdc
, ofs
, 0);
961 #endif // USE_NOTEBOOK_ANTIFLICKER
963 // ----------------------------------------------------------------------------
964 // wxNotebook callbacks
965 // ----------------------------------------------------------------------------
967 void wxNotebook::OnSize(wxSizeEvent
& event
)
969 if ( GetPageCount() == 0 )
971 // Prevents droppings on resize, but does cause some flicker
972 // when there are no pages.
980 // Without this, we can sometimes get droppings at the edges
981 // of a notebook, for example a notebook in a splitter window.
982 // This needs to be reconciled with the RefreshRect calls
983 // at the end of this function, which weren't enough to prevent
986 wxSize sz
= GetClientSize();
988 // Refresh right side
989 wxRect
rect(sz
.x
-4, 0, 4, sz
.y
);
992 // Refresh bottom side
993 rect
= wxRect(0, sz
.y
-4, sz
.x
, 4);
997 rect
= wxRect(0, 0, 4, sz
.y
);
1000 #endif // !__WXWINCE__
1002 // fit all the notebook pages to the tab control's display area
1005 rc
.left
= rc
.top
= 0;
1006 GetSize((int *)&rc
.right
, (int *)&rc
.bottom
);
1008 // save the total size, we'll use it below
1009 int widthNbook
= rc
.right
- rc
.left
,
1010 heightNbook
= rc
.bottom
- rc
.top
;
1012 // there seems to be a bug in the implementation of TabCtrl_AdjustRect(): it
1013 // returns completely false values for multiline tab controls after the tabs
1014 // are added but before getting the first WM_SIZE (off by ~50 pixels, see
1016 // http://sf.net/tracker/index.php?func=detail&aid=645323&group_id=9863&atid=109863
1018 // and the only work around I could find was this ugly hack... without it
1019 // simply toggling the "multiline" checkbox in the notebook sample resulted
1020 // in a noticeable page displacement
1021 if ( HasFlag(wxNB_MULTILINE
) )
1023 // avoid an infinite recursion: we get another notification too!
1024 static bool s_isInOnSize
= false;
1026 if ( !s_isInOnSize
)
1028 s_isInOnSize
= true;
1029 SendMessage(GetHwnd(), WM_SIZE
, SIZE_RESTORED
,
1030 MAKELPARAM(rc
.right
, rc
.bottom
));
1031 s_isInOnSize
= false;
1036 // background bitmap size has changed, update the brush using it too
1038 #endif // wxUSE_UXTHEME
1040 TabCtrl_AdjustRect(GetHwnd(), false, &rc
);
1042 int width
= rc
.right
- rc
.left
,
1043 height
= rc
.bottom
- rc
.top
;
1044 size_t nCount
= m_pages
.Count();
1045 for ( size_t nPage
= 0; nPage
< nCount
; nPage
++ ) {
1046 wxNotebookPage
*pPage
= m_pages
[nPage
];
1047 pPage
->SetSize(rc
.left
, rc
.top
, width
, height
);
1051 // unless we had already repainted everything, we now need to refresh
1052 if ( !HasFlag(wxFULL_REPAINT_ON_RESIZE
) )
1054 // invalidate areas not covered by pages
1055 RefreshRect(wxRect(0, 0, widthNbook
, rc
.top
), false);
1056 RefreshRect(wxRect(0, rc
.top
, rc
.left
, height
), false);
1057 RefreshRect(wxRect(0, rc
.bottom
, widthNbook
, heightNbook
- rc
.bottom
),
1059 RefreshRect(wxRect(rc
.right
, rc
.top
, widthNbook
- rc
.right
, height
),
1063 #if USE_NOTEBOOK_ANTIFLICKER
1064 // subclass the spin control used by the notebook to scroll pages to
1065 // prevent it from flickering on resize
1066 if ( !m_hasSubclassedUpdown
)
1068 // iterate over all child windows to find spin button
1069 for ( HWND child
= ::GetWindow(GetHwnd(), GW_CHILD
);
1071 child
= ::GetWindow(child
, GW_HWNDNEXT
) )
1073 wxWindow
*childWindow
= wxFindWinFromHandle((WXHWND
)child
);
1075 // see if it exists, if no wxWindow found then assume it's the spin
1079 // subclass the spin button to override WM_ERASEBKGND
1080 if ( !gs_wndprocNotebookSpinBtn
)
1081 gs_wndprocNotebookSpinBtn
= (WXFARPROC
)wxGetWindowProc(child
);
1083 wxSetWindowProc(child
, wxNotebookSpinBtnWndProc
);
1084 m_hasSubclassedUpdown
= true;
1089 #endif // USE_NOTEBOOK_ANTIFLICKER
1094 void wxNotebook::OnSelChange(wxNotebookEvent
& event
)
1096 // is it our tab control?
1097 if ( event
.GetEventObject() == this )
1099 UpdateSelection(event
.GetSelection());
1102 // we want to give others a chance to process this message as well
1106 void wxNotebook::OnNavigationKey(wxNavigationKeyEvent
& event
)
1108 if ( event
.IsWindowChange() ) {
1110 AdvanceSelection(event
.GetDirection());
1113 // we get this event in 3 cases
1115 // a) one of our pages might have generated it because the user TABbed
1116 // out from it in which case we should propagate the event upwards and
1117 // our parent will take care of setting the focus to prev/next sibling
1121 // b) the parent panel wants to give the focus to us so that we
1122 // forward it to our selected page. We can't deal with this in
1123 // OnSetFocus() because we don't know which direction the focus came
1124 // from in this case and so can't choose between setting the focus to
1125 // first or last panel child
1129 // c) we ourselves (see MSWTranslateMessage) generated the event
1131 wxWindow
* const parent
= GetParent();
1133 // the wxObject* casts are required to avoid MinGW GCC 2.95.3 ICE
1134 const bool isFromParent
= event
.GetEventObject() == (wxObject
*) parent
;
1135 const bool isFromSelf
= event
.GetEventObject() == (wxObject
*) this;
1137 if ( isFromParent
|| isFromSelf
)
1139 // no, it doesn't come from child, case (b) or (c): forward to a
1140 // page but only if direction is backwards (TAB) or from ourselves,
1141 if ( m_nSelection
!= wxNOT_FOUND
&&
1142 (!event
.GetDirection() || isFromSelf
) )
1144 // so that the page knows that the event comes from it's parent
1145 // and is being propagated downwards
1146 event
.SetEventObject(this);
1148 wxWindow
*page
= m_pages
[m_nSelection
];
1149 if ( !page
->HandleWindowEvent(event
) )
1153 //else: page manages focus inside it itself
1155 else // otherwise set the focus to the notebook itself
1162 // it comes from our child, case (a), pass to the parent, but only
1163 // if the direction is forwards. Otherwise set the focus to the
1164 // notebook itself. The notebook is always the 'first' control of a
1166 if ( !event
.GetDirection() )
1172 event
.SetCurrentFocus(this);
1173 parent
->HandleWindowEvent(event
);
1181 bool wxNotebook::DoDrawBackground(WXHDC hDC
, wxWindow
*child
)
1183 wxUxThemeHandle
theme(child
? child
: this, L
"TAB");
1187 // get the notebook client rect (we're not interested in drawing tabs
1189 wxRect r
= GetPageSize();
1194 wxCopyRectToRECT(r
, rc
);
1196 // map rect to the coords of the window we're drawing in
1198 ::MapWindowPoints(GetHwnd(), GetHwndOf(child
), (POINT
*)&rc
, 2);
1200 // we have the content area (page size), but we need to draw all of the
1201 // background for it to be aligned correctly
1202 wxUxThemeEngine::Get()->GetThemeBackgroundExtent
1211 wxUxThemeEngine::Get()->DrawThemeBackground
1224 WXHBRUSH
wxNotebook::QueryBgBitmap()
1226 wxRect r
= GetPageSize();
1230 WindowHDC
hDC(GetHwnd());
1231 MemoryHDC
hDCMem(hDC
);
1232 CompatibleBitmap
hBmp(hDC
, r
.x
+ r
.width
, r
.y
+ r
.height
);
1234 SelectInHDC
selectBmp(hDCMem
, hBmp
);
1236 if ( !DoDrawBackground((WXHDC
)(HDC
)hDCMem
) )
1239 return (WXHBRUSH
)::CreatePatternBrush(hBmp
);
1242 void wxNotebook::UpdateBgBrush()
1244 if ( m_hbrBackground
)
1245 ::DeleteObject((HBRUSH
)m_hbrBackground
);
1247 if ( !m_hasBgCol
&& wxUxThemeEngine::GetIfActive() )
1249 m_hbrBackground
= QueryBgBitmap();
1251 else // no themes or we've got user-defined solid colour
1253 m_hbrBackground
= NULL
;
1257 WXHBRUSH
wxNotebook::MSWGetBgBrushForChild(WXHDC hDC
, WXHWND hWnd
)
1259 if ( m_hbrBackground
)
1261 // before drawing with the background brush, we need to position it
1264 ::GetWindowRect((HWND
)hWnd
, &rc
);
1266 ::MapWindowPoints(NULL
, GetHwnd(), (POINT
*)&rc
, 1);
1268 if ( !::SetBrushOrgEx((HDC
)hDC
, -rc
.left
, -rc
.top
, NULL
) )
1270 wxLogLastError(_T("SetBrushOrgEx(notebook bg brush)"));
1273 return m_hbrBackground
;
1276 return wxNotebookBase::MSWGetBgBrushForChild(hDC
, hWnd
);
1279 bool wxNotebook::MSWPrintChild(WXHDC hDC
, wxWindow
*child
)
1281 // solid background colour overrides themed background drawing
1282 if ( !UseBgCol() && DoDrawBackground(hDC
, child
) )
1285 // If we're using a solid colour (for example if we've switched off
1286 // theming for this notebook), paint it
1289 wxRect r
= GetPageSize();
1294 wxCopyRectToRECT(r
, rc
);
1296 // map rect to the coords of the window we're drawing in
1298 ::MapWindowPoints(GetHwnd(), GetHwndOf(child
), (POINT
*)&rc
, 2);
1300 wxBrush
brush(GetBackgroundColour());
1301 HBRUSH hbr
= GetHbrushOf(brush
);
1303 ::FillRect((HDC
) hDC
, &rc
, hbr
);
1308 return wxNotebookBase::MSWPrintChild(hDC
, child
);
1311 #endif // wxUSE_UXTHEME
1313 // Windows only: attempts to get colour for UX theme page background
1314 wxColour
wxNotebook::GetThemeBackgroundColour() const
1317 if (wxUxThemeEngine::Get())
1319 wxUxThemeHandle
hTheme((wxNotebook
*) this, L
"TAB");
1322 // This is total guesswork.
1323 // See PlatformSDK\Include\Tmschema.h for values.
1324 // JACS: can also use 9 (TABP_PANE)
1325 COLORREF themeColor
;
1326 bool success
= (S_OK
== wxUxThemeEngine::Get()->GetThemeColor(
1330 3821 /* FILLCOLORHINT */,
1333 return GetBackgroundColour();
1336 [DS] Workaround for WindowBlinds:
1337 Some themes return a near black theme color using FILLCOLORHINT,
1338 this makes notebook pages have an ugly black background and makes
1339 text (usually black) unreadable. Retry again with FILLCOLOR.
1341 This workaround potentially breaks appearance of some themes,
1342 but in practice it already fixes some themes.
1344 if (themeColor
== 1)
1346 wxUxThemeEngine::Get()->GetThemeColor(
1350 3802 /* FILLCOLOR */,
1354 wxColour colour
= wxRGBToColour(themeColor
);
1356 // Under Vista, the tab background colour is reported incorrectly.
1357 // So for the default theme at least, hard-code the colour to something
1358 // that will blend in.
1360 static int s_AeroStatus
= -1;
1361 if (s_AeroStatus
== -1)
1363 WCHAR szwThemeFile
[1024];
1364 WCHAR szwThemeColor
[256];
1365 if (S_OK
== wxUxThemeEngine::Get()->GetCurrentThemeName(szwThemeFile
, 1024, szwThemeColor
, 256, NULL
, 0))
1367 wxString
themeFile(szwThemeFile
), themeColor(szwThemeColor
);
1368 if (themeFile
.Find(wxT("Aero")) != -1 && themeColor
== wxT("NormalColor"))
1377 if (s_AeroStatus
== 1)
1378 colour
= wxColour(255, 255, 255);
1383 #endif // wxUSE_UXTHEME
1385 return GetBackgroundColour();
1388 // ----------------------------------------------------------------------------
1389 // wxNotebook base class virtuals
1390 // ----------------------------------------------------------------------------
1392 #if wxUSE_CONSTRAINTS
1394 // override these 2 functions to do nothing: everything is done in OnSize
1396 void wxNotebook::SetConstraintSizes(bool WXUNUSED(recurse
))
1398 // don't set the sizes of the pages - their correct size is not yet known
1399 wxControl::SetConstraintSizes(false);
1402 bool wxNotebook::DoPhase(int WXUNUSED(nPhase
))
1407 #endif // wxUSE_CONSTRAINTS
1409 // ----------------------------------------------------------------------------
1410 // wxNotebook Windows message handlers
1411 // ----------------------------------------------------------------------------
1413 bool wxNotebook::MSWOnScroll(int orientation
, WXWORD nSBCode
,
1414 WXWORD pos
, WXHWND control
)
1416 // don't generate EVT_SCROLLWIN events for the WM_SCROLLs coming from the
1421 return wxNotebookBase::MSWOnScroll(orientation
, nSBCode
, pos
, control
);
1424 bool wxNotebook::MSWOnNotify(int idCtrl
, WXLPARAM lParam
, WXLPARAM
* result
)
1426 wxNotebookEvent
event(wxEVT_NULL
, m_windowId
);
1428 NMHDR
* hdr
= (NMHDR
*)lParam
;
1429 switch ( hdr
->code
) {
1431 event
.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
);
1434 case TCN_SELCHANGING
:
1435 event
.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
);
1439 return wxControl::MSWOnNotify(idCtrl
, lParam
, result
);
1442 event
.SetSelection(TabCtrl_GetCurSel(GetHwnd()));
1443 event
.SetOldSelection(m_nSelection
);
1444 event
.SetEventObject(this);
1445 event
.SetInt(idCtrl
);
1447 bool processed
= HandleWindowEvent(event
);
1448 *result
= !event
.IsAllowed();
1452 #endif // wxUSE_NOTEBOOK