]> git.saurik.com Git - wxWidgets.git/blob - src/msw/notebook.cpp
Fix needlessly convoluted test in wxXmlResourceHandlerImpl::GetImageList().
[wxWidgets.git] / src / msw / notebook.cpp
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 // Copyright: (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
8 // Licence: wxWindows licence
9 ///////////////////////////////////////////////////////////////////////////////
10
11 // For compilers that support precompilation, includes "wx.h".
12 #include "wx/wxprec.h"
13
14 #ifdef __BORLANDC__
15 #pragma hdrstop
16 #endif
17
18 #if wxUSE_NOTEBOOK
19
20 #include "wx/notebook.h"
21
22 #ifndef WX_PRECOMP
23 #include "wx/msw/wrapcctl.h" // include <commctrl.h> "properly"
24 #include "wx/string.h"
25 #include "wx/dc.h"
26 #include "wx/log.h"
27 #include "wx/event.h"
28 #include "wx/app.h"
29 #include "wx/dcclient.h"
30 #include "wx/dcmemory.h"
31 #include "wx/control.h"
32 #include "wx/panel.h"
33 #endif // WX_PRECOMP
34
35 #include "wx/imaglist.h"
36 #include "wx/sysopt.h"
37
38 #include "wx/msw/private.h"
39 #include "wx/msw/dc.h"
40
41 #include <windowsx.h>
42 #include "wx/msw/winundef.h"
43
44 #if wxUSE_UXTHEME
45 #include "wx/msw/uxtheme.h"
46 #endif
47
48 // ----------------------------------------------------------------------------
49 // macros
50 // ----------------------------------------------------------------------------
51
52 // check that the page index is valid
53 #define IS_VALID_PAGE(nPage) ((nPage) < GetPageCount())
54
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
58 #ifdef __WXWINCE__
59 // notebooks are never resized under CE anyhow
60 #define USE_NOTEBOOK_ANTIFLICKER 0
61 #else
62 #define USE_NOTEBOOK_ANTIFLICKER 1
63 #endif
64
65 // ----------------------------------------------------------------------------
66 // constants
67 // ----------------------------------------------------------------------------
68
69 // This is a work-around for missing defines in gcc-2.95 headers
70 #ifndef TCS_RIGHT
71 #define TCS_RIGHT 0x0002
72 #endif
73
74 #ifndef TCS_VERTICAL
75 #define TCS_VERTICAL 0x0080
76 #endif
77
78 #ifndef TCS_BOTTOM
79 #define TCS_BOTTOM TCS_RIGHT
80 #endif
81
82 // ----------------------------------------------------------------------------
83 // global variables
84 // ----------------------------------------------------------------------------
85
86 #if USE_NOTEBOOK_ANTIFLICKER
87
88 // the pointer to standard spin button wnd proc
89 static WXFARPROC gs_wndprocNotebookSpinBtn = (WXFARPROC)NULL;
90
91 // the pointer to standard tab control wnd proc
92 static WXFARPROC gs_wndprocNotebook = (WXFARPROC)NULL;
93
94 LRESULT APIENTRY _EXPORT wxNotebookWndProc(HWND hwnd,
95 UINT message,
96 WPARAM wParam,
97 LPARAM lParam);
98
99 #endif // USE_NOTEBOOK_ANTIFLICKER
100
101 // ----------------------------------------------------------------------------
102 // global functions
103 // ----------------------------------------------------------------------------
104
105 static bool HasTroubleWithNonTopTabs()
106 {
107 const int verComCtl32 = wxApp::GetComCtl32Version();
108
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;
113 }
114
115 // ----------------------------------------------------------------------------
116 // event table
117 // ----------------------------------------------------------------------------
118
119 BEGIN_EVENT_TABLE(wxNotebook, wxBookCtrlBase)
120 EVT_SIZE(wxNotebook::OnSize)
121 EVT_NAVIGATION_KEY(wxNotebook::OnNavigationKey)
122
123 #if USE_NOTEBOOK_ANTIFLICKER
124 EVT_ERASE_BACKGROUND(wxNotebook::OnEraseBackground)
125 EVT_PAINT(wxNotebook::OnPaint)
126 #endif // USE_NOTEBOOK_ANTIFLICKER
127 END_EVENT_TABLE()
128
129 // ============================================================================
130 // implementation
131 // ============================================================================
132
133 // ----------------------------------------------------------------------------
134 // wxNotebook construction
135 // ----------------------------------------------------------------------------
136
137 // common part of all ctors
138 void wxNotebook::Init()
139 {
140 #if wxUSE_UXTHEME
141 m_hbrBackground = NULL;
142 #endif // wxUSE_UXTHEME
143
144 #if USE_NOTEBOOK_ANTIFLICKER
145 m_hasSubclassedUpdown = false;
146 m_doneUpdateHack = false;
147 #endif // USE_NOTEBOOK_ANTIFLICKER
148 }
149
150 // default for dynamic class
151 wxNotebook::wxNotebook()
152 {
153 Init();
154 }
155
156 // the same arguments as for wxControl
157 wxNotebook::wxNotebook(wxWindow *parent,
158 wxWindowID id,
159 const wxPoint& pos,
160 const wxSize& size,
161 long style,
162 const wxString& name)
163 {
164 Init();
165
166 Create(parent, id, pos, size, style, name);
167 }
168
169 // Create() function
170 bool wxNotebook::Create(wxWindow *parent,
171 wxWindowID id,
172 const wxPoint& pos,
173 const wxSize& size,
174 long style,
175 const wxString& name)
176 {
177 if ( (style & wxBK_ALIGN_MASK) == wxBK_DEFAULT )
178 {
179 #if defined(__POCKETPC__)
180 style |= wxBK_BOTTOM | wxNB_FLAT;
181 #else
182 style |= wxBK_TOP;
183 #endif
184 }
185
186 #ifdef __WXWINCE__
187 // Not sure why, but without this style, there is no border
188 // around the notebook tabs.
189 if (style & wxNB_FLAT)
190 style |= wxBORDER_SUNKEN;
191 #endif
192
193 #if !wxUSE_UXTHEME
194 // ComCtl32 notebook tabs simply don't work unless they're on top if we
195 // have uxtheme, we can work around it later (after control creation), but
196 // if we have been compiled without uxtheme support, we have to clear those
197 // styles
198 if ( HasTroubleWithNonTopTabs() )
199 {
200 style &= ~(wxBK_BOTTOM | wxBK_LEFT | wxBK_RIGHT);
201 }
202 #endif //wxUSE_UXTHEME
203
204 #if defined(__WINE__) && wxUSE_UNICODE
205 LPCTSTR className = L"SysTabControl32";
206 #else
207 LPCTSTR className = WC_TABCONTROL;
208 #endif
209
210 #if USE_NOTEBOOK_ANTIFLICKER
211 // SysTabCtl32 class has natively CS_HREDRAW and CS_VREDRAW enabled and it
212 // causes horrible flicker when resizing notebook, so get rid of it by
213 // using a class without these styles (but otherwise identical to it)
214 if ( !HasFlag(wxFULL_REPAINT_ON_RESIZE) )
215 {
216 static ClassRegistrar s_clsNotebook;
217 if ( !s_clsNotebook.IsInitialized() )
218 {
219 // get a copy of standard class and modify it
220 WNDCLASS wc;
221
222 if ( ::GetClassInfo(NULL, WC_TABCONTROL, &wc) )
223 {
224 gs_wndprocNotebook =
225 reinterpret_cast<WXFARPROC>(wc.lpfnWndProc);
226 wc.lpszClassName = wxT("_wx_SysTabCtl32");
227 wc.style &= ~(CS_HREDRAW | CS_VREDRAW);
228 wc.hInstance = wxGetInstance();
229 wc.lpfnWndProc = wxNotebookWndProc;
230 s_clsNotebook.Register(wc);
231 }
232 else
233 {
234 wxLogLastError(wxT("GetClassInfoEx(SysTabCtl32)"));
235 }
236 }
237
238 // use our custom class if available but fall back to the standard
239 // notebook if we failed to register it
240 if ( s_clsNotebook.IsRegistered() )
241 {
242 // it's ok to use c_str() here as the static s_clsNotebook object
243 // has sufficiently long lifetime
244 className = s_clsNotebook.GetName().c_str();
245 }
246 }
247 #endif // USE_NOTEBOOK_ANTIFLICKER
248
249 if ( !CreateControl(parent, id, pos, size, style | wxTAB_TRAVERSAL,
250 wxDefaultValidator, name) )
251 return false;
252
253 if ( !MSWCreateControl(className, wxEmptyString, pos, size) )
254 return false;
255
256 // Inherit parent attributes and, unlike the default, also inherit the
257 // parent background colour in order to blend in with its background if
258 // it's set to a non-default value.
259 InheritAttributes();
260 if ( parent->InheritsBackgroundColour() && !UseBgCol() )
261 SetBackgroundColour(parent->GetBackgroundColour());
262
263 #if wxUSE_UXTHEME
264 if ( HasFlag(wxNB_NOPAGETHEME) ||
265 wxSystemOptions::IsFalse(wxT("msw.notebook.themed-background")) )
266 {
267 SetBackgroundColour(GetThemeBackgroundColour());
268 }
269 else // use themed background by default
270 {
271 // create backing store
272 UpdateBgBrush();
273 }
274
275 // comctl32.dll 6.0 doesn't support non-top tabs with visual styles (the
276 // control is simply not rendered correctly), so we disable themes
277 // if possible, otherwise we simply clear the styles.
278 if ( HasTroubleWithNonTopTabs() &&
279 (style & (wxBK_BOTTOM | wxBK_LEFT | wxBK_RIGHT)) )
280 {
281 // check if we use themes at all -- if we don't, we're still okay
282 if ( wxUxThemeEngine::GetIfActive() )
283 {
284 wxUxThemeEngine::GetIfActive()->SetWindowTheme(GetHwnd(), L"", L"");
285
286 // correct the background color for the new non-themed control
287 SetBackgroundColour(GetThemeBackgroundColour());
288 }
289 }
290 #endif // wxUSE_UXTHEME
291
292 // Undocumented hack to get flat notebook style
293 // In fact, we should probably only do this in some
294 // curcumstances, i.e. if we know we will have a border
295 // at the bottom (the tab control doesn't draw it itself)
296 #if defined(__POCKETPC__) || defined(__SMARTPHONE__)
297 if (HasFlag(wxNB_FLAT))
298 {
299 SendMessage(GetHwnd(), CCM_SETVERSION, COMCTL32_VERSION, 0);
300 if (!m_hasBgCol)
301 SetBackgroundColour(*wxWHITE);
302 }
303 #endif
304 return true;
305 }
306
307 WXDWORD wxNotebook::MSWGetStyle(long style, WXDWORD *exstyle) const
308 {
309 WXDWORD tabStyle = wxControl::MSWGetStyle(style, exstyle);
310
311 tabStyle |= WS_TABSTOP | TCS_TABS;
312
313 if ( style & wxNB_MULTILINE )
314 tabStyle |= TCS_MULTILINE;
315 if ( style & wxNB_FIXEDWIDTH )
316 tabStyle |= TCS_FIXEDWIDTH;
317
318 if ( style & wxBK_BOTTOM )
319 tabStyle |= TCS_RIGHT;
320 else if ( style & wxBK_LEFT )
321 tabStyle |= TCS_VERTICAL;
322 else if ( style & wxBK_RIGHT )
323 tabStyle |= TCS_VERTICAL | TCS_RIGHT;
324
325 return tabStyle;
326 }
327
328 wxNotebook::~wxNotebook()
329 {
330 #if wxUSE_UXTHEME
331 if ( m_hbrBackground )
332 ::DeleteObject((HBRUSH)m_hbrBackground);
333 #endif // wxUSE_UXTHEME
334 }
335
336 // ----------------------------------------------------------------------------
337 // wxNotebook accessors
338 // ----------------------------------------------------------------------------
339
340 size_t wxNotebook::GetPageCount() const
341 {
342 // consistency check
343 wxASSERT( (int)m_pages.Count() == TabCtrl_GetItemCount(GetHwnd()) );
344
345 return m_pages.Count();
346 }
347
348 int wxNotebook::GetRowCount() const
349 {
350 return TabCtrl_GetRowCount(GetHwnd());
351 }
352
353 int wxNotebook::SetSelection(size_t nPage)
354 {
355 wxCHECK_MSG( IS_VALID_PAGE(nPage), wxNOT_FOUND, wxT("notebook page out of range") );
356
357 if ( m_selection == wxNOT_FOUND || nPage != (size_t)m_selection )
358 {
359 if ( SendPageChangingEvent(nPage) )
360 {
361 // program allows the page change
362 const int selectionOld = m_selection;
363
364 UpdateSelection(nPage);
365
366 TabCtrl_SetCurSel(GetHwnd(), nPage);
367
368 SendPageChangedEvent(selectionOld, nPage);
369 }
370 }
371
372 return m_selection;
373 }
374
375 void wxNotebook::UpdateSelection(int selNew)
376 {
377 if ( m_selection != wxNOT_FOUND )
378 m_pages[m_selection]->Show(false);
379
380 if ( selNew != wxNOT_FOUND )
381 {
382 wxNotebookPage *pPage = m_pages[selNew];
383 pPage->Show(true);
384
385 // In addition to showing the page, we also want to give focus to it to
386 // make it possible to work with it from keyboard easily. However there
387 // are two exceptions: first, don't touch the focus at all if the
388 // notebook itself is not currently shown.
389 if ( ::IsWindowVisible(GetHwnd()) )
390 {
391 // And second, don't give focus away if the tab control itself has
392 // it, as this is how the native property sheets behave: if you
393 // explicitly click on the tab label giving it focus, it will
394 // remain after switching to another page. But if the focus was
395 // inside the notebook page, it switches to the new page.
396 if ( !HasFocus() )
397 pPage->SetFocus();
398 }
399 }
400
401 m_selection = selNew;
402 }
403
404 int wxNotebook::ChangeSelection(size_t nPage)
405 {
406 wxCHECK_MSG( IS_VALID_PAGE(nPage), wxNOT_FOUND, wxT("notebook page out of range") );
407
408 const int selOld = m_selection;
409
410 if ( m_selection == wxNOT_FOUND || nPage != (size_t)m_selection )
411 {
412 TabCtrl_SetCurSel(GetHwnd(), nPage);
413
414 UpdateSelection(nPage);
415 }
416
417 return selOld;
418 }
419
420 bool wxNotebook::SetPageText(size_t nPage, const wxString& strText)
421 {
422 wxCHECK_MSG( IS_VALID_PAGE(nPage), false, wxT("notebook page out of range") );
423
424 TC_ITEM tcItem;
425 tcItem.mask = TCIF_TEXT;
426 tcItem.pszText = wxMSW_CONV_LPTSTR(strText);
427
428 if ( !HasFlag(wxNB_MULTILINE) )
429 return TabCtrl_SetItem(GetHwnd(), nPage, &tcItem) != 0;
430
431 // multiline - we need to set new page size if a line is added or removed
432 int rows = GetRowCount();
433 bool ret = TabCtrl_SetItem(GetHwnd(), nPage, &tcItem) != 0;
434
435 if ( ret && rows != GetRowCount() )
436 {
437 const wxRect r = GetPageSize();
438 const size_t count = m_pages.Count();
439 for ( size_t page = 0; page < count; page++ )
440 m_pages[page]->SetSize(r);
441 }
442
443 return ret;
444 }
445
446 wxString wxNotebook::GetPageText(size_t nPage) const
447 {
448 wxCHECK_MSG( IS_VALID_PAGE(nPage), wxEmptyString, wxT("notebook page out of range") );
449
450 wxChar buf[256];
451 TC_ITEM tcItem;
452 tcItem.mask = TCIF_TEXT;
453 tcItem.pszText = buf;
454 tcItem.cchTextMax = WXSIZEOF(buf);
455
456 wxString str;
457 if ( TabCtrl_GetItem(GetHwnd(), nPage, &tcItem) )
458 str = tcItem.pszText;
459
460 return str;
461 }
462
463 int wxNotebook::GetPageImage(size_t nPage) const
464 {
465 wxCHECK_MSG( IS_VALID_PAGE(nPage), wxNOT_FOUND, wxT("notebook page out of range") );
466
467 TC_ITEM tcItem;
468 tcItem.mask = TCIF_IMAGE;
469
470 return TabCtrl_GetItem(GetHwnd(), nPage, &tcItem) ? tcItem.iImage
471 : wxNOT_FOUND;
472 }
473
474 bool wxNotebook::SetPageImage(size_t nPage, int nImage)
475 {
476 wxCHECK_MSG( IS_VALID_PAGE(nPage), false, wxT("notebook page out of range") );
477
478 TC_ITEM tcItem;
479 tcItem.mask = TCIF_IMAGE;
480 tcItem.iImage = nImage;
481
482 return TabCtrl_SetItem(GetHwnd(), nPage, &tcItem) != 0;
483 }
484
485 void wxNotebook::SetImageList(wxImageList* imageList)
486 {
487 wxNotebookBase::SetImageList(imageList);
488
489 if ( imageList )
490 {
491 (void) TabCtrl_SetImageList(GetHwnd(), GetHimagelistOf(imageList));
492 }
493 }
494
495 // ----------------------------------------------------------------------------
496 // wxNotebook size settings
497 // ----------------------------------------------------------------------------
498
499 wxRect wxNotebook::GetPageSize() const
500 {
501 wxRect r;
502
503 RECT rc;
504 ::GetClientRect(GetHwnd(), &rc);
505
506 // This check is to work around a bug in TabCtrl_AdjustRect which will
507 // cause a crash on win2k or on XP with themes disabled if either
508 // wxNB_MULTILINE is used or tabs are placed on a side, if the rectangle
509 // is too small.
510 //
511 // The value of 20 is chosen arbitrarily but seems to work
512 if ( rc.right > 20 && rc.bottom > 20 )
513 {
514 TabCtrl_AdjustRect(GetHwnd(), false, &rc);
515
516 wxCopyRECTToRect(rc, r);
517 }
518
519 return r;
520 }
521
522 void wxNotebook::SetPageSize(const wxSize& size)
523 {
524 // transform the page size into the notebook size
525 RECT rc;
526 rc.left =
527 rc.top = 0;
528 rc.right = size.x;
529 rc.bottom = size.y;
530
531 TabCtrl_AdjustRect(GetHwnd(), true, &rc);
532
533 // and now set it
534 SetSize(rc.right - rc.left, rc.bottom - rc.top);
535 }
536
537 void wxNotebook::SetPadding(const wxSize& padding)
538 {
539 TabCtrl_SetPadding(GetHwnd(), padding.x, padding.y);
540 }
541
542 // Windows-only at present. Also, you must use the wxNB_FIXEDWIDTH
543 // style.
544 void wxNotebook::SetTabSize(const wxSize& sz)
545 {
546 ::SendMessage(GetHwnd(), TCM_SETITEMSIZE, 0, MAKELPARAM(sz.x, sz.y));
547 }
548
549 wxSize wxNotebook::CalcSizeFromPage(const wxSize& sizePage) const
550 {
551 // we can't use TabCtrl_AdjustRect here because it only works for wxNB_TOP
552 wxSize sizeTotal = sizePage;
553
554 wxSize tabSize;
555 if ( GetPageCount() > 0 )
556 {
557 RECT rect;
558 TabCtrl_GetItemRect(GetHwnd(), 0, &rect);
559 tabSize.x = rect.right - rect.left;
560 tabSize.y = rect.bottom - rect.top;
561 }
562
563 const int rows = GetRowCount();
564
565 // add an extra margin in both directions
566 const int MARGIN = 8;
567 if ( IsVertical() )
568 {
569 sizeTotal.x += MARGIN;
570 sizeTotal.y += tabSize.y * rows + MARGIN;
571 }
572 else // horizontal layout
573 {
574 sizeTotal.x += tabSize.x * rows + MARGIN;
575 sizeTotal.y += MARGIN;
576 }
577
578 return sizeTotal;
579 }
580
581 void wxNotebook::AdjustPageSize(wxNotebookPage *page)
582 {
583 wxCHECK_RET( page, wxT("NULL page in wxNotebook::AdjustPageSize") );
584
585 const wxRect r = GetPageSize();
586 if ( !r.IsEmpty() )
587 {
588 page->SetSize(r);
589 }
590 }
591
592 // ----------------------------------------------------------------------------
593 // wxNotebook operations
594 // ----------------------------------------------------------------------------
595
596 // remove one page from the notebook, without deleting
597 wxNotebookPage *wxNotebook::DoRemovePage(size_t nPage)
598 {
599 wxNotebookPage *pageRemoved = wxNotebookBase::DoRemovePage(nPage);
600 if ( !pageRemoved )
601 return NULL;
602
603 // hide the removed page to maintain the invariant that only the
604 // selected page is visible and others are hidden:
605 pageRemoved->Show(false);
606
607 TabCtrl_DeleteItem(GetHwnd(), nPage);
608
609 if ( m_pages.IsEmpty() )
610 {
611 // no selection any more, the notebook becamse empty
612 m_selection = wxNOT_FOUND;
613 }
614 else // notebook still not empty
615 {
616 int selNew = TabCtrl_GetCurSel(GetHwnd());
617 if ( selNew != wxNOT_FOUND )
618 {
619 // No selection change, just refresh the current selection.
620 // Because it could be that the slection index changed
621 // we need to update it.
622 // Note: this does not mean the selection it self changed.
623 m_selection = selNew;
624 m_pages[m_selection]->Refresh();
625 }
626 else if (int(nPage) == m_selection)
627 {
628 // The selection was deleted.
629
630 // Determine new selection.
631 if (m_selection == int(GetPageCount()))
632 selNew = m_selection - 1;
633 else
634 selNew = m_selection;
635
636 // m_selection must be always valid so reset it before calling
637 // SetSelection()
638 m_selection = wxNOT_FOUND;
639 SetSelection(selNew);
640 }
641 else
642 {
643 wxFAIL; // Windows did not behave ok.
644 }
645 }
646
647 return pageRemoved;
648 }
649
650 // remove all pages
651 bool wxNotebook::DeleteAllPages()
652 {
653 size_t nPageCount = GetPageCount();
654 size_t nPage;
655 for ( nPage = 0; nPage < nPageCount; nPage++ )
656 delete m_pages[nPage];
657
658 m_pages.Clear();
659
660 TabCtrl_DeleteAllItems(GetHwnd());
661
662 m_selection = wxNOT_FOUND;
663
664 InvalidateBestSize();
665 return true;
666 }
667
668 // same as AddPage() but does it at given position
669 bool wxNotebook::InsertPage(size_t nPage,
670 wxNotebookPage *pPage,
671 const wxString& strText,
672 bool bSelect,
673 int imageId)
674 {
675 wxCHECK_MSG( pPage != NULL, false, wxT("NULL page in wxNotebook::InsertPage") );
676 wxCHECK_MSG( IS_VALID_PAGE(nPage) || nPage == GetPageCount(), false,
677 wxT("invalid index in wxNotebook::InsertPage") );
678
679 wxASSERT_MSG( pPage->GetParent() == this,
680 wxT("notebook pages must have notebook as parent") );
681
682 // add a new tab to the control
683 // ----------------------------
684
685 // init all fields to 0
686 TC_ITEM tcItem;
687 wxZeroMemory(tcItem);
688
689 // set the image, if any
690 if ( imageId != -1 )
691 {
692 tcItem.mask |= TCIF_IMAGE;
693 tcItem.iImage = imageId;
694 }
695
696 // and the text
697 if ( !strText.empty() )
698 {
699 tcItem.mask |= TCIF_TEXT;
700 tcItem.pszText = wxMSW_CONV_LPTSTR(strText);
701 }
702
703 // hide the page: unless it is selected, it shouldn't be shown (and if it
704 // is selected it will be shown later)
705 HWND hwnd = GetWinHwnd(pPage);
706 SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~WS_VISIBLE);
707
708 // this updates internal flag too -- otherwise it would get out of sync
709 // with the real state
710 pPage->Show(false);
711
712
713 // fit the notebook page to the tab control's display area: this should be
714 // done before adding it to the notebook or TabCtrl_InsertItem() will
715 // change the notebooks size itself!
716 AdjustPageSize(pPage);
717
718 // finally do insert it
719 if ( TabCtrl_InsertItem(GetHwnd(), nPage, &tcItem) == -1 )
720 {
721 wxLogError(wxT("Can't create the notebook page '%s'."), strText.c_str());
722
723 return false;
724 }
725
726 // need to update the bg brush when the first page is added
727 // so the first panel gets the correct themed background
728 if ( m_pages.empty() )
729 {
730 #if wxUSE_UXTHEME
731 UpdateBgBrush();
732 #endif // wxUSE_UXTHEME
733 }
734
735 // succeeded: save the pointer to the page
736 m_pages.Insert(pPage, nPage);
737
738 // we may need to adjust the size again if the notebook size changed:
739 // normally this only happens for the first page we add (the tabs which
740 // hadn't been there before are now shown) but for a multiline notebook it
741 // can happen for any page at all as a new row could have been started
742 if ( m_pages.GetCount() == 1 || HasFlag(wxNB_MULTILINE) )
743 {
744 AdjustPageSize(pPage);
745
746 // Additionally, force the layout of the notebook itself by posting a
747 // size event to it. If we don't do it, notebooks with pages on the
748 // left or the right side may fail to account for the fact that they
749 // are now big enough to fit all all of their pages on one row and
750 // still reserve space for the second row of tabs, see #1792.
751 const wxSize s = GetSize();
752 ::PostMessage(GetHwnd(), WM_SIZE, SIZE_RESTORED, MAKELPARAM(s.x, s.y));
753 }
754
755 // now deal with the selection
756 // ---------------------------
757
758 // if the inserted page is before the selected one, we must update the
759 // index of the selected page
760 if ( int(nPage) <= m_selection )
761 {
762 // one extra page added
763 m_selection++;
764 }
765
766 DoSetSelectionAfterInsertion(nPage, bSelect);
767
768 InvalidateBestSize();
769
770 return true;
771 }
772
773 int wxNotebook::HitTest(const wxPoint& pt, long *flags) const
774 {
775 TC_HITTESTINFO hitTestInfo;
776 hitTestInfo.pt.x = pt.x;
777 hitTestInfo.pt.y = pt.y;
778 int item = TabCtrl_HitTest(GetHwnd(), &hitTestInfo);
779
780 if ( flags )
781 {
782 *flags = 0;
783
784 if ((hitTestInfo.flags & TCHT_NOWHERE) == TCHT_NOWHERE)
785 *flags |= wxBK_HITTEST_NOWHERE;
786 if ((hitTestInfo.flags & TCHT_ONITEM) == TCHT_ONITEM)
787 *flags |= wxBK_HITTEST_ONITEM;
788 if ((hitTestInfo.flags & TCHT_ONITEMICON) == TCHT_ONITEMICON)
789 *flags |= wxBK_HITTEST_ONICON;
790 if ((hitTestInfo.flags & TCHT_ONITEMLABEL) == TCHT_ONITEMLABEL)
791 *flags |= wxBK_HITTEST_ONLABEL;
792 if ( item == wxNOT_FOUND && GetPageSize().Contains(pt) )
793 *flags |= wxBK_HITTEST_ONPAGE;
794 }
795
796 return item;
797 }
798
799 // ----------------------------------------------------------------------------
800 // flicker-less notebook redraw
801 // ----------------------------------------------------------------------------
802
803 #if USE_NOTEBOOK_ANTIFLICKER
804
805 // wnd proc for the spin button
806 LRESULT APIENTRY _EXPORT wxNotebookSpinBtnWndProc(HWND hwnd,
807 UINT message,
808 WPARAM wParam,
809 LPARAM lParam)
810 {
811 if ( message == WM_ERASEBKGND )
812 return 0;
813
814 return ::CallWindowProc(CASTWNDPROC gs_wndprocNotebookSpinBtn,
815 hwnd, message, wParam, lParam);
816 }
817
818 LRESULT APIENTRY _EXPORT wxNotebookWndProc(HWND hwnd,
819 UINT message,
820 WPARAM wParam,
821 LPARAM lParam)
822 {
823 return ::CallWindowProc(CASTWNDPROC gs_wndprocNotebook,
824 hwnd, message, wParam, lParam);
825 }
826
827 void wxNotebook::OnEraseBackground(wxEraseEvent& WXUNUSED(event))
828 {
829 // do nothing here
830 }
831
832 void wxNotebook::OnPaint(wxPaintEvent& WXUNUSED(event))
833 {
834 wxPaintDC dc(this);
835 wxMemoryDC memdc;
836 RECT rc;
837 ::GetClientRect(GetHwnd(), &rc);
838 wxBitmap bmp(rc.right, rc.bottom);
839 memdc.SelectObject(bmp);
840
841 const wxLayoutDirection dir = dc.GetLayoutDirection();
842 memdc.SetLayoutDirection(dir);
843
844 const HDC hdc = GetHdcOf(memdc);
845
846 // The drawing logic of the native tab control is absolutely impenetrable
847 // but observation shows that in the current Windows versions (XP and 7),
848 // the tab control always erases its entire background in its window proc
849 // when the tabs are top-aligned but does not do it when the tabs are in
850 // any other position.
851 //
852 // This means that we can't rely on our background colour being used for
853 // the blank area in the tab row because this doesn't work in the default
854 // top-aligned case, hence the hack with ExtFloodFill() below. But it also
855 // means that we still do need to erase the DC to account for the other
856 // cases.
857 //
858 // Moreover, just in case some very old or very new (or even future,
859 // although it seems unlikely that this is ever going to change by now)
860 // version of Windows didn't do it like this, do both things in all cases
861 // instead of optimizing away the one of them which doesn't do anything for
862 // the effectively used tab orientation -- better safe than fast.
863
864 // Notice that we use our own background here, not the background used for
865 // the pages, because the tab row background must blend with the parent and
866 // so the background colour inherited from it (if any) must be used.
867 AutoHBRUSH hbr(wxColourToRGB(GetBackgroundColour()));
868
869 ::FillRect(hdc, &rc, hbr);
870
871 MSWDefWindowProc(WM_PAINT, (WPARAM)hdc, 0);
872
873 // At least for the top-aligned tabs, our background colour was overwritten
874 // and so we now replace the default background with our colour. This is
875 // horribly inefficient, of course, but seems to be the only way to do it.
876 if ( UseBgCol() )
877 {
878 SelectInHDC selectBrush(hdc, hbr);
879
880 // Find the point which must contain the default background colour:
881 // this is a hack, of course, but using this point "close" to the
882 // corner seems to work fine in practice.
883 int x = 0,
884 y = 0;
885
886 switch ( GetWindowStyle() & wxBK_ALIGN_MASK )
887 {
888 case wxBK_TOP:
889 x = rc.right - 2;
890 y = 2;
891 break;
892
893 case wxBK_BOTTOM:
894 x = rc.right - 2;
895 y = rc.bottom - 2;
896 break;
897
898 case wxBK_LEFT:
899 x = 2;
900 y = rc.bottom - 2;
901 break;
902
903 case wxBK_RIGHT:
904 x = 2;
905 y = rc.bottom - 2;
906 break;
907 }
908
909 ::ExtFloodFill(hdc, x, y, ::GetSysColor(COLOR_BTNFACE), FLOODFILLSURFACE);
910 }
911
912 // For some reason in RTL mode, source offset has to be -1, otherwise the
913 // right border (physical) remains unpainted.
914 const wxCoord ofs = dir == wxLayout_RightToLeft ? -1 : 0;
915 dc.Blit(ofs, 0, rc.right, rc.bottom, &memdc, ofs, 0);
916 }
917
918 #endif // USE_NOTEBOOK_ANTIFLICKER
919
920 // ----------------------------------------------------------------------------
921 // wxNotebook callbacks
922 // ----------------------------------------------------------------------------
923
924 void wxNotebook::OnSize(wxSizeEvent& event)
925 {
926 if ( GetPageCount() == 0 )
927 {
928 // Prevents droppings on resize, but does cause some flicker
929 // when there are no pages.
930 Refresh();
931 event.Skip();
932 return;
933 }
934 #ifndef __WXWINCE__
935 else
936 {
937 // Without this, we can sometimes get droppings at the edges
938 // of a notebook, for example a notebook in a splitter window.
939 // This needs to be reconciled with the RefreshRect calls
940 // at the end of this function, which weren't enough to prevent
941 // the droppings.
942
943 wxSize sz = GetClientSize();
944
945 // Refresh right side
946 wxRect rect(sz.x-4, 0, 4, sz.y);
947 RefreshRect(rect);
948
949 // Refresh bottom side
950 rect = wxRect(0, sz.y-4, sz.x, 4);
951 RefreshRect(rect);
952
953 // Refresh left side
954 rect = wxRect(0, 0, 4, sz.y);
955 RefreshRect(rect);
956 }
957 #endif // !__WXWINCE__
958
959 // fit all the notebook pages to the tab control's display area
960
961 RECT rc;
962 rc.left = rc.top = 0;
963 GetSize((int *)&rc.right, (int *)&rc.bottom);
964
965 // save the total size, we'll use it below
966 int widthNbook = rc.right - rc.left,
967 heightNbook = rc.bottom - rc.top;
968
969 // there seems to be a bug in the implementation of TabCtrl_AdjustRect(): it
970 // returns completely false values for multiline tab controls after the tabs
971 // are added but before getting the first WM_SIZE (off by ~50 pixels, see
972 //
973 // http://sf.net/tracker/index.php?func=detail&aid=645323&group_id=9863&atid=109863
974 //
975 // and the only work around I could find was this ugly hack... without it
976 // simply toggling the "multiline" checkbox in the notebook sample resulted
977 // in a noticeable page displacement
978 if ( HasFlag(wxNB_MULTILINE) )
979 {
980 // avoid an infinite recursion: we get another notification too!
981 static bool s_isInOnSize = false;
982
983 if ( !s_isInOnSize )
984 {
985 s_isInOnSize = true;
986 SendMessage(GetHwnd(), WM_SIZE, SIZE_RESTORED,
987 MAKELPARAM(rc.right, rc.bottom));
988 s_isInOnSize = false;
989 }
990
991 // The best size depends on the number of rows of tabs, which can
992 // change when the notepad is resized.
993 InvalidateBestSize();
994 }
995
996 #if wxUSE_UXTHEME
997 // background bitmap size has changed, update the brush using it too
998 UpdateBgBrush();
999 #endif // wxUSE_UXTHEME
1000
1001 TabCtrl_AdjustRect(GetHwnd(), false, &rc);
1002
1003 int width = rc.right - rc.left,
1004 height = rc.bottom - rc.top;
1005 size_t nCount = m_pages.Count();
1006 for ( size_t nPage = 0; nPage < nCount; nPage++ ) {
1007 wxNotebookPage *pPage = m_pages[nPage];
1008 pPage->SetSize(rc.left, rc.top, width, height);
1009 }
1010
1011
1012 // unless we had already repainted everything, we now need to refresh
1013 if ( !HasFlag(wxFULL_REPAINT_ON_RESIZE) )
1014 {
1015 // invalidate areas not covered by pages
1016 RefreshRect(wxRect(0, 0, widthNbook, rc.top), false);
1017 RefreshRect(wxRect(0, rc.top, rc.left, height), false);
1018 RefreshRect(wxRect(0, rc.bottom, widthNbook, heightNbook - rc.bottom),
1019 false);
1020 RefreshRect(wxRect(rc.right, rc.top, widthNbook - rc.right, height),
1021 false);
1022 }
1023
1024 #if USE_NOTEBOOK_ANTIFLICKER
1025 // subclass the spin control used by the notebook to scroll pages to
1026 // prevent it from flickering on resize
1027 if ( !m_hasSubclassedUpdown )
1028 {
1029 // iterate over all child windows to find spin button
1030 for ( HWND child = ::GetWindow(GetHwnd(), GW_CHILD);
1031 child;
1032 child = ::GetWindow(child, GW_HWNDNEXT) )
1033 {
1034 wxWindow *childWindow = wxFindWinFromHandle((WXHWND)child);
1035
1036 // see if it exists, if no wxWindow found then assume it's the spin
1037 // btn
1038 if ( !childWindow )
1039 {
1040 // subclass the spin button to override WM_ERASEBKGND
1041 if ( !gs_wndprocNotebookSpinBtn )
1042 gs_wndprocNotebookSpinBtn = (WXFARPROC)wxGetWindowProc(child);
1043
1044 wxSetWindowProc(child, wxNotebookSpinBtnWndProc);
1045 m_hasSubclassedUpdown = true;
1046 break;
1047 }
1048 }
1049 }
1050
1051 // Probably because of the games we play above to avoid flicker sometimes
1052 // the text controls inside notebook pages are not shown correctly (they
1053 // don't have their borders) when the notebook is shown for the first time.
1054 // It's not really clear why does this happen and maybe the bug is in
1055 // wxTextCtrl itself and not here but updating the page when it's about to
1056 // be shown doesn't cost much and works around the problem so do it here
1057 // for now.
1058 if ( !m_doneUpdateHack && IsShownOnScreen() )
1059 {
1060 m_doneUpdateHack = true;
1061 wxWindow* const page = GetCurrentPage();
1062 if ( page )
1063 page->Update();
1064 }
1065 #endif // USE_NOTEBOOK_ANTIFLICKER
1066
1067 event.Skip();
1068 }
1069
1070 void wxNotebook::OnNavigationKey(wxNavigationKeyEvent& event)
1071 {
1072 if ( event.IsWindowChange() ) {
1073 // change pages
1074 AdvanceSelection(event.GetDirection());
1075 }
1076 else {
1077 // we get this event in 3 cases
1078 //
1079 // a) one of our pages might have generated it because the user TABbed
1080 // out from it in which case we should propagate the event upwards and
1081 // our parent will take care of setting the focus to prev/next sibling
1082 //
1083 // or
1084 //
1085 // b) the parent panel wants to give the focus to us so that we
1086 // forward it to our selected page. We can't deal with this in
1087 // OnSetFocus() because we don't know which direction the focus came
1088 // from in this case and so can't choose between setting the focus to
1089 // first or last panel child
1090 //
1091 // or
1092 //
1093 // c) we ourselves (see MSWTranslateMessage) generated the event
1094 //
1095 wxWindow * const parent = GetParent();
1096
1097 // the wxObject* casts are required to avoid MinGW GCC 2.95.3 ICE
1098 const bool isFromParent = event.GetEventObject() == (wxObject*) parent;
1099 const bool isFromSelf = event.GetEventObject() == (wxObject*) this;
1100 const bool isForward = event.GetDirection();
1101
1102 if ( isFromSelf && !isForward )
1103 {
1104 // focus is currently on notebook tab and should leave
1105 // it backwards (Shift-TAB)
1106 event.SetCurrentFocus(this);
1107 parent->HandleWindowEvent(event);
1108 }
1109 else if ( isFromParent || isFromSelf )
1110 {
1111 // no, it doesn't come from child, case (b) or (c): forward to a
1112 // page but only if entering notebook page (i.e. direction is
1113 // backwards (Shift-TAB) comething from out-of-notebook, or
1114 // direction is forward (TAB) from ourselves),
1115 if ( m_selection != wxNOT_FOUND &&
1116 (!event.GetDirection() || isFromSelf) )
1117 {
1118 // so that the page knows that the event comes from it's parent
1119 // and is being propagated downwards
1120 event.SetEventObject(this);
1121
1122 wxWindow *page = m_pages[m_selection];
1123 if ( !page->HandleWindowEvent(event) )
1124 {
1125 page->SetFocus();
1126 }
1127 //else: page manages focus inside it itself
1128 }
1129 else // otherwise set the focus to the notebook itself
1130 {
1131 SetFocus();
1132 }
1133 }
1134 else
1135 {
1136 // it comes from our child, case (a), pass to the parent, but only
1137 // if the direction is forwards. Otherwise set the focus to the
1138 // notebook itself. The notebook is always the 'first' control of a
1139 // page.
1140 if ( !isForward )
1141 {
1142 SetFocus();
1143 }
1144 else if ( parent )
1145 {
1146 event.SetCurrentFocus(this);
1147 parent->HandleWindowEvent(event);
1148 }
1149 }
1150 }
1151 }
1152
1153 #if wxUSE_UXTHEME
1154
1155 bool wxNotebook::DoDrawBackground(WXHDC hDC, wxWindow *child)
1156 {
1157 wxUxThemeHandle theme(child ? child : this, L"TAB");
1158 if ( !theme )
1159 return false;
1160
1161 // get the notebook client rect (we're not interested in drawing tabs
1162 // themselves)
1163 wxRect r = GetPageSize();
1164 if ( r.IsEmpty() )
1165 return false;
1166
1167 RECT rc;
1168 wxCopyRectToRECT(r, rc);
1169
1170 // map rect to the coords of the window we're drawing in
1171 if ( child )
1172 ::MapWindowPoints(GetHwnd(), GetHwndOf(child), (POINT *)&rc, 2);
1173
1174 // we have the content area (page size), but we need to draw all of the
1175 // background for it to be aligned correctly
1176 wxUxThemeEngine::Get()->GetThemeBackgroundExtent
1177 (
1178 theme,
1179 (HDC) hDC,
1180 9 /* TABP_PANE */,
1181 0,
1182 &rc,
1183 &rc
1184 );
1185 wxUxThemeEngine::Get()->DrawThemeBackground
1186 (
1187 theme,
1188 (HDC) hDC,
1189 9 /* TABP_PANE */,
1190 0,
1191 &rc,
1192 NULL
1193 );
1194
1195 return true;
1196 }
1197
1198 WXHBRUSH wxNotebook::QueryBgBitmap()
1199 {
1200 wxRect r = GetPageSize();
1201 if ( r.IsEmpty() )
1202 return 0;
1203
1204 WindowHDC hDC(GetHwnd());
1205 MemoryHDC hDCMem(hDC);
1206 CompatibleBitmap hBmp(hDC, r.x + r.width, r.y + r.height);
1207
1208 SelectInHDC selectBmp(hDCMem, hBmp);
1209
1210 if ( !DoDrawBackground((WXHDC)(HDC)hDCMem) )
1211 return 0;
1212
1213 return (WXHBRUSH)::CreatePatternBrush(hBmp);
1214 }
1215
1216 void wxNotebook::UpdateBgBrush()
1217 {
1218 if ( m_hbrBackground )
1219 ::DeleteObject((HBRUSH)m_hbrBackground);
1220
1221 if ( !m_hasBgCol && wxUxThemeEngine::GetIfActive() )
1222 {
1223 m_hbrBackground = QueryBgBitmap();
1224 }
1225 else // no themes or we've got user-defined solid colour
1226 {
1227 m_hbrBackground = NULL;
1228 }
1229 }
1230
1231 bool wxNotebook::MSWPrintChild(WXHDC hDC, wxWindow *child)
1232 {
1233 // solid background colour overrides themed background drawing
1234 if ( !UseBgCol() && DoDrawBackground(hDC, child) )
1235 return true;
1236
1237 // If we're using a solid colour (for example if we've switched off
1238 // theming for this notebook), paint it
1239 if (UseBgCol())
1240 {
1241 wxRect r = GetPageSize();
1242 if ( r.IsEmpty() )
1243 return false;
1244
1245 RECT rc;
1246 wxCopyRectToRECT(r, rc);
1247
1248 // map rect to the coords of the window we're drawing in
1249 if ( child )
1250 ::MapWindowPoints(GetHwnd(), GetHwndOf(child), (POINT *)&rc, 2);
1251
1252 wxBrush brush(GetBackgroundColour());
1253 HBRUSH hbr = GetHbrushOf(brush);
1254
1255 ::FillRect((HDC) hDC, &rc, hbr);
1256
1257 return true;
1258 }
1259
1260 return wxNotebookBase::MSWPrintChild(hDC, child);
1261 }
1262
1263 #endif // wxUSE_UXTHEME
1264
1265 // Windows only: attempts to get colour for UX theme page background
1266 wxColour wxNotebook::GetThemeBackgroundColour() const
1267 {
1268 #if wxUSE_UXTHEME
1269 if (wxUxThemeEngine::Get())
1270 {
1271 wxUxThemeHandle hTheme((wxNotebook*) this, L"TAB");
1272 if (hTheme)
1273 {
1274 // This is total guesswork.
1275 // See PlatformSDK\Include\Tmschema.h for values.
1276 // JACS: can also use 9 (TABP_PANE)
1277 COLORREF themeColor;
1278 bool success = (S_OK == wxUxThemeEngine::Get()->GetThemeColor(
1279 hTheme,
1280 10 /* TABP_BODY */,
1281 1 /* NORMAL */,
1282 3821 /* FILLCOLORHINT */,
1283 &themeColor));
1284 if (!success)
1285 return GetBackgroundColour();
1286
1287 /*
1288 [DS] Workaround for WindowBlinds:
1289 Some themes return a near black theme color using FILLCOLORHINT,
1290 this makes notebook pages have an ugly black background and makes
1291 text (usually black) unreadable. Retry again with FILLCOLOR.
1292
1293 This workaround potentially breaks appearance of some themes,
1294 but in practice it already fixes some themes.
1295 */
1296 if (themeColor == 1)
1297 {
1298 wxUxThemeEngine::Get()->GetThemeColor(
1299 hTheme,
1300 10 /* TABP_BODY */,
1301 1 /* NORMAL */,
1302 3802 /* FILLCOLOR */,
1303 &themeColor);
1304 }
1305
1306 wxColour colour = wxRGBToColour(themeColor);
1307
1308 // Under Vista, the tab background colour is reported incorrectly.
1309 // So for the default theme at least, hard-code the colour to something
1310 // that will blend in.
1311
1312 static int s_AeroStatus = -1;
1313 if (s_AeroStatus == -1)
1314 {
1315 WCHAR szwThemeFile[1024];
1316 WCHAR szwThemeColor[256];
1317 if (S_OK == wxUxThemeEngine::Get()->GetCurrentThemeName(szwThemeFile, 1024, szwThemeColor, 256, NULL, 0))
1318 {
1319 wxString themeFile(szwThemeFile), themeColor(szwThemeColor);
1320 if (themeFile.Find(wxT("Aero")) != -1 && themeColor == wxT("NormalColor"))
1321 s_AeroStatus = 1;
1322 else
1323 s_AeroStatus = 0;
1324 }
1325 else
1326 s_AeroStatus = 0;
1327 }
1328
1329 if (s_AeroStatus == 1)
1330 colour = wxColour(255, 255, 255);
1331
1332 return colour;
1333 }
1334 }
1335 #endif // wxUSE_UXTHEME
1336
1337 return GetBackgroundColour();
1338 }
1339
1340 // ----------------------------------------------------------------------------
1341 // wxNotebook base class virtuals
1342 // ----------------------------------------------------------------------------
1343
1344 #if wxUSE_CONSTRAINTS
1345
1346 // override these 2 functions to do nothing: everything is done in OnSize
1347
1348 void wxNotebook::SetConstraintSizes(bool WXUNUSED(recurse))
1349 {
1350 // don't set the sizes of the pages - their correct size is not yet known
1351 wxControl::SetConstraintSizes(false);
1352 }
1353
1354 bool wxNotebook::DoPhase(int WXUNUSED(nPhase))
1355 {
1356 return true;
1357 }
1358
1359 #endif // wxUSE_CONSTRAINTS
1360
1361 // ----------------------------------------------------------------------------
1362 // wxNotebook Windows message handlers
1363 // ----------------------------------------------------------------------------
1364
1365 bool wxNotebook::MSWOnScroll(int orientation, WXWORD nSBCode,
1366 WXWORD pos, WXHWND control)
1367 {
1368 // don't generate EVT_SCROLLWIN events for the WM_SCROLLs coming from the
1369 // up-down control
1370 if ( control )
1371 return false;
1372
1373 return wxNotebookBase::MSWOnScroll(orientation, nSBCode, pos, control);
1374 }
1375
1376 bool wxNotebook::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM* result)
1377 {
1378 wxBookCtrlEvent event(wxEVT_NULL, m_windowId);
1379
1380 NMHDR* hdr = (NMHDR *)lParam;
1381 switch ( hdr->code ) {
1382 case TCN_SELCHANGE:
1383 event.SetEventType(wxEVT_NOTEBOOK_PAGE_CHANGED);
1384 break;
1385
1386 case TCN_SELCHANGING:
1387 event.SetEventType(wxEVT_NOTEBOOK_PAGE_CHANGING);
1388 break;
1389
1390 default:
1391 return wxControl::MSWOnNotify(idCtrl, lParam, result);
1392 }
1393
1394 event.SetSelection(TabCtrl_GetCurSel(GetHwnd()));
1395 event.SetOldSelection(m_selection);
1396 event.SetEventObject(this);
1397 event.SetInt(idCtrl);
1398
1399 // Change the selection before generating the event as its handler should
1400 // already see the new page selected.
1401 if ( hdr->code == TCN_SELCHANGE )
1402 UpdateSelection(event.GetSelection());
1403
1404 bool processed = HandleWindowEvent(event);
1405 *result = !event.IsAllowed();
1406 return processed;
1407 }
1408
1409 #endif // wxUSE_NOTEBOOK