]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/msw/notebook.cpp
(hopefully) fixed wxGTK wxScrolledWindow::GetClientSize bug
[wxWidgets.git] / src / msw / notebook.cpp
... / ...
CommitLineData
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 license
10///////////////////////////////////////////////////////////////////////////////
11
12#ifdef __GNUG__
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// wxWindows
26#ifndef WX_PRECOMP
27 #include "wx/string.h"
28#endif // WX_PRECOMP
29
30#include "wx/log.h"
31#include "wx/imaglist.h"
32#include "wx/event.h"
33#include "wx/control.h"
34#include "wx/notebook.h"
35
36#include "wx/msw/private.h"
37
38// Windows standard headers
39#ifndef __WIN95__
40 #error "wxNotebook is only supported Windows 95 and above"
41#endif //Win95
42
43#include <windowsx.h> // for SetWindowFont
44
45#ifndef __TWIN32__
46 #ifdef __GNUWIN32_OLD__
47 #include "wx/msw/gnuwin32/extra.h"
48 #endif
49#endif
50
51#if defined(__WIN95__) && !((defined(__GNUWIN32_OLD__) || defined(__TWIN32__)) && !defined(__CYGWIN10__))
52 #include <commctrl.h>
53#endif
54
55// ----------------------------------------------------------------------------
56// macros
57// ----------------------------------------------------------------------------
58
59// check that the page index is valid
60#define IS_VALID_PAGE(nPage) (((nPage) >= 0) && ((nPage) < GetPageCount()))
61
62// hide the ugly cast
63#define m_hwnd (HWND)GetHWND()
64
65// ----------------------------------------------------------------------------
66// constants
67// ----------------------------------------------------------------------------
68
69// This is a work-around for missing defines in gcc-2.95 headers
70#ifndef TCS_RIGHT
71 #define TCS_RIGHT 0x0002
72#endif
73
74#ifndef TCS_VERTICAL
75 #define TCS_VERTICAL 0x0080
76#endif
77
78#ifndef TCS_BOTTOM
79 #define TCS_BOTTOM TCS_RIGHT
80#endif
81
82// ----------------------------------------------------------------------------
83// event table
84// ----------------------------------------------------------------------------
85
86DEFINE_EVENT_TYPE(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED)
87DEFINE_EVENT_TYPE(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING)
88
89BEGIN_EVENT_TABLE(wxNotebook, wxControl)
90 EVT_NOTEBOOK_PAGE_CHANGED(-1, wxNotebook::OnSelChange)
91
92 EVT_SIZE(wxNotebook::OnSize)
93
94 EVT_SET_FOCUS(wxNotebook::OnSetFocus)
95
96 EVT_NAVIGATION_KEY(wxNotebook::OnNavigationKey)
97END_EVENT_TABLE()
98
99IMPLEMENT_DYNAMIC_CLASS(wxNotebook, wxControl)
100IMPLEMENT_DYNAMIC_CLASS(wxNotebookEvent, wxNotifyEvent)
101
102// ============================================================================
103// implementation
104// ============================================================================
105
106// ----------------------------------------------------------------------------
107// wxNotebook construction
108// ----------------------------------------------------------------------------
109
110// common part of all ctors
111void wxNotebook::Init()
112{
113 m_bOwnsImageList = FALSE;
114 m_imageList = NULL;
115 m_nSelection = -1;
116}
117
118// default for dynamic class
119wxNotebook::wxNotebook()
120{
121 Init();
122}
123
124// the same arguments as for wxControl
125wxNotebook::wxNotebook(wxWindow *parent,
126 wxWindowID id,
127 const wxPoint& pos,
128 const wxSize& size,
129 long style,
130 const wxString& name)
131{
132 Init();
133
134 Create(parent, id, pos, size, style, name);
135}
136
137// Create() function
138bool wxNotebook::Create(wxWindow *parent,
139 wxWindowID id,
140 const wxPoint& pos,
141 const wxSize& size,
142 long style,
143 const wxString& name)
144{
145 // base init
146 if ( !CreateBase(parent, id, pos, size, style, wxDefaultValidator, name) )
147 return FALSE;
148
149 // colors and font
150 m_backgroundColour = wxColour(GetSysColor(COLOR_BTNFACE));
151 m_foregroundColour = *wxBLACK;
152
153 // style
154 m_windowStyle = style | wxTAB_TRAVERSAL;
155
156 long tabStyle = WS_CHILD | WS_VISIBLE | WS_TABSTOP | TCS_TABS;
157
158 if ( m_windowStyle & wxCLIP_SIBLINGS )
159 tabStyle |= WS_CLIPSIBLINGS;
160 if (m_windowStyle & wxCLIP_CHILDREN)
161 tabStyle |= WS_CLIPCHILDREN;
162 if ( m_windowStyle & wxTC_MULTILINE )
163 tabStyle |= TCS_MULTILINE;
164 if ( m_windowStyle & wxBORDER )
165 tabStyle &= WS_BORDER;
166 if (m_windowStyle & wxNB_FIXEDWIDTH)
167 tabStyle |= TCS_FIXEDWIDTH ;
168 if (m_windowStyle & wxNB_BOTTOM)
169 tabStyle |= TCS_RIGHT;
170 if (m_windowStyle & wxNB_LEFT)
171 tabStyle |= TCS_VERTICAL;
172 if (m_windowStyle & wxNB_RIGHT)
173 tabStyle |= TCS_VERTICAL|TCS_RIGHT;
174
175
176 if ( !MSWCreate(GetId(), GetParent(), WC_TABCONTROL,
177 this, NULL, pos.x, pos.y, size.x, size.y,
178 tabStyle, NULL, 0) )
179 {
180 return FALSE;
181 }
182
183 // Not all compilers recognise SetWindowFont
184 ::SendMessage(GetHwnd(), WM_SETFONT,
185 (WPARAM)::GetStockObject(DEFAULT_GUI_FONT), TRUE);
186
187
188 if ( parent != NULL )
189 parent->AddChild(this);
190
191 SubclassWin(m_hWnd);
192
193 return TRUE;
194}
195
196// dtor
197wxNotebook::~wxNotebook()
198{
199 if (m_bOwnsImageList)
200 delete m_imageList;
201}
202
203// ----------------------------------------------------------------------------
204// wxNotebook accessors
205// ----------------------------------------------------------------------------
206int wxNotebook::GetPageCount() const
207{
208 // consistency check
209 wxASSERT( (int)m_pages.Count() == TabCtrl_GetItemCount(m_hwnd) );
210
211 return m_pages.Count();
212}
213
214int wxNotebook::GetRowCount() const
215{
216 return TabCtrl_GetRowCount(m_hwnd);
217}
218
219int wxNotebook::SetSelection(int nPage)
220{
221 wxCHECK_MSG( IS_VALID_PAGE(nPage), -1, wxT("notebook page out of range") );
222
223 ChangePage(m_nSelection, nPage);
224
225 return TabCtrl_SetCurSel(m_hwnd, nPage);
226}
227
228bool wxNotebook::SetPageText(int nPage, const wxString& strText)
229{
230 wxCHECK_MSG( IS_VALID_PAGE(nPage), FALSE, wxT("notebook page out of range") );
231
232 TC_ITEM tcItem;
233 tcItem.mask = TCIF_TEXT;
234 tcItem.pszText = (wxChar *)strText.c_str();
235
236 return TabCtrl_SetItem(m_hwnd, nPage, &tcItem) != 0;
237}
238
239wxString wxNotebook::GetPageText(int nPage) const
240{
241 wxCHECK_MSG( IS_VALID_PAGE(nPage), wxT(""), wxT("notebook page out of range") );
242
243 wxChar buf[256];
244 TC_ITEM tcItem;
245 tcItem.mask = TCIF_TEXT;
246 tcItem.pszText = buf;
247 tcItem.cchTextMax = WXSIZEOF(buf);
248
249 wxString str;
250 if ( TabCtrl_GetItem(m_hwnd, nPage, &tcItem) )
251 str = tcItem.pszText;
252
253 return str;
254}
255
256int wxNotebook::GetPageImage(int nPage) const
257{
258 wxCHECK_MSG( IS_VALID_PAGE(nPage), -1, wxT("notebook page out of range") );
259
260 TC_ITEM tcItem;
261 tcItem.mask = TCIF_IMAGE;
262
263 return TabCtrl_GetItem(m_hwnd, nPage, &tcItem) ? tcItem.iImage : -1;
264}
265
266bool wxNotebook::SetPageImage(int nPage, int nImage)
267{
268 wxCHECK_MSG( IS_VALID_PAGE(nPage), FALSE, wxT("notebook page out of range") );
269
270 TC_ITEM tcItem;
271 tcItem.mask = TCIF_IMAGE;
272 tcItem.iImage = nImage;
273
274 return TabCtrl_SetItem(m_hwnd, nPage, &tcItem) != 0;
275}
276
277void wxNotebook::SetImageList(wxImageList* imageList)
278{
279 if ( m_bOwnsImageList )
280 {
281 delete m_imageList;
282 }
283
284 m_bOwnsImageList = FALSE;
285 m_imageList = imageList;
286
287 TabCtrl_SetImageList(m_hwnd, (HIMAGELIST)imageList->GetHIMAGELIST());
288}
289
290void wxNotebook::AssignImageList(wxImageList* imageList)
291{
292 SetImageList(imageList);
293 m_bOwnsImageList = TRUE;
294}
295
296// ----------------------------------------------------------------------------
297// wxNotebook size settings
298// ----------------------------------------------------------------------------
299
300void wxNotebook::SetPageSize(const wxSize& size)
301{
302 // transform the page size into the notebook size
303 RECT rc;
304 rc.left =
305 rc.top = 0;
306 rc.right = size.x;
307 rc.bottom = size.y;
308
309 TabCtrl_AdjustRect(GetHwnd(), TRUE, &rc);
310
311 // and now set it
312 SetSize(rc.right - rc.left, rc.bottom - rc.top);
313}
314
315void wxNotebook::SetPadding(const wxSize& padding)
316{
317 TabCtrl_SetPadding(GetHwnd(), padding.x, padding.y);
318}
319
320// Windows-only at present. Also, you must use the wxNB_FIXEDWIDTH
321// style.
322void wxNotebook::SetTabSize(const wxSize& sz)
323{
324 ::SendMessage(GetHwnd(), TCM_SETITEMSIZE, 0, MAKELPARAM(sz.x, sz.y));
325}
326
327// ----------------------------------------------------------------------------
328// wxNotebook operations
329// ----------------------------------------------------------------------------
330
331// remove one page from the notebook
332bool wxNotebook::DeletePage(int nPage)
333{
334 wxCHECK_MSG( IS_VALID_PAGE(nPage), FALSE, wxT("notebook page out of range") );
335
336 if ( m_nSelection == nPage ) {
337 // advance selection backwards - the page being deleted shouldn't be left
338 // selected
339 AdvanceSelection(FALSE);
340 }
341
342 TabCtrl_DeleteItem(m_hwnd, nPage);
343
344 delete m_pages[nPage];
345 m_pages.Remove(nPage);
346
347 if ( m_pages.IsEmpty() ) {
348 // no selection if the notebook became empty
349 m_nSelection = -1;
350 }
351 else
352 m_nSelection = TabCtrl_GetCurSel(m_hwnd);
353
354
355 return TRUE;
356}
357
358// remove one page from the notebook, without deleting
359wxNotebookPage *wxNotebook::DoRemovePage(int nPage)
360{
361 wxCHECK_MSG( IS_VALID_PAGE(nPage), NULL, wxT("notebook page out of range") );
362
363 TabCtrl_DeleteItem(m_hwnd, nPage);
364
365 wxNotebookPage *pageRemoved = m_pages[nPage];
366 m_pages.Remove(nPage);
367
368 if ( m_pages.IsEmpty() )
369 m_nSelection = -1;
370 else
371 m_nSelection = TabCtrl_GetCurSel(m_hwnd);
372
373 return pageRemoved;
374}
375
376// remove all pages
377bool wxNotebook::DeleteAllPages()
378{
379 int nPageCount = GetPageCount();
380 int nPage;
381 for ( nPage = 0; nPage < nPageCount; nPage++ )
382 delete m_pages[nPage];
383
384 m_pages.Clear();
385
386 TabCtrl_DeleteAllItems(m_hwnd);
387
388 m_nSelection = -1;
389
390 return TRUE;
391}
392
393// add a page to the notebook
394bool wxNotebook::AddPage(wxNotebookPage *pPage,
395 const wxString& strText,
396 bool bSelect,
397 int imageId)
398{
399 return InsertPage(GetPageCount(), pPage, strText, bSelect, imageId);
400}
401
402// same as AddPage() but does it at given position
403bool wxNotebook::InsertPage(int nPage,
404 wxNotebookPage *pPage,
405 const wxString& strText,
406 bool bSelect,
407 int imageId)
408{
409 wxASSERT( pPage != NULL );
410 wxCHECK( IS_VALID_PAGE(nPage) || nPage == GetPageCount(), FALSE );
411
412 // do add the tab to the control
413
414 // init all fields to 0
415 TC_ITEM tcItem;
416 memset(&tcItem, 0, sizeof(tcItem));
417
418 if ( imageId != -1 )
419 {
420 tcItem.mask |= TCIF_IMAGE;
421 tcItem.iImage = imageId;
422 }
423
424 if ( !strText.IsEmpty() )
425 {
426 tcItem.mask |= TCIF_TEXT;
427 tcItem.pszText = (wxChar *)strText.c_str(); // const_cast
428 }
429
430 if ( TabCtrl_InsertItem(m_hwnd, nPage, &tcItem) == -1 ) {
431 wxLogError(wxT("Can't create the notebook page '%s'."), strText.c_str());
432
433 return FALSE;
434 }
435
436 // if the inserted page is before the selected one, we must update the
437 // index of the selected page
438 if ( nPage <= m_nSelection )
439 {
440 // one extra page added
441 m_nSelection++;
442 }
443
444 // save the pointer to the page
445 m_pages.Insert(pPage, nPage);
446
447 // don't show pages by default (we'll need to adjust their size first)
448 HWND hwnd = GetWinHwnd(pPage);
449 SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~WS_VISIBLE);
450
451 // this updates internal flag too - otherwise it will get out of sync
452 pPage->Show(FALSE);
453
454 // fit the notebook page to the tab control's display area
455 RECT rc;
456 rc.left = rc.top = 0;
457 GetSize((int *)&rc.right, (int *)&rc.bottom);
458 TabCtrl_AdjustRect(m_hwnd, FALSE, &rc);
459 pPage->SetSize(rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top);
460
461 // some page should be selected: either this one or the first one if there is
462 // still no selection
463 int selNew = -1;
464 if ( bSelect )
465 selNew = nPage;
466 else if ( m_nSelection == -1 )
467 selNew = 0;
468
469 if ( selNew != -1 )
470 SetSelection(selNew);
471
472 return TRUE;
473}
474
475// ----------------------------------------------------------------------------
476// wxNotebook callbacks
477// ----------------------------------------------------------------------------
478
479void wxNotebook::OnSize(wxSizeEvent& event)
480{
481 // fit the notebook page to the tab control's display area
482 RECT rc;
483 rc.left = rc.top = 0;
484 GetSize((int *)&rc.right, (int *)&rc.bottom);
485
486 TabCtrl_AdjustRect(m_hwnd, FALSE, &rc);
487
488 int width = rc.right - rc.left,
489 height = rc.bottom - rc.top;
490 size_t nCount = m_pages.Count();
491 for ( size_t nPage = 0; nPage < nCount; nPage++ ) {
492 wxNotebookPage *pPage = m_pages[nPage];
493 pPage->SetSize(rc.left, rc.top, width, height);
494 }
495
496 event.Skip();
497}
498
499void wxNotebook::OnSelChange(wxNotebookEvent& event)
500{
501 // is it our tab control?
502 if ( event.GetEventObject() == this )
503 {
504 int sel = event.GetOldSelection();
505 if ( sel != -1 )
506 m_pages[sel]->Show(FALSE);
507
508 sel = event.GetSelection();
509 if ( sel != -1 )
510 {
511 wxNotebookPage *pPage = m_pages[sel];
512 pPage->Show(TRUE);
513 pPage->SetFocus();
514 }
515
516 m_nSelection = sel;
517 }
518
519 // we want to give others a chance to process this message as well
520 event.Skip();
521}
522
523void wxNotebook::OnSetFocus(wxFocusEvent& event)
524{
525 // this function is only called when the focus is explicitly set (i.e. from
526 // the program) to the notebook - in this case we don't need the
527 // complicated OnNavigationKey() logic because the programmer knows better
528 // what [s]he wants
529
530 // set focus to the currently selected page if any
531 if ( m_nSelection != -1 )
532 m_pages[m_nSelection]->SetFocus();
533
534 event.Skip();
535}
536
537void wxNotebook::OnNavigationKey(wxNavigationKeyEvent& event)
538{
539 if ( event.IsWindowChange() ) {
540 // change pages
541 AdvanceSelection(event.GetDirection());
542 }
543 else {
544 // we get this event in 2 cases
545 //
546 // a) one of our pages might have generated it because the user TABbed
547 // out from it in which case we should propagate the event upwards and
548 // our parent will take care of setting the focus to prev/next sibling
549 //
550 // or
551 //
552 // b) the parent panel wants to give the focus to us so that we
553 // forward it to our selected page. We can't deal with this in
554 // OnSetFocus() because we don't know which direction the focus came
555 // from in this case and so can't choose between setting the focus to
556 // first or last panel child
557
558 wxWindow *parent = GetParent();
559 if ( event.GetEventObject() == parent )
560 {
561 // no, it doesn't come from child, case (b): forward to a page
562 if ( m_nSelection != -1 )
563 {
564 // so that the page knows that the event comes from it's parent
565 // and is being propagated downwards
566 event.SetEventObject(this);
567
568 wxWindow *page = m_pages[m_nSelection];
569 if ( !page->GetEventHandler()->ProcessEvent(event) )
570 {
571 page->SetFocus();
572 }
573 //else: page manages focus inside it itself
574 }
575 else
576 {
577 // we have no pages - still have to give focus to _something_
578 SetFocus();
579 }
580 }
581 else
582 {
583 // it comes from our child, case (a), pass to the parent
584 if ( parent ) {
585 event.SetCurrentFocus(this);
586 parent->GetEventHandler()->ProcessEvent(event);
587 }
588 }
589 }
590}
591
592// ----------------------------------------------------------------------------
593// wxNotebook base class virtuals
594// ----------------------------------------------------------------------------
595
596// override these 2 functions to do nothing: everything is done in OnSize
597
598void wxNotebook::SetConstraintSizes(bool WXUNUSED(recurse))
599{
600 // don't set the sizes of the pages - their correct size is not yet known
601 wxControl::SetConstraintSizes(FALSE);
602}
603
604bool wxNotebook::DoPhase(int WXUNUSED(nPhase))
605{
606 return TRUE;
607}
608
609bool wxNotebook::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM* result)
610{
611 wxNotebookEvent event(wxEVT_NULL, m_windowId);
612
613 NMHDR* hdr = (NMHDR *)lParam;
614 switch ( hdr->code ) {
615 case TCN_SELCHANGE:
616 event.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED);
617 break;
618
619 case TCN_SELCHANGING:
620 event.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING);
621 break;
622
623 default:
624 return wxControl::MSWOnNotify(idCtrl, lParam, result);
625 }
626
627 event.SetSelection(TabCtrl_GetCurSel(m_hwnd));
628 event.SetOldSelection(m_nSelection);
629 event.SetEventObject(this);
630 event.SetInt(idCtrl);
631
632 bool processed = GetEventHandler()->ProcessEvent(event);
633 *result = !event.IsAllowed();
634 return processed;
635}
636
637// ----------------------------------------------------------------------------
638// wxNotebook helper functions
639// ----------------------------------------------------------------------------
640
641// generate the page changing and changed events, hide the currently active
642// panel and show the new one
643void wxNotebook::ChangePage(int nOldSel, int nSel)
644{
645 // MT-FIXME should use a real semaphore
646 static bool s_bInsideChangePage = FALSE;
647
648 // when we call ProcessEvent(), our own OnSelChange() is called which calls
649 // this function - break the infinite loop
650 if ( s_bInsideChangePage )
651 return;
652
653 // it's not an error (the message may be generated by the tab control itself)
654 // and it may happen - just do nothing
655 if ( nSel == nOldSel )
656 return;
657
658 s_bInsideChangePage = TRUE;
659
660 wxNotebookEvent event(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING, m_windowId);
661 event.SetSelection(nSel);
662 event.SetOldSelection(nOldSel);
663 event.SetEventObject(this);
664 if ( GetEventHandler()->ProcessEvent(event) && !event.IsAllowed() )
665 {
666 // program doesn't allow the page change
667 s_bInsideChangePage = FALSE;
668 return;
669 }
670
671 event.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED);
672 GetEventHandler()->ProcessEvent(event);
673
674 s_bInsideChangePage = FALSE;
675}
676
677#endif // wxUSE_NOTEBOOK