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