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