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