]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/msw/notebook.cpp
Warning fix.
[wxWidgets.git] / src / msw / notebook.cpp
... / ...
CommitLineData
1///////////////////////////////////////////////////////////////////////////////
2// Name: msw/notebook.cpp
3// Purpose: implementation of wxNotebook
4// Author: Vadim Zeitlin
5// Modified by:
6// Created: 11.06.98
7// RCS-ID: $Id$
8// Copyright: (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
9// Licence: wxWindows licence
10///////////////////////////////////////////////////////////////////////////////
11
12#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
13#pragma implementation "notebook.h"
14#endif
15
16// For compilers that support precompilation, includes "wx.h".
17#include "wx/wxprec.h"
18
19#ifdef __BORLANDC__
20 #pragma hdrstop
21#endif
22
23#if wxUSE_NOTEBOOK
24
25// wxWidgets
26#ifndef WX_PRECOMP
27 #include "wx/string.h"
28 #include "wx/dc.h"
29#endif // WX_PRECOMP
30
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"
36#include "wx/app.h"
37#include "wx/sysopt.h"
38#include "wx/dcclient.h"
39#include "wx/dcmemory.h"
40
41#include "wx/msw/private.h"
42
43#include <windowsx.h>
44
45#include <commctrl.h>
46
47#include "wx/msw/winundef.h"
48
49#if wxUSE_UXTHEME
50 #include "wx/msw/uxtheme.h"
51#endif
52
53// ----------------------------------------------------------------------------
54// macros
55// ----------------------------------------------------------------------------
56
57// check that the page index is valid
58#define IS_VALID_PAGE(nPage) ((nPage) < GetPageCount())
59
60// you can set USE_NOTEBOOK_ANTIFLICKER to 0 for desktop Windows versions too
61// to disable code whih results in flicker-less notebook redrawing at the
62// expense of some extra GDI resource consumption
63#ifdef __WXWINCE__
64 // notebooks are never resized under CE anyhow
65 #define USE_NOTEBOOK_ANTIFLICKER 0
66#else
67 #define USE_NOTEBOOK_ANTIFLICKER 1
68#endif
69
70// ----------------------------------------------------------------------------
71// constants
72// ----------------------------------------------------------------------------
73
74// This is a work-around for missing defines in gcc-2.95 headers
75#ifndef TCS_RIGHT
76 #define TCS_RIGHT 0x0002
77#endif
78
79#ifndef TCS_VERTICAL
80 #define TCS_VERTICAL 0x0080
81#endif
82
83#ifndef TCS_BOTTOM
84 #define TCS_BOTTOM TCS_RIGHT
85#endif
86
87// ----------------------------------------------------------------------------
88// global variables
89// ----------------------------------------------------------------------------
90
91#if USE_NOTEBOOK_ANTIFLICKER
92
93// the pointer to standard spin button wnd proc
94static WXFARPROC gs_wndprocNotebookSpinBtn = (WXFARPROC)NULL;
95
96#endif // USE_NOTEBOOK_ANTIFLICKER
97
98// ----------------------------------------------------------------------------
99// event table
100// ----------------------------------------------------------------------------
101
102#include <wx/listimpl.cpp>
103
104WX_DEFINE_LIST( wxNotebookPageInfoList ) ;
105
106DEFINE_EVENT_TYPE(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED)
107DEFINE_EVENT_TYPE(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING)
108
109BEGIN_EVENT_TABLE(wxNotebook, wxControl)
110 EVT_NOTEBOOK_PAGE_CHANGED(-1, wxNotebook::OnSelChange)
111 EVT_SIZE(wxNotebook::OnSize)
112 EVT_NAVIGATION_KEY(wxNotebook::OnNavigationKey)
113
114#if USE_NOTEBOOK_ANTIFLICKER
115 EVT_ERASE_BACKGROUND(wxNotebook::OnEraseBackground)
116 EVT_PAINT(wxNotebook::OnPaint)
117#endif // USE_NOTEBOOK_ANTIFLICKER
118END_EVENT_TABLE()
119
120#if wxUSE_EXTENDED_RTTI
121WX_DEFINE_FLAGS( wxNotebookStyle )
122
123wxBEGIN_FLAGS( wxNotebookStyle )
124 // new style border flags, we put them first to
125 // use them for streaming out
126 wxFLAGS_MEMBER(wxBORDER_SIMPLE)
127 wxFLAGS_MEMBER(wxBORDER_SUNKEN)
128 wxFLAGS_MEMBER(wxBORDER_DOUBLE)
129 wxFLAGS_MEMBER(wxBORDER_RAISED)
130 wxFLAGS_MEMBER(wxBORDER_STATIC)
131 wxFLAGS_MEMBER(wxBORDER_NONE)
132
133 // old style border flags
134 wxFLAGS_MEMBER(wxSIMPLE_BORDER)
135 wxFLAGS_MEMBER(wxSUNKEN_BORDER)
136 wxFLAGS_MEMBER(wxDOUBLE_BORDER)
137 wxFLAGS_MEMBER(wxRAISED_BORDER)
138 wxFLAGS_MEMBER(wxSTATIC_BORDER)
139 wxFLAGS_MEMBER(wxBORDER)
140
141 // standard window styles
142 wxFLAGS_MEMBER(wxTAB_TRAVERSAL)
143 wxFLAGS_MEMBER(wxCLIP_CHILDREN)
144 wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW)
145 wxFLAGS_MEMBER(wxWANTS_CHARS)
146 wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE)
147 wxFLAGS_MEMBER(wxALWAYS_SHOW_SB )
148 wxFLAGS_MEMBER(wxVSCROLL)
149 wxFLAGS_MEMBER(wxHSCROLL)
150
151 wxFLAGS_MEMBER(wxNB_FIXEDWIDTH)
152 wxFLAGS_MEMBER(wxNB_LEFT)
153 wxFLAGS_MEMBER(wxNB_RIGHT)
154 wxFLAGS_MEMBER(wxNB_BOTTOM)
155 wxFLAGS_MEMBER(wxNB_NOPAGETHEME)
156 wxFLAGS_MEMBER(wxNB_FLAT)
157
158wxEND_FLAGS( wxNotebookStyle )
159
160IMPLEMENT_DYNAMIC_CLASS_XTI(wxNotebook, wxControl,"wx/notebook.h")
161IMPLEMENT_DYNAMIC_CLASS_XTI(wxNotebookPageInfo, wxObject , "wx/notebook.h" )
162
163wxCOLLECTION_TYPE_INFO( wxNotebookPageInfo * , wxNotebookPageInfoList ) ;
164
165template<> void wxCollectionToVariantArray( wxNotebookPageInfoList const &theList, wxxVariantArray &value)
166{
167 wxListCollectionToVariantArray<wxNotebookPageInfoList::compatibility_iterator>( theList , value ) ;
168}
169
170wxBEGIN_PROPERTIES_TABLE(wxNotebook)
171 wxEVENT_PROPERTY( PageChanging , wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING , wxNotebookEvent )
172 wxEVENT_PROPERTY( PageChanged , wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED , wxNotebookEvent )
173
174 wxPROPERTY_COLLECTION( PageInfos , wxNotebookPageInfoList , wxNotebookPageInfo* , AddPageInfo , GetPageInfos , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
175 wxPROPERTY_FLAGS( WindowStyle , wxNotebookStyle , long , SetWindowStyleFlag , GetWindowStyleFlag , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
176wxEND_PROPERTIES_TABLE()
177
178wxBEGIN_HANDLERS_TABLE(wxNotebook)
179wxEND_HANDLERS_TABLE()
180
181wxCONSTRUCTOR_5( wxNotebook , wxWindow* , Parent , wxWindowID , Id , wxPoint , Position , wxSize , Size , long , WindowStyle)
182
183
184wxBEGIN_PROPERTIES_TABLE(wxNotebookPageInfo)
185 wxREADONLY_PROPERTY( Page , wxNotebookPage* , GetPage , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
186 wxREADONLY_PROPERTY( Text , wxString , GetText , wxString() , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
187 wxREADONLY_PROPERTY( Selected , bool , GetSelected , false, 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
188 wxREADONLY_PROPERTY( ImageId , int , GetImageId , -1 , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
189wxEND_PROPERTIES_TABLE()
190
191wxBEGIN_HANDLERS_TABLE(wxNotebookPageInfo)
192wxEND_HANDLERS_TABLE()
193
194wxCONSTRUCTOR_4( wxNotebookPageInfo , wxNotebookPage* , Page , wxString , Text , bool , Selected , int , ImageId )
195
196#else
197IMPLEMENT_DYNAMIC_CLASS(wxNotebook, wxControl)
198IMPLEMENT_DYNAMIC_CLASS(wxNotebookPageInfo, wxObject )
199#endif
200IMPLEMENT_DYNAMIC_CLASS(wxNotebookEvent, wxNotifyEvent)
201
202// ============================================================================
203// implementation
204// ============================================================================
205
206// ----------------------------------------------------------------------------
207// wxNotebook construction
208// ----------------------------------------------------------------------------
209
210const wxNotebookPageInfoList& wxNotebook::GetPageInfos() const
211{
212 wxNotebookPageInfoList* list = const_cast< wxNotebookPageInfoList* >( &m_pageInfos ) ;
213 WX_CLEAR_LIST( wxNotebookPageInfoList , *list ) ;
214 for( size_t i = 0 ; i < GetPageCount() ; ++i )
215 {
216 wxNotebookPageInfo *info = new wxNotebookPageInfo() ;
217 info->Create( const_cast<wxNotebook*>(this)->GetPage(i) , GetPageText(i) , GetSelection() == int(i) , GetPageImage(i) ) ;
218 list->Append( info ) ;
219 }
220 return m_pageInfos ;
221}
222
223// common part of all ctors
224void wxNotebook::Init()
225{
226 m_imageList = NULL;
227 m_nSelection = -1;
228
229#if wxUSE_UXTHEME
230 m_hbrBackground = NULL;
231#endif // wxUSE_UXTHEME
232}
233
234// default for dynamic class
235wxNotebook::wxNotebook()
236{
237 Init();
238}
239
240// the same arguments as for wxControl
241wxNotebook::wxNotebook(wxWindow *parent,
242 wxWindowID id,
243 const wxPoint& pos,
244 const wxSize& size,
245 long style,
246 const wxString& name)
247{
248 Init();
249
250 Create(parent, id, pos, size, style, name);
251}
252
253// Create() function
254bool wxNotebook::Create(wxWindow *parent,
255 wxWindowID id,
256 const wxPoint& pos,
257 const wxSize& size,
258 long style,
259 const wxString& name)
260{
261#ifdef __WXWINCE__
262 // Not sure why, but without this style, there is no border
263 // around the notebook tabs.
264 if (style & wxNB_FLAT)
265 style |= wxBORDER_SUNKEN;
266#endif
267
268 // comctl32.dll 6.0 doesn't support non-top tabs with visual styles (the
269 // control is simply not rendered correctly), so disable them in this case
270 const int verComCtl32 = wxApp::GetComCtl32Version();
271 if ( verComCtl32 == 600 )
272 {
273 // check if we use themes at all -- if we don't, we're still ok
274#if wxUSE_UXTHEME
275 if ( wxUxThemeEngine::GetIfActive() )
276#endif
277 {
278 style &= ~(wxNB_BOTTOM | wxNB_LEFT | wxNB_RIGHT);
279 }
280 }
281
282 LPCTSTR className = WC_TABCONTROL;
283
284 // SysTabCtl32 class has natively CS_HREDRAW and CS_VREDRAW enabled and it
285 // causes horrible flicker when resizing notebook, so get rid of it by
286 // using a class without these styles (but otherwise identical to it)
287 if ( !HasFlag(wxFULL_REPAINT_ON_RESIZE) )
288 {
289 static ClassRegistrar s_clsNotebook;
290 if ( !s_clsNotebook.IsInitialized() )
291 {
292 // get a copy of standard class and modify it
293 WNDCLASS wc;
294
295 if ( ::GetClassInfo(::GetModuleHandle(NULL), WC_TABCONTROL, &wc) )
296 {
297 wc.lpszClassName = wxT("_wx_SysTabCtl32");
298 wc.style &= ~(CS_HREDRAW | CS_VREDRAW);
299
300 s_clsNotebook.Register(wc);
301 }
302 else
303 {
304 wxLogLastError(_T("GetClassInfoEx(SysTabCtl32)"));
305 }
306 }
307
308 // use our custom class if available but fall back to the standard
309 // notebook if we failed to register it
310 if ( s_clsNotebook.IsRegistered() )
311 {
312 // it's ok to use c_str() here as the static s_clsNotebook object
313 // has sufficiently long lifetime
314 className = s_clsNotebook.GetName().c_str();
315 }
316 }
317
318 if ( !CreateControl(parent, id, pos, size, style | wxTAB_TRAVERSAL,
319 wxDefaultValidator, name) )
320 return false;
321
322 if ( !MSWCreateControl(className, wxEmptyString, pos, size) )
323 return false;
324
325#if wxUSE_UXTHEME
326 if ( HasFlag(wxNB_NOPAGETHEME) ||
327 wxSystemOptions::IsFalse(wxT("msw.notebook.themed-background")) )
328 {
329 SetBackgroundColour(GetThemeBackgroundColour());
330 }
331#endif // wxUSE_UXTHEME
332
333 // Undocumented hack to get flat notebook style
334 // In fact, we should probably only do this in some
335 // curcumstances, i.e. if we know we will have a border
336 // at the bottom (the tab control doesn't draw it itself)
337#if defined(__POCKETPC__) || defined(__SMARTPHONE__)
338 if (HasFlag(wxNB_FLAT))
339 {
340 SendMessage(GetHwnd(), CCM_SETVERSION, COMCTL32_VERSION, 0);
341 if (!m_hasBgCol)
342 SetBackgroundColour(*wxWHITE);
343 }
344#endif
345 return true;
346}
347
348WXDWORD wxNotebook::MSWGetStyle(long style, WXDWORD *exstyle) const
349{
350 WXDWORD tabStyle = wxControl::MSWGetStyle(style, exstyle);
351
352 tabStyle |= WS_TABSTOP | TCS_TABS;
353
354 if ( style & wxNB_MULTILINE )
355 tabStyle |= TCS_MULTILINE;
356 if ( style & wxNB_FIXEDWIDTH )
357 tabStyle |= TCS_FIXEDWIDTH;
358
359 if ( style & wxNB_BOTTOM )
360 tabStyle |= TCS_RIGHT;
361 else if ( style & wxNB_LEFT )
362 tabStyle |= TCS_VERTICAL;
363 else if ( style & wxNB_RIGHT )
364 tabStyle |= TCS_VERTICAL | TCS_RIGHT;
365
366 // ex style
367 if ( exstyle )
368 {
369 // note that we never want to have the default WS_EX_CLIENTEDGE style
370 // as it looks too ugly for the notebooks
371 *exstyle = 0;
372 }
373
374 return tabStyle;
375}
376
377wxNotebook::~wxNotebook()
378{
379#if wxUSE_UXTHEME
380 if ( m_hbrBackground )
381 ::DeleteObject((HBRUSH)m_hbrBackground);
382#endif // wxUSE_UXTHEME
383}
384
385// ----------------------------------------------------------------------------
386// wxNotebook accessors
387// ----------------------------------------------------------------------------
388
389size_t wxNotebook::GetPageCount() const
390{
391 // consistency check
392 wxASSERT( (int)m_pages.Count() == TabCtrl_GetItemCount(GetHwnd()) );
393
394 return m_pages.Count();
395}
396
397int wxNotebook::GetRowCount() const
398{
399 return TabCtrl_GetRowCount(GetHwnd());
400}
401
402int wxNotebook::SetSelection(size_t nPage)
403{
404 wxCHECK_MSG( IS_VALID_PAGE(nPage), wxNOT_FOUND, wxT("notebook page out of range") );
405
406 if ( int(nPage) != m_nSelection )
407 {
408 wxNotebookEvent event(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING, m_windowId);
409 event.SetSelection(nPage);
410 event.SetOldSelection(m_nSelection);
411 event.SetEventObject(this);
412 if ( !GetEventHandler()->ProcessEvent(event) || event.IsAllowed() )
413 {
414 // program allows the page change
415 event.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED);
416 (void)GetEventHandler()->ProcessEvent(event);
417
418 TabCtrl_SetCurSel(GetHwnd(), nPage);
419 }
420 }
421
422 return m_nSelection;
423}
424
425bool wxNotebook::SetPageText(size_t nPage, const wxString& strText)
426{
427 wxCHECK_MSG( IS_VALID_PAGE(nPage), false, wxT("notebook page out of range") );
428
429 TC_ITEM tcItem;
430 tcItem.mask = TCIF_TEXT;
431 tcItem.pszText = (wxChar *)strText.c_str();
432
433 return TabCtrl_SetItem(GetHwnd(), nPage, &tcItem) != 0;
434}
435
436wxString wxNotebook::GetPageText(size_t nPage) const
437{
438 wxCHECK_MSG( IS_VALID_PAGE(nPage), wxEmptyString, wxT("notebook page out of range") );
439
440 wxChar buf[256];
441 TC_ITEM tcItem;
442 tcItem.mask = TCIF_TEXT;
443 tcItem.pszText = buf;
444 tcItem.cchTextMax = WXSIZEOF(buf);
445
446 wxString str;
447 if ( TabCtrl_GetItem(GetHwnd(), nPage, &tcItem) )
448 str = tcItem.pszText;
449
450 return str;
451}
452
453int wxNotebook::GetPageImage(size_t nPage) const
454{
455 wxCHECK_MSG( IS_VALID_PAGE(nPage), -1, wxT("notebook page out of range") );
456
457 TC_ITEM tcItem;
458 tcItem.mask = TCIF_IMAGE;
459
460 return TabCtrl_GetItem(GetHwnd(), nPage, &tcItem) ? tcItem.iImage : -1;
461}
462
463bool wxNotebook::SetPageImage(size_t nPage, int nImage)
464{
465 wxCHECK_MSG( IS_VALID_PAGE(nPage), false, wxT("notebook page out of range") );
466
467 TC_ITEM tcItem;
468 tcItem.mask = TCIF_IMAGE;
469 tcItem.iImage = nImage;
470
471 return TabCtrl_SetItem(GetHwnd(), nPage, &tcItem) != 0;
472}
473
474void wxNotebook::SetImageList(wxImageList* imageList)
475{
476 wxNotebookBase::SetImageList(imageList);
477
478 if ( imageList )
479 {
480 TabCtrl_SetImageList(GetHwnd(), (HIMAGELIST)imageList->GetHIMAGELIST());
481 }
482}
483
484// ----------------------------------------------------------------------------
485// wxNotebook size settings
486// ----------------------------------------------------------------------------
487
488wxRect wxNotebook::GetPageSize() const
489{
490 wxRect r;
491
492 RECT rc;
493 ::GetClientRect(GetHwnd(), &rc);
494
495 // This check is to work around a bug in TabCtrl_AdjustRect which will
496 // cause a crash on win2k, or on XP with themes disabled, if the
497 // wxNB_MULTILINE style is used and the rectangle is very small, (such as
498 // when the notebook is first created.) The value of 20 is just
499 // arbitrarily chosen, if there is a better way to determine this value
500 // then please do so. --RD
501 if ( !HasFlag(wxNB_MULTILINE) || (rc.right > 20 && rc.bottom > 20) )
502 {
503 TabCtrl_AdjustRect(GetHwnd(), false, &rc);
504
505 wxCopyRECTToRect(rc, r);
506 }
507
508 return r;
509}
510
511void wxNotebook::SetPageSize(const wxSize& size)
512{
513 // transform the page size into the notebook size
514 RECT rc;
515 rc.left =
516 rc.top = 0;
517 rc.right = size.x;
518 rc.bottom = size.y;
519
520 TabCtrl_AdjustRect(GetHwnd(), true, &rc);
521
522 // and now set it
523 SetSize(rc.right - rc.left, rc.bottom - rc.top);
524}
525
526void wxNotebook::SetPadding(const wxSize& padding)
527{
528 TabCtrl_SetPadding(GetHwnd(), padding.x, padding.y);
529}
530
531// Windows-only at present. Also, you must use the wxNB_FIXEDWIDTH
532// style.
533void wxNotebook::SetTabSize(const wxSize& sz)
534{
535 ::SendMessage(GetHwnd(), TCM_SETITEMSIZE, 0, MAKELPARAM(sz.x, sz.y));
536}
537
538wxSize wxNotebook::CalcSizeFromPage(const wxSize& sizePage) const
539{
540 wxSize sizeTotal = sizePage;
541
542 // We need to make getting tab size part of the wxWidgets API.
543 wxSize tabSize;
544 if (GetPageCount() > 0)
545 {
546 RECT rect;
547 TabCtrl_GetItemRect((HWND) GetHWND(), 0, & rect);
548 tabSize.x = rect.right - rect.left;
549 tabSize.y = rect.bottom - rect.top;
550 }
551 if ( HasFlag(wxNB_LEFT) || HasFlag(wxNB_RIGHT) )
552 {
553 sizeTotal.x += tabSize.x + 7;
554 sizeTotal.y += 7;
555 }
556 else
557 {
558 sizeTotal.x += 7;
559 sizeTotal.y += tabSize.y + 7;
560 }
561
562 return sizeTotal;
563}
564
565void wxNotebook::AdjustPageSize(wxNotebookPage *page)
566{
567 wxCHECK_RET( page, _T("NULL page in wxNotebook::AdjustPageSize") );
568
569 const wxRect r = GetPageSize();
570 if ( !r.IsEmpty() )
571 {
572 page->SetSize(r);
573 }
574}
575
576// ----------------------------------------------------------------------------
577// wxNotebook operations
578// ----------------------------------------------------------------------------
579
580// remove one page from the notebook, without deleting
581wxNotebookPage *wxNotebook::DoRemovePage(size_t nPage)
582{
583 wxNotebookPage *pageRemoved = wxNotebookBase::DoRemovePage(nPage);
584 if ( !pageRemoved )
585 return NULL;
586
587 TabCtrl_DeleteItem(GetHwnd(), nPage);
588
589 if ( m_pages.IsEmpty() )
590 {
591 // no selection any more, the notebook becamse empty
592 m_nSelection = -1;
593 }
594 else // notebook still not empty
595 {
596 int selNew = TabCtrl_GetCurSel(GetHwnd());
597 if (selNew != -1)
598 {
599 // No selection change, just refresh the current selection.
600 // Because it could be that the slection index changed
601 // we need to update it.
602 // Note: this does not mean the selection it self changed.
603 m_nSelection = selNew;
604 m_pages[m_nSelection]->Refresh();
605 }
606 else if (int(nPage) == m_nSelection)
607 {
608 // The selection was deleted.
609
610 // Determine new selection.
611 if (m_nSelection == int(GetPageCount()))
612 selNew = m_nSelection - 1;
613 else
614 selNew = m_nSelection;
615
616 // m_nSelection must be always valid so reset it before calling
617 // SetSelection()
618 m_nSelection = -1;
619 SetSelection(selNew);
620 }
621 else
622 {
623 wxFAIL; // Windows did not behave ok.
624 }
625 }
626
627 return pageRemoved;
628}
629
630// remove all pages
631bool wxNotebook::DeleteAllPages()
632{
633 size_t nPageCount = GetPageCount();
634 size_t nPage;
635 for ( nPage = 0; nPage < nPageCount; nPage++ )
636 delete m_pages[nPage];
637
638 m_pages.Clear();
639
640 TabCtrl_DeleteAllItems(GetHwnd());
641
642 m_nSelection = -1;
643
644 InvalidateBestSize();
645 return true;
646}
647
648// same as AddPage() but does it at given position
649bool wxNotebook::InsertPage(size_t nPage,
650 wxNotebookPage *pPage,
651 const wxString& strText,
652 bool bSelect,
653 int imageId)
654{
655 wxCHECK_MSG( pPage != NULL, false, _T("NULL page in wxNotebook::InsertPage") );
656 wxCHECK_MSG( IS_VALID_PAGE(nPage) || nPage == GetPageCount(), false,
657 _T("invalid index in wxNotebook::InsertPage") );
658
659 wxASSERT_MSG( pPage->GetParent() == this,
660 _T("notebook pages must have notebook as parent") );
661
662 // add a new tab to the control
663 // ----------------------------
664
665 // init all fields to 0
666 TC_ITEM tcItem;
667 wxZeroMemory(tcItem);
668
669 // set the image, if any
670 if ( imageId != -1 )
671 {
672 tcItem.mask |= TCIF_IMAGE;
673 tcItem.iImage = imageId;
674 }
675
676 // and the text
677 if ( !strText.empty() )
678 {
679 tcItem.mask |= TCIF_TEXT;
680 tcItem.pszText = (wxChar *)strText.c_str(); // const_cast
681 }
682
683 // hide the page: unless it is selected, it shouldn't be shown (and if it
684 // is selected it will be shown later)
685 HWND hwnd = GetWinHwnd(pPage);
686 SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~WS_VISIBLE);
687
688 // this updates internal flag too -- otherwise it would get out of sync
689 // with the real state
690 pPage->Show(false);
691
692
693 // fit the notebook page to the tab control's display area: this should be
694 // done before adding it to the notebook or TabCtrl_InsertItem() will
695 // change the notebooks size itself!
696 AdjustPageSize(pPage);
697
698 // finally do insert it
699 if ( TabCtrl_InsertItem(GetHwnd(), nPage, &tcItem) == -1 )
700 {
701 wxLogError(wxT("Can't create the notebook page '%s'."), strText.c_str());
702
703 return false;
704 }
705
706 // succeeded: save the pointer to the page
707 m_pages.Insert(pPage, nPage);
708
709 // we may need to adjust the size again if the notebook size changed:
710 // normally this only happens for the first page we add (the tabs which
711 // hadn't been there before are now shown) but for a multiline notebook it
712 // can happen for any page at all as a new row could have been started
713 if ( m_pages.GetCount() == 1 || HasFlag(wxNB_MULTILINE) )
714 {
715 AdjustPageSize(pPage);
716 }
717
718 // now deal with the selection
719 // ---------------------------
720
721 // if the inserted page is before the selected one, we must update the
722 // index of the selected page
723 if ( int(nPage) <= m_nSelection )
724 {
725 // one extra page added
726 m_nSelection++;
727 }
728
729 // some page should be selected: either this one or the first one if there
730 // is still no selection
731 int selNew = -1;
732 if ( bSelect )
733 selNew = nPage;
734 else if ( m_nSelection == -1 )
735 selNew = 0;
736
737 if ( selNew != -1 )
738 SetSelection(selNew);
739
740 InvalidateBestSize();
741
742 return true;
743}
744
745int wxNotebook::HitTest(const wxPoint& pt, long *flags) const
746{
747 TC_HITTESTINFO hitTestInfo;
748 hitTestInfo.pt.x = pt.x;
749 hitTestInfo.pt.y = pt.y;
750 int item = TabCtrl_HitTest(GetHwnd(), &hitTestInfo);
751
752 if ( flags )
753 {
754 *flags = 0;
755
756 if ((hitTestInfo.flags & TCHT_NOWHERE) == TCHT_NOWHERE)
757 *flags |= wxNB_HITTEST_NOWHERE;
758 if ((hitTestInfo.flags & TCHT_ONITEM) == TCHT_ONITEM)
759 *flags |= wxNB_HITTEST_ONITEM;
760 if ((hitTestInfo.flags & TCHT_ONITEMICON) == TCHT_ONITEMICON)
761 *flags |= wxNB_HITTEST_ONICON;
762 if ((hitTestInfo.flags & TCHT_ONITEMLABEL) == TCHT_ONITEMLABEL)
763 *flags |= wxNB_HITTEST_ONLABEL;
764 }
765
766 return item;
767}
768
769// ----------------------------------------------------------------------------
770// flicker-less notebook redraw
771// ----------------------------------------------------------------------------
772
773#if USE_NOTEBOOK_ANTIFLICKER
774
775// wnd proc for the spin button
776LRESULT APIENTRY _EXPORT wxNotebookSpinBtnWndProc(HWND hwnd,
777 UINT message,
778 WPARAM wParam,
779 LPARAM lParam)
780{
781 if ( message == WM_ERASEBKGND )
782 return 0;
783
784 return ::CallWindowProc(CASTWNDPROC gs_wndprocNotebookSpinBtn,
785 hwnd, message, wParam, lParam);
786}
787
788
789void wxNotebook::OnEraseBackground(wxEraseEvent& WXUNUSED(event))
790{
791 // do nothing here
792}
793
794void wxNotebook::OnPaint(wxPaintEvent& WXUNUSED(event))
795{
796 wxPaintDC dc(this);
797 wxMemoryDC memdc;
798 RECT rc;
799 ::GetClientRect(GetHwnd(), &rc);
800 wxBitmap bmp(rc.right, rc.bottom);
801 memdc.SelectObject(bmp);
802
803 // if there is no special brush just use the solid background colour
804 HBRUSH hbr = (HBRUSH)m_hbrBackground;
805 if ( !hbr )
806 hbr = GetHbrushOf(wxBrush(GetBackgroundColour()));
807
808 ::FillRect(GetHdcOf(memdc), &rc, hbr);
809
810 MSWDefWindowProc(WM_PAINT, (WPARAM)memdc.GetHDC(), 0);
811
812 dc.Blit(0, 0, rc.right, rc.bottom, &memdc, 0, 0);
813}
814
815#endif // USE_NOTEBOOK_ANTIFLICKER
816
817// ----------------------------------------------------------------------------
818// wxNotebook callbacks
819// ----------------------------------------------------------------------------
820
821void wxNotebook::OnSize(wxSizeEvent& event)
822{
823 if ( GetPageCount() == 0 )
824 {
825 // Prevents droppings on resize, but does cause some flicker
826 // when there are no pages.
827 Refresh();
828 event.Skip();
829 return;
830 }
831#ifndef __WXWINCE__
832 else
833 {
834 // Without this, we can sometimes get droppings at the edges
835 // of a notebook, for example a notebook in a splitter window.
836 // This needs to be reconciled with the RefreshRect calls
837 // at the end of this function, which weren't enough to prevent
838 // the droppings.
839
840 wxSize sz = GetClientSize();
841
842 // Refresh right side
843 wxRect rect(sz.x-4, 0, 4, sz.y);
844 RefreshRect(rect);
845
846 // Refresh bottom side
847 rect = wxRect(0, sz.y-4, sz.x, 4);
848 RefreshRect(rect);
849
850 // Refresh left side
851 rect = wxRect(0, 0, 4, sz.y);
852 RefreshRect(rect);
853 }
854#endif // !__WXWINCE__
855
856 // fit all the notebook pages to the tab control's display area
857
858 RECT rc;
859 rc.left = rc.top = 0;
860 GetSize((int *)&rc.right, (int *)&rc.bottom);
861
862 // save the total size, we'll use it below
863 int widthNbook = rc.right - rc.left,
864 heightNbook = rc.bottom - rc.top;
865
866 // there seems to be a bug in the implementation of TabCtrl_AdjustRect(): it
867 // returns completely false values for multiline tab controls after the tabs
868 // are added but before getting the first WM_SIZE (off by ~50 pixels, see
869 //
870 // http://sf.net/tracker/index.php?func=detail&aid=645323&group_id=9863&atid=109863
871 //
872 // and the only work around I could find was this ugly hack... without it
873 // simply toggling the "multiline" checkbox in the notebook sample resulted
874 // in a noticeable page displacement
875 if ( HasFlag(wxNB_MULTILINE) )
876 {
877 // avoid an infinite recursion: we get another notification too!
878 static bool s_isInOnSize = false;
879
880 if ( !s_isInOnSize )
881 {
882 s_isInOnSize = true;
883 SendMessage(GetHwnd(), WM_SIZE, SIZE_RESTORED,
884 MAKELPARAM(rc.right, rc.bottom));
885 s_isInOnSize = false;
886 }
887 }
888
889#if wxUSE_UXTHEME
890 // background bitmap size has changed, update the brush using it too
891 UpdateBgBrush();
892#endif // wxUSE_UXTHEME
893
894 TabCtrl_AdjustRect(GetHwnd(), false, &rc);
895
896 int width = rc.right - rc.left,
897 height = rc.bottom - rc.top;
898 size_t nCount = m_pages.Count();
899 for ( size_t nPage = 0; nPage < nCount; nPage++ ) {
900 wxNotebookPage *pPage = m_pages[nPage];
901 pPage->SetSize(rc.left, rc.top, width, height);
902 }
903
904
905 // unless we had already repainted everything, we now need to refresh
906 if ( !HasFlag(wxFULL_REPAINT_ON_RESIZE) )
907 {
908 // invalidate areas not covered by pages
909 RefreshRect(wxRect(0, 0, widthNbook, rc.top), false);
910 RefreshRect(wxRect(0, rc.top, rc.left, height), false);
911 RefreshRect(wxRect(0, rc.bottom, widthNbook, heightNbook - rc.bottom),
912 false);
913 RefreshRect(wxRect(rc.right, rc.top, widthNbook - rc.bottom, height),
914 false);
915 }
916
917#if USE_NOTEBOOK_ANTIFLICKER
918 // subclass the spin control used by the notebook to scroll pages to
919 // prevent it from flickering on resize
920 if ( !gs_wndprocNotebookSpinBtn )
921 {
922 // iterate over all child windows to find spin button
923 for ( HWND child = ::GetWindow(GetHwnd(), GW_CHILD);
924 child;
925 child = ::GetWindow(child, GW_HWNDNEXT) )
926 {
927 wxWindow *childWindow = wxFindWinFromHandle((WXHWND)child);
928
929 // see if it exists, if no wxWindow found then assume it's the spin
930 // btn
931 if ( !childWindow )
932 {
933 // subclass the spin button to override WM_ERASEBKGND
934 gs_wndprocNotebookSpinBtn = (WXFARPROC)wxGetWindowProc(child);
935
936 wxSetWindowProc(child, wxNotebookSpinBtnWndProc);
937 break;
938 }
939 }
940 }
941#endif // USE_NOTEBOOK_ANTIFLICKER
942
943 event.Skip();
944}
945
946void wxNotebook::OnSelChange(wxNotebookEvent& event)
947{
948 // is it our tab control?
949 if ( event.GetEventObject() == this )
950 {
951 int sel = event.GetOldSelection();
952 if ( sel != -1 )
953 m_pages[sel]->Show(false);
954
955 sel = event.GetSelection();
956 if ( sel != -1 )
957 {
958 wxNotebookPage *pPage = m_pages[sel];
959 pPage->Show(true);
960 pPage->SetFocus();
961
962 // If the newly focused window is not a child of the new page,
963 // SetFocus was not successful and the notebook itself should be
964 // focused
965 wxWindow *currentFocus = FindFocus();
966 wxWindow *startFocus = currentFocus;
967 while ( currentFocus && currentFocus != pPage && currentFocus != this )
968 currentFocus = currentFocus->GetParent();
969
970 if ( startFocus == pPage || currentFocus != pPage )
971 SetFocus();
972
973 }
974 else // no pages in the notebook, give the focus to itself
975 {
976 SetFocus();
977 }
978
979 m_nSelection = sel;
980 }
981
982 // we want to give others a chance to process this message as well
983 event.Skip();
984}
985
986bool wxNotebook::MSWTranslateMessage(WXMSG *wxmsg)
987{
988 const MSG * const msg = (MSG *)wxmsg;
989
990 // intercept TAB, CTRL+TAB and CTRL+SHIFT+TAB for processing by wxNotebook.
991 // TAB will be passed to the currently selected page, CTRL+TAB and
992 // CTRL+SHIFT+TAB will be processed by the notebook itself. do not
993 // intercept SHIFT+TAB. This goes to the parent of the notebook which will
994 // process it.
995 if ( msg->message == WM_KEYDOWN && msg->wParam == VK_TAB &&
996 msg->hwnd == GetHwnd() &&
997 (wxIsCtrlDown() || !wxIsShiftDown()) )
998 {
999 return MSWProcessMessage(wxmsg);
1000 }
1001
1002 return false;
1003}
1004
1005void wxNotebook::OnNavigationKey(wxNavigationKeyEvent& event)
1006{
1007 if ( event.IsWindowChange() ) {
1008 // change pages
1009 AdvanceSelection(event.GetDirection());
1010 }
1011 else {
1012 // we get this event in 3 cases
1013 //
1014 // a) one of our pages might have generated it because the user TABbed
1015 // out from it in which case we should propagate the event upwards and
1016 // our parent will take care of setting the focus to prev/next sibling
1017 //
1018 // or
1019 //
1020 // b) the parent panel wants to give the focus to us so that we
1021 // forward it to our selected page. We can't deal with this in
1022 // OnSetFocus() because we don't know which direction the focus came
1023 // from in this case and so can't choose between setting the focus to
1024 // first or last panel child
1025 //
1026 // or
1027 //
1028 // c) we ourselves (see MSWTranslateMessage) generated the event
1029 //
1030 wxWindow * const parent = GetParent();
1031
1032 // the wxObject* casts are required to avoid MinGW GCC 2.95.3 ICE
1033 const bool isFromParent = event.GetEventObject() == (wxObject*) parent;
1034 const bool isFromSelf = event.GetEventObject() == (wxObject*) this;
1035
1036 if ( isFromParent || isFromSelf )
1037 {
1038 // no, it doesn't come from child, case (b) or (c): forward to a
1039 // page but only if direction is backwards (TAB) or from ourselves,
1040 if ( m_nSelection != -1 &&
1041 (!event.GetDirection() || isFromSelf) )
1042 {
1043 // so that the page knows that the event comes from it's parent
1044 // and is being propagated downwards
1045 event.SetEventObject(this);
1046
1047 wxWindow *page = m_pages[m_nSelection];
1048 if ( !page->GetEventHandler()->ProcessEvent(event) )
1049 {
1050 page->SetFocus();
1051 }
1052 //else: page manages focus inside it itself
1053 }
1054 else // otherwise set the focus to the notebook itself
1055 {
1056 SetFocus();
1057 }
1058 }
1059 else
1060 {
1061 // it comes from our child, case (a), pass to the parent, but only
1062 // if the direction is forwards. Otherwise set the focus to the
1063 // notebook itself. The notebook is always the 'first' control of a
1064 // page.
1065 if ( !event.GetDirection() )
1066 {
1067 SetFocus();
1068 }
1069 else if ( parent )
1070 {
1071 event.SetCurrentFocus(this);
1072 parent->GetEventHandler()->ProcessEvent(event);
1073 }
1074 }
1075 }
1076}
1077
1078#if wxUSE_UXTHEME
1079
1080bool wxNotebook::DoDrawBackground(WXHDC hDC, wxWindow *child)
1081{
1082 wxUxThemeHandle theme(child ? child : this, L"TAB");
1083 if ( !theme )
1084 return false;
1085
1086 // get the notebook client rect (we're not interested in drawing tabs
1087 // themselves)
1088 wxRect r = GetPageSize();
1089 if ( r.IsEmpty() )
1090 return false;
1091
1092 RECT rc;
1093 wxCopyRectToRECT(r, rc);
1094
1095 // map rect to the coords of the window we're drawing in
1096 if ( child )
1097 ::MapWindowPoints(GetHwnd(), GetHwndOf(child), (POINT *)&rc, 2);
1098
1099
1100 // apparently DrawThemeBackground() modifies the rect passed to it and if we
1101 // don't do these adjustments, there are some drawing artifacts which are
1102 // only visible with some non default themes; so modify the rect here using
1103 // the magic numbers below so that it still paints the correct area
1104 rc.left -= 2;
1105 rc.top -= 2;
1106 rc.right += 4;
1107 rc.bottom += 5;
1108
1109
1110 wxUxThemeEngine::Get()->DrawThemeBackground
1111 (
1112 theme,
1113 (HDC) hDC,
1114 9 /* TABP_PANE */,
1115 0,
1116 &rc,
1117 NULL
1118 );
1119
1120 return true;
1121}
1122
1123WXHBRUSH wxNotebook::QueryBgBitmap()
1124{
1125 wxRect r = GetPageSize();
1126 if ( r.IsEmpty() )
1127 return 0;
1128
1129 WindowHDC hDC(GetHwnd());
1130 MemoryHDC hDCMem(hDC);
1131 CompatibleBitmap hBmp(hDC, r.x + r.width, r.y + r.height);
1132
1133 SelectInHDC selectBmp(hDCMem, hBmp);
1134
1135 if ( !DoDrawBackground((WXHDC)(HDC)hDCMem) )
1136 return 0;
1137
1138 return (WXHBRUSH)::CreatePatternBrush(hBmp);
1139}
1140
1141void wxNotebook::UpdateBgBrush()
1142{
1143 if ( m_hbrBackground )
1144 ::DeleteObject((HBRUSH)m_hbrBackground);
1145
1146 if ( !m_hasBgCol && wxUxThemeEngine::GetIfActive() )
1147 {
1148 m_hbrBackground = QueryBgBitmap();
1149 }
1150 else // no themes or we've got user-defined solid colour
1151 {
1152 m_hbrBackground = NULL;
1153 }
1154}
1155
1156WXHBRUSH wxNotebook::MSWGetBgBrushForChild(WXHDC hDC, WXHWND hWnd)
1157{
1158 if ( m_hbrBackground )
1159 {
1160 // before drawing with the background brush, we need to position it
1161 // correctly
1162 RECT rc;
1163 ::GetWindowRect((HWND)hWnd, &rc);
1164
1165 ::MapWindowPoints(NULL, GetHwnd(), (POINT *)&rc, 1);
1166
1167 if ( !::SetBrushOrgEx((HDC)hDC, -rc.left, -rc.top, NULL) )
1168 {
1169 wxLogLastError(_T("SetBrushOrgEx(notebook bg brush)"));
1170 }
1171
1172 return m_hbrBackground;
1173 }
1174
1175 return wxNotebookBase::MSWGetBgBrushForChild(hDC, hWnd);
1176}
1177
1178bool wxNotebook::MSWPrintChild(WXHDC hDC, wxWindow *child)
1179{
1180 // solid background colour overrides themed background drawing
1181 if ( !UseBgCol() && DoDrawBackground(hDC, child) )
1182 return true;
1183
1184 // If we're using a solid colour (for example if we've switched off
1185 // theming for this notebook), paint it
1186 if (UseBgCol())
1187 {
1188 wxRect r = GetPageSize();
1189 if ( r.IsEmpty() )
1190 return false;
1191
1192 RECT rc;
1193 wxCopyRectToRECT(r, rc);
1194
1195 // map rect to the coords of the window we're drawing in
1196 if ( child )
1197 ::MapWindowPoints(GetHwnd(), GetHwndOf(child), (POINT *)&rc, 2);
1198
1199 wxBrush brush(GetBackgroundColour());
1200 HBRUSH hbr = GetHbrushOf(brush);
1201
1202 ::FillRect((HDC) hDC, &rc, hbr);
1203
1204 return true;
1205 }
1206
1207 return wxNotebookBase::MSWPrintChild(hDC, child);
1208}
1209
1210#endif // wxUSE_UXTHEME
1211
1212// Windows only: attempts to get colour for UX theme page background
1213wxColour wxNotebook::GetThemeBackgroundColour() const
1214{
1215#if wxUSE_UXTHEME
1216 if (wxUxThemeEngine::Get())
1217 {
1218 wxUxThemeHandle hTheme((wxNotebook*) this, L"TAB");
1219 if (hTheme)
1220 {
1221 // This is total guesswork.
1222 // See PlatformSDK\Include\Tmschema.h for values
1223 COLORREF themeColor;
1224 wxUxThemeEngine::Get()->GetThemeColor(
1225 hTheme,
1226 10 /* TABP_BODY */,
1227 1 /* NORMAL */,
1228 3821 /* FILLCOLORHINT */,
1229 &themeColor);
1230
1231 /*
1232 [DS] Workaround for WindowBlinds:
1233 Some themes return a near black theme color using FILLCOLORHINT,
1234 this makes notebook pages have an ugly black background and makes
1235 text (usually black) unreadable. Retry again with FILLCOLOR.
1236
1237 This workaround potentially breaks appearance of some themes,
1238 but in practice it already fixes some themes.
1239 */
1240 if (themeColor == 1)
1241 {
1242 wxUxThemeEngine::Get()->GetThemeColor(
1243 hTheme,
1244 10 /* TABP_BODY */,
1245 1 /* NORMAL */,
1246 3802 /* FILLCOLOR */,
1247 &themeColor);
1248 }
1249
1250 return wxRGBToColour(themeColor);
1251 }
1252 }
1253#endif // wxUSE_UXTHEME
1254
1255 return GetBackgroundColour();
1256}
1257
1258// ----------------------------------------------------------------------------
1259// wxNotebook base class virtuals
1260// ----------------------------------------------------------------------------
1261
1262#if wxUSE_CONSTRAINTS
1263
1264// override these 2 functions to do nothing: everything is done in OnSize
1265
1266void wxNotebook::SetConstraintSizes(bool WXUNUSED(recurse))
1267{
1268 // don't set the sizes of the pages - their correct size is not yet known
1269 wxControl::SetConstraintSizes(false);
1270}
1271
1272bool wxNotebook::DoPhase(int WXUNUSED(nPhase))
1273{
1274 return true;
1275}
1276
1277#endif // wxUSE_CONSTRAINTS
1278
1279// ----------------------------------------------------------------------------
1280// wxNotebook Windows message handlers
1281// ----------------------------------------------------------------------------
1282
1283bool wxNotebook::MSWOnScroll(int orientation, WXWORD nSBCode,
1284 WXWORD pos, WXHWND control)
1285{
1286 // don't generate EVT_SCROLLWIN events for the WM_SCROLLs coming from the
1287 // up-down control
1288 if ( control )
1289 return false;
1290
1291 return wxNotebookBase::MSWOnScroll(orientation, nSBCode, pos, control);
1292}
1293
1294bool wxNotebook::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM* result)
1295{
1296 wxNotebookEvent event(wxEVT_NULL, m_windowId);
1297
1298 NMHDR* hdr = (NMHDR *)lParam;
1299 switch ( hdr->code ) {
1300 case TCN_SELCHANGE:
1301 event.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED);
1302 break;
1303
1304 case TCN_SELCHANGING:
1305 event.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING);
1306 break;
1307
1308 default:
1309 return wxControl::MSWOnNotify(idCtrl, lParam, result);
1310 }
1311
1312 event.SetSelection(TabCtrl_GetCurSel(GetHwnd()));
1313 event.SetOldSelection(m_nSelection);
1314 event.SetEventObject(this);
1315 event.SetInt(idCtrl);
1316
1317 bool processed = GetEventHandler()->ProcessEvent(event);
1318 *result = !event.IsAllowed();
1319 return processed;
1320}
1321
1322#endif // wxUSE_NOTEBOOK