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