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