]> git.saurik.com Git - wxWidgets.git/blob - src/msw/notebook.cpp
f5aa19e9838b0700aa05aef6908cf1c5ea2474bd
[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
234 // default for dynamic class
235 wxNotebook::wxNotebook()
236 {
237 Init();
238 }
239
240 // the same arguments as for wxControl
241 wxNotebook::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
254 bool 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
348 WXDWORD 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
377 wxNotebook::~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
389 size_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
397 int wxNotebook::GetRowCount() const
398 {
399 return TabCtrl_GetRowCount(GetHwnd());
400 }
401
402 int 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
425 bool 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
436 wxString 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
453 int 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
463 bool 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
474 void 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
488 wxRect 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
511 void 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
526 void 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.
533 void wxNotebook::SetTabSize(const wxSize& sz)
534 {
535 ::SendMessage(GetHwnd(), TCM_SETITEMSIZE, 0, MAKELPARAM(sz.x, sz.y));
536 }
537
538 wxSize 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
565 void 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
581 wxNotebookPage *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
631 bool 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
649 bool 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
745 int 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
776 LRESULT 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
789 void wxNotebook::OnEraseBackground(wxEraseEvent& WXUNUSED(event))
790 {
791 // do nothing here
792 }
793
794 void 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 wxBrush brush;
806 if ( !hbr )
807 {
808 brush = wxBrush(GetBackgroundColour());
809 hbr = GetHbrushOf(brush);
810 }
811
812 ::FillRect(GetHdcOf(memdc), &rc, hbr);
813
814 MSWDefWindowProc(WM_PAINT, (WPARAM)memdc.GetHDC(), 0);
815
816 dc.Blit(0, 0, rc.right, rc.bottom, &memdc, 0, 0);
817 }
818
819 #endif // USE_NOTEBOOK_ANTIFLICKER
820
821 // ----------------------------------------------------------------------------
822 // wxNotebook callbacks
823 // ----------------------------------------------------------------------------
824
825 void wxNotebook::OnSize(wxSizeEvent& event)
826 {
827 if ( GetPageCount() == 0 )
828 {
829 // Prevents droppings on resize, but does cause some flicker
830 // when there are no pages.
831 Refresh();
832 event.Skip();
833 return;
834 }
835 #ifndef __WXWINCE__
836 else
837 {
838 // Without this, we can sometimes get droppings at the edges
839 // of a notebook, for example a notebook in a splitter window.
840 // This needs to be reconciled with the RefreshRect calls
841 // at the end of this function, which weren't enough to prevent
842 // the droppings.
843
844 wxSize sz = GetClientSize();
845
846 // Refresh right side
847 wxRect rect(sz.x-4, 0, 4, sz.y);
848 RefreshRect(rect);
849
850 // Refresh bottom side
851 rect = wxRect(0, sz.y-4, sz.x, 4);
852 RefreshRect(rect);
853
854 // Refresh left side
855 rect = wxRect(0, 0, 4, sz.y);
856 RefreshRect(rect);
857 }
858 #endif // !__WXWINCE__
859
860 // fit all the notebook pages to the tab control's display area
861
862 RECT rc;
863 rc.left = rc.top = 0;
864 GetSize((int *)&rc.right, (int *)&rc.bottom);
865
866 // save the total size, we'll use it below
867 int widthNbook = rc.right - rc.left,
868 heightNbook = rc.bottom - rc.top;
869
870 // there seems to be a bug in the implementation of TabCtrl_AdjustRect(): it
871 // returns completely false values for multiline tab controls after the tabs
872 // are added but before getting the first WM_SIZE (off by ~50 pixels, see
873 //
874 // http://sf.net/tracker/index.php?func=detail&aid=645323&group_id=9863&atid=109863
875 //
876 // and the only work around I could find was this ugly hack... without it
877 // simply toggling the "multiline" checkbox in the notebook sample resulted
878 // in a noticeable page displacement
879 if ( HasFlag(wxNB_MULTILINE) )
880 {
881 // avoid an infinite recursion: we get another notification too!
882 static bool s_isInOnSize = false;
883
884 if ( !s_isInOnSize )
885 {
886 s_isInOnSize = true;
887 SendMessage(GetHwnd(), WM_SIZE, SIZE_RESTORED,
888 MAKELPARAM(rc.right, rc.bottom));
889 s_isInOnSize = false;
890 }
891 }
892
893 #if wxUSE_UXTHEME
894 // background bitmap size has changed, update the brush using it too
895 UpdateBgBrush();
896 #endif // wxUSE_UXTHEME
897
898 TabCtrl_AdjustRect(GetHwnd(), false, &rc);
899
900 int width = rc.right - rc.left,
901 height = rc.bottom - rc.top;
902 size_t nCount = m_pages.Count();
903 for ( size_t nPage = 0; nPage < nCount; nPage++ ) {
904 wxNotebookPage *pPage = m_pages[nPage];
905 pPage->SetSize(rc.left, rc.top, width, height);
906 }
907
908
909 // unless we had already repainted everything, we now need to refresh
910 if ( !HasFlag(wxFULL_REPAINT_ON_RESIZE) )
911 {
912 // invalidate areas not covered by pages
913 RefreshRect(wxRect(0, 0, widthNbook, rc.top), false);
914 RefreshRect(wxRect(0, rc.top, rc.left, height), false);
915 RefreshRect(wxRect(0, rc.bottom, widthNbook, heightNbook - rc.bottom),
916 false);
917 RefreshRect(wxRect(rc.right, rc.top, widthNbook - rc.bottom, height),
918 false);
919 }
920
921 #if USE_NOTEBOOK_ANTIFLICKER
922 // subclass the spin control used by the notebook to scroll pages to
923 // prevent it from flickering on resize
924 if ( !gs_wndprocNotebookSpinBtn )
925 {
926 // iterate over all child windows to find spin button
927 for ( HWND child = ::GetWindow(GetHwnd(), GW_CHILD);
928 child;
929 child = ::GetWindow(child, GW_HWNDNEXT) )
930 {
931 wxWindow *childWindow = wxFindWinFromHandle((WXHWND)child);
932
933 // see if it exists, if no wxWindow found then assume it's the spin
934 // btn
935 if ( !childWindow )
936 {
937 // subclass the spin button to override WM_ERASEBKGND
938 gs_wndprocNotebookSpinBtn = (WXFARPROC)wxGetWindowProc(child);
939
940 wxSetWindowProc(child, wxNotebookSpinBtnWndProc);
941 break;
942 }
943 }
944 }
945 #endif // USE_NOTEBOOK_ANTIFLICKER
946
947 event.Skip();
948 }
949
950 void wxNotebook::OnSelChange(wxNotebookEvent& event)
951 {
952 // is it our tab control?
953 if ( event.GetEventObject() == this )
954 {
955 int sel = event.GetOldSelection();
956 if ( sel != -1 )
957 m_pages[sel]->Show(false);
958
959 sel = event.GetSelection();
960 if ( sel != -1 )
961 {
962 wxNotebookPage *pPage = m_pages[sel];
963 pPage->Show(true);
964 pPage->SetFocus();
965
966 // If the newly focused window is not a child of the new page,
967 // SetFocus was not successful and the notebook itself should be
968 // focused
969 wxWindow *currentFocus = FindFocus();
970 wxWindow *startFocus = currentFocus;
971 while ( currentFocus && currentFocus != pPage && currentFocus != this )
972 currentFocus = currentFocus->GetParent();
973
974 if ( startFocus == pPage || currentFocus != pPage )
975 SetFocus();
976
977 }
978 else // no pages in the notebook, give the focus to itself
979 {
980 SetFocus();
981 }
982
983 m_nSelection = sel;
984 }
985
986 // we want to give others a chance to process this message as well
987 event.Skip();
988 }
989
990 bool wxNotebook::MSWTranslateMessage(WXMSG *wxmsg)
991 {
992 const MSG * const msg = (MSG *)wxmsg;
993
994 // intercept TAB, CTRL+TAB and CTRL+SHIFT+TAB for processing by wxNotebook.
995 // TAB will be passed to the currently selected page, CTRL+TAB and
996 // CTRL+SHIFT+TAB will be processed by the notebook itself. do not
997 // intercept SHIFT+TAB. This goes to the parent of the notebook which will
998 // process it.
999 if ( msg->message == WM_KEYDOWN && msg->wParam == VK_TAB &&
1000 msg->hwnd == GetHwnd() &&
1001 (wxIsCtrlDown() || !wxIsShiftDown()) )
1002 {
1003 return MSWProcessMessage(wxmsg);
1004 }
1005
1006 return false;
1007 }
1008
1009 void wxNotebook::OnNavigationKey(wxNavigationKeyEvent& event)
1010 {
1011 if ( event.IsWindowChange() ) {
1012 // change pages
1013 AdvanceSelection(event.GetDirection());
1014 }
1015 else {
1016 // we get this event in 3 cases
1017 //
1018 // a) one of our pages might have generated it because the user TABbed
1019 // out from it in which case we should propagate the event upwards and
1020 // our parent will take care of setting the focus to prev/next sibling
1021 //
1022 // or
1023 //
1024 // b) the parent panel wants to give the focus to us so that we
1025 // forward it to our selected page. We can't deal with this in
1026 // OnSetFocus() because we don't know which direction the focus came
1027 // from in this case and so can't choose between setting the focus to
1028 // first or last panel child
1029 //
1030 // or
1031 //
1032 // c) we ourselves (see MSWTranslateMessage) generated the event
1033 //
1034 wxWindow * const parent = GetParent();
1035
1036 // the wxObject* casts are required to avoid MinGW GCC 2.95.3 ICE
1037 const bool isFromParent = event.GetEventObject() == (wxObject*) parent;
1038 const bool isFromSelf = event.GetEventObject() == (wxObject*) this;
1039
1040 if ( isFromParent || isFromSelf )
1041 {
1042 // no, it doesn't come from child, case (b) or (c): forward to a
1043 // page but only if direction is backwards (TAB) or from ourselves,
1044 if ( m_nSelection != -1 &&
1045 (!event.GetDirection() || isFromSelf) )
1046 {
1047 // so that the page knows that the event comes from it's parent
1048 // and is being propagated downwards
1049 event.SetEventObject(this);
1050
1051 wxWindow *page = m_pages[m_nSelection];
1052 if ( !page->GetEventHandler()->ProcessEvent(event) )
1053 {
1054 page->SetFocus();
1055 }
1056 //else: page manages focus inside it itself
1057 }
1058 else // otherwise set the focus to the notebook itself
1059 {
1060 SetFocus();
1061 }
1062 }
1063 else
1064 {
1065 // it comes from our child, case (a), pass to the parent, but only
1066 // if the direction is forwards. Otherwise set the focus to the
1067 // notebook itself. The notebook is always the 'first' control of a
1068 // page.
1069 if ( !event.GetDirection() )
1070 {
1071 SetFocus();
1072 }
1073 else if ( parent )
1074 {
1075 event.SetCurrentFocus(this);
1076 parent->GetEventHandler()->ProcessEvent(event);
1077 }
1078 }
1079 }
1080 }
1081
1082 #if wxUSE_UXTHEME
1083
1084 bool wxNotebook::DoDrawBackground(WXHDC hDC, wxWindow *child)
1085 {
1086 wxUxThemeHandle theme(child ? child : this, L"TAB");
1087 if ( !theme )
1088 return false;
1089
1090 // get the notebook client rect (we're not interested in drawing tabs
1091 // themselves)
1092 wxRect r = GetPageSize();
1093 if ( r.IsEmpty() )
1094 return false;
1095
1096 RECT rc;
1097 wxCopyRectToRECT(r, rc);
1098
1099 // map rect to the coords of the window we're drawing in
1100 if ( child )
1101 ::MapWindowPoints(GetHwnd(), GetHwndOf(child), (POINT *)&rc, 2);
1102
1103
1104 // apparently DrawThemeBackground() modifies the rect passed to it and if we
1105 // don't do these adjustments, there are some drawing artifacts which are
1106 // only visible with some non default themes; so modify the rect here using
1107 // the magic numbers below so that it still paints the correct area
1108 rc.left -= 2;
1109 rc.top -= 2;
1110 rc.right += 4;
1111 rc.bottom += 5;
1112
1113
1114 wxUxThemeEngine::Get()->DrawThemeBackground
1115 (
1116 theme,
1117 (HDC) hDC,
1118 9 /* TABP_PANE */,
1119 0,
1120 &rc,
1121 NULL
1122 );
1123
1124 return true;
1125 }
1126
1127 WXHBRUSH wxNotebook::QueryBgBitmap()
1128 {
1129 wxRect r = GetPageSize();
1130 if ( r.IsEmpty() )
1131 return 0;
1132
1133 WindowHDC hDC(GetHwnd());
1134 MemoryHDC hDCMem(hDC);
1135 CompatibleBitmap hBmp(hDC, r.x + r.width, r.y + r.height);
1136
1137 SelectInHDC selectBmp(hDCMem, hBmp);
1138
1139 if ( !DoDrawBackground((WXHDC)(HDC)hDCMem) )
1140 return 0;
1141
1142 return (WXHBRUSH)::CreatePatternBrush(hBmp);
1143 }
1144
1145 void wxNotebook::UpdateBgBrush()
1146 {
1147 if ( m_hbrBackground )
1148 ::DeleteObject((HBRUSH)m_hbrBackground);
1149
1150 if ( !m_hasBgCol && wxUxThemeEngine::GetIfActive() )
1151 {
1152 m_hbrBackground = QueryBgBitmap();
1153 }
1154 else // no themes or we've got user-defined solid colour
1155 {
1156 m_hbrBackground = NULL;
1157 }
1158 }
1159
1160 WXHBRUSH wxNotebook::MSWGetBgBrushForChild(WXHDC hDC, WXHWND hWnd)
1161 {
1162 if ( m_hbrBackground )
1163 {
1164 // before drawing with the background brush, we need to position it
1165 // correctly
1166 RECT rc;
1167 ::GetWindowRect((HWND)hWnd, &rc);
1168
1169 ::MapWindowPoints(NULL, GetHwnd(), (POINT *)&rc, 1);
1170
1171 if ( !::SetBrushOrgEx((HDC)hDC, -rc.left, -rc.top, NULL) )
1172 {
1173 wxLogLastError(_T("SetBrushOrgEx(notebook bg brush)"));
1174 }
1175
1176 return m_hbrBackground;
1177 }
1178
1179 return wxNotebookBase::MSWGetBgBrushForChild(hDC, hWnd);
1180 }
1181
1182 bool wxNotebook::MSWPrintChild(WXHDC hDC, wxWindow *child)
1183 {
1184 // solid background colour overrides themed background drawing
1185 if ( !UseBgCol() && DoDrawBackground(hDC, child) )
1186 return true;
1187
1188 // If we're using a solid colour (for example if we've switched off
1189 // theming for this notebook), paint it
1190 if (UseBgCol())
1191 {
1192 wxRect r = GetPageSize();
1193 if ( r.IsEmpty() )
1194 return false;
1195
1196 RECT rc;
1197 wxCopyRectToRECT(r, rc);
1198
1199 // map rect to the coords of the window we're drawing in
1200 if ( child )
1201 ::MapWindowPoints(GetHwnd(), GetHwndOf(child), (POINT *)&rc, 2);
1202
1203 wxBrush brush(GetBackgroundColour());
1204 HBRUSH hbr = GetHbrushOf(brush);
1205
1206 ::FillRect((HDC) hDC, &rc, hbr);
1207
1208 return true;
1209 }
1210
1211 return wxNotebookBase::MSWPrintChild(hDC, child);
1212 }
1213
1214 #endif // wxUSE_UXTHEME
1215
1216 // Windows only: attempts to get colour for UX theme page background
1217 wxColour wxNotebook::GetThemeBackgroundColour() const
1218 {
1219 #if wxUSE_UXTHEME
1220 if (wxUxThemeEngine::Get())
1221 {
1222 wxUxThemeHandle hTheme((wxNotebook*) this, L"TAB");
1223 if (hTheme)
1224 {
1225 // This is total guesswork.
1226 // See PlatformSDK\Include\Tmschema.h for values
1227 COLORREF themeColor;
1228 wxUxThemeEngine::Get()->GetThemeColor(
1229 hTheme,
1230 10 /* TABP_BODY */,
1231 1 /* NORMAL */,
1232 3821 /* FILLCOLORHINT */,
1233 &themeColor);
1234
1235 /*
1236 [DS] Workaround for WindowBlinds:
1237 Some themes return a near black theme color using FILLCOLORHINT,
1238 this makes notebook pages have an ugly black background and makes
1239 text (usually black) unreadable. Retry again with FILLCOLOR.
1240
1241 This workaround potentially breaks appearance of some themes,
1242 but in practice it already fixes some themes.
1243 */
1244 if (themeColor == 1)
1245 {
1246 wxUxThemeEngine::Get()->GetThemeColor(
1247 hTheme,
1248 10 /* TABP_BODY */,
1249 1 /* NORMAL */,
1250 3802 /* FILLCOLOR */,
1251 &themeColor);
1252 }
1253
1254 return wxRGBToColour(themeColor);
1255 }
1256 }
1257 #endif // wxUSE_UXTHEME
1258
1259 return GetBackgroundColour();
1260 }
1261
1262 // ----------------------------------------------------------------------------
1263 // wxNotebook base class virtuals
1264 // ----------------------------------------------------------------------------
1265
1266 #if wxUSE_CONSTRAINTS
1267
1268 // override these 2 functions to do nothing: everything is done in OnSize
1269
1270 void wxNotebook::SetConstraintSizes(bool WXUNUSED(recurse))
1271 {
1272 // don't set the sizes of the pages - their correct size is not yet known
1273 wxControl::SetConstraintSizes(false);
1274 }
1275
1276 bool wxNotebook::DoPhase(int WXUNUSED(nPhase))
1277 {
1278 return true;
1279 }
1280
1281 #endif // wxUSE_CONSTRAINTS
1282
1283 // ----------------------------------------------------------------------------
1284 // wxNotebook Windows message handlers
1285 // ----------------------------------------------------------------------------
1286
1287 bool wxNotebook::MSWOnScroll(int orientation, WXWORD nSBCode,
1288 WXWORD pos, WXHWND control)
1289 {
1290 // don't generate EVT_SCROLLWIN events for the WM_SCROLLs coming from the
1291 // up-down control
1292 if ( control )
1293 return false;
1294
1295 return wxNotebookBase::MSWOnScroll(orientation, nSBCode, pos, control);
1296 }
1297
1298 bool wxNotebook::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM* result)
1299 {
1300 wxNotebookEvent event(wxEVT_NULL, m_windowId);
1301
1302 NMHDR* hdr = (NMHDR *)lParam;
1303 switch ( hdr->code ) {
1304 case TCN_SELCHANGE:
1305 event.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED);
1306 break;
1307
1308 case TCN_SELCHANGING:
1309 event.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING);
1310 break;
1311
1312 default:
1313 return wxControl::MSWOnNotify(idCtrl, lParam, result);
1314 }
1315
1316 event.SetSelection(TabCtrl_GetCurSel(GetHwnd()));
1317 event.SetOldSelection(m_nSelection);
1318 event.SetEventObject(this);
1319 event.SetInt(idCtrl);
1320
1321 bool processed = GetEventHandler()->ProcessEvent(event);
1322 *result = !event.IsAllowed();
1323 return processed;
1324 }
1325
1326 #endif // wxUSE_NOTEBOOK