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