]> git.saurik.com Git - wxWidgets.git/blob - src/msw/notebook.cpp
Correction to previous fix
[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 LPCTSTR className = WC_TABCONTROL;
256
257 // SysTabCtl32 class has natively CS_HREDRAW and CS_VREDRAW enabled and it
258 // causes horrible flicker when resizing notebook, so get rid of it by
259 // using a class without these styles (but otherwise identical to it)
260 if ( !HasFlag(wxFULL_REPAINT_ON_RESIZE) )
261 {
262 static ClassRegistrar s_clsNotebook;
263 if ( !s_clsNotebook.IsInitialized() )
264 {
265 // get a copy of standard class and modify it
266 WNDCLASS wc;
267
268 if ( ::GetClassInfo(::GetModuleHandle(NULL), WC_TABCONTROL, &wc) )
269 {
270 wc.lpszClassName = wxT("_wx_SysTabCtl32");
271 wc.style &= ~(CS_HREDRAW | CS_VREDRAW);
272
273 s_clsNotebook.Register(wc);
274 }
275 else
276 {
277 wxLogLastError(_T("GetClassInfoEx(SysTabCtl32)"));
278 }
279 }
280
281 // use our custom class if available but fall back to the standard
282 // notebook if we failed to register it
283 if ( s_clsNotebook.IsRegistered() )
284 {
285 // it's ok to use c_str() here as the static s_clsNotebook object
286 // has sufficiently long lifetime
287 className = s_clsNotebook.GetName().c_str();
288 }
289 }
290
291 if ( !CreateControl(parent, id, pos, size, style | wxTAB_TRAVERSAL,
292 wxDefaultValidator, name) )
293 return false;
294
295 if ( !MSWCreateControl(className, wxEmptyString, pos, size) )
296 return false;
297
298 return true;
299 }
300
301 WXDWORD wxNotebook::MSWGetStyle(long style, WXDWORD *exstyle) const
302 {
303 WXDWORD tabStyle = wxControl::MSWGetStyle(style, exstyle);
304
305 tabStyle |= WS_TABSTOP | TCS_TABS;
306
307 if ( style & wxNB_MULTILINE )
308 tabStyle |= TCS_MULTILINE;
309 if ( style & wxNB_FIXEDWIDTH )
310 tabStyle |= TCS_FIXEDWIDTH;
311
312 if ( style & wxNB_BOTTOM )
313 tabStyle |= TCS_RIGHT;
314 else if ( style & wxNB_LEFT )
315 tabStyle |= TCS_VERTICAL;
316 else if ( style & wxNB_RIGHT )
317 tabStyle |= TCS_VERTICAL | TCS_RIGHT;
318
319 // ex style
320 if ( exstyle )
321 {
322 // note that we never want to have the default WS_EX_CLIENTEDGE style
323 // as it looks too ugly for the notebooks
324 *exstyle = 0;
325 }
326
327 return tabStyle;
328 }
329
330 wxNotebook::~wxNotebook()
331 {
332 #if wxUSE_UXTHEME
333 if ( m_hbrBackground )
334 ::DeleteObject((HBRUSH)m_hbrBackground);
335 #endif // wxUSE_UXTHEME
336 }
337
338 // ----------------------------------------------------------------------------
339 // wxNotebook accessors
340 // ----------------------------------------------------------------------------
341
342 size_t wxNotebook::GetPageCount() const
343 {
344 // consistency check
345 wxASSERT( (int)m_pages.Count() == TabCtrl_GetItemCount(m_hwnd) );
346
347 return m_pages.Count();
348 }
349
350 int wxNotebook::GetRowCount() const
351 {
352 return TabCtrl_GetRowCount(m_hwnd);
353 }
354
355 int wxNotebook::SetSelection(size_t nPage)
356 {
357 wxCHECK_MSG( IS_VALID_PAGE(nPage), wxNOT_FOUND, wxT("notebook page out of range") );
358
359 if ( int(nPage) != m_nSelection )
360 {
361 wxNotebookEvent event(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING, m_windowId);
362 event.SetSelection(nPage);
363 event.SetOldSelection(m_nSelection);
364 event.SetEventObject(this);
365 if ( !GetEventHandler()->ProcessEvent(event) || event.IsAllowed() )
366 {
367 // program allows the page change
368 event.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED);
369 (void)GetEventHandler()->ProcessEvent(event);
370
371 TabCtrl_SetCurSel(m_hwnd, nPage);
372 }
373 }
374
375 return m_nSelection;
376 }
377
378 bool wxNotebook::SetPageText(size_t nPage, const wxString& strText)
379 {
380 wxCHECK_MSG( IS_VALID_PAGE(nPage), false, wxT("notebook page out of range") );
381
382 TC_ITEM tcItem;
383 tcItem.mask = TCIF_TEXT;
384 tcItem.pszText = (wxChar *)strText.c_str();
385
386 return TabCtrl_SetItem(m_hwnd, nPage, &tcItem) != 0;
387 }
388
389 wxString wxNotebook::GetPageText(size_t nPage) const
390 {
391 wxCHECK_MSG( IS_VALID_PAGE(nPage), wxEmptyString, wxT("notebook page out of range") );
392
393 wxChar buf[256];
394 TC_ITEM tcItem;
395 tcItem.mask = TCIF_TEXT;
396 tcItem.pszText = buf;
397 tcItem.cchTextMax = WXSIZEOF(buf);
398
399 wxString str;
400 if ( TabCtrl_GetItem(m_hwnd, nPage, &tcItem) )
401 str = tcItem.pszText;
402
403 return str;
404 }
405
406 int wxNotebook::GetPageImage(size_t nPage) const
407 {
408 wxCHECK_MSG( IS_VALID_PAGE(nPage), -1, wxT("notebook page out of range") );
409
410 TC_ITEM tcItem;
411 tcItem.mask = TCIF_IMAGE;
412
413 return TabCtrl_GetItem(m_hwnd, nPage, &tcItem) ? tcItem.iImage : -1;
414 }
415
416 bool wxNotebook::SetPageImage(size_t nPage, int nImage)
417 {
418 wxCHECK_MSG( IS_VALID_PAGE(nPage), false, wxT("notebook page out of range") );
419
420 TC_ITEM tcItem;
421 tcItem.mask = TCIF_IMAGE;
422 tcItem.iImage = nImage;
423
424 return TabCtrl_SetItem(m_hwnd, nPage, &tcItem) != 0;
425 }
426
427 void wxNotebook::SetImageList(wxImageList* imageList)
428 {
429 wxNotebookBase::SetImageList(imageList);
430
431 if ( imageList )
432 {
433 TabCtrl_SetImageList(m_hwnd, (HIMAGELIST)imageList->GetHIMAGELIST());
434 }
435 }
436
437 // ----------------------------------------------------------------------------
438 // wxNotebook size settings
439 // ----------------------------------------------------------------------------
440
441 void wxNotebook::SetPageSize(const wxSize& size)
442 {
443 // transform the page size into the notebook size
444 RECT rc;
445 rc.left =
446 rc.top = 0;
447 rc.right = size.x;
448 rc.bottom = size.y;
449
450 TabCtrl_AdjustRect(GetHwnd(), true, &rc);
451
452 // and now set it
453 SetSize(rc.right - rc.left, rc.bottom - rc.top);
454 }
455
456 void wxNotebook::SetPadding(const wxSize& padding)
457 {
458 TabCtrl_SetPadding(GetHwnd(), padding.x, padding.y);
459 }
460
461 // Windows-only at present. Also, you must use the wxNB_FIXEDWIDTH
462 // style.
463 void wxNotebook::SetTabSize(const wxSize& sz)
464 {
465 ::SendMessage(GetHwnd(), TCM_SETITEMSIZE, 0, MAKELPARAM(sz.x, sz.y));
466 }
467
468 wxSize wxNotebook::CalcSizeFromPage(const wxSize& sizePage) const
469 {
470 wxSize sizeTotal = sizePage;
471
472 // We need to make getting tab size part of the wxWidgets API.
473 wxSize tabSize(0, 0);
474 if (GetPageCount() > 0)
475 {
476 RECT rect;
477 TabCtrl_GetItemRect((HWND) GetHWND(), 0, & rect);
478 tabSize.x = rect.right - rect.left;
479 tabSize.y = rect.bottom - rect.top;
480 }
481 if ( HasFlag(wxNB_LEFT) || HasFlag(wxNB_RIGHT) )
482 {
483 sizeTotal.x += tabSize.x + 7;
484 sizeTotal.y += 7;
485 }
486 else
487 {
488 sizeTotal.x += 7;
489 sizeTotal.y += tabSize.y + 7;
490 }
491
492 return sizeTotal;
493 }
494
495 void wxNotebook::AdjustPageSize(wxNotebookPage *page)
496 {
497 wxCHECK_RET( page, _T("NULL page in wxNotebook::AdjustPageSize") );
498
499 RECT rc;
500 rc.left =
501 rc.top = 0;
502
503 // get the page size from the notebook size
504 GetSize((int *)&rc.right, (int *)&rc.bottom);
505
506 // This check is to work around a bug in TabCtrl_AdjustRect which will
507 // cause a crash on win2k, or on XP with themes disabled, if the
508 // wxNB_MULTILINE style is used and the rectangle is very small, (such as
509 // when the notebook is first created.) The value of 20 is just
510 // arbitrarily chosen, if there is a better way to determine this value
511 // then please do so. --RD
512 if (rc.right > 20 && rc.bottom > 20)
513 {
514 TabCtrl_AdjustRect(m_hwnd, false, &rc);
515 page->SetSize(rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top);
516 }
517 }
518
519 // ----------------------------------------------------------------------------
520 // wxNotebook operations
521 // ----------------------------------------------------------------------------
522
523 // remove one page from the notebook, without deleting
524 wxNotebookPage *wxNotebook::DoRemovePage(size_t nPage)
525 {
526 wxNotebookPage *pageRemoved = wxNotebookBase::DoRemovePage(nPage);
527 if ( !pageRemoved )
528 return NULL;
529
530 TabCtrl_DeleteItem(m_hwnd, nPage);
531
532 if ( m_pages.IsEmpty() )
533 {
534 // no selection any more, the notebook becamse empty
535 m_nSelection = -1;
536 }
537 else // notebook still not empty
538 {
539 int selNew = TabCtrl_GetCurSel(m_hwnd);
540 if (selNew != -1)
541 {
542 // No selection change, just refresh the current selection.
543 // Because it could be that the slection index changed
544 // we need to update it.
545 // Note: this does not mean the selection it self changed.
546 m_nSelection = selNew;
547 m_pages[m_nSelection]->Refresh();
548 }
549 else if (int(nPage) == m_nSelection)
550 {
551 // The selection was deleted.
552
553 // Determine new selection.
554 if (m_nSelection == int(GetPageCount()))
555 selNew = m_nSelection - 1;
556 else
557 selNew = m_nSelection;
558
559 // m_nSelection must be always valid so reset it before calling
560 // SetSelection()
561 m_nSelection = -1;
562 SetSelection(selNew);
563 }
564 else
565 {
566 wxFAIL; // Windows did not behave ok.
567 }
568 }
569
570 return pageRemoved;
571 }
572
573 // remove all pages
574 bool wxNotebook::DeleteAllPages()
575 {
576 size_t nPageCount = GetPageCount();
577 size_t nPage;
578 for ( nPage = 0; nPage < nPageCount; nPage++ )
579 delete m_pages[nPage];
580
581 m_pages.Clear();
582
583 TabCtrl_DeleteAllItems(m_hwnd);
584
585 m_nSelection = -1;
586
587 InvalidateBestSize();
588 return true;
589 }
590
591 // same as AddPage() but does it at given position
592 bool wxNotebook::InsertPage(size_t nPage,
593 wxNotebookPage *pPage,
594 const wxString& strText,
595 bool bSelect,
596 int imageId)
597 {
598 wxCHECK_MSG( pPage != NULL, false, _T("NULL page in wxNotebook::InsertPage") );
599 wxCHECK_MSG( IS_VALID_PAGE(nPage) || nPage == GetPageCount(), false,
600 _T("invalid index in wxNotebook::InsertPage") );
601
602 wxASSERT_MSG( pPage->GetParent() == this,
603 _T("notebook pages must have notebook as parent") );
604
605 // add a new tab to the control
606 // ----------------------------
607
608 // init all fields to 0
609 TC_ITEM tcItem;
610 wxZeroMemory(tcItem);
611
612 // set the image, if any
613 if ( imageId != -1 )
614 {
615 tcItem.mask |= TCIF_IMAGE;
616 tcItem.iImage = imageId;
617 }
618
619 // and the text
620 if ( !strText.IsEmpty() )
621 {
622 tcItem.mask |= TCIF_TEXT;
623 tcItem.pszText = (wxChar *)strText.c_str(); // const_cast
624 }
625
626 // hide the page: unless it is selected, it shouldn't be shown (and if it
627 // is selected it will be shown later)
628 HWND hwnd = GetWinHwnd(pPage);
629 SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~WS_VISIBLE);
630
631 // this updates internal flag too -- otherwise it would get out of sync
632 // with the real state
633 pPage->Show(false);
634
635
636 // fit the notebook page to the tab control's display area: this should be
637 // done before adding it to the notebook or TabCtrl_InsertItem() will
638 // change the notebooks size itself!
639 AdjustPageSize(pPage);
640
641 // finally do insert it
642 if ( TabCtrl_InsertItem(m_hwnd, nPage, &tcItem) == -1 )
643 {
644 wxLogError(wxT("Can't create the notebook page '%s'."), strText.c_str());
645
646 return false;
647 }
648
649 // succeeded: save the pointer to the page
650 m_pages.Insert(pPage, nPage);
651
652 // we may need to adjust the size again if the notebook size changed:
653 // normally this only happens for the first page we add (the tabs which
654 // hadn't been there before are now shown) but for a multiline notebook it
655 // can happen for any page at all as a new row could have been started
656 if ( m_pages.GetCount() == 1 || HasFlag(wxNB_MULTILINE) )
657 {
658 AdjustPageSize(pPage);
659 }
660
661 // now deal with the selection
662 // ---------------------------
663
664 // if the inserted page is before the selected one, we must update the
665 // index of the selected page
666 if ( int(nPage) <= m_nSelection )
667 {
668 // one extra page added
669 m_nSelection++;
670 }
671
672 // some page should be selected: either this one or the first one if there
673 // is still no selection
674 int selNew = -1;
675 if ( bSelect )
676 selNew = nPage;
677 else if ( m_nSelection == -1 )
678 selNew = 0;
679
680 if ( selNew != -1 )
681 SetSelection(selNew);
682
683 InvalidateBestSize();
684 return true;
685 }
686
687 int wxNotebook::HitTest(const wxPoint& pt, long *flags) const
688 {
689 TC_HITTESTINFO hitTestInfo;
690 hitTestInfo.pt.x = pt.x;
691 hitTestInfo.pt.y = pt.y;
692 int item = TabCtrl_HitTest(GetHwnd(), &hitTestInfo);
693
694 if ( flags )
695 {
696 *flags = 0;
697
698 if ((hitTestInfo.flags & TCHT_NOWHERE) == TCHT_NOWHERE)
699 *flags |= wxNB_HITTEST_NOWHERE;
700 if ((hitTestInfo.flags & TCHT_ONITEM) == TCHT_ONITEM)
701 *flags |= wxNB_HITTEST_ONITEM;
702 if ((hitTestInfo.flags & TCHT_ONITEMICON) == TCHT_ONITEMICON)
703 *flags |= wxNB_HITTEST_ONICON;
704 if ((hitTestInfo.flags & TCHT_ONITEMLABEL) == TCHT_ONITEMLABEL)
705 *flags |= wxNB_HITTEST_ONLABEL;
706 }
707
708 return item;
709 }
710
711
712 // ----------------------------------------------------------------------------
713 // wxNotebook callbacks
714 // ----------------------------------------------------------------------------
715
716 void wxNotebook::OnSize(wxSizeEvent& event)
717 {
718 // update the background brush
719 #if wxUSE_UXTHEME
720 UpdateBgBrush();
721 #endif // wxUSE_UXTHEME
722
723 // fit all the notebook pages to the tab control's display area
724
725 RECT rc;
726 rc.left = rc.top = 0;
727 GetSize((int *)&rc.right, (int *)&rc.bottom);
728
729 // save the total size, we'll use it below
730 int widthNbook = rc.right - rc.left,
731 heightNbook = rc.bottom - rc.top;
732
733 // there seems to be a bug in the implementation of TabCtrl_AdjustRect(): it
734 // returns completely false values for multiline tab controls after the tabs
735 // are added but before getting the first WM_SIZE (off by ~50 pixels, see
736 //
737 // http://sf.net/tracker/index.php?func=detail&aid=645323&group_id=9863&atid=109863
738 //
739 // and the only work around I could find was this ugly hack... without it
740 // simply toggling the "multiline" checkbox in the notebook sample resulted
741 // in a noticeable page displacement
742 if ( HasFlag(wxNB_MULTILINE) )
743 {
744 // avoid an infinite recursion: we get another notification too!
745 static bool s_isInOnSize = false;
746
747 if ( !s_isInOnSize )
748 {
749 s_isInOnSize = true;
750 SendMessage(GetHwnd(), WM_SIZE, SIZE_RESTORED,
751 MAKELPARAM(rc.right, rc.bottom));
752 s_isInOnSize = false;
753 }
754 }
755
756 TabCtrl_AdjustRect(m_hwnd, false, &rc);
757
758 int width = rc.right - rc.left,
759 height = rc.bottom - rc.top;
760 size_t nCount = m_pages.Count();
761 for ( size_t nPage = 0; nPage < nCount; nPage++ ) {
762 wxNotebookPage *pPage = m_pages[nPage];
763 pPage->SetSize(rc.left, rc.top, width, height);
764 }
765
766
767 // unless we had already repainted everything, we now need to refresh
768 if ( !HasFlag(wxFULL_REPAINT_ON_RESIZE) )
769 {
770 // invalidate areas not covered by pages
771 RefreshRect(wxRect(0, 0, widthNbook, rc.top), false);
772 RefreshRect(wxRect(0, rc.top, rc.left, height), false);
773 RefreshRect(wxRect(0, rc.bottom, widthNbook, heightNbook - rc.bottom),
774 false);
775 RefreshRect(wxRect(rc.right, rc.top, widthNbook - rc.bottom, height),
776 false);
777 }
778
779 event.Skip();
780 }
781
782 void wxNotebook::OnSelChange(wxNotebookEvent& event)
783 {
784 // is it our tab control?
785 if ( event.GetEventObject() == this )
786 {
787 int sel = event.GetOldSelection();
788 if ( sel != -1 )
789 m_pages[sel]->Show(false);
790
791 sel = event.GetSelection();
792 if ( sel != -1 )
793 {
794 wxNotebookPage *pPage = m_pages[sel];
795 pPage->Show(true);
796 pPage->SetFocus();
797
798 // If the newly focused window is not a child of the new page,
799 // SetFocus was not successful and the notebook itself should be
800 // focused
801 wxWindow *currentFocus = FindFocus();
802 wxWindow *startFocus = currentFocus;
803 while ( currentFocus && currentFocus != pPage && currentFocus != this )
804 currentFocus = currentFocus->GetParent();
805
806 if ( startFocus == pPage || currentFocus != pPage )
807 SetFocus();
808
809 }
810 else // no pages in the notebook, give the focus to itself
811 {
812 SetFocus();
813 }
814
815 m_nSelection = sel;
816 }
817
818 // we want to give others a chance to process this message as well
819 event.Skip();
820 }
821
822 bool wxNotebook::MSWTranslateMessage(WXMSG *wxmsg)
823 {
824 const MSG * const msg = (MSG *)wxmsg;
825
826 // intercept TAB, CTRL+TAB and CTRL+SHIFT+TAB for processing by wxNotebook.
827 // TAB will be passed to the currently selected page, CTRL+TAB and
828 // CTRL+SHIFT+TAB will be processed by the notebook itself. do not
829 // intercept SHIFT+TAB. This goes to the parent of the notebook which will
830 // process it.
831 if ( msg->message == WM_KEYDOWN && msg->wParam == VK_TAB &&
832 msg->hwnd == m_hwnd &&
833 (wxIsCtrlDown() || !wxIsShiftDown()) )
834 {
835 return MSWProcessMessage(wxmsg);
836 }
837
838 return false;
839 }
840
841 void wxNotebook::OnNavigationKey(wxNavigationKeyEvent& event)
842 {
843 if ( event.IsWindowChange() ) {
844 // change pages
845 AdvanceSelection(event.GetDirection());
846 }
847 else {
848 // we get this event in 3 cases
849 //
850 // a) one of our pages might have generated it because the user TABbed
851 // out from it in which case we should propagate the event upwards and
852 // our parent will take care of setting the focus to prev/next sibling
853 //
854 // or
855 //
856 // b) the parent panel wants to give the focus to us so that we
857 // forward it to our selected page. We can't deal with this in
858 // OnSetFocus() because we don't know which direction the focus came
859 // from in this case and so can't choose between setting the focus to
860 // first or last panel child
861 //
862 // or
863 //
864 // c) we ourselves (see MSWTranslateMessage) generated the event
865 //
866 wxWindow * const parent = GetParent();
867
868 const bool isFromParent = event.GetEventObject() == parent;
869 const bool isFromSelf = event.GetEventObject() == this;
870
871 if ( isFromParent || isFromSelf )
872 {
873 // no, it doesn't come from child, case (b) or (c): forward to a
874 // page but only if direction is backwards (TAB) or from ourselves,
875 if ( m_nSelection != -1 &&
876 (!event.GetDirection() || isFromSelf) )
877 {
878 // so that the page knows that the event comes from it's parent
879 // and is being propagated downwards
880 event.SetEventObject(this);
881
882 wxWindow *page = m_pages[m_nSelection];
883 if ( !page->GetEventHandler()->ProcessEvent(event) )
884 {
885 page->SetFocus();
886 }
887 //else: page manages focus inside it itself
888 }
889 else // otherwise set the focus to the notebook itself
890 {
891 SetFocus();
892 }
893 }
894 else
895 {
896 // it comes from our child, case (a), pass to the parent, but only
897 // if the direction is forwards. Otherwise set the focus to the
898 // notebook itself. The notebook is always the 'first' control of a
899 // page.
900 if ( !event.GetDirection() )
901 {
902 SetFocus();
903 }
904 else if ( parent )
905 {
906 event.SetCurrentFocus(this);
907 parent->GetEventHandler()->ProcessEvent(event);
908 }
909 }
910 }
911 }
912
913 #if wxUSE_UXTHEME
914
915 WXHANDLE wxNotebook::QueryBgBitmap(wxWindow *win)
916 {
917 RECT rc;
918 GetWindowRect(GetHwnd(), &rc);
919
920 WindowHDC hDC(GetHwnd());
921 MemoryHDC hDCMem(hDC);
922 CompatibleBitmap hBmp(hDC, rc.right - rc.left, rc.bottom - rc.top);
923
924 SelectInHDC selectBmp(hDCMem, hBmp);
925
926 ::SendMessage(GetHwnd(), WM_PRINTCLIENT,
927 (WPARAM)(HDC)hDCMem,
928 PRF_ERASEBKGND | PRF_CLIENT | PRF_NONCLIENT);
929
930 if ( win )
931 {
932 RECT rc2;
933 ::GetWindowRect(GetHwndOf(win), &rc2);
934
935 COLORREF c = ::GetPixel(hDCMem, rc2.left - rc.left, rc2.top - rc.top);
936
937 return (WXHANDLE)c;
938 }
939 //else: we are asked to create the brush
940
941 return (WXHANDLE)::CreatePatternBrush(hBmp);
942 }
943
944 void wxNotebook::UpdateBgBrush()
945 {
946 if ( m_hbrBackground )
947 ::DeleteObject((HBRUSH)m_hbrBackground);
948
949 if ( !m_hasBgCol && wxUxThemeEngine::GetIfActive() )
950 {
951 m_hbrBackground = (WXHBRUSH)QueryBgBitmap();
952 }
953 else // no themes
954 {
955 m_hbrBackground = NULL;
956 }
957 }
958
959 WXHBRUSH wxNotebook::MSWGetBgBrushForChild(WXHDC hDC, wxWindow *win)
960 {
961 if ( m_hbrBackground )
962 {
963 // before drawing with the background brush, we need to position it
964 // correctly
965 RECT rc;
966 ::GetWindowRect(GetHwndOf(win), &rc);
967
968 ::MapWindowPoints(NULL, GetHwnd(), (POINT *)&rc, 1);
969
970 if ( !::SetBrushOrgEx((HDC)hDC, -rc.left, -rc.top, NULL) )
971 {
972 wxLogLastError(_T("SetBrushOrgEx(notebook bg brush)"));
973 }
974
975 return m_hbrBackground;
976 }
977
978 return wxNotebookBase::MSWGetBgBrushForChild(hDC, win);
979 }
980
981 wxColour wxNotebook::MSWGetBgColourForChild(wxWindow *win)
982 {
983 if ( m_hasBgCol )
984 return GetBackgroundColour();
985
986 if ( !wxUxThemeEngine::GetIfActive() )
987 return wxNullColour;
988
989 COLORREF c = (COLORREF)QueryBgBitmap(win);
990
991 return c == CLR_INVALID ? wxNullColour : wxRGBToColour(c);
992 }
993
994 #endif // wxUSE_UXTHEME
995
996 // ----------------------------------------------------------------------------
997 // wxNotebook base class virtuals
998 // ----------------------------------------------------------------------------
999
1000 #if wxUSE_CONSTRAINTS
1001
1002 // override these 2 functions to do nothing: everything is done in OnSize
1003
1004 void wxNotebook::SetConstraintSizes(bool WXUNUSED(recurse))
1005 {
1006 // don't set the sizes of the pages - their correct size is not yet known
1007 wxControl::SetConstraintSizes(false);
1008 }
1009
1010 bool wxNotebook::DoPhase(int WXUNUSED(nPhase))
1011 {
1012 return true;
1013 }
1014
1015 #endif // wxUSE_CONSTRAINTS
1016
1017 // ----------------------------------------------------------------------------
1018 // wxNotebook Windows message handlers
1019 // ----------------------------------------------------------------------------
1020
1021 bool wxNotebook::MSWOnScroll(int orientation, WXWORD nSBCode,
1022 WXWORD pos, WXHWND control)
1023 {
1024 // don't generate EVT_SCROLLWIN events for the WM_SCROLLs coming from the
1025 // up-down control
1026 if ( control )
1027 return false;
1028
1029 return wxNotebookBase::MSWOnScroll(orientation, nSBCode, pos, control);
1030 }
1031
1032 bool wxNotebook::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM* result)
1033 {
1034 wxNotebookEvent event(wxEVT_NULL, m_windowId);
1035
1036 NMHDR* hdr = (NMHDR *)lParam;
1037 switch ( hdr->code ) {
1038 case TCN_SELCHANGE:
1039 event.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED);
1040 break;
1041
1042 case TCN_SELCHANGING:
1043 event.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING);
1044 break;
1045
1046 default:
1047 return wxControl::MSWOnNotify(idCtrl, lParam, result);
1048 }
1049
1050 event.SetSelection(TabCtrl_GetCurSel(m_hwnd));
1051 event.SetOldSelection(m_nSelection);
1052 event.SetEventObject(this);
1053 event.SetInt(idCtrl);
1054
1055 bool processed = GetEventHandler()->ProcessEvent(event);
1056 *result = !event.IsAllowed();
1057 return processed;
1058 }
1059
1060 #endif // wxUSE_NOTEBOOK