1
///////////////////////////////////////////////////////////////////////////////
2
// Name: src/msw/notebook.cpp
3
// Purpose: implementation of wxNotebook
4
// Author: Vadim Zeitlin
7
// RCS-ID: $Id: notebook.cpp,v 1.159.2.4 2006/02/11 15:41:24 JS Exp $
8
// Copyright: (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
9
// Licence: wxWindows licence
10
///////////////////////////////////////////////////////////////////////////////
12
#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
13
#pragma implementation "notebook.h"
16
// For compilers that support precompilation, includes "wx.h".
17
#include "wx/wxprec.h"
27
#include "wx/string.h"
32
#include "wx/imaglist.h"
34
#include "wx/control.h"
35
#include "wx/notebook.h"
37
#include "wx/sysopt.h"
38
#include "wx/dcclient.h"
39
#include "wx/dcmemory.h"
41
#include "wx/msw/private.h"
47
#include "wx/msw/winundef.h"
50
#include "wx/msw/uxtheme.h"
53
// ----------------------------------------------------------------------------
55
// ----------------------------------------------------------------------------
57
// check that the page index is valid
58
#define IS_VALID_PAGE(nPage) ((nPage) < GetPageCount())
60
// you can set USE_NOTEBOOK_ANTIFLICKER to 0 for desktop Windows versions too
61
// to disable code whih results in flicker-less notebook redrawing at the
62
// expense of some extra GDI resource consumption
64
// notebooks are never resized under CE anyhow
65
#define USE_NOTEBOOK_ANTIFLICKER 0
67
#define USE_NOTEBOOK_ANTIFLICKER 1
70
// ----------------------------------------------------------------------------
72
// ----------------------------------------------------------------------------
74
// This is a work-around for missing defines in gcc-2.95 headers
76
#define TCS_RIGHT 0x0002
80
#define TCS_VERTICAL 0x0080
84
#define TCS_BOTTOM TCS_RIGHT
87
// ----------------------------------------------------------------------------
89
// ----------------------------------------------------------------------------
91
#if USE_NOTEBOOK_ANTIFLICKER
93
// the pointer to standard spin button wnd proc
94
static WXFARPROC gs_wndprocNotebookSpinBtn = (WXFARPROC)NULL;
96
// the pointer to standard tab control wnd proc
97
static WXFARPROC gs_wndprocNotebook = (WXFARPROC)NULL;
99
LRESULT APIENTRY _EXPORT wxNotebookWndProc(HWND hwnd,
104
#endif // USE_NOTEBOOK_ANTIFLICKER
106
// ----------------------------------------------------------------------------
108
// ----------------------------------------------------------------------------
110
#include <wx/listimpl.cpp>
112
WX_DEFINE_LIST( wxNotebookPageInfoList ) ;
114
DEFINE_EVENT_TYPE(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED)
115
DEFINE_EVENT_TYPE(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING)
117
BEGIN_EVENT_TABLE(wxNotebook, wxControl)
118
EVT_NOTEBOOK_PAGE_CHANGED(-1, wxNotebook::OnSelChange)
119
EVT_SIZE(wxNotebook::OnSize)
120
EVT_NAVIGATION_KEY(wxNotebook::OnNavigationKey)
122
#if USE_NOTEBOOK_ANTIFLICKER
123
EVT_ERASE_BACKGROUND(wxNotebook::OnEraseBackground)
124
EVT_PAINT(wxNotebook::OnPaint)
125
#endif // USE_NOTEBOOK_ANTIFLICKER
128
#if wxUSE_EXTENDED_RTTI
129
WX_DEFINE_FLAGS( wxNotebookStyle )
131
wxBEGIN_FLAGS( wxNotebookStyle )
132
// new style border flags, we put them first to
133
// use them for streaming out
134
wxFLAGS_MEMBER(wxBORDER_SIMPLE)
135
wxFLAGS_MEMBER(wxBORDER_SUNKEN)
136
wxFLAGS_MEMBER(wxBORDER_DOUBLE)
137
wxFLAGS_MEMBER(wxBORDER_RAISED)
138
wxFLAGS_MEMBER(wxBORDER_STATIC)
139
wxFLAGS_MEMBER(wxBORDER_NONE)
141
// old style border flags
142
wxFLAGS_MEMBER(wxSIMPLE_BORDER)
143
wxFLAGS_MEMBER(wxSUNKEN_BORDER)
144
wxFLAGS_MEMBER(wxDOUBLE_BORDER)
145
wxFLAGS_MEMBER(wxRAISED_BORDER)
146
wxFLAGS_MEMBER(wxSTATIC_BORDER)
147
wxFLAGS_MEMBER(wxBORDER)
149
// standard window styles
150
wxFLAGS_MEMBER(wxTAB_TRAVERSAL)
151
wxFLAGS_MEMBER(wxCLIP_CHILDREN)
152
wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW)
153
wxFLAGS_MEMBER(wxWANTS_CHARS)
154
wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE)
155
wxFLAGS_MEMBER(wxALWAYS_SHOW_SB )
156
wxFLAGS_MEMBER(wxVSCROLL)
157
wxFLAGS_MEMBER(wxHSCROLL)
159
wxFLAGS_MEMBER(wxNB_FIXEDWIDTH)
160
wxFLAGS_MEMBER(wxNB_LEFT)
161
wxFLAGS_MEMBER(wxNB_RIGHT)
162
wxFLAGS_MEMBER(wxNB_BOTTOM)
163
wxFLAGS_MEMBER(wxNB_NOPAGETHEME)
164
wxFLAGS_MEMBER(wxNB_FLAT)
166
wxEND_FLAGS( wxNotebookStyle )
168
IMPLEMENT_DYNAMIC_CLASS_XTI(wxNotebook, wxControl,"wx/notebook.h")
169
IMPLEMENT_DYNAMIC_CLASS_XTI(wxNotebookPageInfo, wxObject , "wx/notebook.h" )
171
wxCOLLECTION_TYPE_INFO( wxNotebookPageInfo * , wxNotebookPageInfoList ) ;
173
template<> void wxCollectionToVariantArray( wxNotebookPageInfoList const &theList, wxxVariantArray &value)
175
wxListCollectionToVariantArray<wxNotebookPageInfoList::compatibility_iterator>( theList , value ) ;
178
wxBEGIN_PROPERTIES_TABLE(wxNotebook)
179
wxEVENT_PROPERTY( PageChanging , wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING , wxNotebookEvent )
180
wxEVENT_PROPERTY( PageChanged , wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED , wxNotebookEvent )
182
wxPROPERTY_COLLECTION( PageInfos , wxNotebookPageInfoList , wxNotebookPageInfo* , AddPageInfo , GetPageInfos , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
183
wxPROPERTY_FLAGS( WindowStyle , wxNotebookStyle , long , SetWindowStyleFlag , GetWindowStyleFlag , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
184
wxEND_PROPERTIES_TABLE()
186
wxBEGIN_HANDLERS_TABLE(wxNotebook)
187
wxEND_HANDLERS_TABLE()
189
wxCONSTRUCTOR_5( wxNotebook , wxWindow* , Parent , wxWindowID , Id , wxPoint , Position , wxSize , Size , long , WindowStyle)
192
wxBEGIN_PROPERTIES_TABLE(wxNotebookPageInfo)
193
wxREADONLY_PROPERTY( Page , wxNotebookPage* , GetPage , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
194
wxREADONLY_PROPERTY( Text , wxString , GetText , wxString() , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
195
wxREADONLY_PROPERTY( Selected , bool , GetSelected , false, 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
196
wxREADONLY_PROPERTY( ImageId , int , GetImageId , -1 , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
197
wxEND_PROPERTIES_TABLE()
199
wxBEGIN_HANDLERS_TABLE(wxNotebookPageInfo)
200
wxEND_HANDLERS_TABLE()
202
wxCONSTRUCTOR_4( wxNotebookPageInfo , wxNotebookPage* , Page , wxString , Text , bool , Selected , int , ImageId )
205
IMPLEMENT_DYNAMIC_CLASS(wxNotebook, wxControl)
206
IMPLEMENT_DYNAMIC_CLASS(wxNotebookPageInfo, wxObject )
208
IMPLEMENT_DYNAMIC_CLASS(wxNotebookEvent, wxNotifyEvent)
210
// ============================================================================
212
// ============================================================================
214
// ----------------------------------------------------------------------------
215
// wxNotebook construction
216
// ----------------------------------------------------------------------------
218
const wxNotebookPageInfoList& wxNotebook::GetPageInfos() const
220
wxNotebookPageInfoList* list = const_cast< wxNotebookPageInfoList* >( &m_pageInfos ) ;
221
WX_CLEAR_LIST( wxNotebookPageInfoList , *list ) ;
222
for( size_t i = 0 ; i < GetPageCount() ; ++i )
224
wxNotebookPageInfo *info = new wxNotebookPageInfo() ;
225
info->Create( const_cast<wxNotebook*>(this)->GetPage(i) , GetPageText(i) , GetSelection() == int(i) , GetPageImage(i) ) ;
226
list->Append( info ) ;
231
// common part of all ctors
232
void wxNotebook::Init()
238
m_hbrBackground = NULL;
239
#endif // wxUSE_UXTHEME
241
#if USE_NOTEBOOK_ANTIFLICKER
242
m_hasSubclassedUpdown = false;
243
#endif // USE_NOTEBOOK_ANTIFLICKER
246
// default for dynamic class
247
wxNotebook::wxNotebook()
252
// the same arguments as for wxControl
253
wxNotebook::wxNotebook(wxWindow *parent,
258
const wxString& name)
262
Create(parent, id, pos, size, style, name);
266
bool wxNotebook::Create(wxWindow *parent,
271
const wxString& name)
274
// Not sure why, but without this style, there is no border
275
// around the notebook tabs.
276
if (style & wxNB_FLAT)
277
style |= wxBORDER_SUNKEN;
281
// ComCtl32 notebook tabs simply don't work unless they're on top if we have uxtheme, we can
282
// work around it later (after control creation), but if we don't have uxtheme, we have to clear
284
const int verComCtl32 = wxApp::GetComCtl32Version();
285
if ( verComCtl32 == 600 )
287
style &= ~(wxNB_BOTTOM | wxNB_LEFT | wxNB_RIGHT);
289
#endif //wxUSE_UXTHEME
291
LPCTSTR className = WC_TABCONTROL;
293
#if USE_NOTEBOOK_ANTIFLICKER
294
// SysTabCtl32 class has natively CS_HREDRAW and CS_VREDRAW enabled and it
295
// causes horrible flicker when resizing notebook, so get rid of it by
296
// using a class without these styles (but otherwise identical to it)
297
if ( !HasFlag(wxFULL_REPAINT_ON_RESIZE) )
299
static ClassRegistrar s_clsNotebook;
300
if ( !s_clsNotebook.IsInitialized() )
302
// get a copy of standard class and modify it
305
if ( ::GetClassInfo(NULL, WC_TABCONTROL, &wc) )
308
wx_reinterpret_cast(WXFARPROC, wc.lpfnWndProc);
309
wc.lpszClassName = wxT("_wx_SysTabCtl32");
310
wc.style &= ~(CS_HREDRAW | CS_VREDRAW);
311
wc.hInstance = wxGetInstance();
312
wc.lpfnWndProc = wxNotebookWndProc;
313
s_clsNotebook.Register(wc);
317
wxLogLastError(_T("GetClassInfoEx(SysTabCtl32)"));
321
// use our custom class if available but fall back to the standard
322
// notebook if we failed to register it
323
if ( s_clsNotebook.IsRegistered() )
325
// it's ok to use c_str() here as the static s_clsNotebook object
326
// has sufficiently long lifetime
327
className = s_clsNotebook.GetName().c_str();
330
#endif // USE_NOTEBOOK_ANTIFLICKER
332
if ( !CreateControl(parent, id, pos, size, style | wxTAB_TRAVERSAL,
333
wxDefaultValidator, name) )
336
if ( !MSWCreateControl(className, wxEmptyString, pos, size) )
340
if ( HasFlag(wxNB_NOPAGETHEME) ||
341
wxSystemOptions::IsFalse(wxT("msw.notebook.themed-background")) )
343
SetBackgroundColour(GetThemeBackgroundColour());
345
else // use themed background by default
347
// create backing store
351
// comctl32.dll 6.0 doesn't support non-top tabs with visual styles (the
352
// control is simply not rendered correctly), so we disable themes
353
// if possible, otherwise we simply clear the styles.
354
// It's probably not possible to have UXTHEME without ComCtl32 6 or better, but lets
356
const int verComCtl32 = wxApp::GetComCtl32Version();
357
if ( verComCtl32 == 600 )
359
// check if we use themes at all -- if we don't, we're still okay
360
if ( wxUxThemeEngine::GetIfActive() && (style & (wxNB_BOTTOM|wxNB_LEFT|wxNB_RIGHT)))
362
wxUxThemeEngine::GetIfActive()->SetWindowTheme((HWND)this->GetHandle(), L"", L"");
363
SetBackgroundColour(GetThemeBackgroundColour()); //correct the background color for the new non-themed control
366
#endif // wxUSE_UXTHEME
368
// Undocumented hack to get flat notebook style
369
// In fact, we should probably only do this in some
370
// curcumstances, i.e. if we know we will have a border
371
// at the bottom (the tab control doesn't draw it itself)
372
#if defined(__POCKETPC__) || defined(__SMARTPHONE__)
373
if (HasFlag(wxNB_FLAT))
375
SendMessage(GetHwnd(), CCM_SETVERSION, COMCTL32_VERSION, 0);
377
SetBackgroundColour(*wxWHITE);
383
WXDWORD wxNotebook::MSWGetStyle(long style, WXDWORD *exstyle) const
385
WXDWORD tabStyle = wxControl::MSWGetStyle(style, exstyle);
387
tabStyle |= WS_TABSTOP | TCS_TABS;
389
if ( style & wxNB_MULTILINE )
390
tabStyle |= TCS_MULTILINE;
391
if ( style & wxNB_FIXEDWIDTH )
392
tabStyle |= TCS_FIXEDWIDTH;
394
if ( style & wxNB_BOTTOM )
395
tabStyle |= TCS_RIGHT;
396
else if ( style & wxNB_LEFT )
397
tabStyle |= TCS_VERTICAL;
398
else if ( style & wxNB_RIGHT )
399
tabStyle |= TCS_VERTICAL | TCS_RIGHT;
404
// note that we never want to have the default WS_EX_CLIENTEDGE style
405
// as it looks too ugly for the notebooks
412
wxNotebook::~wxNotebook()
415
if ( m_hbrBackground )
416
::DeleteObject((HBRUSH)m_hbrBackground);
417
#endif // wxUSE_UXTHEME
420
// ----------------------------------------------------------------------------
421
// wxNotebook accessors
422
// ----------------------------------------------------------------------------
424
size_t wxNotebook::GetPageCount() const
427
wxASSERT( (int)m_pages.Count() == TabCtrl_GetItemCount(GetHwnd()) );
429
return m_pages.Count();
432
int wxNotebook::GetRowCount() const
434
return TabCtrl_GetRowCount(GetHwnd());
437
int wxNotebook::SetSelection(size_t nPage)
439
wxCHECK_MSG( IS_VALID_PAGE(nPage), wxNOT_FOUND, wxT("notebook page out of range") );
441
if ( int(nPage) != m_nSelection )
443
wxNotebookEvent event(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING, m_windowId);
444
event.SetSelection(nPage);
445
event.SetOldSelection(m_nSelection);
446
event.SetEventObject(this);
447
if ( !GetEventHandler()->ProcessEvent(event) || event.IsAllowed() )
449
// program allows the page change
450
event.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED);
451
(void)GetEventHandler()->ProcessEvent(event);
453
TabCtrl_SetCurSel(GetHwnd(), nPage);
460
bool wxNotebook::SetPageText(size_t nPage, const wxString& strText)
462
wxCHECK_MSG( IS_VALID_PAGE(nPage), false, wxT("notebook page out of range") );
465
tcItem.mask = TCIF_TEXT;
466
tcItem.pszText = (wxChar *)strText.c_str();
468
if ( !HasFlag(wxNB_MULTILINE) )
469
return TabCtrl_SetItem(GetHwnd(), nPage, &tcItem) != 0;
471
// multiline - we need to set new page size if a line is added or removed
472
int rows = GetRowCount();
473
bool ret = TabCtrl_SetItem(GetHwnd(), nPage, &tcItem) != 0;
475
if ( ret && rows != GetRowCount() )
477
const wxRect r = GetPageSize();
478
const size_t count = m_pages.Count();
479
for ( size_t page = 0; page < count; page++ )
480
m_pages[page]->SetSize(r);
486
wxString wxNotebook::GetPageText(size_t nPage) const
488
wxCHECK_MSG( IS_VALID_PAGE(nPage), wxEmptyString, wxT("notebook page out of range") );
492
tcItem.mask = TCIF_TEXT;
493
tcItem.pszText = buf;
494
tcItem.cchTextMax = WXSIZEOF(buf);
497
if ( TabCtrl_GetItem(GetHwnd(), nPage, &tcItem) )
498
str = tcItem.pszText;
503
int wxNotebook::GetPageImage(size_t nPage) const
505
wxCHECK_MSG( IS_VALID_PAGE(nPage), -1, wxT("notebook page out of range") );
508
tcItem.mask = TCIF_IMAGE;
510
return TabCtrl_GetItem(GetHwnd(), nPage, &tcItem) ? tcItem.iImage : -1;
513
bool wxNotebook::SetPageImage(size_t nPage, int nImage)
515
wxCHECK_MSG( IS_VALID_PAGE(nPage), false, wxT("notebook page out of range") );
518
tcItem.mask = TCIF_IMAGE;
519
tcItem.iImage = nImage;
521
return TabCtrl_SetItem(GetHwnd(), nPage, &tcItem) != 0;
524
void wxNotebook::SetImageList(wxImageList* imageList)
526
wxNotebookBase::SetImageList(imageList);
530
(void) TabCtrl_SetImageList(GetHwnd(), (HIMAGELIST)imageList->GetHIMAGELIST());
534
// ----------------------------------------------------------------------------
535
// wxNotebook size settings
536
// ----------------------------------------------------------------------------
538
wxRect wxNotebook::GetPageSize() const
543
::GetClientRect(GetHwnd(), &rc);
545
// This check is to work around a bug in TabCtrl_AdjustRect which will
546
// cause a crash on win2k or on XP with themes disabled if either
547
// wxNB_MULTILINE is used or tabs are placed on a side, if the rectangle
550
// The value of 20 is chosen arbitrarily but seems to work
551
if ( rc.right > 20 && rc.bottom > 20 )
553
TabCtrl_AdjustRect(GetHwnd(), false, &rc);
555
wxCopyRECTToRect(rc, r);
561
void wxNotebook::SetPageSize(const wxSize& size)
563
// transform the page size into the notebook size
570
TabCtrl_AdjustRect(GetHwnd(), true, &rc);
573
SetSize(rc.right - rc.left, rc.bottom - rc.top);
576
void wxNotebook::SetPadding(const wxSize& padding)
578
TabCtrl_SetPadding(GetHwnd(), padding.x, padding.y);
581
// Windows-only at present. Also, you must use the wxNB_FIXEDWIDTH
583
void wxNotebook::SetTabSize(const wxSize& sz)
585
::SendMessage(GetHwnd(), TCM_SETITEMSIZE, 0, MAKELPARAM(sz.x, sz.y));
588
wxSize wxNotebook::CalcSizeFromPage(const wxSize& sizePage) const
590
wxSize sizeTotal = sizePage;
592
// We need to make getting tab size part of the wxWidgets API.
594
if (GetPageCount() > 0)
597
TabCtrl_GetItemRect((HWND) GetHWND(), 0, & rect);
598
tabSize.x = rect.right - rect.left;
599
tabSize.y = rect.bottom - rect.top;
601
if ( HasFlag(wxNB_LEFT) || HasFlag(wxNB_RIGHT) )
603
sizeTotal.x += tabSize.x + 7;
609
sizeTotal.y += tabSize.y + 7;
615
void wxNotebook::AdjustPageSize(wxNotebookPage *page)
617
wxCHECK_RET( page, _T("NULL page in wxNotebook::AdjustPageSize") );
619
const wxRect r = GetPageSize();
626
// ----------------------------------------------------------------------------
627
// wxNotebook operations
628
// ----------------------------------------------------------------------------
630
// remove one page from the notebook, without deleting
631
wxNotebookPage *wxNotebook::DoRemovePage(size_t nPage)
633
wxNotebookPage *pageRemoved = wxNotebookBase::DoRemovePage(nPage);
637
TabCtrl_DeleteItem(GetHwnd(), nPage);
639
if ( m_pages.IsEmpty() )
641
// no selection any more, the notebook becamse empty
644
else // notebook still not empty
646
int selNew = TabCtrl_GetCurSel(GetHwnd());
649
// No selection change, just refresh the current selection.
650
// Because it could be that the slection index changed
651
// we need to update it.
652
// Note: this does not mean the selection it self changed.
653
m_nSelection = selNew;
654
m_pages[m_nSelection]->Refresh();
656
else if (int(nPage) == m_nSelection)
658
// The selection was deleted.
660
// Determine new selection.
661
if (m_nSelection == int(GetPageCount()))
662
selNew = m_nSelection - 1;
664
selNew = m_nSelection;
666
// m_nSelection must be always valid so reset it before calling
669
SetSelection(selNew);
673
wxFAIL; // Windows did not behave ok.
681
bool wxNotebook::DeleteAllPages()
683
size_t nPageCount = GetPageCount();
685
for ( nPage = 0; nPage < nPageCount; nPage++ )
686
delete m_pages[nPage];
690
TabCtrl_DeleteAllItems(GetHwnd());
694
InvalidateBestSize();
698
// same as AddPage() but does it at given position
699
bool wxNotebook::InsertPage(size_t nPage,
700
wxNotebookPage *pPage,
701
const wxString& strText,
705
wxCHECK_MSG( pPage != NULL, false, _T("NULL page in wxNotebook::InsertPage") );
706
wxCHECK_MSG( IS_VALID_PAGE(nPage) || nPage == GetPageCount(), false,
707
_T("invalid index in wxNotebook::InsertPage") );
709
wxASSERT_MSG( pPage->GetParent() == this,
710
_T("notebook pages must have notebook as parent") );
712
// add a new tab to the control
713
// ----------------------------
715
// init all fields to 0
717
wxZeroMemory(tcItem);
719
// set the image, if any
722
tcItem.mask |= TCIF_IMAGE;
723
tcItem.iImage = imageId;
727
if ( !strText.empty() )
729
tcItem.mask |= TCIF_TEXT;
730
tcItem.pszText = (wxChar *)strText.c_str(); // const_cast
733
// hide the page: unless it is selected, it shouldn't be shown (and if it
734
// is selected it will be shown later)
735
HWND hwnd = GetWinHwnd(pPage);
736
SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~WS_VISIBLE);
738
// this updates internal flag too -- otherwise it would get out of sync
739
// with the real state
743
// fit the notebook page to the tab control's display area: this should be
744
// done before adding it to the notebook or TabCtrl_InsertItem() will
745
// change the notebooks size itself!
746
AdjustPageSize(pPage);
748
// finally do insert it
749
if ( TabCtrl_InsertItem(GetHwnd(), nPage, &tcItem) == -1 )
751
wxLogError(wxT("Can't create the notebook page '%s'."), strText.c_str());
756
// succeeded: save the pointer to the page
757
m_pages.Insert(pPage, nPage);
759
// we may need to adjust the size again if the notebook size changed:
760
// normally this only happens for the first page we add (the tabs which
761
// hadn't been there before are now shown) but for a multiline notebook it
762
// can happen for any page at all as a new row could have been started
763
if ( m_pages.GetCount() == 1 || HasFlag(wxNB_MULTILINE) )
765
AdjustPageSize(pPage);
768
// now deal with the selection
769
// ---------------------------
771
// if the inserted page is before the selected one, we must update the
772
// index of the selected page
773
if ( int(nPage) <= m_nSelection )
775
// one extra page added
779
// some page should be selected: either this one or the first one if there
780
// is still no selection
784
else if ( m_nSelection == -1 )
788
SetSelection(selNew);
790
InvalidateBestSize();
795
int wxNotebook::HitTest(const wxPoint& pt, long *flags) const
797
TC_HITTESTINFO hitTestInfo;
798
hitTestInfo.pt.x = pt.x;
799
hitTestInfo.pt.y = pt.y;
800
int item = TabCtrl_HitTest(GetHwnd(), &hitTestInfo);
806
if ((hitTestInfo.flags & TCHT_NOWHERE) == TCHT_NOWHERE)
807
*flags |= wxNB_HITTEST_NOWHERE;
808
if ((hitTestInfo.flags & TCHT_ONITEM) == TCHT_ONITEM)
809
*flags |= wxNB_HITTEST_ONITEM;
810
if ((hitTestInfo.flags & TCHT_ONITEMICON) == TCHT_ONITEMICON)
811
*flags |= wxNB_HITTEST_ONICON;
812
if ((hitTestInfo.flags & TCHT_ONITEMLABEL) == TCHT_ONITEMLABEL)
813
*flags |= wxNB_HITTEST_ONLABEL;
819
// ----------------------------------------------------------------------------
820
// flicker-less notebook redraw
821
// ----------------------------------------------------------------------------
823
#if USE_NOTEBOOK_ANTIFLICKER
825
// wnd proc for the spin button
826
LRESULT APIENTRY _EXPORT wxNotebookSpinBtnWndProc(HWND hwnd,
831
if ( message == WM_ERASEBKGND )
834
return ::CallWindowProc(CASTWNDPROC gs_wndprocNotebookSpinBtn,
835
hwnd, message, wParam, lParam);
838
LRESULT APIENTRY _EXPORT wxNotebookWndProc(HWND hwnd,
843
return ::CallWindowProc(CASTWNDPROC gs_wndprocNotebook,
844
hwnd, message, wParam, lParam);
849
void wxNotebook::OnEraseBackground(wxEraseEvent& WXUNUSED(event))
854
void wxNotebook::OnPaint(wxPaintEvent& WXUNUSED(event))
859
::GetClientRect(GetHwnd(), &rc);
860
wxBitmap bmp(rc.right, rc.bottom);
861
memdc.SelectObject(bmp);
863
// if there is no special brush just use the solid background colour
865
HBRUSH hbr = (HBRUSH)m_hbrBackground;
872
brush = wxBrush(GetBackgroundColour());
873
hbr = GetHbrushOf(brush);
876
::FillRect(GetHdcOf(memdc), &rc, hbr);
878
MSWDefWindowProc(WM_PAINT, (WPARAM)memdc.GetHDC(), 0);
880
dc.Blit(0, 0, rc.right, rc.bottom, &memdc, 0, 0);
883
#endif // USE_NOTEBOOK_ANTIFLICKER
885
// ----------------------------------------------------------------------------
886
// wxNotebook callbacks
887
// ----------------------------------------------------------------------------
889
void wxNotebook::OnSize(wxSizeEvent& event)
891
if ( GetPageCount() == 0 )
893
// Prevents droppings on resize, but does cause some flicker
894
// when there are no pages.
902
// Without this, we can sometimes get droppings at the edges
903
// of a notebook, for example a notebook in a splitter window.
904
// This needs to be reconciled with the RefreshRect calls
905
// at the end of this function, which weren't enough to prevent
908
wxSize sz = GetClientSize();
910
// Refresh right side
911
wxRect rect(sz.x-4, 0, 4, sz.y);
914
// Refresh bottom side
915
rect = wxRect(0, sz.y-4, sz.x, 4);
919
rect = wxRect(0, 0, 4, sz.y);
922
#endif // !__WXWINCE__
924
// fit all the notebook pages to the tab control's display area
927
rc.left = rc.top = 0;
928
GetSize((int *)&rc.right, (int *)&rc.bottom);
930
// save the total size, we'll use it below
931
int widthNbook = rc.right - rc.left,
932
heightNbook = rc.bottom - rc.top;
934
// there seems to be a bug in the implementation of TabCtrl_AdjustRect(): it
935
// returns completely false values for multiline tab controls after the tabs
936
// are added but before getting the first WM_SIZE (off by ~50 pixels, see
938
// http://sf.net/tracker/index.php?func=detail&aid=645323&group_id=9863&atid=109863
940
// and the only work around I could find was this ugly hack... without it
941
// simply toggling the "multiline" checkbox in the notebook sample resulted
942
// in a noticeable page displacement
943
if ( HasFlag(wxNB_MULTILINE) )
945
// avoid an infinite recursion: we get another notification too!
946
static bool s_isInOnSize = false;
951
SendMessage(GetHwnd(), WM_SIZE, SIZE_RESTORED,
952
MAKELPARAM(rc.right, rc.bottom));
953
s_isInOnSize = false;
958
// background bitmap size has changed, update the brush using it too
960
#endif // wxUSE_UXTHEME
962
TabCtrl_AdjustRect(GetHwnd(), false, &rc);
964
int width = rc.right - rc.left,
965
height = rc.bottom - rc.top;
966
size_t nCount = m_pages.Count();
967
for ( size_t nPage = 0; nPage < nCount; nPage++ ) {
968
wxNotebookPage *pPage = m_pages[nPage];
969
pPage->SetSize(rc.left, rc.top, width, height);
973
// unless we had already repainted everything, we now need to refresh
974
if ( !HasFlag(wxFULL_REPAINT_ON_RESIZE) )
976
// invalidate areas not covered by pages
977
RefreshRect(wxRect(0, 0, widthNbook, rc.top), false);
978
RefreshRect(wxRect(0, rc.top, rc.left, height), false);
979
RefreshRect(wxRect(0, rc.bottom, widthNbook, heightNbook - rc.bottom),
981
RefreshRect(wxRect(rc.right, rc.top, widthNbook - rc.right, height),
985
#if USE_NOTEBOOK_ANTIFLICKER
986
// subclass the spin control used by the notebook to scroll pages to
987
// prevent it from flickering on resize
988
if ( !m_hasSubclassedUpdown )
990
// iterate over all child windows to find spin button
991
for ( HWND child = ::GetWindow(GetHwnd(), GW_CHILD);
993
child = ::GetWindow(child, GW_HWNDNEXT) )
995
wxWindow *childWindow = wxFindWinFromHandle((WXHWND)child);
997
// see if it exists, if no wxWindow found then assume it's the spin
1001
// subclass the spin button to override WM_ERASEBKGND
1002
if ( !gs_wndprocNotebookSpinBtn )
1003
gs_wndprocNotebookSpinBtn = (WXFARPROC)wxGetWindowProc(child);
1005
wxSetWindowProc(child, wxNotebookSpinBtnWndProc);
1006
m_hasSubclassedUpdown = true;
1011
#endif // USE_NOTEBOOK_ANTIFLICKER
1016
void wxNotebook::OnSelChange(wxNotebookEvent& event)
1018
// is it our tab control?
1019
if ( event.GetEventObject() == this )
1021
int sel = event.GetOldSelection();
1023
m_pages[sel]->Show(false);
1025
sel = event.GetSelection();
1028
wxNotebookPage *pPage = m_pages[sel];
1031
// As per bug report:
1032
// http://sourceforge.net/tracker/index.php?func=detail&aid=1150659&group_id=9863&atid=109863,
1033
// we should not set the page focus (and thereby the focus for
1034
// a child window) since it erroneously selects radio button controls and also
1035
// breaks keyboard handling for a notebook's scroll buttons. So
1036
// we always focus the notebook and not the page.
1040
else // no pages in the notebook, give the focus to itself
1048
// we want to give others a chance to process this message as well
1052
bool wxNotebook::MSWTranslateMessage(WXMSG *wxmsg)
1054
const MSG * const msg = (MSG *)wxmsg;
1056
// intercept TAB, CTRL+TAB and CTRL+SHIFT+TAB for processing by wxNotebook.
1057
// TAB will be passed to the currently selected page, CTRL+TAB and
1058
// CTRL+SHIFT+TAB will be processed by the notebook itself. do not
1059
// intercept SHIFT+TAB. This goes to the parent of the notebook which will
1061
if ( msg->message == WM_KEYDOWN && msg->wParam == VK_TAB &&
1062
msg->hwnd == GetHwnd() &&
1063
(wxIsCtrlDown() || !wxIsShiftDown()) )
1065
return MSWProcessMessage(wxmsg);
1071
void wxNotebook::OnNavigationKey(wxNavigationKeyEvent& event)
1073
if ( event.IsWindowChange() ) {
1075
AdvanceSelection(event.GetDirection());
1078
// we get this event in 3 cases
1080
// a) one of our pages might have generated it because the user TABbed
1081
// out from it in which case we should propagate the event upwards and
1082
// our parent will take care of setting the focus to prev/next sibling
1086
// b) the parent panel wants to give the focus to us so that we
1087
// forward it to our selected page. We can't deal with this in
1088
// OnSetFocus() because we don't know which direction the focus came
1089
// from in this case and so can't choose between setting the focus to
1090
// first or last panel child
1094
// c) we ourselves (see MSWTranslateMessage) generated the event
1096
wxWindow * const parent = GetParent();
1098
// the wxObject* casts are required to avoid MinGW GCC 2.95.3 ICE
1099
const bool isFromParent = event.GetEventObject() == (wxObject*) parent;
1100
const bool isFromSelf = event.GetEventObject() == (wxObject*) this;
1102
if ( isFromParent || isFromSelf )
1104
// no, it doesn't come from child, case (b) or (c): forward to a
1105
// page but only if direction is backwards (TAB) or from ourselves,
1106
if ( m_nSelection != -1 &&
1107
(!event.GetDirection() || isFromSelf) )
1109
// so that the page knows that the event comes from it's parent
1110
// and is being propagated downwards
1111
event.SetEventObject(this);
1113
wxWindow *page = m_pages[m_nSelection];
1114
if ( !page->GetEventHandler()->ProcessEvent(event) )
1118
//else: page manages focus inside it itself
1120
else // otherwise set the focus to the notebook itself
1127
// it comes from our child, case (a), pass to the parent, but only
1128
// if the direction is forwards. Otherwise set the focus to the
1129
// notebook itself. The notebook is always the 'first' control of a
1131
if ( !event.GetDirection() )
1137
event.SetCurrentFocus(this);
1138
parent->GetEventHandler()->ProcessEvent(event);
1146
bool wxNotebook::DoDrawBackground(WXHDC hDC, wxWindow *child)
1148
wxUxThemeHandle theme(child ? child : this, L"TAB");
1152
// get the notebook client rect (we're not interested in drawing tabs
1154
wxRect r = GetPageSize();
1159
wxCopyRectToRECT(r, rc);
1161
// map rect to the coords of the window we're drawing in
1163
::MapWindowPoints(GetHwnd(), GetHwndOf(child), (POINT *)&rc, 2);
1165
// we have the content area (page size), but we need to draw all of the
1166
// background for it to be aligned correctly
1167
wxUxThemeEngine::Get()->GetThemeBackgroundExtent
1176
wxUxThemeEngine::Get()->DrawThemeBackground
1189
WXHBRUSH wxNotebook::QueryBgBitmap()
1191
wxRect r = GetPageSize();
1195
WindowHDC hDC(GetHwnd());
1196
MemoryHDC hDCMem(hDC);
1197
CompatibleBitmap hBmp(hDC, r.x + r.width, r.y + r.height);
1199
SelectInHDC selectBmp(hDCMem, hBmp);
1201
if ( !DoDrawBackground((WXHDC)(HDC)hDCMem) )
1204
return (WXHBRUSH)::CreatePatternBrush(hBmp);
1207
void wxNotebook::UpdateBgBrush()
1209
if ( m_hbrBackground )
1210
::DeleteObject((HBRUSH)m_hbrBackground);
1212
if ( !m_hasBgCol && wxUxThemeEngine::GetIfActive() )
1214
m_hbrBackground = QueryBgBitmap();
1216
else // no themes or we've got user-defined solid colour
1218
m_hbrBackground = NULL;
1222
WXHBRUSH wxNotebook::MSWGetBgBrushForChild(WXHDC hDC, WXHWND hWnd)
1224
if ( m_hbrBackground )
1226
// before drawing with the background brush, we need to position it
1229
::GetWindowRect((HWND)hWnd, &rc);
1231
::MapWindowPoints(NULL, GetHwnd(), (POINT *)&rc, 1);
1233
if ( !::SetBrushOrgEx((HDC)hDC, -rc.left, -rc.top, NULL) )
1235
wxLogLastError(_T("SetBrushOrgEx(notebook bg brush)"));
1238
return m_hbrBackground;
1241
return wxNotebookBase::MSWGetBgBrushForChild(hDC, hWnd);
1244
bool wxNotebook::MSWPrintChild(WXHDC hDC, wxWindow *child)
1246
// solid background colour overrides themed background drawing
1247
if ( !UseBgCol() && DoDrawBackground(hDC, child) )
1250
// If we're using a solid colour (for example if we've switched off
1251
// theming for this notebook), paint it
1254
wxRect r = GetPageSize();
1259
wxCopyRectToRECT(r, rc);
1261
// map rect to the coords of the window we're drawing in
1263
::MapWindowPoints(GetHwnd(), GetHwndOf(child), (POINT *)&rc, 2);
1265
wxBrush brush(GetBackgroundColour());
1266
HBRUSH hbr = GetHbrushOf(brush);
1268
::FillRect((HDC) hDC, &rc, hbr);
1273
return wxNotebookBase::MSWPrintChild(hDC, child);
1276
#endif // wxUSE_UXTHEME
1278
// Windows only: attempts to get colour for UX theme page background
1279
wxColour wxNotebook::GetThemeBackgroundColour() const
1282
if (wxUxThemeEngine::Get())
1284
wxUxThemeHandle hTheme((wxNotebook*) this, L"TAB");
1287
// This is total guesswork.
1288
// See PlatformSDK\Include\Tmschema.h for values
1289
COLORREF themeColor;
1290
wxUxThemeEngine::Get()->GetThemeColor(
1294
3821 /* FILLCOLORHINT */,
1298
[DS] Workaround for WindowBlinds:
1299
Some themes return a near black theme color using FILLCOLORHINT,
1300
this makes notebook pages have an ugly black background and makes
1301
text (usually black) unreadable. Retry again with FILLCOLOR.
1303
This workaround potentially breaks appearance of some themes,
1304
but in practice it already fixes some themes.
1306
if (themeColor == 1)
1308
wxUxThemeEngine::Get()->GetThemeColor(
1312
3802 /* FILLCOLOR */,
1316
return wxRGBToColour(themeColor);
1319
#endif // wxUSE_UXTHEME
1321
return GetBackgroundColour();
1324
// ----------------------------------------------------------------------------
1325
// wxNotebook base class virtuals
1326
// ----------------------------------------------------------------------------
1328
#if wxUSE_CONSTRAINTS
1330
// override these 2 functions to do nothing: everything is done in OnSize
1332
void wxNotebook::SetConstraintSizes(bool WXUNUSED(recurse))
1334
// don't set the sizes of the pages - their correct size is not yet known
1335
wxControl::SetConstraintSizes(false);
1338
bool wxNotebook::DoPhase(int WXUNUSED(nPhase))
1343
#endif // wxUSE_CONSTRAINTS
1345
// ----------------------------------------------------------------------------
1346
// wxNotebook Windows message handlers
1347
// ----------------------------------------------------------------------------
1349
bool wxNotebook::MSWOnScroll(int orientation, WXWORD nSBCode,
1350
WXWORD pos, WXHWND control)
1352
// don't generate EVT_SCROLLWIN events for the WM_SCROLLs coming from the
1357
return wxNotebookBase::MSWOnScroll(orientation, nSBCode, pos, control);
1360
bool wxNotebook::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM* result)
1362
wxNotebookEvent event(wxEVT_NULL, m_windowId);
1364
NMHDR* hdr = (NMHDR *)lParam;
1365
switch ( hdr->code ) {
1367
event.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED);
1370
case TCN_SELCHANGING:
1371
event.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING);
1375
return wxControl::MSWOnNotify(idCtrl, lParam, result);
1378
event.SetSelection(TabCtrl_GetCurSel(GetHwnd()));
1379
event.SetOldSelection(m_nSelection);
1380
event.SetEventObject(this);
1381
event.SetInt(idCtrl);
1383
bool processed = GetEventHandler()->ProcessEvent(event);
1384
*result = !event.IsAllowed();
1388
#endif // wxUSE_NOTEBOOK