]> git.saurik.com Git - wxWidgets.git/blame - src/msw/notebook.cpp
font inheritance fixes (patch 983251)
[wxWidgets.git] / src / msw / notebook.cpp
CommitLineData
88310e2e
VZ
1///////////////////////////////////////////////////////////////////////////////
2// Name: msw/notebook.cpp
3// Purpose: implementation of wxNotebook
4// Author: Vadim Zeitlin
907f37b3 5// Modified by:
88310e2e
VZ
6// Created: 11.06.98
7// RCS-ID: $Id$
8// Copyright: (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
65571936 9// Licence: wxWindows licence
88310e2e
VZ
10///////////////////////////////////////////////////////////////////////////////
11
14f355c2 12#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
a3b46648 13#pragma implementation "notebook.h"
88310e2e
VZ
14#endif
15
16// For compilers that support precompilation, includes "wx.h".
17#include "wx/wxprec.h"
18
19#ifdef __BORLANDC__
1e6feb95 20 #pragma hdrstop
88310e2e
VZ
21#endif
22
1e6feb95
VZ
23#if wxUSE_NOTEBOOK
24
77ffb593 25// wxWidgets
88310e2e 26#ifndef WX_PRECOMP
3096bd2f 27 #include "wx/string.h"
88310e2e
VZ
28#endif // WX_PRECOMP
29
3096bd2f
VZ
30#include "wx/log.h"
31#include "wx/imaglist.h"
32#include "wx/event.h"
33#include "wx/control.h"
34#include "wx/notebook.h"
04eb05b0 35#include "wx/app.h"
88310e2e 36
3096bd2f 37#include "wx/msw/private.h"
88310e2e
VZ
38
39// Windows standard headers
40#ifndef __WIN95__
2432b92d 41 #error "wxNotebook is only supported Windows 95 and above"
88310e2e
VZ
42#endif //Win95
43
aaab7c01
VZ
44#include <windowsx.h> // for SetWindowFont
45
b39dbf34
JS
46#ifdef __GNUWIN32_OLD__
47 #include "wx/msw/gnuwin32/extra.h"
65fd5cb0 48#endif
57c208c5 49
b39dbf34 50#if defined(__WIN95__) && !(defined(__GNUWIN32_OLD__) && !defined(__CYGWIN10__))
c42404a5 51 #include <commctrl.h>
88310e2e
VZ
52#endif
53
85b43fbf
JS
54#include "wx/msw/winundef.h"
55
56#if wxUSE_UXTHEME
57#include "wx/msw/uxtheme.h"
58
59#include "wx/radiobut.h"
60#include "wx/radiobox.h"
61#include "wx/checkbox.h"
62#include "wx/bmpbuttn.h"
63#include "wx/statline.h"
64#include "wx/statbox.h"
65#include "wx/stattext.h"
66#include "wx/slider.h"
67#include "wx/scrolwin.h"
68#include "wx/panel.h"
69#endif
70
88310e2e
VZ
71// ----------------------------------------------------------------------------
72// macros
73// ----------------------------------------------------------------------------
74
75// check that the page index is valid
8d34bf5c 76#define IS_VALID_PAGE(nPage) ((nPage) < GetPageCount())
88310e2e
VZ
77
78// hide the ugly cast
79#define m_hwnd (HWND)GetHWND()
80
74b31181
VZ
81// ----------------------------------------------------------------------------
82// constants
83// ----------------------------------------------------------------------------
84
85// This is a work-around for missing defines in gcc-2.95 headers
86#ifndef TCS_RIGHT
87 #define TCS_RIGHT 0x0002
88#endif
89
90#ifndef TCS_VERTICAL
91 #define TCS_VERTICAL 0x0080
92#endif
93
94#ifndef TCS_BOTTOM
95 #define TCS_BOTTOM TCS_RIGHT
96#endif
97
88310e2e
VZ
98// ----------------------------------------------------------------------------
99// event table
100// ----------------------------------------------------------------------------
101
51741307
SC
102#include <wx/listimpl.cpp>
103
104WX_DEFINE_LIST( wxNotebookPageInfoList ) ;
105
2e4df4bf
VZ
106DEFINE_EVENT_TYPE(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED)
107DEFINE_EVENT_TYPE(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING)
108
d9317fd4 109BEGIN_EVENT_TABLE(wxNotebook, wxControl)
88310e2e
VZ
110 EVT_NOTEBOOK_PAGE_CHANGED(-1, wxNotebook::OnSelChange)
111
9026ad85 112 EVT_SIZE(wxNotebook::OnSize)
42e69d6b 113
88310e2e 114 EVT_NAVIGATION_KEY(wxNotebook::OnNavigationKey)
d9317fd4 115END_EVENT_TABLE()
88310e2e 116
51596bcb 117#if wxUSE_EXTENDED_RTTI
bc9fb572
JS
118WX_DEFINE_FLAGS( wxNotebookStyle )
119
3ff066a4 120wxBEGIN_FLAGS( wxNotebookStyle )
bc9fb572
JS
121 // new style border flags, we put them first to
122 // use them for streaming out
3ff066a4
SC
123 wxFLAGS_MEMBER(wxBORDER_SIMPLE)
124 wxFLAGS_MEMBER(wxBORDER_SUNKEN)
125 wxFLAGS_MEMBER(wxBORDER_DOUBLE)
126 wxFLAGS_MEMBER(wxBORDER_RAISED)
127 wxFLAGS_MEMBER(wxBORDER_STATIC)
128 wxFLAGS_MEMBER(wxBORDER_NONE)
bc9fb572
JS
129
130 // old style border flags
3ff066a4
SC
131 wxFLAGS_MEMBER(wxSIMPLE_BORDER)
132 wxFLAGS_MEMBER(wxSUNKEN_BORDER)
133 wxFLAGS_MEMBER(wxDOUBLE_BORDER)
134 wxFLAGS_MEMBER(wxRAISED_BORDER)
135 wxFLAGS_MEMBER(wxSTATIC_BORDER)
cb0afb26 136 wxFLAGS_MEMBER(wxBORDER)
bc9fb572
JS
137
138 // standard window styles
3ff066a4
SC
139 wxFLAGS_MEMBER(wxTAB_TRAVERSAL)
140 wxFLAGS_MEMBER(wxCLIP_CHILDREN)
141 wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW)
142 wxFLAGS_MEMBER(wxWANTS_CHARS)
cb0afb26 143 wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE)
3ff066a4
SC
144 wxFLAGS_MEMBER(wxALWAYS_SHOW_SB )
145 wxFLAGS_MEMBER(wxVSCROLL)
146 wxFLAGS_MEMBER(wxHSCROLL)
147
148 wxFLAGS_MEMBER(wxNB_FIXEDWIDTH)
149 wxFLAGS_MEMBER(wxNB_LEFT)
150 wxFLAGS_MEMBER(wxNB_RIGHT)
151 wxFLAGS_MEMBER(wxNB_BOTTOM)
152
153wxEND_FLAGS( wxNotebookStyle )
51741307 154
51596bcb 155IMPLEMENT_DYNAMIC_CLASS_XTI(wxNotebook, wxControl,"wx/notebook.h")
51741307
SC
156IMPLEMENT_DYNAMIC_CLASS_XTI(wxNotebookPageInfo, wxObject , "wx/notebook.h" )
157
3ff066a4 158wxCOLLECTION_TYPE_INFO( wxNotebookPageInfo * , wxNotebookPageInfoList ) ;
51596bcb 159
f0a126fe
SC
160template<> void wxCollectionToVariantArray( wxNotebookPageInfoList const &theList, wxxVariantArray &value)
161{
0c6b0084 162 wxListCollectionToVariantArray<wxNotebookPageInfoList::compatibility_iterator>( theList , value ) ;
f0a126fe
SC
163}
164
3ff066a4
SC
165wxBEGIN_PROPERTIES_TABLE(wxNotebook)
166 wxEVENT_PROPERTY( PageChanging , wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING , wxNotebookEvent )
167 wxEVENT_PROPERTY( PageChanged , wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED , wxNotebookEvent )
c5ca409b 168
3ff066a4 169 wxPROPERTY_COLLECTION( PageInfos , wxNotebookPageInfoList , wxNotebookPageInfo* , AddPageInfo , GetPageInfos , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
af498247 170 wxPROPERTY_FLAGS( WindowStyle , wxNotebookStyle , long , SetWindowStyleFlag , GetWindowStyleFlag , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
3ff066a4 171wxEND_PROPERTIES_TABLE()
51596bcb 172
3ff066a4
SC
173wxBEGIN_HANDLERS_TABLE(wxNotebook)
174wxEND_HANDLERS_TABLE()
51596bcb 175
3ff066a4 176wxCONSTRUCTOR_5( wxNotebook , wxWindow* , Parent , wxWindowID , Id , wxPoint , Position , wxSize , Size , long , WindowStyle)
51596bcb 177
51741307 178
3ff066a4 179wxBEGIN_PROPERTIES_TABLE(wxNotebookPageInfo)
af498247 180 wxREADONLY_PROPERTY( Page , wxNotebookPage* , GetPage , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
3ff066a4
SC
181 wxREADONLY_PROPERTY( Text , wxString , GetText , wxString() , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
182 wxREADONLY_PROPERTY( Selected , bool , GetSelected , false, 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
183 wxREADONLY_PROPERTY( ImageId , int , GetImageId , -1 , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
184wxEND_PROPERTIES_TABLE()
51741307 185
3ff066a4
SC
186wxBEGIN_HANDLERS_TABLE(wxNotebookPageInfo)
187wxEND_HANDLERS_TABLE()
51741307 188
3ff066a4 189wxCONSTRUCTOR_4( wxNotebookPageInfo , wxNotebookPage* , Page , wxString , Text , bool , Selected , int , ImageId )
51741307 190
51596bcb 191#else
d9317fd4 192IMPLEMENT_DYNAMIC_CLASS(wxNotebook, wxControl)
51741307 193IMPLEMENT_DYNAMIC_CLASS(wxNotebookPageInfo, wxObject )
51596bcb 194#endif
d9317fd4 195IMPLEMENT_DYNAMIC_CLASS(wxNotebookEvent, wxNotifyEvent)
88310e2e
VZ
196
197// ============================================================================
198// implementation
199// ============================================================================
200
201// ----------------------------------------------------------------------------
202// wxNotebook construction
203// ----------------------------------------------------------------------------
204
51741307
SC
205const wxNotebookPageInfoList& wxNotebook::GetPageInfos() const
206{
207 wxNotebookPageInfoList* list = const_cast< wxNotebookPageInfoList* >( &m_pageInfos ) ;
208 WX_CLEAR_LIST( wxNotebookPageInfoList , *list ) ;
34a0c9f4 209 for( size_t i = 0 ; i < GetPageCount() ; ++i )
51741307
SC
210 {
211 wxNotebookPageInfo *info = new wxNotebookPageInfo() ;
34a0c9f4 212 info->Create( const_cast<wxNotebook*>(this)->GetPage(i) , GetPageText(i) , GetSelection() == int(i) , GetPageImage(i) ) ;
51741307
SC
213 list->Append( info ) ;
214 }
215 return m_pageInfos ;
216}
217
88310e2e
VZ
218// common part of all ctors
219void wxNotebook::Init()
220{
1e6feb95 221 m_imageList = NULL;
88310e2e
VZ
222 m_nSelection = -1;
223}
224
225// default for dynamic class
226wxNotebook::wxNotebook()
227{
228 Init();
229}
230
231// the same arguments as for wxControl
232wxNotebook::wxNotebook(wxWindow *parent,
8b9518ee 233 wxWindowID id,
88310e2e
VZ
234 const wxPoint& pos,
235 const wxSize& size,
8b9518ee 236 long style,
88310e2e
VZ
237 const wxString& name)
238{
239 Init();
240
241 Create(parent, id, pos, size, style, name);
242}
243
244// Create() function
245bool wxNotebook::Create(wxWindow *parent,
8b9518ee 246 wxWindowID id,
88310e2e
VZ
247 const wxPoint& pos,
248 const wxSize& size,
8b9518ee 249 long style,
88310e2e
VZ
250 const wxString& name)
251{
04eb05b0
JS
252 // Does ComCtl32 support non-top tabs?
253 int verComCtl32 = wxApp::GetComCtl32Version();
254 if ( verComCtl32 < 470 || verComCtl32 >= 600 )
255 {
256 if (style & wxNB_BOTTOM)
257 style &= ~wxNB_BOTTOM;
258
259 if (style & wxNB_LEFT)
260 style &= ~wxNB_LEFT;
261
262 if (style & wxNB_RIGHT)
263 style &= ~wxNB_RIGHT;
264 }
265
6dd16e4f
VZ
266 if ( !CreateControl(parent, id, pos, size, style | wxTAB_TRAVERSAL,
267 wxDefaultValidator, name) )
b8bdaa7c 268 return false;
88310e2e 269
fda7962d 270 if ( !MSWCreateControl(WC_TABCONTROL, wxEmptyString, pos, size) )
b8bdaa7c 271 return false;
907f37b3 272
0df3fbd7
VZ
273 SetBackgroundColour(wxColour(::GetSysColor(COLOR_BTNFACE)));
274
b8bdaa7c 275 return true;
0df3fbd7
VZ
276}
277
278WXDWORD wxNotebook::MSWGetStyle(long style, WXDWORD *exstyle) const
279{
280 WXDWORD tabStyle = wxControl::MSWGetStyle(style, exstyle);
281
282 tabStyle |= WS_TABSTOP | TCS_TABS;
283
2b5f62a0 284 if ( style & wxNB_MULTILINE )
0df3fbd7
VZ
285 tabStyle |= TCS_MULTILINE;
286 if ( style & wxNB_FIXEDWIDTH )
287 tabStyle |= TCS_FIXEDWIDTH;
288
289 if ( style & wxNB_BOTTOM )
290 tabStyle |= TCS_RIGHT;
291 else if ( style & wxNB_LEFT )
292 tabStyle |= TCS_VERTICAL;
293 else if ( style & wxNB_RIGHT )
294 tabStyle |= TCS_VERTICAL | TCS_RIGHT;
295
296 // ex style
297 if ( exstyle )
298 {
299 // note that we never want to have the default WS_EX_CLIENTEDGE style
300 // as it looks too ugly for the notebooks
301 *exstyle = 0;
302 }
303
304 return tabStyle;
88310e2e
VZ
305}
306
88310e2e
VZ
307// ----------------------------------------------------------------------------
308// wxNotebook accessors
309// ----------------------------------------------------------------------------
07b8d7ec 310
8d34bf5c 311size_t wxNotebook::GetPageCount() const
88310e2e
VZ
312{
313 // consistency check
1e6feb95 314 wxASSERT( (int)m_pages.Count() == TabCtrl_GetItemCount(m_hwnd) );
88310e2e 315
1e6feb95 316 return m_pages.Count();
88310e2e
VZ
317}
318
319int wxNotebook::GetRowCount() const
320{
321 return TabCtrl_GetRowCount(m_hwnd);
322}
323
8d34bf5c 324int wxNotebook::SetSelection(size_t nPage)
88310e2e 325{
223d09f6 326 wxCHECK_MSG( IS_VALID_PAGE(nPage), -1, wxT("notebook page out of range") );
88310e2e 327
34a0c9f4 328 if ( int(nPage) != m_nSelection )
2b5f62a0
VZ
329 {
330 wxNotebookEvent event(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING, m_windowId);
331 event.SetSelection(nPage);
332 event.SetOldSelection(m_nSelection);
333 event.SetEventObject(this);
334 if ( !GetEventHandler()->ProcessEvent(event) || event.IsAllowed() )
335 {
336 // program allows the page change
337 event.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED);
338 (void)GetEventHandler()->ProcessEvent(event);
339
340 TabCtrl_SetCurSel(m_hwnd, nPage);
341 }
342 }
88310e2e 343
2b5f62a0 344 return m_nSelection;
88310e2e
VZ
345}
346
8d34bf5c 347bool wxNotebook::SetPageText(size_t nPage, const wxString& strText)
88310e2e 348{
b8bdaa7c 349 wxCHECK_MSG( IS_VALID_PAGE(nPage), false, wxT("notebook page out of range") );
88310e2e
VZ
350
351 TC_ITEM tcItem;
352 tcItem.mask = TCIF_TEXT;
837e5743 353 tcItem.pszText = (wxChar *)strText.c_str();
88310e2e
VZ
354
355 return TabCtrl_SetItem(m_hwnd, nPage, &tcItem) != 0;
356}
357
8d34bf5c 358wxString wxNotebook::GetPageText(size_t nPage) const
88310e2e 359{
fda7962d 360 wxCHECK_MSG( IS_VALID_PAGE(nPage), wxEmptyString, wxT("notebook page out of range") );
88310e2e 361
837e5743 362 wxChar buf[256];
88310e2e
VZ
363 TC_ITEM tcItem;
364 tcItem.mask = TCIF_TEXT;
365 tcItem.pszText = buf;
366 tcItem.cchTextMax = WXSIZEOF(buf);
367
368 wxString str;
369 if ( TabCtrl_GetItem(m_hwnd, nPage, &tcItem) )
370 str = tcItem.pszText;
371
372 return str;
373}
374
8d34bf5c 375int wxNotebook::GetPageImage(size_t nPage) const
88310e2e 376{
223d09f6 377 wxCHECK_MSG( IS_VALID_PAGE(nPage), -1, wxT("notebook page out of range") );
88310e2e
VZ
378
379 TC_ITEM tcItem;
380 tcItem.mask = TCIF_IMAGE;
381
382 return TabCtrl_GetItem(m_hwnd, nPage, &tcItem) ? tcItem.iImage : -1;
383}
384
8d34bf5c 385bool wxNotebook::SetPageImage(size_t nPage, int nImage)
88310e2e 386{
b8bdaa7c 387 wxCHECK_MSG( IS_VALID_PAGE(nPage), false, wxT("notebook page out of range") );
88310e2e
VZ
388
389 TC_ITEM tcItem;
390 tcItem.mask = TCIF_IMAGE;
391 tcItem.iImage = nImage;
392
393 return TabCtrl_SetItem(m_hwnd, nPage, &tcItem) != 0;
394}
395
396void wxNotebook::SetImageList(wxImageList* imageList)
907f37b3 397{
07b8d7ec
VZ
398 wxNotebookBase::SetImageList(imageList);
399
400 if ( imageList )
1e6feb95 401 {
07b8d7ec 402 TabCtrl_SetImageList(m_hwnd, (HIMAGELIST)imageList->GetHIMAGELIST());
1e6feb95 403 }
b656febd
VS
404}
405
d9506e77
VZ
406// ----------------------------------------------------------------------------
407// wxNotebook size settings
408// ----------------------------------------------------------------------------
409
410void wxNotebook::SetPageSize(const wxSize& size)
411{
412 // transform the page size into the notebook size
413 RECT rc;
414 rc.left =
415 rc.top = 0;
416 rc.right = size.x;
417 rc.bottom = size.y;
418
b8bdaa7c 419 TabCtrl_AdjustRect(GetHwnd(), true, &rc);
d9506e77
VZ
420
421 // and now set it
422 SetSize(rc.right - rc.left, rc.bottom - rc.top);
423}
424
425void wxNotebook::SetPadding(const wxSize& padding)
426{
427 TabCtrl_SetPadding(GetHwnd(), padding.x, padding.y);
428}
42e69d6b
VZ
429
430// Windows-only at present. Also, you must use the wxNB_FIXEDWIDTH
431// style.
432void wxNotebook::SetTabSize(const wxSize& sz)
433{
434 ::SendMessage(GetHwnd(), TCM_SETITEMSIZE, 0, MAKELPARAM(sz.x, sz.y));
435}
436
2ce7af35
JS
437wxSize wxNotebook::CalcSizeFromPage(const wxSize& sizePage) const
438{
439 wxSize sizeTotal = sizePage;
440
77ffb593 441 // We need to make getting tab size part of the wxWidgets API.
2ce7af35
JS
442 wxSize tabSize(0, 0);
443 if (GetPageCount() > 0)
444 {
445 RECT rect;
446 TabCtrl_GetItemRect((HWND) GetHWND(), 0, & rect);
447 tabSize.x = rect.right - rect.left;
448 tabSize.y = rect.bottom - rect.top;
449 }
450 if ( HasFlag(wxNB_LEFT) || HasFlag(wxNB_RIGHT) )
451 {
452 sizeTotal.x += tabSize.x + 7;
453 sizeTotal.y += 7;
454 }
455 else
456 {
457 sizeTotal.x += 7;
458 sizeTotal.y += tabSize.y + 7;
459 }
460
461 return sizeTotal;
462}
463
2015f2b3
VZ
464void wxNotebook::AdjustPageSize(wxNotebookPage *page)
465{
466 wxCHECK_RET( page, _T("NULL page in wxNotebook::AdjustPageSize") );
467
468 RECT rc;
469 rc.left =
470 rc.top = 0;
471
472 // get the page size from the notebook size
473 GetSize((int *)&rc.right, (int *)&rc.bottom);
2015f2b3 474
14a6b6e5
RD
475 // This check is to work around a bug in TabCtrl_AdjustRect which will
476 // cause a crash on win2k, or on XP with themes disabled, if the
477 // wxNB_MULTILINE style is used and the rectangle is very small, (such as
478 // when the notebook is first created.) The value of 20 is just
479 // arbitrarily chosen, if there is a better way to determine this value
480 // then please do so. --RD
481 if (rc.right > 20 && rc.bottom > 20)
482 {
483 TabCtrl_AdjustRect(m_hwnd, false, &rc);
484 page->SetSize(rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top);
485 }
2015f2b3
VZ
486}
487
88310e2e
VZ
488// ----------------------------------------------------------------------------
489// wxNotebook operations
490// ----------------------------------------------------------------------------
491
621793f4 492// remove one page from the notebook, without deleting
8d34bf5c 493wxNotebookPage *wxNotebook::DoRemovePage(size_t nPage)
621793f4 494{
df7145da
VZ
495 wxNotebookPage *pageRemoved = wxNotebookBase::DoRemovePage(nPage);
496 if ( !pageRemoved )
497 return NULL;
621793f4 498
df7145da 499 TabCtrl_DeleteItem(m_hwnd, nPage);
621793f4 500
df7145da
VZ
501 if ( m_pages.IsEmpty() )
502 {
503 // no selection any more, the notebook becamse empty
504 m_nSelection = -1;
505 }
506 else // notebook still not empty
507 {
623f5f70
JS
508 int selNew = TabCtrl_GetCurSel(m_hwnd);
509 if (selNew != -1)
df7145da 510 {
623f5f70
JS
511 // No selection change, just refresh the current selection.
512 // Because it could be that the slection index changed
513 // we need to update it.
514 // Note: this does not mean the selection it self changed.
515 m_nSelection = selNew;
516 m_pages[m_nSelection]->Refresh();
df7145da 517 }
623f5f70 518 else if (int(nPage) == m_nSelection)
43a997b6 519 {
623f5f70
JS
520 // The selection was deleted.
521
522 // Determine new selection.
523 if (m_nSelection == int(GetPageCount()))
524 selNew = m_nSelection - 1;
525 else
526 selNew = m_nSelection;
527
43a997b6
VZ
528 // m_nSelection must be always valid so reset it before calling
529 // SetSelection()
530 m_nSelection = -1;
531 SetSelection(selNew);
532 }
623f5f70
JS
533 else
534 {
535 wxFAIL; // Windows did not behave ok.
536 }
df7145da 537 }
47f12f58 538
df7145da 539 return pageRemoved;
621793f4
JS
540}
541
88310e2e
VZ
542// remove all pages
543bool wxNotebook::DeleteAllPages()
544{
8d34bf5c
VZ
545 size_t nPageCount = GetPageCount();
546 size_t nPage;
88310e2e 547 for ( nPage = 0; nPage < nPageCount; nPage++ )
1e6feb95 548 delete m_pages[nPage];
88310e2e 549
1e6feb95 550 m_pages.Clear();
88310e2e 551
907f37b3
VZ
552 TabCtrl_DeleteAllItems(m_hwnd);
553
47f12f58
JS
554 m_nSelection = -1;
555
b8bdaa7c 556 return true;
88310e2e
VZ
557}
558
88310e2e 559// same as AddPage() but does it at given position
8d34bf5c 560bool wxNotebook::InsertPage(size_t nPage,
88310e2e
VZ
561 wxNotebookPage *pPage,
562 const wxString& strText,
563 bool bSelect,
564 int imageId)
565{
b8bdaa7c
VZ
566 wxCHECK_MSG( pPage != NULL, false, _T("NULL page in wxNotebook::InsertPage") );
567 wxCHECK_MSG( IS_VALID_PAGE(nPage) || nPage == GetPageCount(), false,
22f3361e 568 _T("invalid index in wxNotebook::InsertPage") );
88310e2e 569
efa14cf2
VZ
570 wxASSERT_MSG( pPage->GetParent() == this,
571 _T("notebook pages must have notebook as parent") );
43427087 572
85b43fbf 573#if wxUSE_UXTHEME && wxUSE_UXTHEME_AUTO
b8bdaa7c
VZ
574 static bool g_TestedForTheme = false;
575 static bool g_UseTheme = false;
c02e5a31
JS
576 if (!g_TestedForTheme)
577 {
578 int commCtrlVersion = wxTheApp->GetComCtl32Version() ;
579
580 g_UseTheme = (commCtrlVersion >= 600);
b8bdaa7c 581 g_TestedForTheme = true;
c02e5a31
JS
582 }
583
85b43fbf
JS
584 // Automatically apply the theme background,
585 // changing the colour of the panel to match the
586 // tab page colour. This won't work well with all
587 // themes but it's a start.
c02e5a31 588 if (g_UseTheme && wxUxThemeEngine::Get() && pPage->IsKindOf(CLASSINFO(wxPanel)))
85b43fbf
JS
589 {
590 ApplyThemeBackground(pPage, GetThemeBackgroundColour());
591 }
592#endif
593
22f3361e
VZ
594 // add a new tab to the control
595 // ----------------------------
58a8ab88 596
22f3361e
VZ
597 // init all fields to 0
598 TC_ITEM tcItem;
599 wxZeroMemory(tcItem);
58a8ab88 600
22f3361e
VZ
601 // set the image, if any
602 if ( imageId != -1 )
603 {
604 tcItem.mask |= TCIF_IMAGE;
605 tcItem.iImage = imageId;
606 }
88310e2e 607
22f3361e
VZ
608 // and the text
609 if ( !strText.IsEmpty() )
610 {
611 tcItem.mask |= TCIF_TEXT;
612 tcItem.pszText = (wxChar *)strText.c_str(); // const_cast
613 }
43427087 614
22f3361e
VZ
615 // fit the notebook page to the tab control's display area: this should be
616 // done before adding it to the notebook or TabCtrl_InsertItem() will
617 // change the notebooks size itself!
2015f2b3 618 AdjustPageSize(pPage);
43427087 619
22f3361e 620 // finally do insert it
2015f2b3
VZ
621 if ( TabCtrl_InsertItem(m_hwnd, nPage, &tcItem) == -1 )
622 {
22f3361e 623 wxLogError(wxT("Can't create the notebook page '%s'."), strText.c_str());
88310e2e 624
b8bdaa7c 625 return false;
22f3361e 626 }
88310e2e 627
22f3361e
VZ
628 // succeeded: save the pointer to the page
629 m_pages.Insert(pPage, nPage);
42e69d6b 630
56b9925b
VZ
631 // we may need to adjust the size again if the notebook size changed:
632 // normally this only happens for the first page we add (the tabs which
633 // hadn't been there before are now shown) but for a multiline notebook it
634 // can happen for any page at all as a new row could have been started
635 if ( m_pages.GetCount() == 1 || HasFlag(wxNB_MULTILINE) )
2015f2b3
VZ
636 {
637 AdjustPageSize(pPage);
638 }
639
22f3361e
VZ
640 // hide the page: unless it is selected, it shouldn't be shown (and if it
641 // is selected it will be shown later)
642 HWND hwnd = GetWinHwnd(pPage);
643 SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~WS_VISIBLE);
0398b1d6 644
22f3361e
VZ
645 // this updates internal flag too -- otherwise it would get out of sync
646 // with the real state
b8bdaa7c 647 pPage->Show(false);
43427087 648
96d37807 649
22f3361e
VZ
650 // now deal with the selection
651 // ---------------------------
652
653 // if the inserted page is before the selected one, we must update the
654 // index of the selected page
34a0c9f4 655 if ( int(nPage) <= m_nSelection )
22f3361e
VZ
656 {
657 // one extra page added
658 m_nSelection++;
659 }
660
661 // some page should be selected: either this one or the first one if there
662 // is still no selection
663 int selNew = -1;
664 if ( bSelect )
665 selNew = nPage;
666 else if ( m_nSelection == -1 )
667 selNew = 0;
668
669 if ( selNew != -1 )
670 SetSelection(selNew);
671
b8bdaa7c 672 return true;
88310e2e
VZ
673}
674
e450aa69 675int wxNotebook::HitTest(const wxPoint& pt, long *flags) const
ef094fa0
JS
676{
677 TC_HITTESTINFO hitTestInfo;
678 hitTestInfo.pt.x = pt.x;
679 hitTestInfo.pt.y = pt.y;
e450aa69 680 int item = TabCtrl_HitTest(GetHwnd(), &hitTestInfo);
ef094fa0 681
e450aa69
VZ
682 if ( flags )
683 {
684 *flags = 0;
685
686 if ((hitTestInfo.flags & TCHT_NOWHERE) == TCHT_NOWHERE)
687 *flags |= wxNB_HITTEST_NOWHERE;
688 if ((hitTestInfo.flags & TCHT_ONITEM) == TCHT_ONITEM)
689 *flags |= wxNB_HITTEST_ONITEM;
690 if ((hitTestInfo.flags & TCHT_ONITEMICON) == TCHT_ONITEMICON)
691 *flags |= wxNB_HITTEST_ONICON;
692 if ((hitTestInfo.flags & TCHT_ONITEMLABEL) == TCHT_ONITEMLABEL)
693 *flags |= wxNB_HITTEST_ONLABEL;
694 }
ef094fa0
JS
695
696 return item;
697}
698
e450aa69 699
88310e2e
VZ
700// ----------------------------------------------------------------------------
701// wxNotebook callbacks
702// ----------------------------------------------------------------------------
703
9026ad85 704void wxNotebook::OnSize(wxSizeEvent& event)
88310e2e 705{
b5c3b538
VZ
706 // fit the notebook page to the tab control's display area
707 RECT rc;
708 rc.left = rc.top = 0;
709 GetSize((int *)&rc.right, (int *)&rc.bottom);
710
56b9925b
VZ
711 // there seems to be a bug in the implementation of TabCtrl_AdjustRect(): it
712 // returns completely false values for multiline tab controls after the tabs
713 // are added but before getting the first WM_SIZE (off by ~50 pixels, see
714 //
715 // http://sf.net/tracker/index.php?func=detail&aid=645323&group_id=9863&atid=109863
716 //
717 // and the only work around I could find was this ugly hack... without it
718 // simply toggling the "multiline" checkbox in the notebook sample resulted
719 // in a noticeable page displacement
720 if ( HasFlag(wxNB_MULTILINE) )
721 {
722 // avoid an infinite recursion: we get another notification too!
723 static bool s_isInOnSize = false;
724
725 if ( !s_isInOnSize )
726 {
727 s_isInOnSize = true;
728 SendMessage(GetHwnd(), WM_SIZE, SIZE_RESTORED,
729 MAKELPARAM(rc.right, rc.bottom));
730 s_isInOnSize = false;
731 }
732 }
733
b8bdaa7c 734 TabCtrl_AdjustRect(m_hwnd, false, &rc);
4b7f2165
VZ
735
736 int width = rc.right - rc.left,
737 height = rc.bottom - rc.top;
1e6feb95 738 size_t nCount = m_pages.Count();
c86f1403 739 for ( size_t nPage = 0; nPage < nCount; nPage++ ) {
1e6feb95 740 wxNotebookPage *pPage = m_pages[nPage];
4b7f2165 741 pPage->SetSize(rc.left, rc.top, width, height);
b5c3b538
VZ
742 }
743
88310e2e
VZ
744 event.Skip();
745}
746
747void wxNotebook::OnSelChange(wxNotebookEvent& event)
748{
749 // is it our tab control?
750 if ( event.GetEventObject() == this )
5d1d2d46 751 {
5d1d2d46
VZ
752 int sel = event.GetOldSelection();
753 if ( sel != -1 )
b8bdaa7c 754 m_pages[sel]->Show(false);
0398b1d6 755
5d1d2d46
VZ
756 sel = event.GetSelection();
757 if ( sel != -1 )
758 {
1e6feb95 759 wxNotebookPage *pPage = m_pages[sel];
b8bdaa7c 760 pPage->Show(true);
1d5b3bf0
VZ
761 pPage->SetFocus();
762
763 // If the newly focused window is not a child of the new page,
764 // SetFocus was not successful and the notebook itself should be
765 // focused
766 wxWindow *currentFocus = FindFocus();
767 wxWindow *startFocus = currentFocus;
768 while ( currentFocus && currentFocus != pPage && currentFocus != this )
769 currentFocus = currentFocus->GetParent();
770
771 if ( startFocus == pPage || currentFocus != pPage )
772 SetFocus();
773
774 }
775 else // no pages in the notebook, give the focus to itself
776 {
777 SetFocus();
5d1d2d46 778 }
0398b1d6 779
5d1d2d46
VZ
780 m_nSelection = sel;
781 }
88310e2e
VZ
782
783 // we want to give others a chance to process this message as well
784 event.Skip();
785}
786
b8bdaa7c 787bool wxNotebook::MSWTranslateMessage(WXMSG *wxmsg)
88310e2e 788{
b8bdaa7c 789 const MSG * const msg = (MSG *)wxmsg;
88310e2e 790
1d5b3bf0
VZ
791 // intercept TAB, CTRL+TAB and CTRL+SHIFT+TAB for processing by wxNotebook.
792 // TAB will be passed to the currently selected page, CTRL+TAB and
793 // CTRL+SHIFT+TAB will be processed by the notebook itself. do not
794 // intercept SHIFT+TAB. This goes to the parent of the notebook which will
795 // process it.
b8bdaa7c
VZ
796 if ( msg->message == WM_KEYDOWN && msg->wParam == VK_TAB &&
797 msg->hwnd == m_hwnd &&
1d5b3bf0 798 (wxIsCtrlDown() || !wxIsShiftDown()) )
b8bdaa7c
VZ
799 {
800 return MSWProcessMessage(wxmsg);
801 }
d9506e77 802
b8bdaa7c 803 return false;
88310e2e
VZ
804}
805
806void wxNotebook::OnNavigationKey(wxNavigationKeyEvent& event)
807{
d9506e77
VZ
808 if ( event.IsWindowChange() ) {
809 // change pages
810 AdvanceSelection(event.GetDirection());
811 }
812 else {
b8bdaa7c 813 // we get this event in 3 cases
d9506e77
VZ
814 //
815 // a) one of our pages might have generated it because the user TABbed
816 // out from it in which case we should propagate the event upwards and
817 // our parent will take care of setting the focus to prev/next sibling
818 //
819 // or
820 //
821 // b) the parent panel wants to give the focus to us so that we
822 // forward it to our selected page. We can't deal with this in
823 // OnSetFocus() because we don't know which direction the focus came
824 // from in this case and so can't choose between setting the focus to
825 // first or last panel child
b8bdaa7c
VZ
826 //
827 // or
828 //
829 // c) we ourselves (see MSWTranslateMessage) generated the event
830 //
831 wxWindow * const parent = GetParent();
832
833 const bool isFromParent = event.GetEventObject() == parent;
834 const bool isFromSelf = event.GetEventObject() == this;
835
836 if ( isFromParent || isFromSelf )
d9506e77 837 {
b8bdaa7c
VZ
838 // no, it doesn't come from child, case (b) or (c): forward to a
839 // page but only if direction is backwards (TAB) or from ourselves,
840 if ( m_nSelection != -1 &&
841 (!event.GetDirection() || isFromSelf) )
d9506e77
VZ
842 {
843 // so that the page knows that the event comes from it's parent
844 // and is being propagated downwards
845 event.SetEventObject(this);
846
1e6feb95 847 wxWindow *page = m_pages[m_nSelection];
d9506e77
VZ
848 if ( !page->GetEventHandler()->ProcessEvent(event) )
849 {
850 page->SetFocus();
851 }
852 //else: page manages focus inside it itself
853 }
b8bdaa7c 854 else // otherwise set the focus to the notebook itself
d9506e77 855 {
d9506e77
VZ
856 SetFocus();
857 }
858 }
859 else
860 {
b8bdaa7c
VZ
861 // it comes from our child, case (a), pass to the parent, but only
862 // if the direction is forwards. Otherwise set the focus to the
863 // notebook itself. The notebook is always the 'first' control of a
864 // page.
865 if ( !event.GetDirection() )
866 {
867 SetFocus();
868 }
869 else if ( parent )
870 {
d9506e77
VZ
871 event.SetCurrentFocus(this);
872 parent->GetEventHandler()->ProcessEvent(event);
873 }
874 }
88310e2e 875 }
88310e2e
VZ
876}
877
878// ----------------------------------------------------------------------------
879// wxNotebook base class virtuals
880// ----------------------------------------------------------------------------
b5c3b538 881
0b481c72
VZ
882#if wxUSE_CONSTRAINTS
883
b5c3b538
VZ
884// override these 2 functions to do nothing: everything is done in OnSize
885
4b7f2165 886void wxNotebook::SetConstraintSizes(bool WXUNUSED(recurse))
b5c3b538
VZ
887{
888 // don't set the sizes of the pages - their correct size is not yet known
b8bdaa7c 889 wxControl::SetConstraintSizes(false);
b5c3b538
VZ
890}
891
4b7f2165 892bool wxNotebook::DoPhase(int WXUNUSED(nPhase))
b5c3b538 893{
b8bdaa7c 894 return true;
b5c3b538
VZ
895}
896
0b481c72
VZ
897#endif // wxUSE_CONSTRAINTS
898
0df3fbd7
VZ
899// ----------------------------------------------------------------------------
900// wxNotebook Windows message handlers
901// ----------------------------------------------------------------------------
902
903bool wxNotebook::MSWOnScroll(int orientation, WXWORD nSBCode,
904 WXWORD pos, WXHWND control)
905{
906 // don't generate EVT_SCROLLWIN events for the WM_SCROLLs coming from the
907 // up-down control
908 if ( control )
b8bdaa7c 909 return false;
0df3fbd7
VZ
910
911 return wxNotebookBase::MSWOnScroll(orientation, nSBCode, pos, control);
912}
913
a23fd0e1 914bool wxNotebook::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM* result)
88310e2e 915{
93a19f17 916 wxNotebookEvent event(wxEVT_NULL, m_windowId);
88310e2e
VZ
917
918 NMHDR* hdr = (NMHDR *)lParam;
919 switch ( hdr->code ) {
920 case TCN_SELCHANGE:
921 event.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED);
922 break;
923
924 case TCN_SELCHANGING:
925 event.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING);
926 break;
927
fd3f686c 928 default:
a23fd0e1 929 return wxControl::MSWOnNotify(idCtrl, lParam, result);
88310e2e
VZ
930 }
931
93a19f17
VZ
932 event.SetSelection(TabCtrl_GetCurSel(m_hwnd));
933 event.SetOldSelection(m_nSelection);
88310e2e 934 event.SetEventObject(this);
a23fd0e1 935 event.SetInt(idCtrl);
88310e2e 936
fd3f686c
VZ
937 bool processed = GetEventHandler()->ProcessEvent(event);
938 *result = !event.IsAllowed();
939 return processed;
88310e2e
VZ
940}
941
85b43fbf
JS
942// Windows only: attempts to get colour for UX theme page background
943wxColour wxNotebook::GetThemeBackgroundColour()
944{
945#if wxUSE_UXTHEME
946 if (wxUxThemeEngine::Get())
947 {
2142d7ba 948 wxUxThemeHandle hTheme(this, L"TAB");
85b43fbf
JS
949 if (hTheme)
950 {
951 // This is total guesswork.
952 // See PlatformSDK\Include\Tmschema.h for values
953 COLORREF themeColor;
2eb85fe4 954 wxUxThemeEngine::Get()->GetThemeColor(
2142d7ba
VZ
955 hTheme,
956 10 /* TABP_BODY */,
957 1 /* NORMAL */,
20fa338c 958 3821 /* FILLCOLORHINT */,
2eb85fe4 959 &themeColor);
20fa338c
DS
960
961 /*
962 [DS] Workaround for WindowBlinds:
963 Some themes return a near black theme color using FILLCOLORHINT,
964 this makes notebook pages have an ugly black background and makes
965 text (usually black) unreadable. Retry again with FILLCOLOR.
966
967 This workaround potentially breaks appearance of some themes,
968 but in practice it already fixes some themes.
969 */
970 if (themeColor == 1)
971 {
972 wxUxThemeEngine::Get()->GetThemeColor(
973 hTheme,
974 10 /* TABP_BODY */,
975 1 /* NORMAL */,
976 3802 /* FILLCOLOR */,
977 &themeColor);
978 }
979
85b43fbf
JS
980 wxColour colour(GetRValue(themeColor), GetGValue(themeColor), GetBValue(themeColor));
981 return colour;
982 }
983 }
2142d7ba
VZ
984#endif // wxUSE_UXTHEME
985
85b43fbf
JS
986 return GetBackgroundColour();
987}
988
989// Windows only: attempts to apply the UX theme page background to this page
840015f0 990#if wxUSE_UXTHEME
85b43fbf 991void wxNotebook::ApplyThemeBackground(wxWindow* window, const wxColour& colour)
840015f0
MB
992#else
993void wxNotebook::ApplyThemeBackground(wxWindow*, const wxColour&)
994#endif
85b43fbf
JS
995{
996#if wxUSE_UXTHEME
85b43fbf 997
cc0bffac
RD
998 window->ApplyParentThemeBackground(colour);
999
fc10daf3 1000 for ( wxWindowList::compatibility_iterator node = window->GetChildren().GetFirst(); node; node = node->GetNext() )
85b43fbf
JS
1001 {
1002 wxWindow *child = node->GetData();
1003 ApplyThemeBackground(child, colour);
1004 }
1005#endif
1006}
1007
ffafd8a5 1008#if wxUSE_UXTHEME
c140b7e7 1009WXLRESULT wxNotebook::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
c02e5a31 1010{
b8bdaa7c 1011 static bool g_TestedForTheme = false;
ffafd8a5 1012 static bool g_supportsThemes = false;
c02e5a31
JS
1013 switch ( nMsg )
1014 {
1015 case WM_ERASEBKGND:
1016 {
1017 if (!g_TestedForTheme)
1018 {
1019 int commCtrlVersion = wxTheApp->GetComCtl32Version() ;
1020
ffafd8a5 1021 g_supportsThemes = (commCtrlVersion >= 600);
b8bdaa7c 1022 g_TestedForTheme = true;
c02e5a31
JS
1023 }
1024
ffafd8a5 1025 // If currently an XP theme is active, it seems we can get away
c02e5a31 1026 // with not drawing a background, which reduces flicker.
ffafd8a5
DS
1027 if (g_supportsThemes)
1028 {
1029 wxUxThemeEngine *p = wxUxThemeEngine::Get();
1030 if (p && p->IsThemeActive() )
1031 {
1032 return true;
1033 }
1034 }
c02e5a31
JS
1035 }
1036 }
1037
1038 return wxControl::MSWWindowProc(nMsg, wParam, lParam);
1039}
ffafd8a5 1040#endif // #if wxUSE_UXTHEME
c02e5a31 1041
1e6feb95 1042#endif // wxUSE_NOTEBOOK