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(_T("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 // note that we never want to have the default WS_EX_CLIENTEDGE style
425 // as it looks too ugly for the notebooks
432 wxNotebook::~wxNotebook()
435 if ( m_hbrBackground
)
436 ::DeleteObject((HBRUSH
)m_hbrBackground
);
437 #endif // wxUSE_UXTHEME
440 // ----------------------------------------------------------------------------
441 // wxNotebook accessors
442 // ----------------------------------------------------------------------------
444 size_t wxNotebook::GetPageCount() const
447 wxASSERT( (int)m_pages
.Count() == TabCtrl_GetItemCount(GetHwnd()) );
449 return m_pages
.Count();
452 int wxNotebook::GetRowCount() const
454 return TabCtrl_GetRowCount(GetHwnd());
457 int wxNotebook::SetSelection(size_t nPage
)
459 wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxNOT_FOUND
, wxT("notebook page out of range") );
461 if ( m_nSelection
== wxNOT_FOUND
|| nPage
!= (size_t)m_nSelection
)
463 if ( SendPageChangingEvent(nPage
) )
465 // program allows the page change
466 SendPageChangedEvent(m_nSelection
, nPage
);
468 TabCtrl_SetCurSel(GetHwnd(), nPage
);
475 void wxNotebook::UpdateSelection(int selNew
)
477 if ( m_nSelection
!= wxNOT_FOUND
)
478 m_pages
[m_nSelection
]->Show(false);
480 if ( selNew
!= wxNOT_FOUND
)
482 wxNotebookPage
*pPage
= m_pages
[selNew
];
486 // Changing the page should give the focus to it but, as per bug report
487 // http://sf.net/tracker/index.php?func=detail&aid=1150659&group_id=9863&atid=109863,
488 // we should not set the focus to it directly since it erroneously
489 // selects radio buttons and breaks keyboard handling for a notebook's
490 // scroll buttons. So give focus to the notebook and not the page.
492 // but don't do this is the notebook is hidden
493 if ( ::IsWindowVisible(GetHwnd()) )
496 m_nSelection
= selNew
;
499 int wxNotebook::ChangeSelection(size_t nPage
)
501 wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxNOT_FOUND
, wxT("notebook page out of range") );
503 if ( m_nSelection
== wxNOT_FOUND
|| nPage
!= (size_t)m_nSelection
)
505 TabCtrl_SetCurSel(GetHwnd(), nPage
);
507 UpdateSelection(nPage
);
513 bool wxNotebook::SetPageText(size_t nPage
, const wxString
& strText
)
515 wxCHECK_MSG( IS_VALID_PAGE(nPage
), false, wxT("notebook page out of range") );
518 tcItem
.mask
= TCIF_TEXT
;
519 tcItem
.pszText
= (wxChar
*)strText
.wx_str();
521 if ( !HasFlag(wxNB_MULTILINE
) )
522 return TabCtrl_SetItem(GetHwnd(), nPage
, &tcItem
) != 0;
524 // multiline - we need to set new page size if a line is added or removed
525 int rows
= GetRowCount();
526 bool ret
= TabCtrl_SetItem(GetHwnd(), nPage
, &tcItem
) != 0;
528 if ( ret
&& rows
!= GetRowCount() )
530 const wxRect r
= GetPageSize();
531 const size_t count
= m_pages
.Count();
532 for ( size_t page
= 0; page
< count
; page
++ )
533 m_pages
[page
]->SetSize(r
);
539 wxString
wxNotebook::GetPageText(size_t nPage
) const
541 wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxEmptyString
, wxT("notebook page out of range") );
545 tcItem
.mask
= TCIF_TEXT
;
546 tcItem
.pszText
= buf
;
547 tcItem
.cchTextMax
= WXSIZEOF(buf
);
550 if ( TabCtrl_GetItem(GetHwnd(), nPage
, &tcItem
) )
551 str
= tcItem
.pszText
;
556 int wxNotebook::GetPageImage(size_t nPage
) const
558 wxCHECK_MSG( IS_VALID_PAGE(nPage
), wxNOT_FOUND
, wxT("notebook page out of range") );
561 tcItem
.mask
= TCIF_IMAGE
;
563 return TabCtrl_GetItem(GetHwnd(), nPage
, &tcItem
) ? tcItem
.iImage
567 bool wxNotebook::SetPageImage(size_t nPage
, int nImage
)
569 wxCHECK_MSG( IS_VALID_PAGE(nPage
), false, wxT("notebook page out of range") );
572 tcItem
.mask
= TCIF_IMAGE
;
573 tcItem
.iImage
= nImage
;
575 return TabCtrl_SetItem(GetHwnd(), nPage
, &tcItem
) != 0;
578 void wxNotebook::SetImageList(wxImageList
* imageList
)
580 wxNotebookBase::SetImageList(imageList
);
584 (void) TabCtrl_SetImageList(GetHwnd(), GetHimagelistOf(imageList
));
588 // ----------------------------------------------------------------------------
589 // wxNotebook size settings
590 // ----------------------------------------------------------------------------
592 wxRect
wxNotebook::GetPageSize() const
597 ::GetClientRect(GetHwnd(), &rc
);
599 // This check is to work around a bug in TabCtrl_AdjustRect which will
600 // cause a crash on win2k or on XP with themes disabled if either
601 // wxNB_MULTILINE is used or tabs are placed on a side, if the rectangle
604 // The value of 20 is chosen arbitrarily but seems to work
605 if ( rc
.right
> 20 && rc
.bottom
> 20 )
607 TabCtrl_AdjustRect(GetHwnd(), false, &rc
);
609 wxCopyRECTToRect(rc
, r
);
615 void wxNotebook::SetPageSize(const wxSize
& size
)
617 // transform the page size into the notebook size
624 TabCtrl_AdjustRect(GetHwnd(), true, &rc
);
627 SetSize(rc
.right
- rc
.left
, rc
.bottom
- rc
.top
);
630 void wxNotebook::SetPadding(const wxSize
& padding
)
632 TabCtrl_SetPadding(GetHwnd(), padding
.x
, padding
.y
);
635 // Windows-only at present. Also, you must use the wxNB_FIXEDWIDTH
637 void wxNotebook::SetTabSize(const wxSize
& sz
)
639 ::SendMessage(GetHwnd(), TCM_SETITEMSIZE
, 0, MAKELPARAM(sz
.x
, sz
.y
));
642 wxSize
wxNotebook::CalcSizeFromPage(const wxSize
& sizePage
) const
644 // we can't use TabCtrl_AdjustRect here because it only works for wxNB_TOP
645 wxSize sizeTotal
= sizePage
;
648 if ( GetPageCount() > 0 )
651 TabCtrl_GetItemRect(GetHwnd(), 0, &rect
);
652 tabSize
.x
= rect
.right
- rect
.left
;
653 tabSize
.y
= rect
.bottom
- rect
.top
;
656 const int rows
= GetRowCount();
658 // add an extra margin in both directions
659 const int MARGIN
= 8;
662 sizeTotal
.x
+= MARGIN
;
663 sizeTotal
.y
+= tabSize
.y
* rows
+ MARGIN
;
665 else // horizontal layout
667 sizeTotal
.x
+= tabSize
.x
* rows
+ MARGIN
;
668 sizeTotal
.y
+= MARGIN
;
674 void wxNotebook::AdjustPageSize(wxNotebookPage
*page
)
676 wxCHECK_RET( page
, _T("NULL page in wxNotebook::AdjustPageSize") );
678 const wxRect r
= GetPageSize();
685 // ----------------------------------------------------------------------------
686 // wxNotebook operations
687 // ----------------------------------------------------------------------------
689 // remove one page from the notebook, without deleting
690 wxNotebookPage
*wxNotebook::DoRemovePage(size_t nPage
)
692 wxNotebookPage
*pageRemoved
= wxNotebookBase::DoRemovePage(nPage
);
696 TabCtrl_DeleteItem(GetHwnd(), nPage
);
698 if ( m_pages
.IsEmpty() )
700 // no selection any more, the notebook becamse empty
701 m_nSelection
= wxNOT_FOUND
;
703 else // notebook still not empty
705 int selNew
= TabCtrl_GetCurSel(GetHwnd());
706 if ( selNew
!= wxNOT_FOUND
)
708 // No selection change, just refresh the current selection.
709 // Because it could be that the slection index changed
710 // we need to update it.
711 // Note: this does not mean the selection it self changed.
712 m_nSelection
= selNew
;
713 m_pages
[m_nSelection
]->Refresh();
715 else if (int(nPage
) == m_nSelection
)
717 // The selection was deleted.
719 // Determine new selection.
720 if (m_nSelection
== int(GetPageCount()))
721 selNew
= m_nSelection
- 1;
723 selNew
= m_nSelection
;
725 // m_nSelection must be always valid so reset it before calling
727 m_nSelection
= wxNOT_FOUND
;
728 SetSelection(selNew
);
732 wxFAIL
; // Windows did not behave ok.
740 bool wxNotebook::DeleteAllPages()
742 size_t nPageCount
= GetPageCount();
744 for ( nPage
= 0; nPage
< nPageCount
; nPage
++ )
745 delete m_pages
[nPage
];
749 TabCtrl_DeleteAllItems(GetHwnd());
751 m_nSelection
= wxNOT_FOUND
;
753 InvalidateBestSize();
757 // same as AddPage() but does it at given position
758 bool wxNotebook::InsertPage(size_t nPage
,
759 wxNotebookPage
*pPage
,
760 const wxString
& strText
,
764 wxCHECK_MSG( pPage
!= NULL
, false, _T("NULL page in wxNotebook::InsertPage") );
765 wxCHECK_MSG( IS_VALID_PAGE(nPage
) || nPage
== GetPageCount(), false,
766 _T("invalid index in wxNotebook::InsertPage") );
768 wxASSERT_MSG( pPage
->GetParent() == this,
769 _T("notebook pages must have notebook as parent") );
771 // add a new tab to the control
772 // ----------------------------
774 // init all fields to 0
776 wxZeroMemory(tcItem
);
778 // set the image, if any
781 tcItem
.mask
|= TCIF_IMAGE
;
782 tcItem
.iImage
= imageId
;
786 if ( !strText
.empty() )
788 tcItem
.mask
|= TCIF_TEXT
;
789 tcItem
.pszText
= (wxChar
*)strText
.wx_str(); // const_cast
792 // hide the page: unless it is selected, it shouldn't be shown (and if it
793 // is selected it will be shown later)
794 HWND hwnd
= GetWinHwnd(pPage
);
795 SetWindowLong(hwnd
, GWL_STYLE
, GetWindowLong(hwnd
, GWL_STYLE
) & ~WS_VISIBLE
);
797 // this updates internal flag too -- otherwise it would get out of sync
798 // with the real state
802 // fit the notebook page to the tab control's display area: this should be
803 // done before adding it to the notebook or TabCtrl_InsertItem() will
804 // change the notebooks size itself!
805 AdjustPageSize(pPage
);
807 // finally do insert it
808 if ( TabCtrl_InsertItem(GetHwnd(), nPage
, &tcItem
) == -1 )
810 wxLogError(wxT("Can't create the notebook page '%s'."), strText
.c_str());
815 // need to update the bg brush when the first page is added
816 // so the first panel gets the correct themed background
817 if ( m_pages
.empty() )
821 #endif // wxUSE_UXTHEME
824 // succeeded: save the pointer to the page
825 m_pages
.Insert(pPage
, nPage
);
827 // we may need to adjust the size again if the notebook size changed:
828 // normally this only happens for the first page we add (the tabs which
829 // hadn't been there before are now shown) but for a multiline notebook it
830 // can happen for any page at all as a new row could have been started
831 if ( m_pages
.GetCount() == 1 || HasFlag(wxNB_MULTILINE
) )
833 AdjustPageSize(pPage
);
836 // now deal with the selection
837 // ---------------------------
839 // if the inserted page is before the selected one, we must update the
840 // index of the selected page
841 if ( int(nPage
) <= m_nSelection
)
843 // one extra page added
847 // some page should be selected: either this one or the first one if there
848 // is still no selection
849 int selNew
= wxNOT_FOUND
;
852 else if ( m_nSelection
== wxNOT_FOUND
)
855 if ( selNew
!= wxNOT_FOUND
)
856 SetSelection(selNew
);
858 InvalidateBestSize();
863 int wxNotebook::HitTest(const wxPoint
& pt
, long *flags
) const
865 TC_HITTESTINFO hitTestInfo
;
866 hitTestInfo
.pt
.x
= pt
.x
;
867 hitTestInfo
.pt
.y
= pt
.y
;
868 int item
= TabCtrl_HitTest(GetHwnd(), &hitTestInfo
);
874 if ((hitTestInfo
.flags
& TCHT_NOWHERE
) == TCHT_NOWHERE
)
875 *flags
|= wxBK_HITTEST_NOWHERE
;
876 if ((hitTestInfo
.flags
& TCHT_ONITEM
) == TCHT_ONITEM
)
877 *flags
|= wxBK_HITTEST_ONITEM
;
878 if ((hitTestInfo
.flags
& TCHT_ONITEMICON
) == TCHT_ONITEMICON
)
879 *flags
|= wxBK_HITTEST_ONICON
;
880 if ((hitTestInfo
.flags
& TCHT_ONITEMLABEL
) == TCHT_ONITEMLABEL
)
881 *flags
|= wxBK_HITTEST_ONLABEL
;
882 if ( item
== wxNOT_FOUND
&& GetPageSize().Contains(pt
) )
883 *flags
|= wxBK_HITTEST_ONPAGE
;
889 // ----------------------------------------------------------------------------
890 // flicker-less notebook redraw
891 // ----------------------------------------------------------------------------
893 #if USE_NOTEBOOK_ANTIFLICKER
895 // wnd proc for the spin button
896 LRESULT APIENTRY _EXPORT
wxNotebookSpinBtnWndProc(HWND hwnd
,
901 if ( message
== WM_ERASEBKGND
)
904 return ::CallWindowProc(CASTWNDPROC gs_wndprocNotebookSpinBtn
,
905 hwnd
, message
, wParam
, lParam
);
908 LRESULT APIENTRY _EXPORT
wxNotebookWndProc(HWND hwnd
,
913 return ::CallWindowProc(CASTWNDPROC gs_wndprocNotebook
,
914 hwnd
, message
, wParam
, lParam
);
917 void wxNotebook::OnEraseBackground(wxEraseEvent
& WXUNUSED(event
))
922 void wxNotebook::OnPaint(wxPaintEvent
& WXUNUSED(event
))
927 ::GetClientRect(GetHwnd(), &rc
);
928 wxBitmap
bmp(rc
.right
, rc
.bottom
);
929 memdc
.SelectObject(bmp
);
931 const wxLayoutDirection dir
= dc
.GetLayoutDirection();
932 memdc
.SetLayoutDirection(dir
);
934 // if there is no special brush just use the solid background colour
936 HBRUSH hbr
= (HBRUSH
)m_hbrBackground
;
943 brush
= wxBrush(GetBackgroundColour());
944 hbr
= GetHbrushOf(brush
);
947 wxMSWDCImpl
*impl
= (wxMSWDCImpl
*) memdc
.GetImpl();
949 ::FillRect(GetHdcOf(*impl
), &rc
, hbr
);
951 MSWDefWindowProc(WM_PAINT
, (WPARAM
)(impl
->GetHDC()), 0);
953 // For some reason in RTL mode, source offset has to be -1, otherwise the
954 // right border (physical) remains unpainted.
955 const wxCoord ofs
= dir
== wxLayout_RightToLeft
? -1 : 0;
956 dc
.Blit(ofs
, 0, rc
.right
, rc
.bottom
, &memdc
, ofs
, 0);
959 #endif // USE_NOTEBOOK_ANTIFLICKER
961 // ----------------------------------------------------------------------------
962 // wxNotebook callbacks
963 // ----------------------------------------------------------------------------
965 void wxNotebook::OnSize(wxSizeEvent
& event
)
967 if ( GetPageCount() == 0 )
969 // Prevents droppings on resize, but does cause some flicker
970 // when there are no pages.
978 // Without this, we can sometimes get droppings at the edges
979 // of a notebook, for example a notebook in a splitter window.
980 // This needs to be reconciled with the RefreshRect calls
981 // at the end of this function, which weren't enough to prevent
984 wxSize sz
= GetClientSize();
986 // Refresh right side
987 wxRect
rect(sz
.x
-4, 0, 4, sz
.y
);
990 // Refresh bottom side
991 rect
= wxRect(0, sz
.y
-4, sz
.x
, 4);
995 rect
= wxRect(0, 0, 4, sz
.y
);
998 #endif // !__WXWINCE__
1000 // fit all the notebook pages to the tab control's display area
1003 rc
.left
= rc
.top
= 0;
1004 GetSize((int *)&rc
.right
, (int *)&rc
.bottom
);
1006 // save the total size, we'll use it below
1007 int widthNbook
= rc
.right
- rc
.left
,
1008 heightNbook
= rc
.bottom
- rc
.top
;
1010 // there seems to be a bug in the implementation of TabCtrl_AdjustRect(): it
1011 // returns completely false values for multiline tab controls after the tabs
1012 // are added but before getting the first WM_SIZE (off by ~50 pixels, see
1014 // http://sf.net/tracker/index.php?func=detail&aid=645323&group_id=9863&atid=109863
1016 // and the only work around I could find was this ugly hack... without it
1017 // simply toggling the "multiline" checkbox in the notebook sample resulted
1018 // in a noticeable page displacement
1019 if ( HasFlag(wxNB_MULTILINE
) )
1021 // avoid an infinite recursion: we get another notification too!
1022 static bool s_isInOnSize
= false;
1024 if ( !s_isInOnSize
)
1026 s_isInOnSize
= true;
1027 SendMessage(GetHwnd(), WM_SIZE
, SIZE_RESTORED
,
1028 MAKELPARAM(rc
.right
, rc
.bottom
));
1029 s_isInOnSize
= false;
1032 // The best size depends on the number of rows of tabs, which can
1033 // change when the notepad is resized.
1034 InvalidateBestSize();
1038 // background bitmap size has changed, update the brush using it too
1040 #endif // wxUSE_UXTHEME
1042 TabCtrl_AdjustRect(GetHwnd(), false, &rc
);
1044 int width
= rc
.right
- rc
.left
,
1045 height
= rc
.bottom
- rc
.top
;
1046 size_t nCount
= m_pages
.Count();
1047 for ( size_t nPage
= 0; nPage
< nCount
; nPage
++ ) {
1048 wxNotebookPage
*pPage
= m_pages
[nPage
];
1049 pPage
->SetSize(rc
.left
, rc
.top
, width
, height
);
1053 // unless we had already repainted everything, we now need to refresh
1054 if ( !HasFlag(wxFULL_REPAINT_ON_RESIZE
) )
1056 // invalidate areas not covered by pages
1057 RefreshRect(wxRect(0, 0, widthNbook
, rc
.top
), false);
1058 RefreshRect(wxRect(0, rc
.top
, rc
.left
, height
), false);
1059 RefreshRect(wxRect(0, rc
.bottom
, widthNbook
, heightNbook
- rc
.bottom
),
1061 RefreshRect(wxRect(rc
.right
, rc
.top
, widthNbook
- rc
.right
, height
),
1065 #if USE_NOTEBOOK_ANTIFLICKER
1066 // subclass the spin control used by the notebook to scroll pages to
1067 // prevent it from flickering on resize
1068 if ( !m_hasSubclassedUpdown
)
1070 // iterate over all child windows to find spin button
1071 for ( HWND child
= ::GetWindow(GetHwnd(), GW_CHILD
);
1073 child
= ::GetWindow(child
, GW_HWNDNEXT
) )
1075 wxWindow
*childWindow
= wxFindWinFromHandle((WXHWND
)child
);
1077 // see if it exists, if no wxWindow found then assume it's the spin
1081 // subclass the spin button to override WM_ERASEBKGND
1082 if ( !gs_wndprocNotebookSpinBtn
)
1083 gs_wndprocNotebookSpinBtn
= (WXFARPROC
)wxGetWindowProc(child
);
1085 wxSetWindowProc(child
, wxNotebookSpinBtnWndProc
);
1086 m_hasSubclassedUpdown
= true;
1091 #endif // USE_NOTEBOOK_ANTIFLICKER
1096 void wxNotebook::OnSelChange(wxBookCtrlEvent
& event
)
1098 // is it our tab control?
1099 if ( event
.GetEventObject() == this )
1101 UpdateSelection(event
.GetSelection());
1104 // we want to give others a chance to process this message as well
1108 void wxNotebook::OnNavigationKey(wxNavigationKeyEvent
& event
)
1110 if ( event
.IsWindowChange() ) {
1112 AdvanceSelection(event
.GetDirection());
1115 // we get this event in 3 cases
1117 // a) one of our pages might have generated it because the user TABbed
1118 // out from it in which case we should propagate the event upwards and
1119 // our parent will take care of setting the focus to prev/next sibling
1123 // b) the parent panel wants to give the focus to us so that we
1124 // forward it to our selected page. We can't deal with this in
1125 // OnSetFocus() because we don't know which direction the focus came
1126 // from in this case and so can't choose between setting the focus to
1127 // first or last panel child
1131 // c) we ourselves (see MSWTranslateMessage) generated the event
1133 wxWindow
* const parent
= GetParent();
1135 // the wxObject* casts are required to avoid MinGW GCC 2.95.3 ICE
1136 const bool isFromParent
= event
.GetEventObject() == (wxObject
*) parent
;
1137 const bool isFromSelf
= event
.GetEventObject() == (wxObject
*) this;
1139 if ( isFromParent
|| isFromSelf
)
1141 // no, it doesn't come from child, case (b) or (c): forward to a
1142 // page but only if direction is backwards (TAB) or from ourselves,
1143 if ( m_nSelection
!= wxNOT_FOUND
&&
1144 (!event
.GetDirection() || isFromSelf
) )
1146 // so that the page knows that the event comes from it's parent
1147 // and is being propagated downwards
1148 event
.SetEventObject(this);
1150 wxWindow
*page
= m_pages
[m_nSelection
];
1151 if ( !page
->HandleWindowEvent(event
) )
1155 //else: page manages focus inside it itself
1157 else // otherwise set the focus to the notebook itself
1164 // it comes from our child, case (a), pass to the parent, but only
1165 // if the direction is forwards. Otherwise set the focus to the
1166 // notebook itself. The notebook is always the 'first' control of a
1168 if ( !event
.GetDirection() )
1174 event
.SetCurrentFocus(this);
1175 parent
->HandleWindowEvent(event
);
1183 bool wxNotebook::DoDrawBackground(WXHDC hDC
, wxWindow
*child
)
1185 wxUxThemeHandle
theme(child
? child
: this, L
"TAB");
1189 // get the notebook client rect (we're not interested in drawing tabs
1191 wxRect r
= GetPageSize();
1196 wxCopyRectToRECT(r
, rc
);
1198 // map rect to the coords of the window we're drawing in
1200 ::MapWindowPoints(GetHwnd(), GetHwndOf(child
), (POINT
*)&rc
, 2);
1202 // we have the content area (page size), but we need to draw all of the
1203 // background for it to be aligned correctly
1204 wxUxThemeEngine::Get()->GetThemeBackgroundExtent
1213 wxUxThemeEngine::Get()->DrawThemeBackground
1226 WXHBRUSH
wxNotebook::QueryBgBitmap()
1228 wxRect r
= GetPageSize();
1232 WindowHDC
hDC(GetHwnd());
1233 MemoryHDC
hDCMem(hDC
);
1234 CompatibleBitmap
hBmp(hDC
, r
.x
+ r
.width
, r
.y
+ r
.height
);
1236 SelectInHDC
selectBmp(hDCMem
, hBmp
);
1238 if ( !DoDrawBackground((WXHDC
)(HDC
)hDCMem
) )
1241 return (WXHBRUSH
)::CreatePatternBrush(hBmp
);
1244 void wxNotebook::UpdateBgBrush()
1246 if ( m_hbrBackground
)
1247 ::DeleteObject((HBRUSH
)m_hbrBackground
);
1249 if ( !m_hasBgCol
&& wxUxThemeEngine::GetIfActive() )
1251 m_hbrBackground
= QueryBgBitmap();
1253 else // no themes or we've got user-defined solid colour
1255 m_hbrBackground
= NULL
;
1259 WXHBRUSH
wxNotebook::MSWGetBgBrushForChild(WXHDC hDC
, WXHWND hWnd
)
1261 if ( m_hbrBackground
)
1263 // before drawing with the background brush, we need to position it
1266 ::GetWindowRect((HWND
)hWnd
, &rc
);
1268 ::MapWindowPoints(NULL
, GetHwnd(), (POINT
*)&rc
, 1);
1270 if ( !::SetBrushOrgEx((HDC
)hDC
, -rc
.left
, -rc
.top
, NULL
) )
1272 wxLogLastError(_T("SetBrushOrgEx(notebook bg brush)"));
1275 return m_hbrBackground
;
1278 return wxNotebookBase::MSWGetBgBrushForChild(hDC
, hWnd
);
1281 bool wxNotebook::MSWPrintChild(WXHDC hDC
, wxWindow
*child
)
1283 // solid background colour overrides themed background drawing
1284 if ( !UseBgCol() && DoDrawBackground(hDC
, child
) )
1287 // If we're using a solid colour (for example if we've switched off
1288 // theming for this notebook), paint it
1291 wxRect r
= GetPageSize();
1296 wxCopyRectToRECT(r
, rc
);
1298 // map rect to the coords of the window we're drawing in
1300 ::MapWindowPoints(GetHwnd(), GetHwndOf(child
), (POINT
*)&rc
, 2);
1302 wxBrush
brush(GetBackgroundColour());
1303 HBRUSH hbr
= GetHbrushOf(brush
);
1305 ::FillRect((HDC
) hDC
, &rc
, hbr
);
1310 return wxNotebookBase::MSWPrintChild(hDC
, child
);
1313 #endif // wxUSE_UXTHEME
1315 // Windows only: attempts to get colour for UX theme page background
1316 wxColour
wxNotebook::GetThemeBackgroundColour() const
1319 if (wxUxThemeEngine::Get())
1321 wxUxThemeHandle
hTheme((wxNotebook
*) this, L
"TAB");
1324 // This is total guesswork.
1325 // See PlatformSDK\Include\Tmschema.h for values.
1326 // JACS: can also use 9 (TABP_PANE)
1327 COLORREF themeColor
;
1328 bool success
= (S_OK
== wxUxThemeEngine::Get()->GetThemeColor(
1332 3821 /* FILLCOLORHINT */,
1335 return GetBackgroundColour();
1338 [DS] Workaround for WindowBlinds:
1339 Some themes return a near black theme color using FILLCOLORHINT,
1340 this makes notebook pages have an ugly black background and makes
1341 text (usually black) unreadable. Retry again with FILLCOLOR.
1343 This workaround potentially breaks appearance of some themes,
1344 but in practice it already fixes some themes.
1346 if (themeColor
== 1)
1348 wxUxThemeEngine::Get()->GetThemeColor(
1352 3802 /* FILLCOLOR */,
1356 wxColour colour
= wxRGBToColour(themeColor
);
1358 // Under Vista, the tab background colour is reported incorrectly.
1359 // So for the default theme at least, hard-code the colour to something
1360 // that will blend in.
1362 static int s_AeroStatus
= -1;
1363 if (s_AeroStatus
== -1)
1365 WCHAR szwThemeFile
[1024];
1366 WCHAR szwThemeColor
[256];
1367 if (S_OK
== wxUxThemeEngine::Get()->GetCurrentThemeName(szwThemeFile
, 1024, szwThemeColor
, 256, NULL
, 0))
1369 wxString
themeFile(szwThemeFile
), themeColor(szwThemeColor
);
1370 if (themeFile
.Find(wxT("Aero")) != -1 && themeColor
== wxT("NormalColor"))
1379 if (s_AeroStatus
== 1)
1380 colour
= wxColour(255, 255, 255);
1385 #endif // wxUSE_UXTHEME
1387 return GetBackgroundColour();
1390 // ----------------------------------------------------------------------------
1391 // wxNotebook base class virtuals
1392 // ----------------------------------------------------------------------------
1394 #if wxUSE_CONSTRAINTS
1396 // override these 2 functions to do nothing: everything is done in OnSize
1398 void wxNotebook::SetConstraintSizes(bool WXUNUSED(recurse
))
1400 // don't set the sizes of the pages - their correct size is not yet known
1401 wxControl::SetConstraintSizes(false);
1404 bool wxNotebook::DoPhase(int WXUNUSED(nPhase
))
1409 #endif // wxUSE_CONSTRAINTS
1411 // ----------------------------------------------------------------------------
1412 // wxNotebook Windows message handlers
1413 // ----------------------------------------------------------------------------
1415 bool wxNotebook::MSWOnScroll(int orientation
, WXWORD nSBCode
,
1416 WXWORD pos
, WXHWND control
)
1418 // don't generate EVT_SCROLLWIN events for the WM_SCROLLs coming from the
1423 return wxNotebookBase::MSWOnScroll(orientation
, nSBCode
, pos
, control
);
1426 bool wxNotebook::MSWOnNotify(int idCtrl
, WXLPARAM lParam
, WXLPARAM
* result
)
1428 wxBookCtrlEvent
event(wxEVT_NULL
, m_windowId
);
1430 NMHDR
* hdr
= (NMHDR
*)lParam
;
1431 switch ( hdr
->code
) {
1433 event
.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
);
1436 case TCN_SELCHANGING
:
1437 event
.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
);
1441 return wxControl::MSWOnNotify(idCtrl
, lParam
, result
);
1444 event
.SetSelection(TabCtrl_GetCurSel(GetHwnd()));
1445 event
.SetOldSelection(m_nSelection
);
1446 event
.SetEventObject(this);
1447 event
.SetInt(idCtrl
);
1449 bool processed
= HandleWindowEvent(event
);
1450 *result
= !event
.IsAllowed();
1454 #endif // wxUSE_NOTEBOOK