]> git.saurik.com Git - wxWidgets.git/blob - src/msw/notebook.cpp
3f2390950ca0b731370110f07cf19c29acf3d690
[wxWidgets.git] / src / msw / notebook.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: 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 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
13 #pragma implementation "notebook.h"
14 #endif
15
16 // For compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
18
19 #ifdef __BORLANDC__
20 #pragma hdrstop
21 #endif
22
23 #if wxUSE_NOTEBOOK
24
25 // wxWidgets
26 #ifndef WX_PRECOMP
27 #include "wx/string.h"
28 #include "wx/dc.h"
29 #endif // WX_PRECOMP
30
31 #include "wx/log.h"
32 #include "wx/imaglist.h"
33 #include "wx/event.h"
34 #include "wx/control.h"
35 #include "wx/notebook.h"
36 #include "wx/app.h"
37 #include "wx/sysopt.h"
38
39 #include "wx/msw/private.h"
40
41 #include <windowsx.h>
42
43 #ifdef __GNUWIN32_OLD__
44 #include "wx/msw/gnuwin32/extra.h"
45 #endif
46
47 #if !(defined(__GNUWIN32_OLD__) && !defined(__CYGWIN10__))
48 #include <commctrl.h>
49 #endif
50
51 #include "wx/msw/winundef.h"
52
53 #if wxUSE_UXTHEME
54 #include "wx/msw/uxtheme.h"
55 #endif
56
57 // ----------------------------------------------------------------------------
58 // macros
59 // ----------------------------------------------------------------------------
60
61 // check that the page index is valid
62 #define IS_VALID_PAGE(nPage) ((nPage) < GetPageCount())
63
64 // hide the ugly cast
65 #define m_hwnd (HWND)GetHWND()
66
67 // ----------------------------------------------------------------------------
68 // constants
69 // ----------------------------------------------------------------------------
70
71 // This is a work-around for missing defines in gcc-2.95 headers
72 #ifndef TCS_RIGHT
73 #define TCS_RIGHT 0x0002
74 #endif
75
76 #ifndef TCS_VERTICAL
77 #define TCS_VERTICAL 0x0080
78 #endif
79
80 #ifndef TCS_BOTTOM
81 #define TCS_BOTTOM TCS_RIGHT
82 #endif
83
84 // ----------------------------------------------------------------------------
85 // event table
86 // ----------------------------------------------------------------------------
87
88 #include <wx/listimpl.cpp>
89
90 WX_DEFINE_LIST( wxNotebookPageInfoList ) ;
91
92 DEFINE_EVENT_TYPE(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED)
93 DEFINE_EVENT_TYPE(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING)
94
95 BEGIN_EVENT_TABLE(wxNotebook, wxControl)
96 EVT_NOTEBOOK_PAGE_CHANGED(-1, wxNotebook::OnSelChange)
97
98 EVT_SIZE(wxNotebook::OnSize)
99
100 EVT_NAVIGATION_KEY(wxNotebook::OnNavigationKey)
101 END_EVENT_TABLE()
102
103 #if wxUSE_EXTENDED_RTTI
104 WX_DEFINE_FLAGS( wxNotebookStyle )
105
106 wxBEGIN_FLAGS( wxNotebookStyle )
107 // new style border flags, we put them first to
108 // use them for streaming out
109 wxFLAGS_MEMBER(wxBORDER_SIMPLE)
110 wxFLAGS_MEMBER(wxBORDER_SUNKEN)
111 wxFLAGS_MEMBER(wxBORDER_DOUBLE)
112 wxFLAGS_MEMBER(wxBORDER_RAISED)
113 wxFLAGS_MEMBER(wxBORDER_STATIC)
114 wxFLAGS_MEMBER(wxBORDER_NONE)
115
116 // old style border flags
117 wxFLAGS_MEMBER(wxSIMPLE_BORDER)
118 wxFLAGS_MEMBER(wxSUNKEN_BORDER)
119 wxFLAGS_MEMBER(wxDOUBLE_BORDER)
120 wxFLAGS_MEMBER(wxRAISED_BORDER)
121 wxFLAGS_MEMBER(wxSTATIC_BORDER)
122 wxFLAGS_MEMBER(wxBORDER)
123
124 // standard window styles
125 wxFLAGS_MEMBER(wxTAB_TRAVERSAL)
126 wxFLAGS_MEMBER(wxCLIP_CHILDREN)
127 wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW)
128 wxFLAGS_MEMBER(wxWANTS_CHARS)
129 wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE)
130 wxFLAGS_MEMBER(wxALWAYS_SHOW_SB )
131 wxFLAGS_MEMBER(wxVSCROLL)
132 wxFLAGS_MEMBER(wxHSCROLL)
133
134 wxFLAGS_MEMBER(wxNB_FIXEDWIDTH)
135 wxFLAGS_MEMBER(wxNB_LEFT)
136 wxFLAGS_MEMBER(wxNB_RIGHT)
137 wxFLAGS_MEMBER(wxNB_BOTTOM)
138
139 wxEND_FLAGS( wxNotebookStyle )
140
141 IMPLEMENT_DYNAMIC_CLASS_XTI(wxNotebook, wxControl,"wx/notebook.h")
142 IMPLEMENT_DYNAMIC_CLASS_XTI(wxNotebookPageInfo, wxObject , "wx/notebook.h" )
143
144 wxCOLLECTION_TYPE_INFO( wxNotebookPageInfo * , wxNotebookPageInfoList ) ;
145
146 template<> void wxCollectionToVariantArray( wxNotebookPageInfoList const &theList, wxxVariantArray &value)
147 {
148 wxListCollectionToVariantArray<wxNotebookPageInfoList::compatibility_iterator>( theList , value ) ;
149 }
150
151 wxBEGIN_PROPERTIES_TABLE(wxNotebook)
152 wxEVENT_PROPERTY( PageChanging , wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING , wxNotebookEvent )
153 wxEVENT_PROPERTY( PageChanged , wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED , wxNotebookEvent )
154
155 wxPROPERTY_COLLECTION( PageInfos , wxNotebookPageInfoList , wxNotebookPageInfo* , AddPageInfo , GetPageInfos , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
156 wxPROPERTY_FLAGS( WindowStyle , wxNotebookStyle , long , SetWindowStyleFlag , GetWindowStyleFlag , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
157 wxEND_PROPERTIES_TABLE()
158
159 wxBEGIN_HANDLERS_TABLE(wxNotebook)
160 wxEND_HANDLERS_TABLE()
161
162 wxCONSTRUCTOR_5( wxNotebook , wxWindow* , Parent , wxWindowID , Id , wxPoint , Position , wxSize , Size , long , WindowStyle)
163
164
165 wxBEGIN_PROPERTIES_TABLE(wxNotebookPageInfo)
166 wxREADONLY_PROPERTY( Page , wxNotebookPage* , GetPage , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
167 wxREADONLY_PROPERTY( Text , wxString , GetText , wxString() , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
168 wxREADONLY_PROPERTY( Selected , bool , GetSelected , false, 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
169 wxREADONLY_PROPERTY( ImageId , int , GetImageId , -1 , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
170 wxEND_PROPERTIES_TABLE()
171
172 wxBEGIN_HANDLERS_TABLE(wxNotebookPageInfo)
173 wxEND_HANDLERS_TABLE()
174
175 wxCONSTRUCTOR_4( wxNotebookPageInfo , wxNotebookPage* , Page , wxString , Text , bool , Selected , int , ImageId )
176
177 #else
178 IMPLEMENT_DYNAMIC_CLASS(wxNotebook, wxControl)
179 IMPLEMENT_DYNAMIC_CLASS(wxNotebookPageInfo, wxObject )
180 #endif
181 IMPLEMENT_DYNAMIC_CLASS(wxNotebookEvent, wxNotifyEvent)
182
183 // ============================================================================
184 // implementation
185 // ============================================================================
186
187 // ----------------------------------------------------------------------------
188 // wxNotebook construction
189 // ----------------------------------------------------------------------------
190
191 const wxNotebookPageInfoList& wxNotebook::GetPageInfos() const
192 {
193 wxNotebookPageInfoList* list = const_cast< wxNotebookPageInfoList* >( &m_pageInfos ) ;
194 WX_CLEAR_LIST( wxNotebookPageInfoList , *list ) ;
195 for( size_t i = 0 ; i < GetPageCount() ; ++i )
196 {
197 wxNotebookPageInfo *info = new wxNotebookPageInfo() ;
198 info->Create( const_cast<wxNotebook*>(this)->GetPage(i) , GetPageText(i) , GetSelection() == int(i) , GetPageImage(i) ) ;
199 list->Append( info ) ;
200 }
201 return m_pageInfos ;
202 }
203
204 // common part of all ctors
205 void wxNotebook::Init()
206 {
207 m_imageList = NULL;
208 m_nSelection = -1;
209
210 #if wxUSE_UXTHEME
211 m_hbrBackground = NULL;
212 #endif // wxUSE_UXTHEME
213 }
214
215 // default for dynamic class
216 wxNotebook::wxNotebook()
217 {
218 Init();
219 }
220
221 // the same arguments as for wxControl
222 wxNotebook::wxNotebook(wxWindow *parent,
223 wxWindowID id,
224 const wxPoint& pos,
225 const wxSize& size,
226 long style,
227 const wxString& name)
228 {
229 Init();
230
231 Create(parent, id, pos, size, style, name);
232 }
233
234 // Create() function
235 bool wxNotebook::Create(wxWindow *parent,
236 wxWindowID id,
237 const wxPoint& pos,
238 const wxSize& size,
239 long style,
240 const wxString& name)
241 {
242 // comctl32.dll 6.0 doesn't support non-top tabs with visual styles (the
243 // control is simply not rendered correctly), so disable them in this case
244 const int verComCtl32 = wxApp::GetComCtl32Version();
245 if ( verComCtl32 == 600 )
246 {
247 // check if we use themes at all -- if we don't, we're still ok
248 #if wxUSE_UXTHEME
249 if ( wxUxThemeEngine::GetIfActive() )
250 #endif
251 {
252 style &= ~(wxNB_BOTTOM | wxNB_LEFT | wxNB_RIGHT);
253 }
254 }
255
256 LPCTSTR className = WC_TABCONTROL;
257
258 // SysTabCtl32 class has natively CS_HREDRAW and CS_VREDRAW enabled and it
259 // causes horrible flicker when resizing notebook, so get rid of it by
260 // using a class without these styles (but otherwise identical to it)
261 if ( !HasFlag(wxFULL_REPAINT_ON_RESIZE) )
262 {
263 static ClassRegistrar s_clsNotebook;
264 if ( !s_clsNotebook.IsInitialized() )
265 {
266 // get a copy of standard class and modify it
267 WNDCLASS wc;
268
269 if ( ::GetClassInfo(::GetModuleHandle(NULL), WC_TABCONTROL, &wc) )
270 {
271 wc.lpszClassName = wxT("_wx_SysTabCtl32");
272 wc.style &= ~(CS_HREDRAW | CS_VREDRAW);
273
274 s_clsNotebook.Register(wc);
275 }
276 else
277 {
278 wxLogLastError(_T("GetClassInfoEx(SysTabCtl32)"));
279 }
280 }
281
282 // use our custom class if available but fall back to the standard
283 // notebook if we failed to register it
284 if ( s_clsNotebook.IsRegistered() )
285 {
286 // it's ok to use c_str() here as the static s_clsNotebook object
287 // has sufficiently long lifetime
288 className = s_clsNotebook.GetName().c_str();
289 }
290 }
291
292 if ( !CreateControl(parent, id, pos, size, style | wxTAB_TRAVERSAL,
293 wxDefaultValidator, name) )
294 return false;
295
296 if ( !MSWCreateControl(className, wxEmptyString, pos, size) )
297 return false;
298
299 return true;
300 }
301
302 WXDWORD wxNotebook::MSWGetStyle(long style, WXDWORD *exstyle) const
303 {
304 WXDWORD tabStyle = wxControl::MSWGetStyle(style, exstyle);
305
306 tabStyle |= WS_TABSTOP | TCS_TABS;
307
308 if ( style & wxNB_MULTILINE )
309 tabStyle |= TCS_MULTILINE;
310 if ( style & wxNB_FIXEDWIDTH )
311 tabStyle |= TCS_FIXEDWIDTH;
312
313 if ( style & wxNB_BOTTOM )
314 tabStyle |= TCS_RIGHT;
315 else if ( style & wxNB_LEFT )
316 tabStyle |= TCS_VERTICAL;
317 else if ( style & wxNB_RIGHT )
318 tabStyle |= TCS_VERTICAL | TCS_RIGHT;
319
320 // ex style
321 if ( exstyle )
322 {
323 // note that we never want to have the default WS_EX_CLIENTEDGE style
324 // as it looks too ugly for the notebooks
325 *exstyle = 0;
326 }
327
328 return tabStyle;
329 }
330
331 wxNotebook::~wxNotebook()
332 {
333 #if wxUSE_UXTHEME
334 if ( m_hbrBackground )
335 ::DeleteObject((HBRUSH)m_hbrBackground);
336 #endif // wxUSE_UXTHEME
337 }
338
339 // ----------------------------------------------------------------------------
340 // wxNotebook accessors
341 // ----------------------------------------------------------------------------
342
343 size_t wxNotebook::GetPageCount() const
344 {
345 // consistency check
346 wxASSERT( (int)m_pages.Count() == TabCtrl_GetItemCount(m_hwnd) );
347
348 return m_pages.Count();
349 }
350
351 int wxNotebook::GetRowCount() const
352 {
353 return TabCtrl_GetRowCount(m_hwnd);
354 }
355
356 int wxNotebook::SetSelection(size_t nPage)
357 {
358 wxCHECK_MSG( IS_VALID_PAGE(nPage), wxNOT_FOUND, wxT("notebook page out of range") );
359
360 if ( int(nPage) != m_nSelection )
361 {
362 wxNotebookEvent event(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING, m_windowId);
363 event.SetSelection(nPage);
364 event.SetOldSelection(m_nSelection);
365 event.SetEventObject(this);
366 if ( !GetEventHandler()->ProcessEvent(event) || event.IsAllowed() )
367 {
368 // program allows the page change
369 event.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED);
370 (void)GetEventHandler()->ProcessEvent(event);
371
372 TabCtrl_SetCurSel(m_hwnd, nPage);
373 }
374 }
375
376 return m_nSelection;
377 }
378
379 bool wxNotebook::SetPageText(size_t nPage, const wxString& strText)
380 {
381 wxCHECK_MSG( IS_VALID_PAGE(nPage), false, wxT("notebook page out of range") );
382
383 TC_ITEM tcItem;
384 tcItem.mask = TCIF_TEXT;
385 tcItem.pszText = (wxChar *)strText.c_str();
386
387 return TabCtrl_SetItem(m_hwnd, nPage, &tcItem) != 0;
388 }
389
390 wxString wxNotebook::GetPageText(size_t nPage) const
391 {
392 wxCHECK_MSG( IS_VALID_PAGE(nPage), wxEmptyString, wxT("notebook page out of range") );
393
394 wxChar buf[256];
395 TC_ITEM tcItem;
396 tcItem.mask = TCIF_TEXT;
397 tcItem.pszText = buf;
398 tcItem.cchTextMax = WXSIZEOF(buf);
399
400 wxString str;
401 if ( TabCtrl_GetItem(m_hwnd, nPage, &tcItem) )
402 str = tcItem.pszText;
403
404 return str;
405 }
406
407 int wxNotebook::GetPageImage(size_t nPage) const
408 {
409 wxCHECK_MSG( IS_VALID_PAGE(nPage), -1, wxT("notebook page out of range") );
410
411 TC_ITEM tcItem;
412 tcItem.mask = TCIF_IMAGE;
413
414 return TabCtrl_GetItem(m_hwnd, nPage, &tcItem) ? tcItem.iImage : -1;
415 }
416
417 bool wxNotebook::SetPageImage(size_t nPage, int nImage)
418 {
419 wxCHECK_MSG( IS_VALID_PAGE(nPage), false, wxT("notebook page out of range") );
420
421 TC_ITEM tcItem;
422 tcItem.mask = TCIF_IMAGE;
423 tcItem.iImage = nImage;
424
425 return TabCtrl_SetItem(m_hwnd, nPage, &tcItem) != 0;
426 }
427
428 void wxNotebook::SetImageList(wxImageList* imageList)
429 {
430 wxNotebookBase::SetImageList(imageList);
431
432 if ( imageList )
433 {
434 TabCtrl_SetImageList(m_hwnd, (HIMAGELIST)imageList->GetHIMAGELIST());
435 }
436 }
437
438 // ----------------------------------------------------------------------------
439 // wxNotebook size settings
440 // ----------------------------------------------------------------------------
441
442 void wxNotebook::SetPageSize(const wxSize& size)
443 {
444 // transform the page size into the notebook size
445 RECT rc;
446 rc.left =
447 rc.top = 0;
448 rc.right = size.x;
449 rc.bottom = size.y;
450
451 TabCtrl_AdjustRect(GetHwnd(), true, &rc);
452
453 // and now set it
454 SetSize(rc.right - rc.left, rc.bottom - rc.top);
455 }
456
457 void wxNotebook::SetPadding(const wxSize& padding)
458 {
459 TabCtrl_SetPadding(GetHwnd(), padding.x, padding.y);
460 }
461
462 // Windows-only at present. Also, you must use the wxNB_FIXEDWIDTH
463 // style.
464 void wxNotebook::SetTabSize(const wxSize& sz)
465 {
466 ::SendMessage(GetHwnd(), TCM_SETITEMSIZE, 0, MAKELPARAM(sz.x, sz.y));
467 }
468
469 wxSize wxNotebook::CalcSizeFromPage(const wxSize& sizePage) const
470 {
471 wxSize sizeTotal = sizePage;
472
473 // We need to make getting tab size part of the wxWidgets API.
474 wxSize tabSize(0, 0);
475 if (GetPageCount() > 0)
476 {
477 RECT rect;
478 TabCtrl_GetItemRect((HWND) GetHWND(), 0, & rect);
479 tabSize.x = rect.right - rect.left;
480 tabSize.y = rect.bottom - rect.top;
481 }
482 if ( HasFlag(wxNB_LEFT) || HasFlag(wxNB_RIGHT) )
483 {
484 sizeTotal.x += tabSize.x + 7;
485 sizeTotal.y += 7;
486 }
487 else
488 {
489 sizeTotal.x += 7;
490 sizeTotal.y += tabSize.y + 7;
491 }
492
493 return sizeTotal;
494 }
495
496 void wxNotebook::AdjustPageSize(wxNotebookPage *page)
497 {
498 wxCHECK_RET( page, _T("NULL page in wxNotebook::AdjustPageSize") );
499
500 RECT rc;
501 rc.left =
502 rc.top = 0;
503
504 // get the page size from the notebook size
505 GetSize((int *)&rc.right, (int *)&rc.bottom);
506
507 // This check is to work around a bug in TabCtrl_AdjustRect which will
508 // cause a crash on win2k, or on XP with themes disabled, if the
509 // wxNB_MULTILINE style is used and the rectangle is very small, (such as
510 // when the notebook is first created.) The value of 20 is just
511 // arbitrarily chosen, if there is a better way to determine this value
512 // then please do so. --RD
513 if (rc.right > 20 && rc.bottom > 20)
514 {
515 TabCtrl_AdjustRect(m_hwnd, false, &rc);
516 page->SetSize(rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top);
517 }
518 }
519
520 // ----------------------------------------------------------------------------
521 // wxNotebook operations
522 // ----------------------------------------------------------------------------
523
524 // remove one page from the notebook, without deleting
525 wxNotebookPage *wxNotebook::DoRemovePage(size_t nPage)
526 {
527 wxNotebookPage *pageRemoved = wxNotebookBase::DoRemovePage(nPage);
528 if ( !pageRemoved )
529 return NULL;
530
531 TabCtrl_DeleteItem(m_hwnd, nPage);
532
533 if ( m_pages.IsEmpty() )
534 {
535 // no selection any more, the notebook becamse empty
536 m_nSelection = -1;
537 }
538 else // notebook still not empty
539 {
540 int selNew = TabCtrl_GetCurSel(m_hwnd);
541 if (selNew != -1)
542 {
543 // No selection change, just refresh the current selection.
544 // Because it could be that the slection index changed
545 // we need to update it.
546 // Note: this does not mean the selection it self changed.
547 m_nSelection = selNew;
548 m_pages[m_nSelection]->Refresh();
549 }
550 else if (int(nPage) == m_nSelection)
551 {
552 // The selection was deleted.
553
554 // Determine new selection.
555 if (m_nSelection == int(GetPageCount()))
556 selNew = m_nSelection - 1;
557 else
558 selNew = m_nSelection;
559
560 // m_nSelection must be always valid so reset it before calling
561 // SetSelection()
562 m_nSelection = -1;
563 SetSelection(selNew);
564 }
565 else
566 {
567 wxFAIL; // Windows did not behave ok.
568 }
569 }
570
571 return pageRemoved;
572 }
573
574 // remove all pages
575 bool wxNotebook::DeleteAllPages()
576 {
577 size_t nPageCount = GetPageCount();
578 size_t nPage;
579 for ( nPage = 0; nPage < nPageCount; nPage++ )
580 delete m_pages[nPage];
581
582 m_pages.Clear();
583
584 TabCtrl_DeleteAllItems(m_hwnd);
585
586 m_nSelection = -1;
587
588 InvalidateBestSize();
589 return true;
590 }
591
592 // same as AddPage() but does it at given position
593 bool wxNotebook::InsertPage(size_t nPage,
594 wxNotebookPage *pPage,
595 const wxString& strText,
596 bool bSelect,
597 int imageId)
598 {
599 wxCHECK_MSG( pPage != NULL, false, _T("NULL page in wxNotebook::InsertPage") );
600 wxCHECK_MSG( IS_VALID_PAGE(nPage) || nPage == GetPageCount(), false,
601 _T("invalid index in wxNotebook::InsertPage") );
602
603 wxASSERT_MSG( pPage->GetParent() == this,
604 _T("notebook pages must have notebook as parent") );
605
606 // add a new tab to the control
607 // ----------------------------
608
609 // init all fields to 0
610 TC_ITEM tcItem;
611 wxZeroMemory(tcItem);
612
613 // set the image, if any
614 if ( imageId != -1 )
615 {
616 tcItem.mask |= TCIF_IMAGE;
617 tcItem.iImage = imageId;
618 }
619
620 // and the text
621 if ( !strText.IsEmpty() )
622 {
623 tcItem.mask |= TCIF_TEXT;
624 tcItem.pszText = (wxChar *)strText.c_str(); // const_cast
625 }
626
627 // hide the page: unless it is selected, it shouldn't be shown (and if it
628 // is selected it will be shown later)
629 HWND hwnd = GetWinHwnd(pPage);
630 SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~WS_VISIBLE);
631
632 // this updates internal flag too -- otherwise it would get out of sync
633 // with the real state
634 pPage->Show(false);
635
636
637 // fit the notebook page to the tab control's display area: this should be
638 // done before adding it to the notebook or TabCtrl_InsertItem() will
639 // change the notebooks size itself!
640 AdjustPageSize(pPage);
641
642 // finally do insert it
643 if ( TabCtrl_InsertItem(m_hwnd, nPage, &tcItem) == -1 )
644 {
645 wxLogError(wxT("Can't create the notebook page '%s'."), strText.c_str());
646
647 return false;
648 }
649
650 // succeeded: save the pointer to the page
651 m_pages.Insert(pPage, nPage);
652
653 // we may need to adjust the size again if the notebook size changed:
654 // normally this only happens for the first page we add (the tabs which
655 // hadn't been there before are now shown) but for a multiline notebook it
656 // can happen for any page at all as a new row could have been started
657 if ( m_pages.GetCount() == 1 || HasFlag(wxNB_MULTILINE) )
658 {
659 AdjustPageSize(pPage);
660 }
661
662 // now deal with the selection
663 // ---------------------------
664
665 // if the inserted page is before the selected one, we must update the
666 // index of the selected page
667 if ( int(nPage) <= m_nSelection )
668 {
669 // one extra page added
670 m_nSelection++;
671 }
672
673 // some page should be selected: either this one or the first one if there
674 // is still no selection
675 int selNew = -1;
676 if ( bSelect )
677 selNew = nPage;
678 else if ( m_nSelection == -1 )
679 selNew = 0;
680
681 if ( selNew != -1 )
682 SetSelection(selNew);
683
684 InvalidateBestSize();
685
686 if (HasFlag(wxNB_NOPAGETHEME) || (wxSystemOptions::HasOption(wxT("msw.notebook.themed-background")) &&
687 wxSystemOptions::GetOptionInt(wxT("msw.notebook.themed-background")) == 0))
688 {
689 wxColour col = GetThemeBackgroundColour();
690 if (col.Ok())
691 {
692 pPage->SetBackgroundColour(col);
693 }
694 }
695
696 return true;
697 }
698
699 int wxNotebook::HitTest(const wxPoint& pt, long *flags) const
700 {
701 TC_HITTESTINFO hitTestInfo;
702 hitTestInfo.pt.x = pt.x;
703 hitTestInfo.pt.y = pt.y;
704 int item = TabCtrl_HitTest(GetHwnd(), &hitTestInfo);
705
706 if ( flags )
707 {
708 *flags = 0;
709
710 if ((hitTestInfo.flags & TCHT_NOWHERE) == TCHT_NOWHERE)
711 *flags |= wxNB_HITTEST_NOWHERE;
712 if ((hitTestInfo.flags & TCHT_ONITEM) == TCHT_ONITEM)
713 *flags |= wxNB_HITTEST_ONITEM;
714 if ((hitTestInfo.flags & TCHT_ONITEMICON) == TCHT_ONITEMICON)
715 *flags |= wxNB_HITTEST_ONICON;
716 if ((hitTestInfo.flags & TCHT_ONITEMLABEL) == TCHT_ONITEMLABEL)
717 *flags |= wxNB_HITTEST_ONLABEL;
718 }
719
720 return item;
721 }
722
723
724 // ----------------------------------------------------------------------------
725 // wxNotebook callbacks
726 // ----------------------------------------------------------------------------
727
728 void wxNotebook::OnSize(wxSizeEvent& event)
729 {
730 // update the background brush
731 #if wxUSE_UXTHEME
732 UpdateBgBrush();
733 #endif // wxUSE_UXTHEME
734
735 // fit all the notebook pages to the tab control's display area
736
737 RECT rc;
738 rc.left = rc.top = 0;
739 GetSize((int *)&rc.right, (int *)&rc.bottom);
740
741 // save the total size, we'll use it below
742 int widthNbook = rc.right - rc.left,
743 heightNbook = rc.bottom - rc.top;
744
745 // there seems to be a bug in the implementation of TabCtrl_AdjustRect(): it
746 // returns completely false values for multiline tab controls after the tabs
747 // are added but before getting the first WM_SIZE (off by ~50 pixels, see
748 //
749 // http://sf.net/tracker/index.php?func=detail&aid=645323&group_id=9863&atid=109863
750 //
751 // and the only work around I could find was this ugly hack... without it
752 // simply toggling the "multiline" checkbox in the notebook sample resulted
753 // in a noticeable page displacement
754 if ( HasFlag(wxNB_MULTILINE) )
755 {
756 // avoid an infinite recursion: we get another notification too!
757 static bool s_isInOnSize = false;
758
759 if ( !s_isInOnSize )
760 {
761 s_isInOnSize = true;
762 SendMessage(GetHwnd(), WM_SIZE, SIZE_RESTORED,
763 MAKELPARAM(rc.right, rc.bottom));
764 s_isInOnSize = false;
765 }
766 }
767
768 TabCtrl_AdjustRect(m_hwnd, false, &rc);
769
770 int width = rc.right - rc.left,
771 height = rc.bottom - rc.top;
772 size_t nCount = m_pages.Count();
773 for ( size_t nPage = 0; nPage < nCount; nPage++ ) {
774 wxNotebookPage *pPage = m_pages[nPage];
775 pPage->SetSize(rc.left, rc.top, width, height);
776 }
777
778
779 // unless we had already repainted everything, we now need to refresh
780 if ( !HasFlag(wxFULL_REPAINT_ON_RESIZE) )
781 {
782 // invalidate areas not covered by pages
783 RefreshRect(wxRect(0, 0, widthNbook, rc.top), false);
784 RefreshRect(wxRect(0, rc.top, rc.left, height), false);
785 RefreshRect(wxRect(0, rc.bottom, widthNbook, heightNbook - rc.bottom),
786 false);
787 RefreshRect(wxRect(rc.right, rc.top, widthNbook - rc.bottom, height),
788 false);
789 }
790
791 event.Skip();
792 }
793
794 void wxNotebook::OnSelChange(wxNotebookEvent& event)
795 {
796 // is it our tab control?
797 if ( event.GetEventObject() == this )
798 {
799 int sel = event.GetOldSelection();
800 if ( sel != -1 )
801 m_pages[sel]->Show(false);
802
803 sel = event.GetSelection();
804 if ( sel != -1 )
805 {
806 wxNotebookPage *pPage = m_pages[sel];
807 pPage->Show(true);
808 pPage->SetFocus();
809
810 // If the newly focused window is not a child of the new page,
811 // SetFocus was not successful and the notebook itself should be
812 // focused
813 wxWindow *currentFocus = FindFocus();
814 wxWindow *startFocus = currentFocus;
815 while ( currentFocus && currentFocus != pPage && currentFocus != this )
816 currentFocus = currentFocus->GetParent();
817
818 if ( startFocus == pPage || currentFocus != pPage )
819 SetFocus();
820
821 }
822 else // no pages in the notebook, give the focus to itself
823 {
824 SetFocus();
825 }
826
827 m_nSelection = sel;
828 }
829
830 // we want to give others a chance to process this message as well
831 event.Skip();
832 }
833
834 bool wxNotebook::MSWTranslateMessage(WXMSG *wxmsg)
835 {
836 const MSG * const msg = (MSG *)wxmsg;
837
838 // intercept TAB, CTRL+TAB and CTRL+SHIFT+TAB for processing by wxNotebook.
839 // TAB will be passed to the currently selected page, CTRL+TAB and
840 // CTRL+SHIFT+TAB will be processed by the notebook itself. do not
841 // intercept SHIFT+TAB. This goes to the parent of the notebook which will
842 // process it.
843 if ( msg->message == WM_KEYDOWN && msg->wParam == VK_TAB &&
844 msg->hwnd == m_hwnd &&
845 (wxIsCtrlDown() || !wxIsShiftDown()) )
846 {
847 return MSWProcessMessage(wxmsg);
848 }
849
850 return false;
851 }
852
853 void wxNotebook::OnNavigationKey(wxNavigationKeyEvent& event)
854 {
855 if ( event.IsWindowChange() ) {
856 // change pages
857 AdvanceSelection(event.GetDirection());
858 }
859 else {
860 // we get this event in 3 cases
861 //
862 // a) one of our pages might have generated it because the user TABbed
863 // out from it in which case we should propagate the event upwards and
864 // our parent will take care of setting the focus to prev/next sibling
865 //
866 // or
867 //
868 // b) the parent panel wants to give the focus to us so that we
869 // forward it to our selected page. We can't deal with this in
870 // OnSetFocus() because we don't know which direction the focus came
871 // from in this case and so can't choose between setting the focus to
872 // first or last panel child
873 //
874 // or
875 //
876 // c) we ourselves (see MSWTranslateMessage) generated the event
877 //
878 wxWindow * const parent = GetParent();
879
880 const bool isFromParent = event.GetEventObject() == parent;
881 const bool isFromSelf = event.GetEventObject() == this;
882
883 if ( isFromParent || isFromSelf )
884 {
885 // no, it doesn't come from child, case (b) or (c): forward to a
886 // page but only if direction is backwards (TAB) or from ourselves,
887 if ( m_nSelection != -1 &&
888 (!event.GetDirection() || isFromSelf) )
889 {
890 // so that the page knows that the event comes from it's parent
891 // and is being propagated downwards
892 event.SetEventObject(this);
893
894 wxWindow *page = m_pages[m_nSelection];
895 if ( !page->GetEventHandler()->ProcessEvent(event) )
896 {
897 page->SetFocus();
898 }
899 //else: page manages focus inside it itself
900 }
901 else // otherwise set the focus to the notebook itself
902 {
903 SetFocus();
904 }
905 }
906 else
907 {
908 // it comes from our child, case (a), pass to the parent, but only
909 // if the direction is forwards. Otherwise set the focus to the
910 // notebook itself. The notebook is always the 'first' control of a
911 // page.
912 if ( !event.GetDirection() )
913 {
914 SetFocus();
915 }
916 else if ( parent )
917 {
918 event.SetCurrentFocus(this);
919 parent->GetEventHandler()->ProcessEvent(event);
920 }
921 }
922 }
923 }
924
925 #if wxUSE_UXTHEME
926
927 WXHANDLE wxNotebook::QueryBgBitmap(wxWindow *win)
928 {
929 RECT rc;
930 GetWindowRect(GetHwnd(), &rc);
931
932 WindowHDC hDC(GetHwnd());
933 MemoryHDC hDCMem(hDC);
934 CompatibleBitmap hBmp(hDC, rc.right - rc.left, rc.bottom - rc.top);
935
936 SelectInHDC selectBmp(hDCMem, hBmp);
937
938 ::SendMessage(GetHwnd(), WM_PRINTCLIENT,
939 (WPARAM)(HDC)hDCMem,
940 PRF_ERASEBKGND | PRF_CLIENT | PRF_NONCLIENT);
941
942 if ( win )
943 {
944 RECT rc2;
945 ::GetWindowRect(GetHwndOf(win), &rc2);
946
947 COLORREF c = ::GetPixel(hDCMem, rc2.left - rc.left, rc2.top - rc.top);
948
949 return (WXHANDLE)c;
950 }
951 //else: we are asked to create the brush
952
953 return (WXHANDLE)::CreatePatternBrush(hBmp);
954 }
955
956 void wxNotebook::UpdateBgBrush()
957 {
958 if ( m_hbrBackground )
959 ::DeleteObject((HBRUSH)m_hbrBackground);
960
961 if ( !m_hasBgCol && wxUxThemeEngine::GetIfActive() )
962 {
963 m_hbrBackground = (WXHBRUSH)QueryBgBitmap();
964 }
965 else // no themes
966 {
967 m_hbrBackground = NULL;
968 }
969 }
970
971 WXHBRUSH wxNotebook::MSWGetBgBrushForChild(WXHDC hDC, wxWindow *win)
972 {
973 if ( m_hbrBackground )
974 {
975 // before drawing with the background brush, we need to position it
976 // correctly
977 RECT rc;
978 ::GetWindowRect(GetHwndOf(win), &rc);
979
980 ::MapWindowPoints(NULL, GetHwnd(), (POINT *)&rc, 1);
981
982 if ( !::SetBrushOrgEx((HDC)hDC, -rc.left, -rc.top, NULL) )
983 {
984 wxLogLastError(_T("SetBrushOrgEx(notebook bg brush)"));
985 }
986
987 return m_hbrBackground;
988 }
989
990 return wxNotebookBase::MSWGetBgBrushForChild(hDC, win);
991 }
992
993 wxColour wxNotebook::MSWGetBgColourForChild(wxWindow *win)
994 {
995 if ( m_hasBgCol )
996 return GetBackgroundColour();
997
998 if ( !wxUxThemeEngine::GetIfActive() )
999 return wxNullColour;
1000
1001 COLORREF c = (COLORREF)QueryBgBitmap(win);
1002
1003 return c == CLR_INVALID ? wxNullColour : wxRGBToColour(c);
1004 }
1005
1006 #endif // wxUSE_UXTHEME
1007
1008 // Windows only: attempts to get colour for UX theme page background
1009 wxColour wxNotebook::GetThemeBackgroundColour() const
1010 {
1011 #if wxUSE_UXTHEME
1012 if (wxUxThemeEngine::Get())
1013 {
1014 wxUxThemeHandle hTheme((wxNotebook*) this, L"TAB");
1015 if (hTheme)
1016 {
1017 // This is total guesswork.
1018 // See PlatformSDK\Include\Tmschema.h for values
1019 COLORREF themeColor;
1020 wxUxThemeEngine::Get()->GetThemeColor(
1021 hTheme,
1022 10 /* TABP_BODY */,
1023 1 /* NORMAL */,
1024 3821 /* FILLCOLORHINT */,
1025 &themeColor);
1026
1027 /*
1028 [DS] Workaround for WindowBlinds:
1029 Some themes return a near black theme color using FILLCOLORHINT,
1030 this makes notebook pages have an ugly black background and makes
1031 text (usually black) unreadable. Retry again with FILLCOLOR.
1032
1033 This workaround potentially breaks appearance of some themes,
1034 but in practice it already fixes some themes.
1035 */
1036 if (themeColor == 1)
1037 {
1038 wxUxThemeEngine::Get()->GetThemeColor(
1039 hTheme,
1040 10 /* TABP_BODY */,
1041 1 /* NORMAL */,
1042 3802 /* FILLCOLOR */,
1043 &themeColor);
1044 }
1045
1046 wxColour colour(GetRValue(themeColor), GetGValue(themeColor), GetBValue(themeColor));
1047 return colour;
1048 }
1049 }
1050 #endif // wxUSE_UXTHEME
1051
1052 return GetBackgroundColour();
1053 }
1054
1055 // ----------------------------------------------------------------------------
1056 // wxNotebook base class virtuals
1057 // ----------------------------------------------------------------------------
1058
1059 #if wxUSE_CONSTRAINTS
1060
1061 // override these 2 functions to do nothing: everything is done in OnSize
1062
1063 void wxNotebook::SetConstraintSizes(bool WXUNUSED(recurse))
1064 {
1065 // don't set the sizes of the pages - their correct size is not yet known
1066 wxControl::SetConstraintSizes(false);
1067 }
1068
1069 bool wxNotebook::DoPhase(int WXUNUSED(nPhase))
1070 {
1071 return true;
1072 }
1073
1074 #endif // wxUSE_CONSTRAINTS
1075
1076 // ----------------------------------------------------------------------------
1077 // wxNotebook Windows message handlers
1078 // ----------------------------------------------------------------------------
1079
1080 bool wxNotebook::MSWOnScroll(int orientation, WXWORD nSBCode,
1081 WXWORD pos, WXHWND control)
1082 {
1083 // don't generate EVT_SCROLLWIN events for the WM_SCROLLs coming from the
1084 // up-down control
1085 if ( control )
1086 return false;
1087
1088 return wxNotebookBase::MSWOnScroll(orientation, nSBCode, pos, control);
1089 }
1090
1091 bool wxNotebook::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM* result)
1092 {
1093 wxNotebookEvent event(wxEVT_NULL, m_windowId);
1094
1095 NMHDR* hdr = (NMHDR *)lParam;
1096 switch ( hdr->code ) {
1097 case TCN_SELCHANGE:
1098 event.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED);
1099 break;
1100
1101 case TCN_SELCHANGING:
1102 event.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING);
1103 break;
1104
1105 default:
1106 return wxControl::MSWOnNotify(idCtrl, lParam, result);
1107 }
1108
1109 event.SetSelection(TabCtrl_GetCurSel(m_hwnd));
1110 event.SetOldSelection(m_nSelection);
1111 event.SetEventObject(this);
1112 event.SetInt(idCtrl);
1113
1114 bool processed = GetEventHandler()->ProcessEvent(event);
1115 *result = !event.IsAllowed();
1116 return processed;
1117 }
1118
1119 #endif // wxUSE_NOTEBOOK