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