1 ///////////////////////////////////////////////////////////////////////////////
2 // Name:        src/msw/notebook.cpp
3 // Purpose:     implementation of wxNotebook
4 // Author:      Vadim Zeitlin
5 // Modified by:
6 // Created:     11.06.98
7 // RCS-ID:      $Id: notebook.cpp 59897 2009-03-27 22:37:22Z VZ $
8 // Copyright:   (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
9 // Licence:     wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
11 
12 // For compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
14 
15 #ifdef __BORLANDC__
16     #pragma hdrstop
17 #endif
18 
19 #if wxUSE_NOTEBOOK
20 
21 #include "wx/notebook.h"
22 
23 #ifndef WX_PRECOMP
24     #include "wx/msw/wrapcctl.h" // include <commctrl.h> "properly"
25     #include "wx/string.h"
26     #include "wx/dc.h"
27     #include "wx/log.h"
28     #include "wx/event.h"
29     #include "wx/app.h"
30     #include "wx/dcclient.h"
31     #include "wx/dcmemory.h"
32     #include "wx/control.h"
33 #endif  // WX_PRECOMP
34 
35 #include "wx/imaglist.h"
36 #include "wx/sysopt.h"
37 
38 #include "wx/msw/private.h"
39 
40 #include <windowsx.h>
41 #include "wx/msw/winundef.h"
42 
43 #if wxUSE_UXTHEME
44     #include "wx/msw/uxtheme.h"
45 #endif
46 
47 // ----------------------------------------------------------------------------
48 // macros
49 // ----------------------------------------------------------------------------
50 
51 // check that the page index is valid
52 #define IS_VALID_PAGE(nPage) ((nPage) < GetPageCount())
53 
54 // you can set USE_NOTEBOOK_ANTIFLICKER to 0 for desktop Windows versions too
55 // to disable code whih results in flicker-less notebook redrawing at the
56 // expense of some extra GDI resource consumption
57 #ifdef __WXWINCE__
58     // notebooks are never resized under CE anyhow
59     #define USE_NOTEBOOK_ANTIFLICKER    0
60 #else
61     #define USE_NOTEBOOK_ANTIFLICKER    1
62 #endif
63 
64 // ----------------------------------------------------------------------------
65 // constants
66 // ----------------------------------------------------------------------------
67 
68 // This is a work-around for missing defines in gcc-2.95 headers
69 #ifndef TCS_RIGHT
70     #define TCS_RIGHT       0x0002
71 #endif
72 
73 #ifndef TCS_VERTICAL
74     #define TCS_VERTICAL    0x0080
75 #endif
76 
77 #ifndef TCS_BOTTOM
78     #define TCS_BOTTOM      TCS_RIGHT
79 #endif
80 
81 // ----------------------------------------------------------------------------
82 // global variables
83 // ----------------------------------------------------------------------------
84 
85 #if USE_NOTEBOOK_ANTIFLICKER
86 
87 // the pointer to standard spin button wnd proc
88 static WXFARPROC gs_wndprocNotebookSpinBtn = (WXFARPROC)NULL;
89 
90 // the pointer to standard tab control wnd proc
91 static WXFARPROC gs_wndprocNotebook = (WXFARPROC)NULL;
92 
93 LRESULT APIENTRY _EXPORT wxNotebookWndProc(HWND hwnd,
94                                            UINT message,
95                                            WPARAM wParam,
96                                            LPARAM lParam);
97 
98 #endif // USE_NOTEBOOK_ANTIFLICKER
99 
100 // ----------------------------------------------------------------------------
101 // global functions
102 // ----------------------------------------------------------------------------
103 
HasTroubleWithNonTopTabs()104 static bool HasTroubleWithNonTopTabs()
105 {
106     const int verComCtl32 = wxApp::GetComCtl32Version();
107 
108     // 600 is XP, 616 is Vista -- and both have a problem with tabs not on top
109     // (but don't just test for >= 600 as Microsoft might decide to fix it in
110     // later versions, who knows...)
111     return verComCtl32 >= 600 && verComCtl32 <= 616;
112 }
113 
114 // ----------------------------------------------------------------------------
115 // event table
116 // ----------------------------------------------------------------------------
117 
118 #include "wx/listimpl.cpp"
119 
120 WX_DEFINE_LIST( wxNotebookPageInfoList )
121 
122 DEFINE_EVENT_TYPE(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED)
123 DEFINE_EVENT_TYPE(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING)
124 
125 BEGIN_EVENT_TABLE(wxNotebook, wxControl)
126     EVT_NOTEBOOK_PAGE_CHANGED(wxID_ANY, wxNotebook::OnSelChange)
127     EVT_SIZE(wxNotebook::OnSize)
128     EVT_NAVIGATION_KEY(wxNotebook::OnNavigationKey)
129 
130 #if USE_NOTEBOOK_ANTIFLICKER
131     EVT_ERASE_BACKGROUND(wxNotebook::OnEraseBackground)
132     EVT_PAINT(wxNotebook::OnPaint)
133 #endif // USE_NOTEBOOK_ANTIFLICKER
134 END_EVENT_TABLE()
135 
136 #if wxUSE_EXTENDED_RTTI
137 WX_DEFINE_FLAGS( wxNotebookStyle )
138 
139 wxBEGIN_FLAGS( wxNotebookStyle )
140     // new style border flags, we put them first to
141     // use them for streaming out
142     wxFLAGS_MEMBER(wxBORDER_SIMPLE)
143     wxFLAGS_MEMBER(wxBORDER_SUNKEN)
144     wxFLAGS_MEMBER(wxBORDER_DOUBLE)
145     wxFLAGS_MEMBER(wxBORDER_RAISED)
146     wxFLAGS_MEMBER(wxBORDER_STATIC)
147     wxFLAGS_MEMBER(wxBORDER_NONE)
148 
149     // old style border flags
150     wxFLAGS_MEMBER(wxSIMPLE_BORDER)
151     wxFLAGS_MEMBER(wxSUNKEN_BORDER)
152     wxFLAGS_MEMBER(wxDOUBLE_BORDER)
153     wxFLAGS_MEMBER(wxRAISED_BORDER)
154     wxFLAGS_MEMBER(wxSTATIC_BORDER)
155     wxFLAGS_MEMBER(wxBORDER)
156 
157     // standard window styles
158     wxFLAGS_MEMBER(wxTAB_TRAVERSAL)
159     wxFLAGS_MEMBER(wxCLIP_CHILDREN)
160     wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW)
161     wxFLAGS_MEMBER(wxWANTS_CHARS)
162     wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE)
163     wxFLAGS_MEMBER(wxALWAYS_SHOW_SB )
164     wxFLAGS_MEMBER(wxVSCROLL)
165     wxFLAGS_MEMBER(wxHSCROLL)
166 
167     wxFLAGS_MEMBER(wxNB_FIXEDWIDTH)
168     wxFLAGS_MEMBER(wxBK_DEFAULT)
169     wxFLAGS_MEMBER(wxBK_TOP)
170     wxFLAGS_MEMBER(wxBK_LEFT)
171     wxFLAGS_MEMBER(wxBK_RIGHT)
172     wxFLAGS_MEMBER(wxBK_BOTTOM)
173     wxFLAGS_MEMBER(wxNB_NOPAGETHEME)
174     wxFLAGS_MEMBER(wxNB_FLAT)
175 
176 wxEND_FLAGS( wxNotebookStyle )
177 
178 IMPLEMENT_DYNAMIC_CLASS_XTI(wxNotebook, wxBookCtrlBase,"wx/notebook.h")
179 IMPLEMENT_DYNAMIC_CLASS_XTI(wxNotebookPageInfo, wxObject , "wx/notebook.h" )
180 
181 wxCOLLECTION_TYPE_INFO( wxNotebookPageInfo * , wxNotebookPageInfoList ) ;
182 
wxCollectionToVariantArray(wxNotebookPageInfoList const & theList,wxxVariantArray & value)183 template<> void wxCollectionToVariantArray( wxNotebookPageInfoList const &theList, wxxVariantArray &value)
184 {
185     wxListCollectionToVariantArray<wxNotebookPageInfoList::compatibility_iterator>( theList , value ) ;
186 }
187 
188 wxBEGIN_PROPERTIES_TABLE(wxNotebook)
wxEVENT_PROPERTY(PageChanging,wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING,wxNotebookEvent)189     wxEVENT_PROPERTY( PageChanging , wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING , wxNotebookEvent )
190     wxEVENT_PROPERTY( PageChanged , wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED , wxNotebookEvent )
191 
192     wxPROPERTY_COLLECTION( PageInfos , wxNotebookPageInfoList , wxNotebookPageInfo* , AddPageInfo , GetPageInfos , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
193     wxPROPERTY_FLAGS( WindowStyle , wxNotebookStyle , long , SetWindowStyleFlag , GetWindowStyleFlag , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
194 wxEND_PROPERTIES_TABLE()
195 
196 wxBEGIN_HANDLERS_TABLE(wxNotebook)
197 wxEND_HANDLERS_TABLE()
198 
199 wxCONSTRUCTOR_5( wxNotebook , wxWindow* , Parent , wxWindowID , Id , wxPoint , Position , wxSize , Size , long , WindowStyle)
200 
201 
202 wxBEGIN_PROPERTIES_TABLE(wxNotebookPageInfo)
203     wxREADONLY_PROPERTY( Page , wxNotebookPage* , GetPage , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
204     wxREADONLY_PROPERTY( Text , wxString , GetText , wxString() , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
205     wxREADONLY_PROPERTY( Selected , bool , GetSelected , false, 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
206     wxREADONLY_PROPERTY( ImageId , int , GetImageId , -1 , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
207 wxEND_PROPERTIES_TABLE()
208 
209 wxBEGIN_HANDLERS_TABLE(wxNotebookPageInfo)
210 wxEND_HANDLERS_TABLE()
211 
212 wxCONSTRUCTOR_4( wxNotebookPageInfo , wxNotebookPage* , Page , wxString , Text , bool , Selected , int , ImageId )
213 
214 #else
215 IMPLEMENT_DYNAMIC_CLASS(wxNotebook, wxBookCtrlBase)
216 IMPLEMENT_DYNAMIC_CLASS(wxNotebookPageInfo, wxObject )
217 #endif
218 IMPLEMENT_DYNAMIC_CLASS(wxNotebookEvent, wxNotifyEvent)
219 
220 // ============================================================================
221 // implementation
222 // ============================================================================
223 
224 // ----------------------------------------------------------------------------
225 // wxNotebook construction
226 // ----------------------------------------------------------------------------
227 
228 const wxNotebookPageInfoList& wxNotebook::GetPageInfos() const
229 {
230     wxNotebookPageInfoList* list = const_cast< wxNotebookPageInfoList* >( &m_pageInfos ) ;
231     WX_CLEAR_LIST( wxNotebookPageInfoList , *list ) ;
232     for( size_t i = 0 ; i < GetPageCount() ; ++i )
233     {
234         wxNotebookPageInfo *info = new wxNotebookPageInfo() ;
235         info->Create( const_cast<wxNotebook*>(this)->GetPage(i) , GetPageText(i) , GetSelection() == int(i) , GetPageImage(i) ) ;
236         list->Append( info ) ;
237     }
238     return m_pageInfos ;
239 }
240 
241 // common part of all ctors
Init()242 void wxNotebook::Init()
243 {
244     m_imageList = NULL;
245     m_nSelection = wxNOT_FOUND;
246 
247 #if wxUSE_UXTHEME
248     m_hbrBackground = NULL;
249 #endif // wxUSE_UXTHEME
250 
251 #if USE_NOTEBOOK_ANTIFLICKER
252     m_hasSubclassedUpdown = false;
253 #endif // USE_NOTEBOOK_ANTIFLICKER
254 }
255 
256 // default for dynamic class
wxNotebook()257 wxNotebook::wxNotebook()
258 {
259   Init();
260 }
261 
262 // the same arguments as for wxControl
wxNotebook(wxWindow * parent,wxWindowID id,const wxPoint & pos,const wxSize & size,long style,const wxString & name)263 wxNotebook::wxNotebook(wxWindow *parent,
264                        wxWindowID id,
265                        const wxPoint& pos,
266                        const wxSize& size,
267                        long style,
268                        const wxString& name)
269 {
270   Init();
271 
272   Create(parent, id, pos, size, style, name);
273 }
274 
275 // Create() function
Create(wxWindow * parent,wxWindowID id,const wxPoint & pos,const wxSize & size,long style,const wxString & name)276 bool wxNotebook::Create(wxWindow *parent,
277                         wxWindowID id,
278                         const wxPoint& pos,
279                         const wxSize& size,
280                         long style,
281                         const wxString& name)
282 {
283     if ( (style & wxBK_ALIGN_MASK) == wxBK_DEFAULT )
284     {
285 #if defined(__POCKETPC__)
286         style |= wxBK_BOTTOM | wxNB_FLAT;
287 #else
288         style |= wxBK_TOP;
289 #endif
290     }
291 
292 #ifdef __WXWINCE__
293     // Not sure why, but without this style, there is no border
294     // around the notebook tabs.
295     if (style & wxNB_FLAT)
296         style |= wxBORDER_SUNKEN;
297 #endif
298 
299 #if !wxUSE_UXTHEME
300     // ComCtl32 notebook tabs simply don't work unless they're on top if we
301     // have uxtheme, we can work around it later (after control creation), but
302     // if we have been compiled without uxtheme support, we have to clear those
303     // styles
304     if ( HasTroubleWithNonTopTabs() )
305     {
306         style &= ~(wxBK_BOTTOM | wxBK_LEFT | wxBK_RIGHT);
307     }
308 #endif //wxUSE_UXTHEME
309 
310 #if defined(__WINE__) && wxUSE_UNICODE
311     LPCTSTR className = L"SysTabControl32";
312 #else
313     LPCTSTR className = WC_TABCONTROL;
314 #endif
315 
316 #if USE_NOTEBOOK_ANTIFLICKER
317     // SysTabCtl32 class has natively CS_HREDRAW and CS_VREDRAW enabled and it
318     // causes horrible flicker when resizing notebook, so get rid of it by
319     // using a class without these styles (but otherwise identical to it)
320     if ( !HasFlag(wxFULL_REPAINT_ON_RESIZE) )
321     {
322         static ClassRegistrar s_clsNotebook;
323         if ( !s_clsNotebook.IsInitialized() )
324         {
325             // get a copy of standard class and modify it
326             WNDCLASS wc;
327 
328             if ( ::GetClassInfo(NULL, WC_TABCONTROL, &wc) )
329             {
330                 gs_wndprocNotebook =
331                     wx_reinterpret_cast(WXFARPROC, wc.lpfnWndProc);
332                 wc.lpszClassName = wxT("_wx_SysTabCtl32");
333                 wc.style &= ~(CS_HREDRAW | CS_VREDRAW);
334                 wc.hInstance = wxGetInstance();
335                 wc.lpfnWndProc = wxNotebookWndProc;
336                 s_clsNotebook.Register(wc);
337             }
338             else
339             {
340                 wxLogLastError(_T("GetClassInfoEx(SysTabCtl32)"));
341             }
342         }
343 
344         // use our custom class if available but fall back to the standard
345         // notebook if we failed to register it
346         if ( s_clsNotebook.IsRegistered() )
347         {
348             // it's ok to use c_str() here as the static s_clsNotebook object
349             // has sufficiently long lifetime
350             className = s_clsNotebook.GetName().c_str();
351         }
352     }
353 #endif // USE_NOTEBOOK_ANTIFLICKER
354 
355     if ( !CreateControl(parent, id, pos, size, style | wxTAB_TRAVERSAL,
356                         wxDefaultValidator, name) )
357         return false;
358 
359     if ( !MSWCreateControl(className, wxEmptyString, pos, size) )
360         return false;
361 
362 #if wxUSE_UXTHEME
363     if ( HasFlag(wxNB_NOPAGETHEME) ||
364             wxSystemOptions::IsFalse(wxT("msw.notebook.themed-background")) )
365     {
366         SetBackgroundColour(GetThemeBackgroundColour());
367     }
368     else // use themed background by default
369     {
370         // create backing store
371         UpdateBgBrush();
372     }
373 
374     // comctl32.dll 6.0 doesn't support non-top tabs with visual styles (the
375     // control is simply not rendered correctly), so we disable themes
376     // if possible, otherwise we simply clear the styles.
377     if ( HasTroubleWithNonTopTabs() &&
378             (style & (wxBK_BOTTOM | wxBK_LEFT | wxBK_RIGHT)) )
379     {
380         // check if we use themes at all -- if we don't, we're still okay
381         if ( wxUxThemeEngine::GetIfActive() )
382         {
383             wxUxThemeEngine::GetIfActive()->SetWindowTheme(GetHwnd(), L"", L"");
384 
385             // correct the background color for the new non-themed control
386             SetBackgroundColour(GetThemeBackgroundColour());
387         }
388     }
389 #endif // wxUSE_UXTHEME
390 
391     // Undocumented hack to get flat notebook style
392     // In fact, we should probably only do this in some
393     // curcumstances, i.e. if we know we will have a border
394     // at the bottom (the tab control doesn't draw it itself)
395 #if defined(__POCKETPC__) || defined(__SMARTPHONE__)
396     if (HasFlag(wxNB_FLAT))
397     {
398         SendMessage(GetHwnd(), CCM_SETVERSION, COMCTL32_VERSION, 0);
399         if (!m_hasBgCol)
400             SetBackgroundColour(*wxWHITE);
401     }
402 #endif
403     return true;
404 }
405 
MSWGetStyle(long style,WXDWORD * exstyle) const406 WXDWORD wxNotebook::MSWGetStyle(long style, WXDWORD *exstyle) const
407 {
408     WXDWORD tabStyle = wxControl::MSWGetStyle(style, exstyle);
409 
410     tabStyle |= WS_TABSTOP | TCS_TABS;
411 
412     if ( style & wxNB_MULTILINE )
413         tabStyle |= TCS_MULTILINE;
414     if ( style & wxNB_FIXEDWIDTH )
415         tabStyle |= TCS_FIXEDWIDTH;
416 
417     if ( style & wxBK_BOTTOM )
418         tabStyle |= TCS_RIGHT;
419     else if ( style & wxBK_LEFT )
420         tabStyle |= TCS_VERTICAL;
421     else if ( style & wxBK_RIGHT )
422         tabStyle |= TCS_VERTICAL | TCS_RIGHT;
423 
424     if ( exstyle )
425     {
426         // note that we never want to have the default WS_EX_CLIENTEDGE style
427         // as it looks too ugly for the notebooks
428         *exstyle &= ~WS_EX_CLIENTEDGE;
429     }
430 
431     return tabStyle;
432 }
433 
~wxNotebook()434 wxNotebook::~wxNotebook()
435 {
436 #if wxUSE_UXTHEME
437     if ( m_hbrBackground )
438         ::DeleteObject((HBRUSH)m_hbrBackground);
439 #endif // wxUSE_UXTHEME
440 }
441 
442 // ----------------------------------------------------------------------------
443 // wxNotebook accessors
444 // ----------------------------------------------------------------------------
445 
GetPageCount() const446 size_t wxNotebook::GetPageCount() const
447 {
448     // consistency check
449     wxASSERT( (int)m_pages.Count() == TabCtrl_GetItemCount(GetHwnd()) );
450 
451     return m_pages.Count();
452 }
453 
GetRowCount() const454 int wxNotebook::GetRowCount() const
455 {
456     return TabCtrl_GetRowCount(GetHwnd());
457 }
458 
SetSelection(size_t nPage)459 int wxNotebook::SetSelection(size_t nPage)
460 {
461     wxCHECK_MSG( IS_VALID_PAGE(nPage), wxNOT_FOUND, wxT("notebook page out of range") );
462 
463     if ( m_nSelection == wxNOT_FOUND || nPage != (size_t)m_nSelection )
464     {
465         if ( SendPageChangingEvent(nPage) )
466         {
467             // program allows the page change
468             SendPageChangedEvent(m_nSelection, nPage);
469 
470             TabCtrl_SetCurSel(GetHwnd(), nPage);
471         }
472     }
473 
474     return m_nSelection;
475 }
476 
UpdateSelection(int selNew)477 void wxNotebook::UpdateSelection(int selNew)
478 {
479     if ( m_nSelection != wxNOT_FOUND )
480         m_pages[m_nSelection]->Show(false);
481 
482     if ( selNew != wxNOT_FOUND )
483     {
484         wxNotebookPage *pPage = m_pages[selNew];
485         pPage->Show(true);
486     }
487 
488     // Changing the page should give the focus to it but, as per bug report
489     // http://sf.net/tracker/index.php?func=detail&aid=1150659&group_id=9863&atid=109863,
490     // we should not set the focus to it directly since it erroneously
491     // selects radio buttons and breaks keyboard handling for a notebook's
492     // scroll buttons. So give focus to the notebook and not the page.
493 
494     // but don't do this is the notebook is hidden
495     if ( ::IsWindowVisible(GetHwnd()) )
496         SetFocus();
497 
498     m_nSelection = selNew;
499 }
500 
ChangeSelection(size_t nPage)501 int wxNotebook::ChangeSelection(size_t nPage)
502 {
503     wxCHECK_MSG( IS_VALID_PAGE(nPage), wxNOT_FOUND, wxT("notebook page out of range") );
504 
505     if ( m_nSelection == wxNOT_FOUND || nPage != (size_t)m_nSelection )
506     {
507         TabCtrl_SetCurSel(GetHwnd(), nPage);
508 
509         UpdateSelection(nPage);
510     }
511 
512     return m_nSelection;
513 }
514 
SetPageText(size_t nPage,const wxString & strText)515 bool wxNotebook::SetPageText(size_t nPage, const wxString& strText)
516 {
517     wxCHECK_MSG( IS_VALID_PAGE(nPage), false, wxT("notebook page out of range") );
518 
519     TC_ITEM tcItem;
520     tcItem.mask = TCIF_TEXT;
521     tcItem.pszText = (wxChar *)strText.c_str();
522 
523     if ( !HasFlag(wxNB_MULTILINE) )
524         return TabCtrl_SetItem(GetHwnd(), nPage, &tcItem) != 0;
525 
526     // multiline - we need to set new page size if a line is added or removed
527     int rows = GetRowCount();
528     bool ret = TabCtrl_SetItem(GetHwnd(), nPage, &tcItem) != 0;
529 
530     if ( ret && rows != GetRowCount() )
531     {
532         const wxRect r = GetPageSize();
533         const size_t count = m_pages.Count();
534         for ( size_t page = 0; page < count; page++ )
535             m_pages[page]->SetSize(r);
536     }
537 
538     return ret;
539 }
540 
GetPageText(size_t nPage) const541 wxString wxNotebook::GetPageText(size_t nPage) const
542 {
543     wxCHECK_MSG( IS_VALID_PAGE(nPage), wxEmptyString, wxT("notebook page out of range") );
544 
545     wxChar buf[256];
546     TC_ITEM tcItem;
547     tcItem.mask = TCIF_TEXT;
548     tcItem.pszText = buf;
549     tcItem.cchTextMax = WXSIZEOF(buf);
550 
551     wxString str;
552     if ( TabCtrl_GetItem(GetHwnd(), nPage, &tcItem) )
553         str = tcItem.pszText;
554 
555     return str;
556 }
557 
GetPageImage(size_t nPage) const558 int wxNotebook::GetPageImage(size_t nPage) const
559 {
560     wxCHECK_MSG( IS_VALID_PAGE(nPage), wxNOT_FOUND, wxT("notebook page out of range") );
561 
562     TC_ITEM tcItem;
563     tcItem.mask = TCIF_IMAGE;
564 
565     return TabCtrl_GetItem(GetHwnd(), nPage, &tcItem) ? tcItem.iImage
566                                                       : wxNOT_FOUND;
567 }
568 
SetPageImage(size_t nPage,int nImage)569 bool wxNotebook::SetPageImage(size_t nPage, int nImage)
570 {
571     wxCHECK_MSG( IS_VALID_PAGE(nPage), false, wxT("notebook page out of range") );
572 
573     TC_ITEM tcItem;
574     tcItem.mask = TCIF_IMAGE;
575     tcItem.iImage = nImage;
576 
577     return TabCtrl_SetItem(GetHwnd(), nPage, &tcItem) != 0;
578 }
579 
SetImageList(wxImageList * imageList)580 void wxNotebook::SetImageList(wxImageList* imageList)
581 {
582     wxNotebookBase::SetImageList(imageList);
583 
584     if ( imageList )
585     {
586         (void) TabCtrl_SetImageList(GetHwnd(), GetHimagelistOf(imageList));
587     }
588 }
589 
590 // ----------------------------------------------------------------------------
591 // wxNotebook size settings
592 // ----------------------------------------------------------------------------
593 
GetPageSize() const594 wxRect wxNotebook::GetPageSize() const
595 {
596     wxRect r;
597 
598     RECT rc;
599     ::GetClientRect(GetHwnd(), &rc);
600 
601     // This check is to work around a bug in TabCtrl_AdjustRect which will
602     // cause a crash on win2k or on XP with themes disabled if either
603     // wxNB_MULTILINE is used or tabs are placed on a side, if the rectangle
604     // is too small.
605     //
606     // The value of 20 is chosen arbitrarily but seems to work
607     if ( rc.right > 20 && rc.bottom > 20 )
608     {
609         TabCtrl_AdjustRect(GetHwnd(), false, &rc);
610 
611         wxCopyRECTToRect(rc, r);
612     }
613 
614     return r;
615 }
616 
SetPageSize(const wxSize & size)617 void wxNotebook::SetPageSize(const wxSize& size)
618 {
619     // transform the page size into the notebook size
620     RECT rc;
621     rc.left =
622     rc.top = 0;
623     rc.right = size.x;
624     rc.bottom = size.y;
625 
626     TabCtrl_AdjustRect(GetHwnd(), true, &rc);
627 
628     // and now set it
629     SetSize(rc.right - rc.left, rc.bottom - rc.top);
630 }
631 
SetPadding(const wxSize & padding)632 void wxNotebook::SetPadding(const wxSize& padding)
633 {
634     TabCtrl_SetPadding(GetHwnd(), padding.x, padding.y);
635 }
636 
637 // Windows-only at present. Also, you must use the wxNB_FIXEDWIDTH
638 // style.
SetTabSize(const wxSize & sz)639 void wxNotebook::SetTabSize(const wxSize& sz)
640 {
641     ::SendMessage(GetHwnd(), TCM_SETITEMSIZE, 0, MAKELPARAM(sz.x, sz.y));
642 }
643 
CalcSizeFromPage(const wxSize & sizePage) const644 wxSize wxNotebook::CalcSizeFromPage(const wxSize& sizePage) const
645 {
646     // we can't use TabCtrl_AdjustRect here because it only works for wxNB_TOP
647     wxSize sizeTotal = sizePage;
648 
649     wxSize tabSize;
650     if ( GetPageCount() > 0 )
651     {
652         RECT rect;
653         TabCtrl_GetItemRect(GetHwnd(), 0, &rect);
654         tabSize.x = rect.right - rect.left;
655         tabSize.y = rect.bottom - rect.top;
656     }
657 
658     const int rows = GetRowCount();
659 
660     // add an extra margin in both directions
661     const int MARGIN = 8;
662     if ( IsVertical() )
663     {
664         sizeTotal.x += MARGIN;
665         sizeTotal.y += tabSize.y * rows + MARGIN;
666     }
667     else // horizontal layout
668     {
669         sizeTotal.x += tabSize.x * rows + MARGIN;
670         sizeTotal.y += MARGIN;
671     }
672 
673     return sizeTotal;
674 }
675 
AdjustPageSize(wxNotebookPage * page)676 void wxNotebook::AdjustPageSize(wxNotebookPage *page)
677 {
678     wxCHECK_RET( page, _T("NULL page in wxNotebook::AdjustPageSize") );
679 
680     const wxRect r = GetPageSize();
681     if ( !r.IsEmpty() )
682     {
683         page->SetSize(r);
684     }
685 }
686 
687 // ----------------------------------------------------------------------------
688 // wxNotebook operations
689 // ----------------------------------------------------------------------------
690 
691 // remove one page from the notebook, without deleting
DoRemovePage(size_t nPage)692 wxNotebookPage *wxNotebook::DoRemovePage(size_t nPage)
693 {
694     wxNotebookPage *pageRemoved = wxNotebookBase::DoRemovePage(nPage);
695     if ( !pageRemoved )
696         return NULL;
697 
698     TabCtrl_DeleteItem(GetHwnd(), nPage);
699 
700     if ( m_pages.IsEmpty() )
701     {
702         // no selection any more, the notebook becamse empty
703         m_nSelection = wxNOT_FOUND;
704     }
705     else // notebook still not empty
706     {
707         int selNew = TabCtrl_GetCurSel(GetHwnd());
708         if ( selNew != wxNOT_FOUND )
709         {
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();
716         }
717         else if (int(nPage) == m_nSelection)
718         {
719             // The selection was deleted.
720 
721             // Determine new selection.
722             if (m_nSelection == int(GetPageCount()))
723                 selNew = m_nSelection - 1;
724             else
725                 selNew = m_nSelection;
726 
727             // m_nSelection must be always valid so reset it before calling
728             // SetSelection()
729             m_nSelection = wxNOT_FOUND;
730             SetSelection(selNew);
731         }
732         else
733         {
734             wxFAIL; // Windows did not behave ok.
735         }
736     }
737 
738     return pageRemoved;
739 }
740 
741 // remove all pages
DeleteAllPages()742 bool wxNotebook::DeleteAllPages()
743 {
744     size_t nPageCount = GetPageCount();
745     size_t nPage;
746     for ( nPage = 0; nPage < nPageCount; nPage++ )
747         delete m_pages[nPage];
748 
749     m_pages.Clear();
750 
751     TabCtrl_DeleteAllItems(GetHwnd());
752 
753     m_nSelection = wxNOT_FOUND;
754 
755     InvalidateBestSize();
756     return true;
757 }
758 
759 // same as AddPage() but does it at given position
InsertPage(size_t nPage,wxNotebookPage * pPage,const wxString & strText,bool bSelect,int imageId)760 bool wxNotebook::InsertPage(size_t nPage,
761                             wxNotebookPage *pPage,
762                             const wxString& strText,
763                             bool bSelect,
764                             int imageId)
765 {
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") );
769 
770     wxASSERT_MSG( pPage->GetParent() == this,
771                     _T("notebook pages must have notebook as parent") );
772 
773     // add a new tab to the control
774     // ----------------------------
775 
776     // init all fields to 0
777     TC_ITEM tcItem;
778     wxZeroMemory(tcItem);
779 
780     // set the image, if any
781     if ( imageId != -1 )
782     {
783         tcItem.mask |= TCIF_IMAGE;
784         tcItem.iImage  = imageId;
785     }
786 
787     // and the text
788     if ( !strText.empty() )
789     {
790         tcItem.mask |= TCIF_TEXT;
791         tcItem.pszText = (wxChar *)strText.c_str(); // const_cast
792     }
793 
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);
798 
799     // this updates internal flag too -- otherwise it would get out of sync
800     // with the real state
801     pPage->Show(false);
802 
803 
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);
808 
809     // finally do insert it
810     if ( TabCtrl_InsertItem(GetHwnd(), nPage, &tcItem) == -1 )
811     {
812         wxLogError(wxT("Can't create the notebook page '%s'."), strText.c_str());
813 
814         return false;
815     }
816 
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() )
820     {
821 #if wxUSE_UXTHEME
822         UpdateBgBrush();
823 #endif // wxUSE_UXTHEME
824     }
825 
826     // succeeded: save the pointer to the page
827     m_pages.Insert(pPage, nPage);
828 
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) )
834     {
835         AdjustPageSize(pPage);
836     }
837 
838     // now deal with the selection
839     // ---------------------------
840 
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 )
844     {
845         // one extra page added
846         m_nSelection++;
847     }
848 
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;
852     if ( bSelect )
853         selNew = nPage;
854     else if ( m_nSelection == wxNOT_FOUND )
855         selNew = 0;
856 
857     if ( selNew != wxNOT_FOUND )
858         SetSelection(selNew);
859 
860     InvalidateBestSize();
861 
862     return true;
863 }
864 
HitTest(const wxPoint & pt,long * flags) const865 int wxNotebook::HitTest(const wxPoint& pt, long *flags) const
866 {
867     TC_HITTESTINFO hitTestInfo;
868     hitTestInfo.pt.x = pt.x;
869     hitTestInfo.pt.y = pt.y;
870     int item = TabCtrl_HitTest(GetHwnd(), &hitTestInfo);
871 
872     if ( flags )
873     {
874         *flags = 0;
875 
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;
886     }
887 
888     return item;
889 }
890 
891 // ----------------------------------------------------------------------------
892 // flicker-less notebook redraw
893 // ----------------------------------------------------------------------------
894 
895 #if USE_NOTEBOOK_ANTIFLICKER
896 
897 // wnd proc for the spin button
wxNotebookSpinBtnWndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)898 LRESULT APIENTRY _EXPORT wxNotebookSpinBtnWndProc(HWND hwnd,
899                                                   UINT message,
900                                                   WPARAM wParam,
901                                                   LPARAM lParam)
902 {
903     if ( message == WM_ERASEBKGND )
904         return 0;
905 
906     return ::CallWindowProc(CASTWNDPROC gs_wndprocNotebookSpinBtn,
907                             hwnd, message, wParam, lParam);
908 }
909 
wxNotebookWndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)910 LRESULT APIENTRY _EXPORT wxNotebookWndProc(HWND hwnd,
911                                            UINT message,
912                                            WPARAM wParam,
913                                            LPARAM lParam)
914 {
915     return ::CallWindowProc(CASTWNDPROC gs_wndprocNotebook,
916                             hwnd, message, wParam, lParam);
917 }
918 
OnEraseBackground(wxEraseEvent & WXUNUSED (event))919 void wxNotebook::OnEraseBackground(wxEraseEvent& WXUNUSED(event))
920 {
921     // do nothing here
922 }
923 
OnPaint(wxPaintEvent & WXUNUSED (event))924 void wxNotebook::OnPaint(wxPaintEvent& WXUNUSED(event))
925 {
926     wxPaintDC dc(this);
927     wxMemoryDC memdc;
928     RECT rc;
929     ::GetClientRect(GetHwnd(), &rc);
930     wxBitmap bmp(rc.right, rc.bottom);
931     memdc.SelectObject(bmp);
932 
933     const wxLayoutDirection dir = dc.GetLayoutDirection();
934     memdc.SetLayoutDirection(dir);
935 
936     // if there is no special brush just use the solid background colour
937 #if wxUSE_UXTHEME
938     HBRUSH hbr = (HBRUSH)m_hbrBackground;
939 #else
940     HBRUSH hbr = 0;
941 #endif
942     wxBrush brush;
943     if ( !hbr )
944     {
945         brush = wxBrush(GetBackgroundColour());
946         hbr = GetHbrushOf(brush);
947     }
948 
949     ::FillRect(GetHdcOf(memdc), &rc, hbr);
950 
951     MSWDefWindowProc(WM_PAINT, (WPARAM)memdc.GetHDC(), 0);
952 
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);
957 }
958 
959 #endif // USE_NOTEBOOK_ANTIFLICKER
960 
961 // ----------------------------------------------------------------------------
962 // wxNotebook callbacks
963 // ----------------------------------------------------------------------------
964 
OnSize(wxSizeEvent & event)965 void wxNotebook::OnSize(wxSizeEvent& event)
966 {
967     if ( GetPageCount() == 0 )
968     {
969         // Prevents droppings on resize, but does cause some flicker
970         // when there are no pages.
971         Refresh();
972         event.Skip();
973         return;
974     }
975 #ifndef __WXWINCE__
976     else
977     {
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
982         // the droppings.
983 
984         wxSize sz = GetClientSize();
985 
986         // Refresh right side
987         wxRect rect(sz.x-4, 0, 4, sz.y);
988         RefreshRect(rect);
989 
990         // Refresh bottom side
991         rect = wxRect(0, sz.y-4, sz.x, 4);
992         RefreshRect(rect);
993 
994         // Refresh left side
995         rect = wxRect(0, 0, 4, sz.y);
996         RefreshRect(rect);
997     }
998 #endif // !__WXWINCE__
999 
1000     // fit all the notebook pages to the tab control's display area
1001 
1002     RECT rc;
1003     rc.left = rc.top = 0;
1004     GetSize((int *)&rc.right, (int *)&rc.bottom);
1005 
1006     // save the total size, we'll use it below
1007     int widthNbook = rc.right - rc.left,
1008         heightNbook = rc.bottom - rc.top;
1009 
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
1013     //
1014     // http://sf.net/tracker/index.php?func=detail&aid=645323&group_id=9863&atid=109863
1015     //
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) )
1020     {
1021         // avoid an infinite recursion: we get another notification too!
1022         static bool s_isInOnSize = false;
1023 
1024         if ( !s_isInOnSize )
1025         {
1026             s_isInOnSize = true;
1027             SendMessage(GetHwnd(), WM_SIZE, SIZE_RESTORED,
1028                     MAKELPARAM(rc.right, rc.bottom));
1029             s_isInOnSize = false;
1030         }
1031 
1032         // The best size depends on the number of rows of tabs, which can
1033         // change when the notepad is resized.
1034         InvalidateBestSize();
1035     }
1036 
1037 #if wxUSE_UXTHEME
1038     // background bitmap size has changed, update the brush using it too
1039     UpdateBgBrush();
1040 #endif // wxUSE_UXTHEME
1041 
1042     TabCtrl_AdjustRect(GetHwnd(), false, &rc);
1043 
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);
1050     }
1051 
1052 
1053     // unless we had already repainted everything, we now need to refresh
1054     if ( !HasFlag(wxFULL_REPAINT_ON_RESIZE) )
1055     {
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),
1060                     false);
1061         RefreshRect(wxRect(rc.right, rc.top, widthNbook - rc.right, height),
1062                     false);
1063     }
1064 
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 )
1069     {
1070         // iterate over all child windows to find spin button
1071         for ( HWND child = ::GetWindow(GetHwnd(), GW_CHILD);
1072               child;
1073               child = ::GetWindow(child, GW_HWNDNEXT) )
1074         {
1075             wxWindow *childWindow = wxFindWinFromHandle((WXHWND)child);
1076 
1077             // see if it exists, if no wxWindow found then assume it's the spin
1078             // btn
1079             if ( !childWindow )
1080             {
1081                 // subclass the spin button to override WM_ERASEBKGND
1082                 if ( !gs_wndprocNotebookSpinBtn )
1083                     gs_wndprocNotebookSpinBtn = (WXFARPROC)wxGetWindowProc(child);
1084 
1085                 wxSetWindowProc(child, wxNotebookSpinBtnWndProc);
1086                 m_hasSubclassedUpdown = true;
1087                 break;
1088             }
1089         }
1090     }
1091 #endif // USE_NOTEBOOK_ANTIFLICKER
1092 
1093     event.Skip();
1094 }
1095 
OnSelChange(wxNotebookEvent & event)1096 void wxNotebook::OnSelChange(wxNotebookEvent& event)
1097 {
1098     // is it our tab control?
1099     if ( event.GetEventObject() == this )
1100     {
1101         UpdateSelection(event.GetSelection());
1102     }
1103 
1104     // we want to give others a chance to process this message as well
1105     event.Skip();
1106 }
1107 
OnNavigationKey(wxNavigationKeyEvent & event)1108 void wxNotebook::OnNavigationKey(wxNavigationKeyEvent& event)
1109 {
1110     if ( event.IsWindowChange() ) {
1111         // change pages
1112         AdvanceSelection(event.GetDirection());
1113     }
1114     else {
1115         // we get this event in 3 cases
1116         //
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
1120         //
1121         // or
1122         //
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
1128         //
1129         // or
1130         //
1131         // c) we ourselves (see MSWTranslateMessage) generated the event
1132         //
1133         wxWindow * const parent = GetParent();
1134 
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;
1138 
1139         if ( isFromParent || isFromSelf )
1140         {
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) )
1145             {
1146                 // so that the page knows that the event comes from it's parent
1147                 // and is being propagated downwards
1148                 event.SetEventObject(this);
1149 
1150                 wxWindow *page = m_pages[m_nSelection];
1151                 if ( !page->GetEventHandler()->ProcessEvent(event) )
1152                 {
1153                     page->SetFocus();
1154                 }
1155                 //else: page manages focus inside it itself
1156             }
1157             else // otherwise set the focus to the notebook itself
1158             {
1159                 SetFocus();
1160             }
1161         }
1162         else
1163         {
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
1167             // page.
1168             if ( !event.GetDirection() )
1169             {
1170                 SetFocus();
1171             }
1172             else if ( parent )
1173             {
1174                 event.SetCurrentFocus(this);
1175                 parent->GetEventHandler()->ProcessEvent(event);
1176             }
1177         }
1178     }
1179 }
1180 
1181 #if wxUSE_UXTHEME
1182 
DoDrawBackground(WXHDC hDC,wxWindow * child)1183 bool wxNotebook::DoDrawBackground(WXHDC hDC, wxWindow *child)
1184 {
1185     wxUxThemeHandle theme(child ? child : this, L"TAB");
1186     if ( !theme )
1187         return false;
1188 
1189     // get the notebook client rect (we're not interested in drawing tabs
1190     // themselves)
1191     wxRect r = GetPageSize();
1192     if ( r.IsEmpty() )
1193         return false;
1194 
1195     RECT rc;
1196     wxCopyRectToRECT(r, rc);
1197 
1198     // map rect to the coords of the window we're drawing in
1199     if ( child )
1200         ::MapWindowPoints(GetHwnd(), GetHwndOf(child), (POINT *)&rc, 2);
1201 
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
1205                             (
1206                                 theme,
1207                                 (HDC) hDC,
1208                                 9 /* TABP_PANE */,
1209                                 0,
1210                                 &rc,
1211                                 &rc
1212                             );
1213     wxUxThemeEngine::Get()->DrawThemeBackground
1214                             (
1215                                 theme,
1216                                 (HDC) hDC,
1217                                 9 /* TABP_PANE */,
1218                                 0,
1219                                 &rc,
1220                                 NULL
1221                             );
1222 
1223     return true;
1224 }
1225 
QueryBgBitmap()1226 WXHBRUSH wxNotebook::QueryBgBitmap()
1227 {
1228     wxRect r = GetPageSize();
1229     if ( r.IsEmpty() )
1230         return 0;
1231 
1232     WindowHDC hDC(GetHwnd());
1233     MemoryHDC hDCMem(hDC);
1234     CompatibleBitmap hBmp(hDC, r.x + r.width, r.y + r.height);
1235 
1236     SelectInHDC selectBmp(hDCMem, hBmp);
1237 
1238     if ( !DoDrawBackground((WXHDC)(HDC)hDCMem) )
1239         return 0;
1240 
1241     return (WXHBRUSH)::CreatePatternBrush(hBmp);
1242 }
1243 
UpdateBgBrush()1244 void wxNotebook::UpdateBgBrush()
1245 {
1246     if ( m_hbrBackground )
1247         ::DeleteObject((HBRUSH)m_hbrBackground);
1248 
1249     if ( !m_hasBgCol && wxUxThemeEngine::GetIfActive() )
1250     {
1251         m_hbrBackground = QueryBgBitmap();
1252     }
1253     else // no themes or we've got user-defined solid colour
1254     {
1255         m_hbrBackground = NULL;
1256     }
1257 }
1258 
MSWGetBgBrushForChild(WXHDC hDC,WXHWND hWnd)1259 WXHBRUSH wxNotebook::MSWGetBgBrushForChild(WXHDC hDC, WXHWND hWnd)
1260 {
1261     if ( m_hbrBackground )
1262     {
1263         // before drawing with the background brush, we need to position it
1264         // correctly
1265         RECT rc;
1266         ::GetWindowRect((HWND)hWnd, &rc);
1267 
1268         ::MapWindowPoints(NULL, GetHwnd(), (POINT *)&rc, 1);
1269 
1270         if ( !::SetBrushOrgEx((HDC)hDC, -rc.left, -rc.top, NULL) )
1271         {
1272             wxLogLastError(_T("SetBrushOrgEx(notebook bg brush)"));
1273         }
1274 
1275         return m_hbrBackground;
1276     }
1277 
1278     return wxNotebookBase::MSWGetBgBrushForChild(hDC, hWnd);
1279 }
1280 
MSWPrintChild(WXHDC hDC,wxWindow * child)1281 bool wxNotebook::MSWPrintChild(WXHDC hDC, wxWindow *child)
1282 {
1283     // solid background colour overrides themed background drawing
1284     if ( !UseBgCol() && DoDrawBackground(hDC, child) )
1285         return true;
1286 
1287     // If we're using a solid colour (for example if we've switched off
1288     // theming for this notebook), paint it
1289     if (UseBgCol())
1290     {
1291         wxRect r = GetPageSize();
1292         if ( r.IsEmpty() )
1293             return false;
1294 
1295         RECT rc;
1296         wxCopyRectToRECT(r, rc);
1297 
1298         // map rect to the coords of the window we're drawing in
1299         if ( child )
1300             ::MapWindowPoints(GetHwnd(), GetHwndOf(child), (POINT *)&rc, 2);
1301 
1302         wxBrush brush(GetBackgroundColour());
1303         HBRUSH hbr = GetHbrushOf(brush);
1304 
1305         ::FillRect((HDC) hDC, &rc, hbr);
1306 
1307         return true;
1308     }
1309 
1310     return wxNotebookBase::MSWPrintChild(hDC, child);
1311 }
1312 
1313 #endif // wxUSE_UXTHEME
1314 
1315 // Windows only: attempts to get colour for UX theme page background
GetThemeBackgroundColour() const1316 wxColour wxNotebook::GetThemeBackgroundColour() const
1317 {
1318 #if wxUSE_UXTHEME
1319     if (wxUxThemeEngine::Get())
1320     {
1321         wxUxThemeHandle hTheme((wxNotebook*) this, L"TAB");
1322         if (hTheme)
1323         {
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(
1329                                         hTheme,
1330                                         10 /* TABP_BODY */,
1331                                         1 /* NORMAL */,
1332                                         3821 /* FILLCOLORHINT */,
1333                                         &themeColor));
1334             if (!success)
1335                 return GetBackgroundColour();
1336 
1337             /*
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.
1342 
1343             This workaround potentially breaks appearance of some themes,
1344             but in practice it already fixes some themes.
1345             */
1346             if (themeColor == 1)
1347             {
1348                 wxUxThemeEngine::Get()->GetThemeColor(
1349                                             hTheme,
1350                                             10 /* TABP_BODY */,
1351                                             1 /* NORMAL */,
1352                                             3802 /* FILLCOLOR */,
1353                                             &themeColor);
1354             }
1355 
1356             wxColour colour = wxRGBToColour(themeColor);
1357 
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.
1361 
1362             static int s_AeroStatus = -1;
1363             if (s_AeroStatus == -1)
1364             {
1365                 WCHAR szwThemeFile[1024];
1366                 WCHAR szwThemeColor[256];
1367                 if (S_OK == wxUxThemeEngine::Get()->GetCurrentThemeName(szwThemeFile, 1024, szwThemeColor, 256, NULL, 0))
1368                 {
1369                     wxString themeFile(szwThemeFile), themeColor(szwThemeColor);
1370                     if (themeFile.Find(wxT("Aero")) != -1 && themeColor == wxT("NormalColor"))
1371                         s_AeroStatus = 1;
1372                     else
1373                         s_AeroStatus = 0;
1374                 }
1375                 else
1376                     s_AeroStatus = 0;
1377             }
1378 
1379             if (s_AeroStatus == 1)
1380                 colour = wxColour(255, 255, 255);
1381 
1382             return colour;
1383         }
1384     }
1385 #endif // wxUSE_UXTHEME
1386 
1387     return GetBackgroundColour();
1388 }
1389 
1390 // ----------------------------------------------------------------------------
1391 // wxNotebook base class virtuals
1392 // ----------------------------------------------------------------------------
1393 
1394 #if wxUSE_CONSTRAINTS
1395 
1396 // override these 2 functions to do nothing: everything is done in OnSize
1397 
SetConstraintSizes(bool WXUNUSED (recurse))1398 void wxNotebook::SetConstraintSizes(bool WXUNUSED(recurse))
1399 {
1400   // don't set the sizes of the pages - their correct size is not yet known
1401   wxControl::SetConstraintSizes(false);
1402 }
1403 
DoPhase(int WXUNUSED (nPhase))1404 bool wxNotebook::DoPhase(int WXUNUSED(nPhase))
1405 {
1406   return true;
1407 }
1408 
1409 #endif // wxUSE_CONSTRAINTS
1410 
1411 // ----------------------------------------------------------------------------
1412 // wxNotebook Windows message handlers
1413 // ----------------------------------------------------------------------------
1414 
MSWOnScroll(int orientation,WXWORD nSBCode,WXWORD pos,WXHWND control)1415 bool wxNotebook::MSWOnScroll(int orientation, WXWORD nSBCode,
1416                              WXWORD pos, WXHWND control)
1417 {
1418     // don't generate EVT_SCROLLWIN events for the WM_SCROLLs coming from the
1419     // up-down control
1420     if ( control )
1421         return false;
1422 
1423     return wxNotebookBase::MSWOnScroll(orientation, nSBCode, pos, control);
1424 }
1425 
MSWOnNotify(int idCtrl,WXLPARAM lParam,WXLPARAM * result)1426 bool wxNotebook::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM* result)
1427 {
1428   wxNotebookEvent event(wxEVT_NULL, m_windowId);
1429 
1430   NMHDR* hdr = (NMHDR *)lParam;
1431   switch ( hdr->code ) {
1432     case TCN_SELCHANGE:
1433       event.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED);
1434       break;
1435 
1436     case TCN_SELCHANGING:
1437       event.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING);
1438       break;
1439 
1440     default:
1441       return wxControl::MSWOnNotify(idCtrl, lParam, result);
1442   }
1443 
1444   event.SetSelection(TabCtrl_GetCurSel(GetHwnd()));
1445   event.SetOldSelection(m_nSelection);
1446   event.SetEventObject(this);
1447   event.SetInt(idCtrl);
1448 
1449   bool processed = GetEventHandler()->ProcessEvent(event);
1450   *result = !event.IsAllowed();
1451   return processed;
1452 }
1453 
1454 #endif // wxUSE_NOTEBOOK
1455