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