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