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