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