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