]> git.saurik.com Git - wxWidgets.git/blame - src/msw/notebook.cpp
Fix for probably rare but potential refcount leak.
[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)
078cf5cb 129
bc9fb572 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
078cf5cb 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
078cf5cb 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;
078cf5cb 258
04eb05b0
JS
259 if (style & wxNB_LEFT)
260 style &= ~wxNB_LEFT;
078cf5cb 261
04eb05b0
JS
262 if (style & wxNB_RIGHT)
263 style &= ~wxNB_RIGHT;
264 }
078cf5cb 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{
078cf5cb 326 wxCHECK_MSG( IS_VALID_PAGE(nPage), wxNOT_FOUND, 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;
078cf5cb 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 511 // No selection change, just refresh the current selection.
078cf5cb
WS
512 // Because it could be that the slection index changed
513 // we need to update it.
623f5f70
JS
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 520 // The selection was deleted.
078cf5cb 521
623f5f70
JS
522 // Determine new selection.
523 if (m_nSelection == int(GetPageCount()))
524 selNew = m_nSelection - 1;
525 else
526 selNew = m_nSelection;
078cf5cb 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
37144cf0 556 InvalidateBestSize();
b8bdaa7c 557 return true;
88310e2e
VZ
558}
559
88310e2e 560// same as AddPage() but does it at given position
8d34bf5c 561bool wxNotebook::InsertPage(size_t nPage,
88310e2e
VZ
562 wxNotebookPage *pPage,
563 const wxString& strText,
564 bool bSelect,
565 int imageId)
566{
b8bdaa7c
VZ
567 wxCHECK_MSG( pPage != NULL, false, _T("NULL page in wxNotebook::InsertPage") );
568 wxCHECK_MSG( IS_VALID_PAGE(nPage) || nPage == GetPageCount(), false,
22f3361e 569 _T("invalid index in wxNotebook::InsertPage") );
88310e2e 570
efa14cf2
VZ
571 wxASSERT_MSG( pPage->GetParent() == this,
572 _T("notebook pages must have notebook as parent") );
43427087 573
85b43fbf 574#if wxUSE_UXTHEME && wxUSE_UXTHEME_AUTO
b8bdaa7c
VZ
575 static bool g_TestedForTheme = false;
576 static bool g_UseTheme = false;
c02e5a31
JS
577 if (!g_TestedForTheme)
578 {
579 int commCtrlVersion = wxTheApp->GetComCtl32Version() ;
078cf5cb 580
c02e5a31 581 g_UseTheme = (commCtrlVersion >= 600);
b8bdaa7c 582 g_TestedForTheme = true;
c02e5a31 583 }
078cf5cb 584
85b43fbf
JS
585 // Automatically apply the theme background,
586 // changing the colour of the panel to match the
587 // tab page colour. This won't work well with all
588 // themes but it's a start.
c02e5a31 589 if (g_UseTheme && wxUxThemeEngine::Get() && pPage->IsKindOf(CLASSINFO(wxPanel)))
85b43fbf
JS
590 {
591 ApplyThemeBackground(pPage, GetThemeBackgroundColour());
592 }
593#endif
594
22f3361e
VZ
595 // add a new tab to the control
596 // ----------------------------
58a8ab88 597
22f3361e
VZ
598 // init all fields to 0
599 TC_ITEM tcItem;
600 wxZeroMemory(tcItem);
58a8ab88 601
22f3361e
VZ
602 // set the image, if any
603 if ( imageId != -1 )
604 {
605 tcItem.mask |= TCIF_IMAGE;
606 tcItem.iImage = imageId;
607 }
88310e2e 608
22f3361e
VZ
609 // and the text
610 if ( !strText.IsEmpty() )
611 {
612 tcItem.mask |= TCIF_TEXT;
613 tcItem.pszText = (wxChar *)strText.c_str(); // const_cast
614 }
43427087 615
e830a6a6
RD
616 // hide the page: unless it is selected, it shouldn't be shown (and if it
617 // is selected it will be shown later)
618 HWND hwnd = GetWinHwnd(pPage);
619 SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~WS_VISIBLE);
620
621 // this updates internal flag too -- otherwise it would get out of sync
622 // with the real state
623 pPage->Show(false);
624
625
22f3361e
VZ
626 // fit the notebook page to the tab control's display area: this should be
627 // done before adding it to the notebook or TabCtrl_InsertItem() will
628 // change the notebooks size itself!
2015f2b3 629 AdjustPageSize(pPage);
43427087 630
22f3361e 631 // finally do insert it
2015f2b3
VZ
632 if ( TabCtrl_InsertItem(m_hwnd, nPage, &tcItem) == -1 )
633 {
22f3361e 634 wxLogError(wxT("Can't create the notebook page '%s'."), strText.c_str());
88310e2e 635
b8bdaa7c 636 return false;
22f3361e 637 }
88310e2e 638
22f3361e
VZ
639 // succeeded: save the pointer to the page
640 m_pages.Insert(pPage, nPage);
42e69d6b 641
56b9925b
VZ
642 // we may need to adjust the size again if the notebook size changed:
643 // normally this only happens for the first page we add (the tabs which
644 // hadn't been there before are now shown) but for a multiline notebook it
645 // can happen for any page at all as a new row could have been started
646 if ( m_pages.GetCount() == 1 || HasFlag(wxNB_MULTILINE) )
2015f2b3
VZ
647 {
648 AdjustPageSize(pPage);
649 }
650
22f3361e
VZ
651 // now deal with the selection
652 // ---------------------------
653
654 // if the inserted page is before the selected one, we must update the
655 // index of the selected page
34a0c9f4 656 if ( int(nPage) <= m_nSelection )
22f3361e
VZ
657 {
658 // one extra page added
659 m_nSelection++;
660 }
661
662 // some page should be selected: either this one or the first one if there
663 // is still no selection
664 int selNew = -1;
665 if ( bSelect )
666 selNew = nPage;
667 else if ( m_nSelection == -1 )
668 selNew = 0;
669
670 if ( selNew != -1 )
671 SetSelection(selNew);
672
37144cf0 673 InvalidateBestSize();
b8bdaa7c 674 return true;
88310e2e
VZ
675}
676
e450aa69 677int wxNotebook::HitTest(const wxPoint& pt, long *flags) const
ef094fa0
JS
678{
679 TC_HITTESTINFO hitTestInfo;
680 hitTestInfo.pt.x = pt.x;
681 hitTestInfo.pt.y = pt.y;
e450aa69 682 int item = TabCtrl_HitTest(GetHwnd(), &hitTestInfo);
ef094fa0 683
e450aa69
VZ
684 if ( flags )
685 {
686 *flags = 0;
687
688 if ((hitTestInfo.flags & TCHT_NOWHERE) == TCHT_NOWHERE)
689 *flags |= wxNB_HITTEST_NOWHERE;
690 if ((hitTestInfo.flags & TCHT_ONITEM) == TCHT_ONITEM)
691 *flags |= wxNB_HITTEST_ONITEM;
692 if ((hitTestInfo.flags & TCHT_ONITEMICON) == TCHT_ONITEMICON)
693 *flags |= wxNB_HITTEST_ONICON;
694 if ((hitTestInfo.flags & TCHT_ONITEMLABEL) == TCHT_ONITEMLABEL)
695 *flags |= wxNB_HITTEST_ONLABEL;
696 }
ef094fa0
JS
697
698 return item;
699}
700
e450aa69 701
88310e2e
VZ
702// ----------------------------------------------------------------------------
703// wxNotebook callbacks
704// ----------------------------------------------------------------------------
705
9026ad85 706void wxNotebook::OnSize(wxSizeEvent& event)
88310e2e 707{
b5c3b538
VZ
708 // fit the notebook page to the tab control's display area
709 RECT rc;
710 rc.left = rc.top = 0;
711 GetSize((int *)&rc.right, (int *)&rc.bottom);
712
56b9925b
VZ
713 // there seems to be a bug in the implementation of TabCtrl_AdjustRect(): it
714 // returns completely false values for multiline tab controls after the tabs
715 // are added but before getting the first WM_SIZE (off by ~50 pixels, see
716 //
717 // http://sf.net/tracker/index.php?func=detail&aid=645323&group_id=9863&atid=109863
718 //
719 // and the only work around I could find was this ugly hack... without it
720 // simply toggling the "multiline" checkbox in the notebook sample resulted
721 // in a noticeable page displacement
722 if ( HasFlag(wxNB_MULTILINE) )
723 {
724 // avoid an infinite recursion: we get another notification too!
725 static bool s_isInOnSize = false;
726
727 if ( !s_isInOnSize )
728 {
729 s_isInOnSize = true;
730 SendMessage(GetHwnd(), WM_SIZE, SIZE_RESTORED,
731 MAKELPARAM(rc.right, rc.bottom));
732 s_isInOnSize = false;
733 }
734 }
735
b8bdaa7c 736 TabCtrl_AdjustRect(m_hwnd, false, &rc);
4b7f2165
VZ
737
738 int width = rc.right - rc.left,
739 height = rc.bottom - rc.top;
1e6feb95 740 size_t nCount = m_pages.Count();
c86f1403 741 for ( size_t nPage = 0; nPage < nCount; nPage++ ) {
1e6feb95 742 wxNotebookPage *pPage = m_pages[nPage];
4b7f2165 743 pPage->SetSize(rc.left, rc.top, width, height);
b5c3b538
VZ
744 }
745
88310e2e
VZ
746 event.Skip();
747}
748
749void wxNotebook::OnSelChange(wxNotebookEvent& event)
750{
751 // is it our tab control?
752 if ( event.GetEventObject() == this )
5d1d2d46 753 {
5d1d2d46
VZ
754 int sel = event.GetOldSelection();
755 if ( sel != -1 )
b8bdaa7c 756 m_pages[sel]->Show(false);
0398b1d6 757
5d1d2d46
VZ
758 sel = event.GetSelection();
759 if ( sel != -1 )
760 {
1e6feb95 761 wxNotebookPage *pPage = m_pages[sel];
b8bdaa7c 762 pPage->Show(true);
1d5b3bf0
VZ
763 pPage->SetFocus();
764
765 // If the newly focused window is not a child of the new page,
766 // SetFocus was not successful and the notebook itself should be
767 // focused
768 wxWindow *currentFocus = FindFocus();
769 wxWindow *startFocus = currentFocus;
770 while ( currentFocus && currentFocus != pPage && currentFocus != this )
771 currentFocus = currentFocus->GetParent();
772
773 if ( startFocus == pPage || currentFocus != pPage )
774 SetFocus();
775
776 }
777 else // no pages in the notebook, give the focus to itself
778 {
779 SetFocus();
5d1d2d46 780 }
0398b1d6 781
5d1d2d46
VZ
782 m_nSelection = sel;
783 }
88310e2e
VZ
784
785 // we want to give others a chance to process this message as well
786 event.Skip();
787}
788
b8bdaa7c 789bool wxNotebook::MSWTranslateMessage(WXMSG *wxmsg)
88310e2e 790{
b8bdaa7c 791 const MSG * const msg = (MSG *)wxmsg;
88310e2e 792
1d5b3bf0
VZ
793 // intercept TAB, CTRL+TAB and CTRL+SHIFT+TAB for processing by wxNotebook.
794 // TAB will be passed to the currently selected page, CTRL+TAB and
795 // CTRL+SHIFT+TAB will be processed by the notebook itself. do not
796 // intercept SHIFT+TAB. This goes to the parent of the notebook which will
797 // process it.
b8bdaa7c
VZ
798 if ( msg->message == WM_KEYDOWN && msg->wParam == VK_TAB &&
799 msg->hwnd == m_hwnd &&
1d5b3bf0 800 (wxIsCtrlDown() || !wxIsShiftDown()) )
b8bdaa7c
VZ
801 {
802 return MSWProcessMessage(wxmsg);
803 }
d9506e77 804
b8bdaa7c 805 return false;
88310e2e
VZ
806}
807
808void wxNotebook::OnNavigationKey(wxNavigationKeyEvent& event)
809{
d9506e77
VZ
810 if ( event.IsWindowChange() ) {
811 // change pages
812 AdvanceSelection(event.GetDirection());
813 }
814 else {
b8bdaa7c 815 // we get this event in 3 cases
d9506e77
VZ
816 //
817 // a) one of our pages might have generated it because the user TABbed
818 // out from it in which case we should propagate the event upwards and
819 // our parent will take care of setting the focus to prev/next sibling
820 //
821 // or
822 //
823 // b) the parent panel wants to give the focus to us so that we
824 // forward it to our selected page. We can't deal with this in
825 // OnSetFocus() because we don't know which direction the focus came
826 // from in this case and so can't choose between setting the focus to
827 // first or last panel child
b8bdaa7c
VZ
828 //
829 // or
830 //
831 // c) we ourselves (see MSWTranslateMessage) generated the event
832 //
833 wxWindow * const parent = GetParent();
834
835 const bool isFromParent = event.GetEventObject() == parent;
836 const bool isFromSelf = event.GetEventObject() == this;
837
838 if ( isFromParent || isFromSelf )
d9506e77 839 {
b8bdaa7c
VZ
840 // no, it doesn't come from child, case (b) or (c): forward to a
841 // page but only if direction is backwards (TAB) or from ourselves,
842 if ( m_nSelection != -1 &&
843 (!event.GetDirection() || isFromSelf) )
d9506e77
VZ
844 {
845 // so that the page knows that the event comes from it's parent
846 // and is being propagated downwards
847 event.SetEventObject(this);
848
1e6feb95 849 wxWindow *page = m_pages[m_nSelection];
d9506e77
VZ
850 if ( !page->GetEventHandler()->ProcessEvent(event) )
851 {
852 page->SetFocus();
853 }
854 //else: page manages focus inside it itself
855 }
b8bdaa7c 856 else // otherwise set the focus to the notebook itself
d9506e77 857 {
d9506e77
VZ
858 SetFocus();
859 }
860 }
861 else
862 {
b8bdaa7c
VZ
863 // it comes from our child, case (a), pass to the parent, but only
864 // if the direction is forwards. Otherwise set the focus to the
865 // notebook itself. The notebook is always the 'first' control of a
866 // page.
867 if ( !event.GetDirection() )
868 {
869 SetFocus();
870 }
871 else if ( parent )
872 {
d9506e77
VZ
873 event.SetCurrentFocus(this);
874 parent->GetEventHandler()->ProcessEvent(event);
875 }
876 }
88310e2e 877 }
88310e2e
VZ
878}
879
880// ----------------------------------------------------------------------------
881// wxNotebook base class virtuals
882// ----------------------------------------------------------------------------
b5c3b538 883
0b481c72
VZ
884#if wxUSE_CONSTRAINTS
885
b5c3b538
VZ
886// override these 2 functions to do nothing: everything is done in OnSize
887
4b7f2165 888void wxNotebook::SetConstraintSizes(bool WXUNUSED(recurse))
b5c3b538
VZ
889{
890 // don't set the sizes of the pages - their correct size is not yet known
b8bdaa7c 891 wxControl::SetConstraintSizes(false);
b5c3b538
VZ
892}
893
4b7f2165 894bool wxNotebook::DoPhase(int WXUNUSED(nPhase))
b5c3b538 895{
b8bdaa7c 896 return true;
b5c3b538
VZ
897}
898
0b481c72
VZ
899#endif // wxUSE_CONSTRAINTS
900
0df3fbd7
VZ
901// ----------------------------------------------------------------------------
902// wxNotebook Windows message handlers
903// ----------------------------------------------------------------------------
904
905bool wxNotebook::MSWOnScroll(int orientation, WXWORD nSBCode,
906 WXWORD pos, WXHWND control)
907{
908 // don't generate EVT_SCROLLWIN events for the WM_SCROLLs coming from the
909 // up-down control
910 if ( control )
b8bdaa7c 911 return false;
0df3fbd7
VZ
912
913 return wxNotebookBase::MSWOnScroll(orientation, nSBCode, pos, control);
914}
915
a23fd0e1 916bool wxNotebook::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM* result)
88310e2e 917{
93a19f17 918 wxNotebookEvent event(wxEVT_NULL, m_windowId);
88310e2e
VZ
919
920 NMHDR* hdr = (NMHDR *)lParam;
921 switch ( hdr->code ) {
922 case TCN_SELCHANGE:
923 event.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED);
924 break;
925
926 case TCN_SELCHANGING:
927 event.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING);
928 break;
929
fd3f686c 930 default:
a23fd0e1 931 return wxControl::MSWOnNotify(idCtrl, lParam, result);
88310e2e
VZ
932 }
933
93a19f17
VZ
934 event.SetSelection(TabCtrl_GetCurSel(m_hwnd));
935 event.SetOldSelection(m_nSelection);
88310e2e 936 event.SetEventObject(this);
a23fd0e1 937 event.SetInt(idCtrl);
88310e2e 938
fd3f686c
VZ
939 bool processed = GetEventHandler()->ProcessEvent(event);
940 *result = !event.IsAllowed();
941 return processed;
88310e2e
VZ
942}
943
85b43fbf
JS
944// Windows only: attempts to get colour for UX theme page background
945wxColour wxNotebook::GetThemeBackgroundColour()
946{
947#if wxUSE_UXTHEME
948 if (wxUxThemeEngine::Get())
949 {
2142d7ba 950 wxUxThemeHandle hTheme(this, L"TAB");
85b43fbf
JS
951 if (hTheme)
952 {
953 // This is total guesswork.
954 // See PlatformSDK\Include\Tmschema.h for values
955 COLORREF themeColor;
2eb85fe4 956 wxUxThemeEngine::Get()->GetThemeColor(
2142d7ba
VZ
957 hTheme,
958 10 /* TABP_BODY */,
959 1 /* NORMAL */,
20fa338c 960 3821 /* FILLCOLORHINT */,
2eb85fe4 961 &themeColor);
20fa338c
DS
962
963 /*
964 [DS] Workaround for WindowBlinds:
965 Some themes return a near black theme color using FILLCOLORHINT,
966 this makes notebook pages have an ugly black background and makes
967 text (usually black) unreadable. Retry again with FILLCOLOR.
968
969 This workaround potentially breaks appearance of some themes,
970 but in practice it already fixes some themes.
971 */
972 if (themeColor == 1)
973 {
974 wxUxThemeEngine::Get()->GetThemeColor(
975 hTheme,
976 10 /* TABP_BODY */,
977 1 /* NORMAL */,
978 3802 /* FILLCOLOR */,
979 &themeColor);
980 }
981
85b43fbf
JS
982 wxColour colour(GetRValue(themeColor), GetGValue(themeColor), GetBValue(themeColor));
983 return colour;
984 }
985 }
2142d7ba
VZ
986#endif // wxUSE_UXTHEME
987
85b43fbf
JS
988 return GetBackgroundColour();
989}
990
991// Windows only: attempts to apply the UX theme page background to this page
840015f0 992#if wxUSE_UXTHEME
85b43fbf 993void wxNotebook::ApplyThemeBackground(wxWindow* window, const wxColour& colour)
840015f0
MB
994#else
995void wxNotebook::ApplyThemeBackground(wxWindow*, const wxColour&)
996#endif
85b43fbf
JS
997{
998#if wxUSE_UXTHEME
85b43fbf 999
cc0bffac 1000 window->ApplyParentThemeBackground(colour);
078cf5cb 1001
fc10daf3 1002 for ( wxWindowList::compatibility_iterator node = window->GetChildren().GetFirst(); node; node = node->GetNext() )
85b43fbf
JS
1003 {
1004 wxWindow *child = node->GetData();
1005 ApplyThemeBackground(child, colour);
1006 }
1007#endif
1008}
1009
ffafd8a5 1010#if wxUSE_UXTHEME
c140b7e7 1011WXLRESULT wxNotebook::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
c02e5a31 1012{
b8bdaa7c 1013 static bool g_TestedForTheme = false;
ffafd8a5 1014 static bool g_supportsThemes = false;
c02e5a31
JS
1015 switch ( nMsg )
1016 {
1017 case WM_ERASEBKGND:
1018 {
1019 if (!g_TestedForTheme)
1020 {
1021 int commCtrlVersion = wxTheApp->GetComCtl32Version() ;
1022
ffafd8a5 1023 g_supportsThemes = (commCtrlVersion >= 600);
b8bdaa7c 1024 g_TestedForTheme = true;
c02e5a31
JS
1025 }
1026
ffafd8a5 1027 // If currently an XP theme is active, it seems we can get away
c02e5a31 1028 // with not drawing a background, which reduces flicker.
ffafd8a5
DS
1029 if (g_supportsThemes)
1030 {
1031 wxUxThemeEngine *p = wxUxThemeEngine::Get();
1032 if (p && p->IsThemeActive() )
1033 {
1034 return true;
1035 }
1036 }
c02e5a31
JS
1037 }
1038 }
1039
1040 return wxControl::MSWWindowProc(nMsg, wParam, lParam);
1041}
ffafd8a5 1042#endif // #if wxUSE_UXTHEME
c02e5a31 1043
1e6feb95 1044#endif // wxUSE_NOTEBOOK