]> git.saurik.com Git - wxWidgets.git/blame - src/msw/notebook.cpp
Add support for wxSL_INVERSE flag. Also uses new inverse logic to make Mac vertical...
[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
6dd16e4f
VZ
255 if ( !CreateControl(parent, id, pos, size, style | wxTAB_TRAVERSAL,
256 wxDefaultValidator, name) )
b8bdaa7c 257 return false;
88310e2e 258
fda7962d 259 if ( !MSWCreateControl(WC_TABCONTROL, wxEmptyString, pos, size) )
b8bdaa7c 260 return false;
907f37b3 261
b8bdaa7c 262 return true;
0df3fbd7
VZ
263}
264
265WXDWORD wxNotebook::MSWGetStyle(long style, WXDWORD *exstyle) const
266{
267 WXDWORD tabStyle = wxControl::MSWGetStyle(style, exstyle);
268
269 tabStyle |= WS_TABSTOP | TCS_TABS;
270
2b5f62a0 271 if ( style & wxNB_MULTILINE )
0df3fbd7
VZ
272 tabStyle |= TCS_MULTILINE;
273 if ( style & wxNB_FIXEDWIDTH )
274 tabStyle |= TCS_FIXEDWIDTH;
275
276 if ( style & wxNB_BOTTOM )
277 tabStyle |= TCS_RIGHT;
278 else if ( style & wxNB_LEFT )
279 tabStyle |= TCS_VERTICAL;
280 else if ( style & wxNB_RIGHT )
281 tabStyle |= TCS_VERTICAL | TCS_RIGHT;
282
283 // ex style
284 if ( exstyle )
285 {
286 // note that we never want to have the default WS_EX_CLIENTEDGE style
287 // as it looks too ugly for the notebooks
288 *exstyle = 0;
289 }
290
291 return tabStyle;
88310e2e
VZ
292}
293
caf95d2a
VZ
294wxNotebook::~wxNotebook()
295{
296#if wxUSE_UXTHEME
297 if ( m_hbrBackground )
298 ::DeleteObject((HBRUSH)m_hbrBackground);
299#endif // wxUSE_UXTHEME
300}
301
88310e2e
VZ
302// ----------------------------------------------------------------------------
303// wxNotebook accessors
304// ----------------------------------------------------------------------------
07b8d7ec 305
8d34bf5c 306size_t wxNotebook::GetPageCount() const
88310e2e
VZ
307{
308 // consistency check
1e6feb95 309 wxASSERT( (int)m_pages.Count() == TabCtrl_GetItemCount(m_hwnd) );
88310e2e 310
1e6feb95 311 return m_pages.Count();
88310e2e
VZ
312}
313
314int wxNotebook::GetRowCount() const
315{
316 return TabCtrl_GetRowCount(m_hwnd);
317}
318
8d34bf5c 319int wxNotebook::SetSelection(size_t nPage)
88310e2e 320{
078cf5cb 321 wxCHECK_MSG( IS_VALID_PAGE(nPage), wxNOT_FOUND, wxT("notebook page out of range") );
88310e2e 322
34a0c9f4 323 if ( int(nPage) != m_nSelection )
2b5f62a0
VZ
324 {
325 wxNotebookEvent event(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING, m_windowId);
326 event.SetSelection(nPage);
327 event.SetOldSelection(m_nSelection);
328 event.SetEventObject(this);
329 if ( !GetEventHandler()->ProcessEvent(event) || event.IsAllowed() )
330 {
331 // program allows the page change
332 event.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED);
333 (void)GetEventHandler()->ProcessEvent(event);
334
335 TabCtrl_SetCurSel(m_hwnd, nPage);
336 }
337 }
88310e2e 338
2b5f62a0 339 return m_nSelection;
88310e2e
VZ
340}
341
8d34bf5c 342bool wxNotebook::SetPageText(size_t nPage, const wxString& strText)
88310e2e 343{
b8bdaa7c 344 wxCHECK_MSG( IS_VALID_PAGE(nPage), false, wxT("notebook page out of range") );
88310e2e
VZ
345
346 TC_ITEM tcItem;
347 tcItem.mask = TCIF_TEXT;
837e5743 348 tcItem.pszText = (wxChar *)strText.c_str();
88310e2e
VZ
349
350 return TabCtrl_SetItem(m_hwnd, nPage, &tcItem) != 0;
351}
352
8d34bf5c 353wxString wxNotebook::GetPageText(size_t nPage) const
88310e2e 354{
fda7962d 355 wxCHECK_MSG( IS_VALID_PAGE(nPage), wxEmptyString, wxT("notebook page out of range") );
88310e2e 356
837e5743 357 wxChar buf[256];
88310e2e
VZ
358 TC_ITEM tcItem;
359 tcItem.mask = TCIF_TEXT;
360 tcItem.pszText = buf;
361 tcItem.cchTextMax = WXSIZEOF(buf);
362
363 wxString str;
364 if ( TabCtrl_GetItem(m_hwnd, nPage, &tcItem) )
365 str = tcItem.pszText;
366
367 return str;
368}
369
8d34bf5c 370int wxNotebook::GetPageImage(size_t nPage) const
88310e2e 371{
223d09f6 372 wxCHECK_MSG( IS_VALID_PAGE(nPage), -1, wxT("notebook page out of range") );
88310e2e
VZ
373
374 TC_ITEM tcItem;
375 tcItem.mask = TCIF_IMAGE;
376
377 return TabCtrl_GetItem(m_hwnd, nPage, &tcItem) ? tcItem.iImage : -1;
378}
379
8d34bf5c 380bool wxNotebook::SetPageImage(size_t nPage, int nImage)
88310e2e 381{
b8bdaa7c 382 wxCHECK_MSG( IS_VALID_PAGE(nPage), false, wxT("notebook page out of range") );
88310e2e
VZ
383
384 TC_ITEM tcItem;
385 tcItem.mask = TCIF_IMAGE;
386 tcItem.iImage = nImage;
387
388 return TabCtrl_SetItem(m_hwnd, nPage, &tcItem) != 0;
389}
390
391void wxNotebook::SetImageList(wxImageList* imageList)
907f37b3 392{
07b8d7ec
VZ
393 wxNotebookBase::SetImageList(imageList);
394
395 if ( imageList )
1e6feb95 396 {
07b8d7ec 397 TabCtrl_SetImageList(m_hwnd, (HIMAGELIST)imageList->GetHIMAGELIST());
1e6feb95 398 }
b656febd
VS
399}
400
d9506e77
VZ
401// ----------------------------------------------------------------------------
402// wxNotebook size settings
403// ----------------------------------------------------------------------------
404
405void wxNotebook::SetPageSize(const wxSize& size)
406{
407 // transform the page size into the notebook size
408 RECT rc;
409 rc.left =
410 rc.top = 0;
411 rc.right = size.x;
412 rc.bottom = size.y;
413
b8bdaa7c 414 TabCtrl_AdjustRect(GetHwnd(), true, &rc);
d9506e77
VZ
415
416 // and now set it
417 SetSize(rc.right - rc.left, rc.bottom - rc.top);
418}
419
420void wxNotebook::SetPadding(const wxSize& padding)
421{
422 TabCtrl_SetPadding(GetHwnd(), padding.x, padding.y);
423}
42e69d6b
VZ
424
425// Windows-only at present. Also, you must use the wxNB_FIXEDWIDTH
426// style.
427void wxNotebook::SetTabSize(const wxSize& sz)
428{
429 ::SendMessage(GetHwnd(), TCM_SETITEMSIZE, 0, MAKELPARAM(sz.x, sz.y));
430}
431
2ce7af35
JS
432wxSize wxNotebook::CalcSizeFromPage(const wxSize& sizePage) const
433{
434 wxSize sizeTotal = sizePage;
078cf5cb 435
77ffb593 436 // We need to make getting tab size part of the wxWidgets API.
2ce7af35
JS
437 wxSize tabSize(0, 0);
438 if (GetPageCount() > 0)
439 {
440 RECT rect;
441 TabCtrl_GetItemRect((HWND) GetHWND(), 0, & rect);
442 tabSize.x = rect.right - rect.left;
443 tabSize.y = rect.bottom - rect.top;
444 }
445 if ( HasFlag(wxNB_LEFT) || HasFlag(wxNB_RIGHT) )
446 {
447 sizeTotal.x += tabSize.x + 7;
448 sizeTotal.y += 7;
449 }
450 else
451 {
452 sizeTotal.x += 7;
453 sizeTotal.y += tabSize.y + 7;
454 }
455
456 return sizeTotal;
457}
458
2015f2b3
VZ
459void wxNotebook::AdjustPageSize(wxNotebookPage *page)
460{
461 wxCHECK_RET( page, _T("NULL page in wxNotebook::AdjustPageSize") );
462
463 RECT rc;
464 rc.left =
465 rc.top = 0;
466
467 // get the page size from the notebook size
468 GetSize((int *)&rc.right, (int *)&rc.bottom);
2015f2b3 469
14a6b6e5
RD
470 // This check is to work around a bug in TabCtrl_AdjustRect which will
471 // cause a crash on win2k, or on XP with themes disabled, if the
472 // wxNB_MULTILINE style is used and the rectangle is very small, (such as
473 // when the notebook is first created.) The value of 20 is just
474 // arbitrarily chosen, if there is a better way to determine this value
475 // then please do so. --RD
476 if (rc.right > 20 && rc.bottom > 20)
477 {
478 TabCtrl_AdjustRect(m_hwnd, false, &rc);
479 page->SetSize(rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top);
480 }
2015f2b3
VZ
481}
482
88310e2e
VZ
483// ----------------------------------------------------------------------------
484// wxNotebook operations
485// ----------------------------------------------------------------------------
486
621793f4 487// remove one page from the notebook, without deleting
8d34bf5c 488wxNotebookPage *wxNotebook::DoRemovePage(size_t nPage)
621793f4 489{
df7145da
VZ
490 wxNotebookPage *pageRemoved = wxNotebookBase::DoRemovePage(nPage);
491 if ( !pageRemoved )
492 return NULL;
621793f4 493
df7145da 494 TabCtrl_DeleteItem(m_hwnd, nPage);
621793f4 495
df7145da
VZ
496 if ( m_pages.IsEmpty() )
497 {
498 // no selection any more, the notebook becamse empty
499 m_nSelection = -1;
500 }
501 else // notebook still not empty
502 {
623f5f70
JS
503 int selNew = TabCtrl_GetCurSel(m_hwnd);
504 if (selNew != -1)
df7145da 505 {
623f5f70 506 // No selection change, just refresh the current selection.
078cf5cb
WS
507 // Because it could be that the slection index changed
508 // we need to update it.
623f5f70
JS
509 // Note: this does not mean the selection it self changed.
510 m_nSelection = selNew;
511 m_pages[m_nSelection]->Refresh();
df7145da 512 }
623f5f70 513 else if (int(nPage) == m_nSelection)
43a997b6 514 {
623f5f70 515 // The selection was deleted.
078cf5cb 516
623f5f70
JS
517 // Determine new selection.
518 if (m_nSelection == int(GetPageCount()))
519 selNew = m_nSelection - 1;
520 else
521 selNew = m_nSelection;
078cf5cb 522
43a997b6
VZ
523 // m_nSelection must be always valid so reset it before calling
524 // SetSelection()
525 m_nSelection = -1;
526 SetSelection(selNew);
527 }
623f5f70
JS
528 else
529 {
530 wxFAIL; // Windows did not behave ok.
531 }
df7145da 532 }
47f12f58 533
df7145da 534 return pageRemoved;
621793f4
JS
535}
536
88310e2e
VZ
537// remove all pages
538bool wxNotebook::DeleteAllPages()
539{
8d34bf5c
VZ
540 size_t nPageCount = GetPageCount();
541 size_t nPage;
88310e2e 542 for ( nPage = 0; nPage < nPageCount; nPage++ )
1e6feb95 543 delete m_pages[nPage];
88310e2e 544
1e6feb95 545 m_pages.Clear();
88310e2e 546
907f37b3
VZ
547 TabCtrl_DeleteAllItems(m_hwnd);
548
47f12f58
JS
549 m_nSelection = -1;
550
37144cf0 551 InvalidateBestSize();
b8bdaa7c 552 return true;
88310e2e
VZ
553}
554
88310e2e 555// same as AddPage() but does it at given position
8d34bf5c 556bool wxNotebook::InsertPage(size_t nPage,
88310e2e
VZ
557 wxNotebookPage *pPage,
558 const wxString& strText,
559 bool bSelect,
560 int imageId)
561{
b8bdaa7c
VZ
562 wxCHECK_MSG( pPage != NULL, false, _T("NULL page in wxNotebook::InsertPage") );
563 wxCHECK_MSG( IS_VALID_PAGE(nPage) || nPage == GetPageCount(), false,
22f3361e 564 _T("invalid index in wxNotebook::InsertPage") );
88310e2e 565
efa14cf2
VZ
566 wxASSERT_MSG( pPage->GetParent() == this,
567 _T("notebook pages must have notebook as parent") );
43427087 568
22f3361e
VZ
569 // add a new tab to the control
570 // ----------------------------
58a8ab88 571
22f3361e
VZ
572 // init all fields to 0
573 TC_ITEM tcItem;
574 wxZeroMemory(tcItem);
58a8ab88 575
22f3361e
VZ
576 // set the image, if any
577 if ( imageId != -1 )
578 {
579 tcItem.mask |= TCIF_IMAGE;
580 tcItem.iImage = imageId;
581 }
88310e2e 582
22f3361e
VZ
583 // and the text
584 if ( !strText.IsEmpty() )
585 {
586 tcItem.mask |= TCIF_TEXT;
587 tcItem.pszText = (wxChar *)strText.c_str(); // const_cast
588 }
43427087 589
e830a6a6
RD
590 // hide the page: unless it is selected, it shouldn't be shown (and if it
591 // is selected it will be shown later)
592 HWND hwnd = GetWinHwnd(pPage);
593 SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~WS_VISIBLE);
594
595 // this updates internal flag too -- otherwise it would get out of sync
596 // with the real state
597 pPage->Show(false);
598
599
22f3361e
VZ
600 // fit the notebook page to the tab control's display area: this should be
601 // done before adding it to the notebook or TabCtrl_InsertItem() will
602 // change the notebooks size itself!
2015f2b3 603 AdjustPageSize(pPage);
43427087 604
22f3361e 605 // finally do insert it
2015f2b3
VZ
606 if ( TabCtrl_InsertItem(m_hwnd, nPage, &tcItem) == -1 )
607 {
22f3361e 608 wxLogError(wxT("Can't create the notebook page '%s'."), strText.c_str());
88310e2e 609
b8bdaa7c 610 return false;
22f3361e 611 }
88310e2e 612
22f3361e
VZ
613 // succeeded: save the pointer to the page
614 m_pages.Insert(pPage, nPage);
42e69d6b 615
56b9925b
VZ
616 // we may need to adjust the size again if the notebook size changed:
617 // normally this only happens for the first page we add (the tabs which
618 // hadn't been there before are now shown) but for a multiline notebook it
619 // can happen for any page at all as a new row could have been started
620 if ( m_pages.GetCount() == 1 || HasFlag(wxNB_MULTILINE) )
2015f2b3
VZ
621 {
622 AdjustPageSize(pPage);
623 }
624
22f3361e
VZ
625 // now deal with the selection
626 // ---------------------------
627
628 // if the inserted page is before the selected one, we must update the
629 // index of the selected page
34a0c9f4 630 if ( int(nPage) <= m_nSelection )
22f3361e
VZ
631 {
632 // one extra page added
633 m_nSelection++;
634 }
635
636 // some page should be selected: either this one or the first one if there
637 // is still no selection
638 int selNew = -1;
639 if ( bSelect )
640 selNew = nPage;
641 else if ( m_nSelection == -1 )
642 selNew = 0;
643
644 if ( selNew != -1 )
645 SetSelection(selNew);
646
37144cf0 647 InvalidateBestSize();
b8bdaa7c 648 return true;
88310e2e
VZ
649}
650
e450aa69 651int wxNotebook::HitTest(const wxPoint& pt, long *flags) const
ef094fa0
JS
652{
653 TC_HITTESTINFO hitTestInfo;
654 hitTestInfo.pt.x = pt.x;
655 hitTestInfo.pt.y = pt.y;
e450aa69 656 int item = TabCtrl_HitTest(GetHwnd(), &hitTestInfo);
ef094fa0 657
e450aa69
VZ
658 if ( flags )
659 {
660 *flags = 0;
661
662 if ((hitTestInfo.flags & TCHT_NOWHERE) == TCHT_NOWHERE)
663 *flags |= wxNB_HITTEST_NOWHERE;
664 if ((hitTestInfo.flags & TCHT_ONITEM) == TCHT_ONITEM)
665 *flags |= wxNB_HITTEST_ONITEM;
666 if ((hitTestInfo.flags & TCHT_ONITEMICON) == TCHT_ONITEMICON)
667 *flags |= wxNB_HITTEST_ONICON;
668 if ((hitTestInfo.flags & TCHT_ONITEMLABEL) == TCHT_ONITEMLABEL)
669 *flags |= wxNB_HITTEST_ONLABEL;
670 }
ef094fa0
JS
671
672 return item;
673}
674
e450aa69 675
88310e2e
VZ
676// ----------------------------------------------------------------------------
677// wxNotebook callbacks
678// ----------------------------------------------------------------------------
679
9026ad85 680void wxNotebook::OnSize(wxSizeEvent& event)
88310e2e 681{
de371316
VZ
682#if wxUSE_UXTHEME
683 UpdateBgBrush();
684#endif // wxUSE_UXTHEME
685
b5c3b538
VZ
686 // fit the notebook page to the tab control's display area
687 RECT rc;
688 rc.left = rc.top = 0;
689 GetSize((int *)&rc.right, (int *)&rc.bottom);
690
56b9925b
VZ
691 // there seems to be a bug in the implementation of TabCtrl_AdjustRect(): it
692 // returns completely false values for multiline tab controls after the tabs
693 // are added but before getting the first WM_SIZE (off by ~50 pixels, see
694 //
695 // http://sf.net/tracker/index.php?func=detail&aid=645323&group_id=9863&atid=109863
696 //
697 // and the only work around I could find was this ugly hack... without it
698 // simply toggling the "multiline" checkbox in the notebook sample resulted
699 // in a noticeable page displacement
700 if ( HasFlag(wxNB_MULTILINE) )
701 {
702 // avoid an infinite recursion: we get another notification too!
703 static bool s_isInOnSize = false;
704
705 if ( !s_isInOnSize )
706 {
707 s_isInOnSize = true;
708 SendMessage(GetHwnd(), WM_SIZE, SIZE_RESTORED,
709 MAKELPARAM(rc.right, rc.bottom));
710 s_isInOnSize = false;
711 }
712 }
713
b8bdaa7c 714 TabCtrl_AdjustRect(m_hwnd, false, &rc);
4b7f2165
VZ
715
716 int width = rc.right - rc.left,
717 height = rc.bottom - rc.top;
1e6feb95 718 size_t nCount = m_pages.Count();
c86f1403 719 for ( size_t nPage = 0; nPage < nCount; nPage++ ) {
1e6feb95 720 wxNotebookPage *pPage = m_pages[nPage];
4b7f2165 721 pPage->SetSize(rc.left, rc.top, width, height);
b5c3b538
VZ
722 }
723
88310e2e
VZ
724 event.Skip();
725}
726
727void wxNotebook::OnSelChange(wxNotebookEvent& event)
728{
729 // is it our tab control?
730 if ( event.GetEventObject() == this )
5d1d2d46 731 {
5d1d2d46
VZ
732 int sel = event.GetOldSelection();
733 if ( sel != -1 )
b8bdaa7c 734 m_pages[sel]->Show(false);
0398b1d6 735
5d1d2d46
VZ
736 sel = event.GetSelection();
737 if ( sel != -1 )
738 {
1e6feb95 739 wxNotebookPage *pPage = m_pages[sel];
b8bdaa7c 740 pPage->Show(true);
1d5b3bf0
VZ
741 pPage->SetFocus();
742
743 // If the newly focused window is not a child of the new page,
744 // SetFocus was not successful and the notebook itself should be
745 // focused
746 wxWindow *currentFocus = FindFocus();
747 wxWindow *startFocus = currentFocus;
748 while ( currentFocus && currentFocus != pPage && currentFocus != this )
749 currentFocus = currentFocus->GetParent();
750
751 if ( startFocus == pPage || currentFocus != pPage )
752 SetFocus();
753
754 }
755 else // no pages in the notebook, give the focus to itself
756 {
757 SetFocus();
5d1d2d46 758 }
0398b1d6 759
5d1d2d46
VZ
760 m_nSelection = sel;
761 }
88310e2e
VZ
762
763 // we want to give others a chance to process this message as well
764 event.Skip();
765}
766
b8bdaa7c 767bool wxNotebook::MSWTranslateMessage(WXMSG *wxmsg)
88310e2e 768{
b8bdaa7c 769 const MSG * const msg = (MSG *)wxmsg;
88310e2e 770
1d5b3bf0
VZ
771 // intercept TAB, CTRL+TAB and CTRL+SHIFT+TAB for processing by wxNotebook.
772 // TAB will be passed to the currently selected page, CTRL+TAB and
773 // CTRL+SHIFT+TAB will be processed by the notebook itself. do not
774 // intercept SHIFT+TAB. This goes to the parent of the notebook which will
775 // process it.
b8bdaa7c
VZ
776 if ( msg->message == WM_KEYDOWN && msg->wParam == VK_TAB &&
777 msg->hwnd == m_hwnd &&
1d5b3bf0 778 (wxIsCtrlDown() || !wxIsShiftDown()) )
b8bdaa7c
VZ
779 {
780 return MSWProcessMessage(wxmsg);
781 }
d9506e77 782
b8bdaa7c 783 return false;
88310e2e
VZ
784}
785
786void wxNotebook::OnNavigationKey(wxNavigationKeyEvent& event)
787{
d9506e77
VZ
788 if ( event.IsWindowChange() ) {
789 // change pages
790 AdvanceSelection(event.GetDirection());
791 }
792 else {
b8bdaa7c 793 // we get this event in 3 cases
d9506e77
VZ
794 //
795 // a) one of our pages might have generated it because the user TABbed
796 // out from it in which case we should propagate the event upwards and
797 // our parent will take care of setting the focus to prev/next sibling
798 //
799 // or
800 //
801 // b) the parent panel wants to give the focus to us so that we
802 // forward it to our selected page. We can't deal with this in
803 // OnSetFocus() because we don't know which direction the focus came
804 // from in this case and so can't choose between setting the focus to
805 // first or last panel child
b8bdaa7c
VZ
806 //
807 // or
808 //
809 // c) we ourselves (see MSWTranslateMessage) generated the event
810 //
811 wxWindow * const parent = GetParent();
812
813 const bool isFromParent = event.GetEventObject() == parent;
814 const bool isFromSelf = event.GetEventObject() == this;
815
816 if ( isFromParent || isFromSelf )
d9506e77 817 {
b8bdaa7c
VZ
818 // no, it doesn't come from child, case (b) or (c): forward to a
819 // page but only if direction is backwards (TAB) or from ourselves,
820 if ( m_nSelection != -1 &&
821 (!event.GetDirection() || isFromSelf) )
d9506e77
VZ
822 {
823 // so that the page knows that the event comes from it's parent
824 // and is being propagated downwards
825 event.SetEventObject(this);
826
1e6feb95 827 wxWindow *page = m_pages[m_nSelection];
d9506e77
VZ
828 if ( !page->GetEventHandler()->ProcessEvent(event) )
829 {
830 page->SetFocus();
831 }
832 //else: page manages focus inside it itself
833 }
b8bdaa7c 834 else // otherwise set the focus to the notebook itself
d9506e77 835 {
d9506e77
VZ
836 SetFocus();
837 }
838 }
839 else
840 {
b8bdaa7c
VZ
841 // it comes from our child, case (a), pass to the parent, but only
842 // if the direction is forwards. Otherwise set the focus to the
843 // notebook itself. The notebook is always the 'first' control of a
844 // page.
845 if ( !event.GetDirection() )
846 {
847 SetFocus();
848 }
849 else if ( parent )
850 {
d9506e77
VZ
851 event.SetCurrentFocus(this);
852 parent->GetEventHandler()->ProcessEvent(event);
853 }
854 }
88310e2e 855 }
88310e2e
VZ
856}
857
caf95d2a
VZ
858#if wxUSE_UXTHEME
859
c4a95f6f 860WXHANDLE wxNotebook::QueryBgBitmap(wxWindow *win)
caf95d2a 861{
c4a95f6f
VZ
862 RECT rc;
863 GetWindowRect(GetHwnd(), &rc);
864
865 WindowHDC hDC(GetHwnd());
866 MemoryHDC hDCMem(hDC);
867 CompatibleBitmap hBmp(hDC, rc.right - rc.left, rc.bottom - rc.top);
868
869 SelectInHDC selectBmp(hDCMem, hBmp);
caf95d2a 870
c4a95f6f
VZ
871 ::SendMessage(GetHwnd(), WM_PRINTCLIENT,
872 (WPARAM)(HDC)hDCMem,
873 PRF_ERASEBKGND | PRF_CLIENT | PRF_NONCLIENT);
874
875 if ( win )
caf95d2a 876 {
c4a95f6f
VZ
877 RECT rc2;
878 ::GetWindowRect(GetHwndOf(win), &rc2);
caf95d2a 879
c4a95f6f 880 COLORREF c = ::GetPixel(hDCMem, rc2.left - rc.left, rc2.top - rc.top);
caf95d2a 881
c4a95f6f
VZ
882 return (WXHANDLE)c;
883 }
0f770734
DS
884
885 // else we are asked to create the brush
886 return (WXHANDLE)::CreatePatternBrush(hBmp);
c4a95f6f 887}
caf95d2a 888
c4a95f6f
VZ
889void wxNotebook::UpdateBgBrush()
890{
891 if ( m_hbrBackground )
892 ::DeleteObject((HBRUSH)m_hbrBackground);
caf95d2a 893
c4a95f6f
VZ
894 if ( !m_hasBgCol && wxUxThemeEngine::GetIfActive() )
895 {
896 m_hbrBackground = (WXHBRUSH)QueryBgBitmap();
caf95d2a
VZ
897 }
898 else // no themes
899 {
900 m_hbrBackground = NULL;
901 }
902}
903
c4a95f6f 904WXHBRUSH wxNotebook::MSWGetBgBrushForChild(WXHDC hDC, wxWindow *win)
caf95d2a 905{
caf95d2a
VZ
906 if ( m_hbrBackground )
907 {
908 // before drawing with the background brush, we need to position it
909 // correctly
caf95d2a
VZ
910 RECT rc;
911 ::GetWindowRect(GetHwndOf(win), &rc);
912
913 ::MapWindowPoints(NULL, GetHwnd(), (POINT *)&rc, 1);
914
5c836c46 915 if ( !::SetBrushOrgEx((HDC)hDC, -rc.left, -rc.top, NULL) )
caf95d2a
VZ
916 {
917 wxLogLastError(_T("SetBrushOrgEx(notebook bg brush)"));
918 }
c4a95f6f
VZ
919
920 return m_hbrBackground;
5c836c46
VZ
921 }
922
c4a95f6f 923 return wxNotebookBase::MSWGetBgBrushForChild(hDC, win);
5c836c46 924}
caf95d2a 925
c4a95f6f 926wxColour wxNotebook::MSWGetBgColourForChild(wxWindow *win)
9b7d2b81 927{
c4a95f6f
VZ
928 if ( m_hasBgCol )
929 return GetBackgroundColour();
9b7d2b81 930
c4a95f6f
VZ
931 if ( !wxUxThemeEngine::GetIfActive() )
932 return wxNullColour;
933
934 COLORREF c = (COLORREF)QueryBgBitmap(win);
935
936 return c == CLR_INVALID ? wxNullColour : wxRGBToColour(c);
caf95d2a
VZ
937}
938
939#endif // wxUSE_UXTHEME
940
88310e2e
VZ
941// ----------------------------------------------------------------------------
942// wxNotebook base class virtuals
943// ----------------------------------------------------------------------------
b5c3b538 944
0b481c72
VZ
945#if wxUSE_CONSTRAINTS
946
b5c3b538
VZ
947// override these 2 functions to do nothing: everything is done in OnSize
948
4b7f2165 949void wxNotebook::SetConstraintSizes(bool WXUNUSED(recurse))
b5c3b538
VZ
950{
951 // don't set the sizes of the pages - their correct size is not yet known
b8bdaa7c 952 wxControl::SetConstraintSizes(false);
b5c3b538
VZ
953}
954
4b7f2165 955bool wxNotebook::DoPhase(int WXUNUSED(nPhase))
b5c3b538 956{
b8bdaa7c 957 return true;
b5c3b538
VZ
958}
959
0b481c72
VZ
960#endif // wxUSE_CONSTRAINTS
961
0df3fbd7
VZ
962// ----------------------------------------------------------------------------
963// wxNotebook Windows message handlers
964// ----------------------------------------------------------------------------
965
966bool wxNotebook::MSWOnScroll(int orientation, WXWORD nSBCode,
967 WXWORD pos, WXHWND control)
968{
969 // don't generate EVT_SCROLLWIN events for the WM_SCROLLs coming from the
970 // up-down control
971 if ( control )
b8bdaa7c 972 return false;
0df3fbd7
VZ
973
974 return wxNotebookBase::MSWOnScroll(orientation, nSBCode, pos, control);
975}
976
a23fd0e1 977bool wxNotebook::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM* result)
88310e2e 978{
93a19f17 979 wxNotebookEvent event(wxEVT_NULL, m_windowId);
88310e2e
VZ
980
981 NMHDR* hdr = (NMHDR *)lParam;
982 switch ( hdr->code ) {
983 case TCN_SELCHANGE:
984 event.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED);
985 break;
986
987 case TCN_SELCHANGING:
988 event.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING);
989 break;
990
fd3f686c 991 default:
a23fd0e1 992 return wxControl::MSWOnNotify(idCtrl, lParam, result);
88310e2e
VZ
993 }
994
93a19f17
VZ
995 event.SetSelection(TabCtrl_GetCurSel(m_hwnd));
996 event.SetOldSelection(m_nSelection);
88310e2e 997 event.SetEventObject(this);
a23fd0e1 998 event.SetInt(idCtrl);
88310e2e 999
fd3f686c
VZ
1000 bool processed = GetEventHandler()->ProcessEvent(event);
1001 *result = !event.IsAllowed();
1002 return processed;
88310e2e
VZ
1003}
1004
1e6feb95 1005#endif // wxUSE_NOTEBOOK