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