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