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