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 BEGIN_EVENT_TABLE(wxNotebook
, wxBookCtrlBase
)
124 EVT_NOTEBOOK_PAGE_CHANGED(wxID_ANY
, wxNotebook::OnSelChange
)
125 EVT_SIZE(wxNotebook::OnSize
)
126 EVT_NAVIGATION_KEY(wxNotebook::OnNavigationKey
)
128 #if USE_NOTEBOOK_ANTIFLICKER
129 EVT_ERASE_BACKGROUND(wxNotebook::OnEraseBackground
)
130 EVT_PAINT(wxNotebook::OnPaint
)
131 #endif // USE_NOTEBOOK_ANTIFLICKER
134 #if wxUSE_EXTENDED_RTTI
135 WX_DEFINE_FLAGS( wxNotebookStyle
)
137 wxBEGIN_FLAGS( wxNotebookStyle
)
138 // new style border flags, we put them first to
139 // use them for streaming out
140 wxFLAGS_MEMBER(wxBORDER_SIMPLE
)
141 wxFLAGS_MEMBER(wxBORDER_SUNKEN
)
142 wxFLAGS_MEMBER(wxBORDER_DOUBLE
)
143 wxFLAGS_MEMBER(wxBORDER_RAISED
)
144 wxFLAGS_MEMBER(wxBORDER_STATIC
)
145 wxFLAGS_MEMBER(wxBORDER_NONE
)
147 // old style border flags
148 wxFLAGS_MEMBER(wxSIMPLE_BORDER
)
149 wxFLAGS_MEMBER(wxSUNKEN_BORDER
)
150 wxFLAGS_MEMBER(wxDOUBLE_BORDER
)
151 wxFLAGS_MEMBER(wxRAISED_BORDER
)
152 wxFLAGS_MEMBER(wxSTATIC_BORDER
)
153 wxFLAGS_MEMBER(wxBORDER
)
155 // standard window styles
156 wxFLAGS_MEMBER(wxTAB_TRAVERSAL
)
157 wxFLAGS_MEMBER(wxCLIP_CHILDREN
)
158 wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW
)
159 wxFLAGS_MEMBER(wxWANTS_CHARS
)
160 wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE
)
161 wxFLAGS_MEMBER(wxALWAYS_SHOW_SB
)
162 wxFLAGS_MEMBER(wxVSCROLL
)
163 wxFLAGS_MEMBER(wxHSCROLL
)
165 wxFLAGS_MEMBER(wxNB_FIXEDWIDTH
)
166 wxFLAGS_MEMBER(wxBK_DEFAULT
)
167 wxFLAGS_MEMBER(wxBK_TOP
)
168 wxFLAGS_MEMBER(wxBK_LEFT
)
169 wxFLAGS_MEMBER(wxBK_RIGHT
)
170 wxFLAGS_MEMBER(wxBK_BOTTOM
)
171 wxFLAGS_MEMBER(wxNB_NOPAGETHEME
)
172 wxFLAGS_MEMBER(wxNB_FLAT
)
174 wxEND_FLAGS( wxNotebookStyle
)
176 IMPLEMENT_DYNAMIC_CLASS_XTI(wxNotebook
, wxBookCtrlBase
,"wx/notebook.h")
177 IMPLEMENT_DYNAMIC_CLASS_XTI(wxNotebookPageInfo
, wxObject
, "wx/notebook.h" )
179 wxCOLLECTION_TYPE_INFO( wxNotebookPageInfo
* , wxNotebookPageInfoList
) ;
181 template<> void wxCollectionToVariantArray( wxNotebookPageInfoList
const &theList
, wxxVariantArray
&value
)
183 wxListCollectionToVariantArray
<wxNotebookPageInfoList::compatibility_iterator
>( theList
, value
) ;
186 wxBEGIN_PROPERTIES_TABLE(wxNotebook
)
187 wxEVENT_PROPERTY( PageChanging
, wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
, wxBookCtrlEvent
)
188 wxEVENT_PROPERTY( PageChanged
, wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
, wxBookCtrlEvent
)
190 wxPROPERTY_COLLECTION( PageInfos
, wxNotebookPageInfoList
, wxNotebookPageInfo
* , AddPageInfo
, GetPageInfos
, 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
191 wxPROPERTY_FLAGS( WindowStyle
, wxNotebookStyle
, long , SetWindowStyleFlag
, GetWindowStyleFlag
, EMPTY_MACROVALUE
, 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
192 wxEND_PROPERTIES_TABLE()
194 wxBEGIN_HANDLERS_TABLE(wxNotebook
)
195 wxEND_HANDLERS_TABLE()
197 wxCONSTRUCTOR_5( wxNotebook
, wxWindow
* , Parent
, wxWindowID
, Id
, wxPoint
, Position
, wxSize
, Size
, long , WindowStyle
)
200 wxBEGIN_PROPERTIES_TABLE(wxNotebookPageInfo
)
201 wxREADONLY_PROPERTY( Page
, wxNotebookPage
* , GetPage
, EMPTY_MACROVALUE
, 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
202 wxREADONLY_PROPERTY( Text
, wxString
, GetText
, wxString() , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
203 wxREADONLY_PROPERTY( Selected
, bool , GetSelected
, false, 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
204 wxREADONLY_PROPERTY( ImageId
, int , GetImageId
, -1 , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
205 wxEND_PROPERTIES_TABLE()
207 wxBEGIN_HANDLERS_TABLE(wxNotebookPageInfo
)
208 wxEND_HANDLERS_TABLE()
210 wxCONSTRUCTOR_4( wxNotebookPageInfo
, wxNotebookPage
* , Page
, wxString
, Text
, bool , Selected
, int , ImageId
)
213 IMPLEMENT_DYNAMIC_CLASS(wxNotebook
, wxBookCtrlBase
)
214 IMPLEMENT_DYNAMIC_CLASS(wxNotebookPageInfo
, wxObject
)
217 // ============================================================================
219 // ============================================================================
221 // ----------------------------------------------------------------------------
222 // wxNotebook construction
223 // ----------------------------------------------------------------------------
225 const wxNotebookPageInfoList
& wxNotebook::GetPageInfos() const
227 wxNotebookPageInfoList
* list
= const_cast< wxNotebookPageInfoList
* >( &m_pageInfos
) ;
228 WX_CLEAR_LIST( wxNotebookPageInfoList
, *list
) ;
229 for( size_t i
= 0 ; i
< GetPageCount() ; ++i
)
231 wxNotebookPageInfo
*info
= new wxNotebookPageInfo() ;
232 info
->Create( const_cast<wxNotebook
*>(this)->GetPage(i
) , GetPageText(i
) , GetSelection() == int(i
) , GetPageImage(i
) ) ;
233 list
->Append( info
) ;
238 // common part of all ctors
239 void wxNotebook::Init()
242 m_nSelection
= wxNOT_FOUND
;
245 m_hbrBackground
= NULL
;
246 #endif // wxUSE_UXTHEME
248 #if USE_NOTEBOOK_ANTIFLICKER
249 m_hasSubclassedUpdown
= false;
250 #endif // USE_NOTEBOOK_ANTIFLICKER
253 // default for dynamic class
254 wxNotebook::wxNotebook()
259 // the same arguments as for wxControl
260 wxNotebook::wxNotebook(wxWindow
*parent
,
265 const wxString
& name
)
269 Create(parent
, id
, pos
, size
, style
, name
);
273 bool wxNotebook::Create(wxWindow
*parent
,
278 const wxString
& name
)
280 if ( (style
& wxBK_ALIGN_MASK
) == wxBK_DEFAULT
)
282 #if defined(__POCKETPC__)
283 style
|= wxBK_BOTTOM
| wxNB_FLAT
;
290 // Not sure why, but without this style, there is no border
291 // around the notebook tabs.
292 if (style
& wxNB_FLAT
)
293 style
|= wxBORDER_SUNKEN
;
297 // ComCtl32 notebook tabs simply don't work unless they're on top if we
298 // have uxtheme, we can work around it later (after control creation), but
299 // if we have been compiled without uxtheme support, we have to clear those
301 if ( HasTroubleWithNonTopTabs() )
303 style
&= ~(wxBK_BOTTOM
| wxBK_LEFT
| wxBK_RIGHT
);
305 #endif //wxUSE_UXTHEME
307 #if defined(__WINE__) && wxUSE_UNICODE
308 LPCTSTR className
= L
"SysTabControl32";
310 LPCTSTR className
= WC_TABCONTROL
;
313 #if USE_NOTEBOOK_ANTIFLICKER
314 // SysTabCtl32 class has natively CS_HREDRAW and CS_VREDRAW enabled and it
315 // causes horrible flicker when resizing notebook, so get rid of it by
316 // using a class without these styles (but otherwise identical to it)
317 if ( !HasFlag(wxFULL_REPAINT_ON_RESIZE
) )
319 static ClassRegistrar s_clsNotebook
;
320 if ( !s_clsNotebook
.IsInitialized() )
322 // get a copy of standard class and modify it
325 if ( ::GetClassInfo(NULL
, WC_TABCONTROL
, &wc
) )
328 reinterpret_cast<WXFARPROC
>(wc
.lpfnWndProc
);
329 wc
.lpszClassName
= wxT("_wx_SysTabCtl32");
330 wc
.style
&= ~(CS_HREDRAW
| CS_VREDRAW
);
331 wc
.hInstance
= wxGetInstance();
332 wc
.lpfnWndProc
= wxNotebookWndProc
;
333 s_clsNotebook
.Register(wc
);
337 wxLogLastError(wxT("GetClassInfoEx(SysTabCtl32)"));
341 // use our custom class if available but fall back to the standard
342 // notebook if we failed to register it
343 if ( s_clsNotebook
.IsRegistered() )
345 // it's ok to use c_str() here as the static s_clsNotebook object
346 // has sufficiently long lifetime
347 className
= s_clsNotebook
.GetName().c_str();
350 #endif // USE_NOTEBOOK_ANTIFLICKER
352 if ( !CreateControl(parent
, id
, pos
, size
, style
| wxTAB_TRAVERSAL
,
353 wxDefaultValidator
, name
) )
356 if ( !MSWCreateControl(className
, wxEmptyString
, pos
, size
) )
360 if ( HasFlag(wxNB_NOPAGETHEME
) ||
361 wxSystemOptions::IsFalse(wxT("msw.notebook.themed-background")) )
363 SetBackgroundColour(GetThemeBackgroundColour());
365 else // use themed background by default
367 // create backing store
371 // comctl32.dll 6.0 doesn't support non-top tabs with visual styles (the
372 // control is simply not rendered correctly), so we disable themes
373 // if possible, otherwise we simply clear the styles.
374 if ( HasTroubleWithNonTopTabs() &&
375 (style
& (wxBK_BOTTOM
| wxBK_LEFT
| wxBK_RIGHT
)) )
377 // check if we use themes at all -- if we don't, we're still okay
378 if ( wxUxThemeEngine::GetIfActive() )
380 wxUxThemeEngine::GetIfActive()->SetWindowTheme(GetHwnd(), L
"", L
"");
382 // correct the background color for the new non-themed control
383 SetBackgroundColour(GetThemeBackgroundColour());
386 #endif // wxUSE_UXTHEME
388 // Undocumented hack to get flat notebook style
389 // In fact, we should probably only do this in some
390 // curcumstances, i.e. if we know we will have a border
391 // at the bottom (the tab control doesn't draw it itself)
392 #if defined(__POCKETPC__) || defined(__SMARTPHONE__)
393 if (HasFlag(wxNB_FLAT
))
395 SendMessage(GetHwnd(), CCM_SETVERSION
, COMCTL32_VERSION
, 0);
397 SetBackgroundColour(*wxWHITE
);
403 WXDWORD
wxNotebook::MSWGetStyle(long style
, WXDWORD
*exstyle
) const
405 WXDWORD tabStyle
= wxControl::MSWGetStyle(style
, exstyle
);
407 tabStyle
|= WS_TABSTOP
| TCS_TABS
;
409 if ( style
& wxNB_MULTILINE
)
410 tabStyle
|= TCS_MULTILINE
;
411 if ( style
& wxNB_FIXEDWIDTH
)
412 tabStyle
|= TCS_FIXEDWIDTH
;
414 if ( style
& wxBK_BOTTOM
)
415 tabStyle
|= TCS_RIGHT
;
416 else if ( style
& wxBK_LEFT
)
417 tabStyle
|= TCS_VERTICAL
;
418 else if ( style
& wxBK_RIGHT
)
419 tabStyle
|= TCS_VERTICAL
| TCS_RIGHT
;
424 wxNotebook::~wxNotebook()
427 if ( m_hbrBackground
)
428 ::DeleteObject((HBRUSH
)m_hbrBackground
);
429 #endif // wxUSE_UXTHEME
432 // ----------------------------------------------------------------------------
433 // wxNotebook accessors
434 // ----------------------------------------------------------------------------
436 size_t wxNotebook::GetPageCount() const
439 wxASSERT( (int)m_pages
.Count() == TabCtrl_GetItemCount(GetHwnd()) );
441 return m_pages
.Count();
444 int wxNotebook::GetRowCount() const
446 return TabCtrl_GetRowCount(GetHwnd());
449 int wxNotebook::SetSelection(size_t nPage
)
451 wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxNOT_FOUND
, wxT("notebook page out of range") );
453 if ( m_nSelection
== wxNOT_FOUND
|| nPage
!= (size_t)m_nSelection
)
455 if ( SendPageChangingEvent(nPage
) )
457 // program allows the page change
458 SendPageChangedEvent(m_nSelection
, nPage
);
460 TabCtrl_SetCurSel(GetHwnd(), nPage
);
467 void wxNotebook::UpdateSelection(int selNew
)
469 if ( m_nSelection
!= wxNOT_FOUND
)
470 m_pages
[m_nSelection
]->Show(false);
472 if ( selNew
!= wxNOT_FOUND
)
474 wxNotebookPage
*pPage
= m_pages
[selNew
];
478 // Changing the page should give the focus to it but, as per bug report
479 // http://sf.net/tracker/index.php?func=detail&aid=1150659&group_id=9863&atid=109863,
480 // we should not set the focus to it directly since it erroneously
481 // selects radio buttons and breaks keyboard handling for a notebook's
482 // scroll buttons. So give focus to the notebook and not the page.
484 // but don't do this is the notebook is hidden
485 if ( ::IsWindowVisible(GetHwnd()) )
488 m_nSelection
= selNew
;
491 int wxNotebook::ChangeSelection(size_t nPage
)
493 wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxNOT_FOUND
, wxT("notebook page out of range") );
495 if ( m_nSelection
== wxNOT_FOUND
|| nPage
!= (size_t)m_nSelection
)
497 TabCtrl_SetCurSel(GetHwnd(), nPage
);
499 UpdateSelection(nPage
);
505 bool wxNotebook::SetPageText(size_t nPage
, const wxString
& strText
)
507 wxCHECK_MSG( IS_VALID_PAGE(nPage
), false, wxT("notebook page out of range") );
510 tcItem
.mask
= TCIF_TEXT
;
511 tcItem
.pszText
= (wxChar
*)strText
.wx_str();
513 if ( !HasFlag(wxNB_MULTILINE
) )
514 return TabCtrl_SetItem(GetHwnd(), nPage
, &tcItem
) != 0;
516 // multiline - we need to set new page size if a line is added or removed
517 int rows
= GetRowCount();
518 bool ret
= TabCtrl_SetItem(GetHwnd(), nPage
, &tcItem
) != 0;
520 if ( ret
&& rows
!= GetRowCount() )
522 const wxRect r
= GetPageSize();
523 const size_t count
= m_pages
.Count();
524 for ( size_t page
= 0; page
< count
; page
++ )
525 m_pages
[page
]->SetSize(r
);
531 wxString
wxNotebook::GetPageText(size_t nPage
) const
533 wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxEmptyString
, wxT("notebook page out of range") );
537 tcItem
.mask
= TCIF_TEXT
;
538 tcItem
.pszText
= buf
;
539 tcItem
.cchTextMax
= WXSIZEOF(buf
);
542 if ( TabCtrl_GetItem(GetHwnd(), nPage
, &tcItem
) )
543 str
= tcItem
.pszText
;
548 int wxNotebook::GetPageImage(size_t nPage
) const
550 wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxNOT_FOUND
, wxT("notebook page out of range") );
553 tcItem
.mask
= TCIF_IMAGE
;
555 return TabCtrl_GetItem(GetHwnd(), nPage
, &tcItem
) ? tcItem
.iImage
559 bool wxNotebook::SetPageImage(size_t nPage
, int nImage
)
561 wxCHECK_MSG( IS_VALID_PAGE(nPage
), false, wxT("notebook page out of range") );
564 tcItem
.mask
= TCIF_IMAGE
;
565 tcItem
.iImage
= nImage
;
567 return TabCtrl_SetItem(GetHwnd(), nPage
, &tcItem
) != 0;
570 void wxNotebook::SetImageList(wxImageList
* imageList
)
572 wxNotebookBase::SetImageList(imageList
);
576 (void) TabCtrl_SetImageList(GetHwnd(), GetHimagelistOf(imageList
));
580 // ----------------------------------------------------------------------------
581 // wxNotebook size settings
582 // ----------------------------------------------------------------------------
584 wxRect
wxNotebook::GetPageSize() const
589 ::GetClientRect(GetHwnd(), &rc
);
591 // This check is to work around a bug in TabCtrl_AdjustRect which will
592 // cause a crash on win2k or on XP with themes disabled if either
593 // wxNB_MULTILINE is used or tabs are placed on a side, if the rectangle
596 // The value of 20 is chosen arbitrarily but seems to work
597 if ( rc
.right
> 20 && rc
.bottom
> 20 )
599 TabCtrl_AdjustRect(GetHwnd(), false, &rc
);
601 wxCopyRECTToRect(rc
, r
);
607 void wxNotebook::SetPageSize(const wxSize
& size
)
609 // transform the page size into the notebook size
616 TabCtrl_AdjustRect(GetHwnd(), true, &rc
);
619 SetSize(rc
.right
- rc
.left
, rc
.bottom
- rc
.top
);
622 void wxNotebook::SetPadding(const wxSize
& padding
)
624 TabCtrl_SetPadding(GetHwnd(), padding
.x
, padding
.y
);
627 // Windows-only at present. Also, you must use the wxNB_FIXEDWIDTH
629 void wxNotebook::SetTabSize(const wxSize
& sz
)
631 ::SendMessage(GetHwnd(), TCM_SETITEMSIZE
, 0, MAKELPARAM(sz
.x
, sz
.y
));
634 wxSize
wxNotebook::CalcSizeFromPage(const wxSize
& sizePage
) const
636 // we can't use TabCtrl_AdjustRect here because it only works for wxNB_TOP
637 wxSize sizeTotal
= sizePage
;
640 if ( GetPageCount() > 0 )
643 TabCtrl_GetItemRect(GetHwnd(), 0, &rect
);
644 tabSize
.x
= rect
.right
- rect
.left
;
645 tabSize
.y
= rect
.bottom
- rect
.top
;
648 const int rows
= GetRowCount();
650 // add an extra margin in both directions
651 const int MARGIN
= 8;
654 sizeTotal
.x
+= MARGIN
;
655 sizeTotal
.y
+= tabSize
.y
* rows
+ MARGIN
;
657 else // horizontal layout
659 sizeTotal
.x
+= tabSize
.x
* rows
+ MARGIN
;
660 sizeTotal
.y
+= MARGIN
;
666 void wxNotebook::AdjustPageSize(wxNotebookPage
*page
)
668 wxCHECK_RET( page
, wxT("NULL page in wxNotebook::AdjustPageSize") );
670 const wxRect r
= GetPageSize();
677 // ----------------------------------------------------------------------------
678 // wxNotebook operations
679 // ----------------------------------------------------------------------------
681 // remove one page from the notebook, without deleting
682 wxNotebookPage
*wxNotebook::DoRemovePage(size_t nPage
)
684 wxNotebookPage
*pageRemoved
= wxNotebookBase::DoRemovePage(nPage
);
688 TabCtrl_DeleteItem(GetHwnd(), nPage
);
690 if ( m_pages
.IsEmpty() )
692 // no selection any more, the notebook becamse empty
693 m_nSelection
= wxNOT_FOUND
;
695 else // notebook still not empty
697 int selNew
= TabCtrl_GetCurSel(GetHwnd());
698 if ( selNew
!= wxNOT_FOUND
)
700 // No selection change, just refresh the current selection.
701 // Because it could be that the slection index changed
702 // we need to update it.
703 // Note: this does not mean the selection it self changed.
704 m_nSelection
= selNew
;
705 m_pages
[m_nSelection
]->Refresh();
707 else if (int(nPage
) == m_nSelection
)
709 // The selection was deleted.
711 // Determine new selection.
712 if (m_nSelection
== int(GetPageCount()))
713 selNew
= m_nSelection
- 1;
715 selNew
= m_nSelection
;
717 // m_nSelection must be always valid so reset it before calling
719 m_nSelection
= wxNOT_FOUND
;
720 SetSelection(selNew
);
724 wxFAIL
; // Windows did not behave ok.
732 bool wxNotebook::DeleteAllPages()
734 size_t nPageCount
= GetPageCount();
736 for ( nPage
= 0; nPage
< nPageCount
; nPage
++ )
737 delete m_pages
[nPage
];
741 TabCtrl_DeleteAllItems(GetHwnd());
743 m_nSelection
= wxNOT_FOUND
;
745 InvalidateBestSize();
749 // same as AddPage() but does it at given position
750 bool wxNotebook::InsertPage(size_t nPage
,
751 wxNotebookPage
*pPage
,
752 const wxString
& strText
,
756 wxCHECK_MSG( pPage
!= NULL
, false, wxT("NULL page in wxNotebook::InsertPage") );
757 wxCHECK_MSG( IS_VALID_PAGE(nPage
) || nPage
== GetPageCount(), false,
758 wxT("invalid index in wxNotebook::InsertPage") );
760 wxASSERT_MSG( pPage
->GetParent() == this,
761 wxT("notebook pages must have notebook as parent") );
763 // add a new tab to the control
764 // ----------------------------
766 // init all fields to 0
768 wxZeroMemory(tcItem
);
770 // set the image, if any
773 tcItem
.mask
|= TCIF_IMAGE
;
774 tcItem
.iImage
= imageId
;
778 if ( !strText
.empty() )
780 tcItem
.mask
|= TCIF_TEXT
;
781 tcItem
.pszText
= (wxChar
*)strText
.wx_str(); // const_cast
784 // hide the page: unless it is selected, it shouldn't be shown (and if it
785 // is selected it will be shown later)
786 HWND hwnd
= GetWinHwnd(pPage
);
787 SetWindowLong(hwnd
, GWL_STYLE
, GetWindowLong(hwnd
, GWL_STYLE
) & ~WS_VISIBLE
);
789 // this updates internal flag too -- otherwise it would get out of sync
790 // with the real state
794 // fit the notebook page to the tab control's display area: this should be
795 // done before adding it to the notebook or TabCtrl_InsertItem() will
796 // change the notebooks size itself!
797 AdjustPageSize(pPage
);
799 // finally do insert it
800 if ( TabCtrl_InsertItem(GetHwnd(), nPage
, &tcItem
) == -1 )
802 wxLogError(wxT("Can't create the notebook page '%s'."), strText
.c_str());
807 // need to update the bg brush when the first page is added
808 // so the first panel gets the correct themed background
809 if ( m_pages
.empty() )
813 #endif // wxUSE_UXTHEME
816 // succeeded: save the pointer to the page
817 m_pages
.Insert(pPage
, nPage
);
819 // we may need to adjust the size again if the notebook size changed:
820 // normally this only happens for the first page we add (the tabs which
821 // hadn't been there before are now shown) but for a multiline notebook it
822 // can happen for any page at all as a new row could have been started
823 if ( m_pages
.GetCount() == 1 || HasFlag(wxNB_MULTILINE
) )
825 AdjustPageSize(pPage
);
828 // now deal with the selection
829 // ---------------------------
831 // if the inserted page is before the selected one, we must update the
832 // index of the selected page
833 if ( int(nPage
) <= m_nSelection
)
835 // one extra page added
839 // some page should be selected: either this one or the first one if there
840 // is still no selection
841 int selNew
= wxNOT_FOUND
;
844 else if ( m_nSelection
== wxNOT_FOUND
)
847 if ( selNew
!= wxNOT_FOUND
)
848 SetSelection(selNew
);
850 InvalidateBestSize();
855 int wxNotebook::HitTest(const wxPoint
& pt
, long *flags
) const
857 TC_HITTESTINFO hitTestInfo
;
858 hitTestInfo
.pt
.x
= pt
.x
;
859 hitTestInfo
.pt
.y
= pt
.y
;
860 int item
= TabCtrl_HitTest(GetHwnd(), &hitTestInfo
);
866 if ((hitTestInfo
.flags
& TCHT_NOWHERE
) == TCHT_NOWHERE
)
867 *flags
|= wxBK_HITTEST_NOWHERE
;
868 if ((hitTestInfo
.flags
& TCHT_ONITEM
) == TCHT_ONITEM
)
869 *flags
|= wxBK_HITTEST_ONITEM
;
870 if ((hitTestInfo
.flags
& TCHT_ONITEMICON
) == TCHT_ONITEMICON
)
871 *flags
|= wxBK_HITTEST_ONICON
;
872 if ((hitTestInfo
.flags
& TCHT_ONITEMLABEL
) == TCHT_ONITEMLABEL
)
873 *flags
|= wxBK_HITTEST_ONLABEL
;
874 if ( item
== wxNOT_FOUND
&& GetPageSize().Contains(pt
) )
875 *flags
|= wxBK_HITTEST_ONPAGE
;
881 // ----------------------------------------------------------------------------
882 // flicker-less notebook redraw
883 // ----------------------------------------------------------------------------
885 #if USE_NOTEBOOK_ANTIFLICKER
887 // wnd proc for the spin button
888 LRESULT APIENTRY _EXPORT
wxNotebookSpinBtnWndProc(HWND hwnd
,
893 if ( message
== WM_ERASEBKGND
)
896 return ::CallWindowProc(CASTWNDPROC gs_wndprocNotebookSpinBtn
,
897 hwnd
, message
, wParam
, lParam
);
900 LRESULT APIENTRY _EXPORT
wxNotebookWndProc(HWND hwnd
,
905 return ::CallWindowProc(CASTWNDPROC gs_wndprocNotebook
,
906 hwnd
, message
, wParam
, lParam
);
909 void wxNotebook::OnEraseBackground(wxEraseEvent
& WXUNUSED(event
))
914 void wxNotebook::OnPaint(wxPaintEvent
& WXUNUSED(event
))
919 ::GetClientRect(GetHwnd(), &rc
);
920 wxBitmap
bmp(rc
.right
, rc
.bottom
);
921 memdc
.SelectObject(bmp
);
923 const wxLayoutDirection dir
= dc
.GetLayoutDirection();
924 memdc
.SetLayoutDirection(dir
);
926 // if there is no special brush just use the solid background colour
928 HBRUSH hbr
= (HBRUSH
)m_hbrBackground
;
935 brush
= wxBrush(GetBackgroundColour());
936 hbr
= GetHbrushOf(brush
);
939 wxMSWDCImpl
*impl
= (wxMSWDCImpl
*) memdc
.GetImpl();
941 ::FillRect(GetHdcOf(*impl
), &rc
, hbr
);
943 MSWDefWindowProc(WM_PAINT
, (WPARAM
)(impl
->GetHDC()), 0);
945 // For some reason in RTL mode, source offset has to be -1, otherwise the
946 // right border (physical) remains unpainted.
947 const wxCoord ofs
= dir
== wxLayout_RightToLeft
? -1 : 0;
948 dc
.Blit(ofs
, 0, rc
.right
, rc
.bottom
, &memdc
, ofs
, 0);
951 #endif // USE_NOTEBOOK_ANTIFLICKER
953 // ----------------------------------------------------------------------------
954 // wxNotebook callbacks
955 // ----------------------------------------------------------------------------
957 void wxNotebook::OnSize(wxSizeEvent
& event
)
959 if ( GetPageCount() == 0 )
961 // Prevents droppings on resize, but does cause some flicker
962 // when there are no pages.
970 // Without this, we can sometimes get droppings at the edges
971 // of a notebook, for example a notebook in a splitter window.
972 // This needs to be reconciled with the RefreshRect calls
973 // at the end of this function, which weren't enough to prevent
976 wxSize sz
= GetClientSize();
978 // Refresh right side
979 wxRect
rect(sz
.x
-4, 0, 4, sz
.y
);
982 // Refresh bottom side
983 rect
= wxRect(0, sz
.y
-4, sz
.x
, 4);
987 rect
= wxRect(0, 0, 4, sz
.y
);
990 #endif // !__WXWINCE__
992 // fit all the notebook pages to the tab control's display area
995 rc
.left
= rc
.top
= 0;
996 GetSize((int *)&rc
.right
, (int *)&rc
.bottom
);
998 // save the total size, we'll use it below
999 int widthNbook
= rc
.right
- rc
.left
,
1000 heightNbook
= rc
.bottom
- rc
.top
;
1002 // there seems to be a bug in the implementation of TabCtrl_AdjustRect(): it
1003 // returns completely false values for multiline tab controls after the tabs
1004 // are added but before getting the first WM_SIZE (off by ~50 pixels, see
1006 // http://sf.net/tracker/index.php?func=detail&aid=645323&group_id=9863&atid=109863
1008 // and the only work around I could find was this ugly hack... without it
1009 // simply toggling the "multiline" checkbox in the notebook sample resulted
1010 // in a noticeable page displacement
1011 if ( HasFlag(wxNB_MULTILINE
) )
1013 // avoid an infinite recursion: we get another notification too!
1014 static bool s_isInOnSize
= false;
1016 if ( !s_isInOnSize
)
1018 s_isInOnSize
= true;
1019 SendMessage(GetHwnd(), WM_SIZE
, SIZE_RESTORED
,
1020 MAKELPARAM(rc
.right
, rc
.bottom
));
1021 s_isInOnSize
= false;
1024 // The best size depends on the number of rows of tabs, which can
1025 // change when the notepad is resized.
1026 InvalidateBestSize();
1030 // background bitmap size has changed, update the brush using it too
1032 #endif // wxUSE_UXTHEME
1034 TabCtrl_AdjustRect(GetHwnd(), false, &rc
);
1036 int width
= rc
.right
- rc
.left
,
1037 height
= rc
.bottom
- rc
.top
;
1038 size_t nCount
= m_pages
.Count();
1039 for ( size_t nPage
= 0; nPage
< nCount
; nPage
++ ) {
1040 wxNotebookPage
*pPage
= m_pages
[nPage
];
1041 pPage
->SetSize(rc
.left
, rc
.top
, width
, height
);
1045 // unless we had already repainted everything, we now need to refresh
1046 if ( !HasFlag(wxFULL_REPAINT_ON_RESIZE
) )
1048 // invalidate areas not covered by pages
1049 RefreshRect(wxRect(0, 0, widthNbook
, rc
.top
), false);
1050 RefreshRect(wxRect(0, rc
.top
, rc
.left
, height
), false);
1051 RefreshRect(wxRect(0, rc
.bottom
, widthNbook
, heightNbook
- rc
.bottom
),
1053 RefreshRect(wxRect(rc
.right
, rc
.top
, widthNbook
- rc
.right
, height
),
1057 #if USE_NOTEBOOK_ANTIFLICKER
1058 // subclass the spin control used by the notebook to scroll pages to
1059 // prevent it from flickering on resize
1060 if ( !m_hasSubclassedUpdown
)
1062 // iterate over all child windows to find spin button
1063 for ( HWND child
= ::GetWindow(GetHwnd(), GW_CHILD
);
1065 child
= ::GetWindow(child
, GW_HWNDNEXT
) )
1067 wxWindow
*childWindow
= wxFindWinFromHandle((WXHWND
)child
);
1069 // see if it exists, if no wxWindow found then assume it's the spin
1073 // subclass the spin button to override WM_ERASEBKGND
1074 if ( !gs_wndprocNotebookSpinBtn
)
1075 gs_wndprocNotebookSpinBtn
= (WXFARPROC
)wxGetWindowProc(child
);
1077 wxSetWindowProc(child
, wxNotebookSpinBtnWndProc
);
1078 m_hasSubclassedUpdown
= true;
1083 #endif // USE_NOTEBOOK_ANTIFLICKER
1088 void wxNotebook::OnSelChange(wxBookCtrlEvent
& event
)
1090 // is it our tab control?
1091 if ( event
.GetEventObject() == this )
1093 UpdateSelection(event
.GetSelection());
1096 // we want to give others a chance to process this message as well
1100 void wxNotebook::OnNavigationKey(wxNavigationKeyEvent
& event
)
1102 if ( event
.IsWindowChange() ) {
1104 AdvanceSelection(event
.GetDirection());
1107 // we get this event in 3 cases
1109 // a) one of our pages might have generated it because the user TABbed
1110 // out from it in which case we should propagate the event upwards and
1111 // our parent will take care of setting the focus to prev/next sibling
1115 // b) the parent panel wants to give the focus to us so that we
1116 // forward it to our selected page. We can't deal with this in
1117 // OnSetFocus() because we don't know which direction the focus came
1118 // from in this case and so can't choose between setting the focus to
1119 // first or last panel child
1123 // c) we ourselves (see MSWTranslateMessage) generated the event
1125 wxWindow
* const parent
= GetParent();
1127 // the wxObject* casts are required to avoid MinGW GCC 2.95.3 ICE
1128 const bool isFromParent
= event
.GetEventObject() == (wxObject
*) parent
;
1129 const bool isFromSelf
= event
.GetEventObject() == (wxObject
*) this;
1131 if ( isFromParent
|| isFromSelf
)
1133 // no, it doesn't come from child, case (b) or (c): forward to a
1134 // page but only if direction is backwards (TAB) or from ourselves,
1135 if ( m_nSelection
!= wxNOT_FOUND
&&
1136 (!event
.GetDirection() || isFromSelf
) )
1138 // so that the page knows that the event comes from it's parent
1139 // and is being propagated downwards
1140 event
.SetEventObject(this);
1142 wxWindow
*page
= m_pages
[m_nSelection
];
1143 if ( !page
->HandleWindowEvent(event
) )
1147 //else: page manages focus inside it itself
1149 else // otherwise set the focus to the notebook itself
1156 // it comes from our child, case (a), pass to the parent, but only
1157 // if the direction is forwards. Otherwise set the focus to the
1158 // notebook itself. The notebook is always the 'first' control of a
1160 if ( !event
.GetDirection() )
1166 event
.SetCurrentFocus(this);
1167 parent
->HandleWindowEvent(event
);
1175 bool wxNotebook::DoDrawBackground(WXHDC hDC
, wxWindow
*child
)
1177 wxUxThemeHandle
theme(child
? child
: this, L
"TAB");
1181 // get the notebook client rect (we're not interested in drawing tabs
1183 wxRect r
= GetPageSize();
1188 wxCopyRectToRECT(r
, rc
);
1190 // map rect to the coords of the window we're drawing in
1192 ::MapWindowPoints(GetHwnd(), GetHwndOf(child
), (POINT
*)&rc
, 2);
1194 // we have the content area (page size), but we need to draw all of the
1195 // background for it to be aligned correctly
1196 wxUxThemeEngine::Get()->GetThemeBackgroundExtent
1205 wxUxThemeEngine::Get()->DrawThemeBackground
1218 WXHBRUSH
wxNotebook::QueryBgBitmap()
1220 wxRect r
= GetPageSize();
1224 WindowHDC
hDC(GetHwnd());
1225 MemoryHDC
hDCMem(hDC
);
1226 CompatibleBitmap
hBmp(hDC
, r
.x
+ r
.width
, r
.y
+ r
.height
);
1228 SelectInHDC
selectBmp(hDCMem
, hBmp
);
1230 if ( !DoDrawBackground((WXHDC
)(HDC
)hDCMem
) )
1233 return (WXHBRUSH
)::CreatePatternBrush(hBmp
);
1236 void wxNotebook::UpdateBgBrush()
1238 if ( m_hbrBackground
)
1239 ::DeleteObject((HBRUSH
)m_hbrBackground
);
1241 if ( !m_hasBgCol
&& wxUxThemeEngine::GetIfActive() )
1243 m_hbrBackground
= QueryBgBitmap();
1245 else // no themes or we've got user-defined solid colour
1247 m_hbrBackground
= NULL
;
1251 WXHBRUSH
wxNotebook::MSWGetBgBrushForChild(WXHDC hDC
, wxWindow
*child
)
1253 if ( m_hbrBackground
)
1255 // before drawing with the background brush, we need to position it
1258 ::GetWindowRect(GetHwndOf(child
), &rc
);
1260 ::MapWindowPoints(NULL
, GetHwnd(), (POINT
*)&rc
, 1);
1262 if ( !::SetBrushOrgEx((HDC
)hDC
, -rc
.left
, -rc
.top
, NULL
) )
1264 wxLogLastError(wxT("SetBrushOrgEx(notebook bg brush)"));
1267 return m_hbrBackground
;
1270 return wxNotebookBase::MSWGetBgBrushForChild(hDC
, child
);
1273 bool wxNotebook::MSWPrintChild(WXHDC hDC
, wxWindow
*child
)
1275 // solid background colour overrides themed background drawing
1276 if ( !UseBgCol() && DoDrawBackground(hDC
, child
) )
1279 // If we're using a solid colour (for example if we've switched off
1280 // theming for this notebook), paint it
1283 wxRect r
= GetPageSize();
1288 wxCopyRectToRECT(r
, rc
);
1290 // map rect to the coords of the window we're drawing in
1292 ::MapWindowPoints(GetHwnd(), GetHwndOf(child
), (POINT
*)&rc
, 2);
1294 wxBrush
brush(GetBackgroundColour());
1295 HBRUSH hbr
= GetHbrushOf(brush
);
1297 ::FillRect((HDC
) hDC
, &rc
, hbr
);
1302 return wxNotebookBase::MSWPrintChild(hDC
, child
);
1305 #endif // wxUSE_UXTHEME
1307 // Windows only: attempts to get colour for UX theme page background
1308 wxColour
wxNotebook::GetThemeBackgroundColour() const
1311 if (wxUxThemeEngine::Get())
1313 wxUxThemeHandle
hTheme((wxNotebook
*) this, L
"TAB");
1316 // This is total guesswork.
1317 // See PlatformSDK\Include\Tmschema.h for values.
1318 // JACS: can also use 9 (TABP_PANE)
1319 COLORREF themeColor
;
1320 bool success
= (S_OK
== wxUxThemeEngine::Get()->GetThemeColor(
1324 3821 /* FILLCOLORHINT */,
1327 return GetBackgroundColour();
1330 [DS] Workaround for WindowBlinds:
1331 Some themes return a near black theme color using FILLCOLORHINT,
1332 this makes notebook pages have an ugly black background and makes
1333 text (usually black) unreadable. Retry again with FILLCOLOR.
1335 This workaround potentially breaks appearance of some themes,
1336 but in practice it already fixes some themes.
1338 if (themeColor
== 1)
1340 wxUxThemeEngine::Get()->GetThemeColor(
1344 3802 /* FILLCOLOR */,
1348 wxColour colour
= wxRGBToColour(themeColor
);
1350 // Under Vista, the tab background colour is reported incorrectly.
1351 // So for the default theme at least, hard-code the colour to something
1352 // that will blend in.
1354 static int s_AeroStatus
= -1;
1355 if (s_AeroStatus
== -1)
1357 WCHAR szwThemeFile
[1024];
1358 WCHAR szwThemeColor
[256];
1359 if (S_OK
== wxUxThemeEngine::Get()->GetCurrentThemeName(szwThemeFile
, 1024, szwThemeColor
, 256, NULL
, 0))
1361 wxString
themeFile(szwThemeFile
), themeColor(szwThemeColor
);
1362 if (themeFile
.Find(wxT("Aero")) != -1 && themeColor
== wxT("NormalColor"))
1371 if (s_AeroStatus
== 1)
1372 colour
= wxColour(255, 255, 255);
1377 #endif // wxUSE_UXTHEME
1379 return GetBackgroundColour();
1382 // ----------------------------------------------------------------------------
1383 // wxNotebook base class virtuals
1384 // ----------------------------------------------------------------------------
1386 #if wxUSE_CONSTRAINTS
1388 // override these 2 functions to do nothing: everything is done in OnSize
1390 void wxNotebook::SetConstraintSizes(bool WXUNUSED(recurse
))
1392 // don't set the sizes of the pages - their correct size is not yet known
1393 wxControl::SetConstraintSizes(false);
1396 bool wxNotebook::DoPhase(int WXUNUSED(nPhase
))
1401 #endif // wxUSE_CONSTRAINTS
1403 // ----------------------------------------------------------------------------
1404 // wxNotebook Windows message handlers
1405 // ----------------------------------------------------------------------------
1407 bool wxNotebook::MSWOnScroll(int orientation
, WXWORD nSBCode
,
1408 WXWORD pos
, WXHWND control
)
1410 // don't generate EVT_SCROLLWIN events for the WM_SCROLLs coming from the
1415 return wxNotebookBase::MSWOnScroll(orientation
, nSBCode
, pos
, control
);
1418 bool wxNotebook::MSWOnNotify(int idCtrl
, WXLPARAM lParam
, WXLPARAM
* result
)
1420 wxBookCtrlEvent
event(wxEVT_NULL
, m_windowId
);
1422 NMHDR
* hdr
= (NMHDR
*)lParam
;
1423 switch ( hdr
->code
) {
1425 event
.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
);
1428 case TCN_SELCHANGING
:
1429 event
.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
);
1433 return wxControl::MSWOnNotify(idCtrl
, lParam
, result
);
1436 event
.SetSelection(TabCtrl_GetCurSel(GetHwnd()));
1437 event
.SetOldSelection(m_nSelection
);
1438 event
.SetEventObject(this);
1439 event
.SetInt(idCtrl
);
1441 bool processed
= HandleWindowEvent(event
);
1442 *result
= !event
.IsAllowed();
1446 #endif // wxUSE_NOTEBOOK