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