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