Changed wxWindow::Create to wxControl because wxNotebook is derived from it.
[wxWidgets.git] / src / generic / notebook.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: notebook.cpp
3 // Purpose: implementation of wxNotebook
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 17/09/98
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
20 #pragma implementation "notebook.h"
21 #endif
22
23 #ifdef __VMS
24 #pragma message disable unscomzer
25 #endif
26
27 // For compilers that support precompilation, includes "wx.h".
28 #include "wx/wxprec.h"
29
30 #ifdef __BORLANDC__
31 #pragma hdrstop
32 #endif
33
34 #include "wx/string.h"
35 #include "wx/log.h"
36 #include "wx/settings.h"
37 #include "wx/generic/imaglist.h"
38 #include "wx/notebook.h"
39 #include "wx/dcclient.h"
40 #include "wx/generic/tabg.h"
41
42 // ----------------------------------------------------------------------------
43 // macros
44 // ----------------------------------------------------------------------------
45
46 // check that the page index is valid
47 #define IS_VALID_PAGE(nPage) (((nPage) >= 0) && ((nPage) < GetPageCount()))
48
49 // ----------------------------------------------------------------------------
50 // event table
51 // ----------------------------------------------------------------------------
52
53 DEFINE_EVENT_TYPE(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED)
54 DEFINE_EVENT_TYPE(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING)
55
56 BEGIN_EVENT_TABLE(wxNotebook, wxControl)
57 EVT_NOTEBOOK_PAGE_CHANGED(-1, wxNotebook::OnSelChange)
58 EVT_SIZE(wxNotebook::OnSize)
59 EVT_PAINT(wxNotebook::OnPaint)
60 EVT_MOUSE_EVENTS(wxNotebook::OnMouseEvent)
61 EVT_SET_FOCUS(wxNotebook::OnSetFocus)
62 EVT_NAVIGATION_KEY(wxNotebook::OnNavigationKey)
63 END_EVENT_TABLE()
64
65 IMPLEMENT_DYNAMIC_CLASS(wxNotebook, wxControl)
66 IMPLEMENT_DYNAMIC_CLASS(wxNotebookEvent, wxCommandEvent)
67
68 // ============================================================================
69 // implementation
70 // ============================================================================
71
72 // ============================================================================
73 // Private class
74 // ============================================================================
75
76 // This reuses wxTabView to draw the tabs.
77 class WXDLLEXPORT wxNotebookTabView: public wxTabView
78 {
79 DECLARE_DYNAMIC_CLASS(wxNotebookTabView)
80 public:
81 wxNotebookTabView(wxNotebook* notebook, long style = wxTAB_STYLE_DRAW_BOX | wxTAB_STYLE_COLOUR_INTERIOR);
82 ~wxNotebookTabView(void);
83
84 // Called when a tab is activated
85 virtual void OnTabActivate(int activateId, int deactivateId);
86 // Allows vetoing
87 virtual bool OnTabPreActivate(int activateId, int deactivateId);
88
89 protected:
90 wxNotebook* m_notebook;
91 };
92
93 // ----------------------------------------------------------------------------
94 // wxNotebook construction
95 // ----------------------------------------------------------------------------
96
97 // common part of all ctors
98 void wxNotebook::Init()
99 {
100 m_tabView = (wxNotebookTabView*) NULL;
101 m_nSelection = -1;
102 }
103
104 // default for dynamic class
105 wxNotebook::wxNotebook()
106 {
107 Init();
108 }
109
110 // the same arguments as for wxControl
111 wxNotebook::wxNotebook(wxWindow *parent,
112 wxWindowID id,
113 const wxPoint& pos,
114 const wxSize& size,
115 long style,
116 const wxString& name)
117 {
118 Init();
119
120 Create(parent, id, pos, size, style, name);
121 }
122
123 // Create() function
124 bool wxNotebook::Create(wxWindow *parent,
125 wxWindowID id,
126 const wxPoint& pos,
127 const wxSize& size,
128 long style,
129 const wxString& name)
130 {
131 // base init
132 SetName(name);
133
134 m_windowId = id == -1 ? NewControlId() : id;
135
136 if (!wxControl::Create(parent, id, pos, size, style|wxNO_BORDER, wxDefaultValidator, name))
137 return FALSE;
138
139 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE));
140
141 SetTabView(new wxNotebookTabView(this));
142
143 return TRUE;
144 }
145
146 // dtor
147 wxNotebook::~wxNotebook()
148 {
149 delete m_tabView;
150 }
151
152 // ----------------------------------------------------------------------------
153 // wxNotebook accessors
154 // ----------------------------------------------------------------------------
155 int wxNotebook::GetRowCount() const
156 {
157 // TODO
158 return 0;
159 }
160
161 int wxNotebook::SetSelection(size_t nPage)
162 {
163 wxASSERT( IS_VALID_PAGE(nPage) );
164
165 #if defined (__WIN16__)
166 m_tabView->SetTabSelection(nPage);
167 #else
168 wxNotebookPage* pPage = GetPage(nPage);
169
170 m_tabView->SetTabSelection((int) (long) pPage);
171 #endif
172 // TODO
173 return 0;
174 }
175
176 #if 0
177 void wxNotebook::AdvanceSelection(bool bForward)
178 {
179 int nSel = GetSelection();
180 int nMax = GetPageCount() - 1;
181 if ( bForward )
182 SetSelection(nSel == nMax ? 0 : nSel + 1);
183 else
184 SetSelection(nSel == 0 ? nMax : nSel - 1);
185 }
186 #endif
187
188 bool wxNotebook::SetPageText(size_t nPage, const wxString& strText)
189 {
190 wxASSERT( IS_VALID_PAGE(nPage) );
191 #if defined (__WIN16__)
192 m_tabView->SetTabText(nPage, strText);
193 Refresh();
194 return TRUE;
195 #else
196 wxNotebookPage* page = GetPage(nPage);
197 if (page)
198 {
199 m_tabView->SetTabText((int) (long) page, strText);
200 Refresh();
201 return TRUE;
202 }
203 #endif
204 return FALSE;
205 }
206
207 wxString wxNotebook::GetPageText(size_t nPage) const
208 {
209 wxASSERT( IS_VALID_PAGE(nPage) );
210
211 #if defined (__WIN16__)
212 return m_tabView->GetTabText(nPage);
213 #else
214 wxNotebookPage* page = ((wxNotebook*)this)->GetPage(nPage);
215 if (page)
216 return m_tabView->GetTabText((int) (long) page);
217 else
218 return wxEmptyString;
219 #endif
220 }
221
222 int wxNotebook::GetPageImage(size_t nPage) const
223 {
224 wxASSERT( IS_VALID_PAGE(nPage) );
225
226 // TODO
227 return 0;
228 }
229
230 bool wxNotebook::SetPageImage(size_t nPage, int nImage)
231 {
232 wxASSERT( IS_VALID_PAGE(nPage) );
233
234 // TODO
235 return FALSE;
236 }
237
238 // set the size (the same for all pages)
239 void wxNotebook::SetPageSize(const wxSize& size)
240 {
241 // TODO
242 }
243
244 // set the padding between tabs (in pixels)
245 void wxNotebook::SetPadding(const wxSize& padding)
246 {
247 // TODO
248 }
249
250 // set the size of the tabs for wxNB_FIXEDWIDTH controls
251 void wxNotebook::SetTabSize(const wxSize& sz)
252 {
253 // TODO
254 }
255
256 // ----------------------------------------------------------------------------
257 // wxNotebook operations
258 // ----------------------------------------------------------------------------
259
260 // remove one page from the notebook and delete it
261 bool wxNotebook::DeletePage(size_t nPage)
262 {
263 wxCHECK( IS_VALID_PAGE(nPage), FALSE );
264
265 if (m_nSelection != -1)
266 {
267 m_pages[m_nSelection]->Show(FALSE);
268 m_pages[m_nSelection]->Lower();
269 }
270
271 wxNotebookPage* pPage = GetPage(nPage);
272 #if defined (__WIN16__)
273 m_tabView->RemoveTab(nPage);
274 #else
275 m_tabView->RemoveTab((int) (long) pPage);
276 #endif
277
278 m_pages.Remove(pPage);
279 delete pPage;
280
281 if (m_pages.GetCount() == 0)
282 {
283 m_nSelection = -1;
284 m_tabView->SetTabSelection(-1, FALSE);
285 }
286 else if (m_nSelection > -1)
287 {
288 m_nSelection = -1;
289 #if defined (__WIN16__)
290 m_tabView->SetTabSelection(0, FALSE);
291 #else
292 m_tabView->SetTabSelection((int) (long) GetPage(0), FALSE);
293 #endif
294 if (m_nSelection != 0)
295 ChangePage(-1, 0);
296 }
297
298 RefreshLayout(FALSE);
299
300 return TRUE;
301 }
302
303 bool wxNotebook::DeletePage(wxNotebookPage* page)
304 {
305 int pagePos = FindPagePosition(page);
306 if (pagePos > -1)
307 return DeletePage(pagePos);
308 else
309 return FALSE;
310 }
311
312 bool wxNotebook::RemovePage(size_t nPage)
313 {
314 return DoRemovePage(nPage) != NULL;
315 }
316
317 // remove one page from the notebook
318 wxWindow* wxNotebook::DoRemovePage(size_t nPage)
319 {
320 wxCHECK( IS_VALID_PAGE(nPage), FALSE );
321
322 m_pages[nPage]->Show(FALSE);
323 // m_pages[nPage]->Lower();
324
325 wxNotebookPage* pPage = GetPage(nPage);
326 #if defined (__WIN16__)
327 m_tabView->RemoveTab(nPage);
328 #else
329 m_tabView->RemoveTab((int) (long) pPage);
330 #endif
331
332 m_pages.Remove(pPage);
333
334 if (m_pages.GetCount() == 0)
335 {
336 m_nSelection = -1;
337 m_tabView->SetTabSelection(-1, TRUE);
338 }
339 else if (m_nSelection > -1)
340 {
341 // Only change the selection if the page we
342 // deleted was the selection.
343 if (nPage == (size_t)m_nSelection)
344 {
345 m_nSelection = -1;
346 // Select the first tab. Generates a ChangePage.
347 m_tabView->SetTabSelection(0, TRUE);
348 }
349 else
350 {
351 // We must adjust which tab we think is selected.
352 // If greater than the page we deleted, it must be moved down
353 // a notch.
354 if (size_t(m_nSelection) > nPage)
355 m_nSelection -- ;
356 }
357 }
358
359 RefreshLayout(FALSE);
360
361 return pPage;
362 }
363
364 bool wxNotebook::RemovePage(wxNotebookPage* page)
365 {
366 int pagePos = FindPagePosition(page);
367 if (pagePos > -1)
368 return RemovePage(pagePos);
369 else
370 return FALSE;
371 }
372
373 // Find the position of the wxNotebookPage, -1 if not found.
374 int wxNotebook::FindPagePosition(wxNotebookPage* page) const
375 {
376 size_t nPageCount = GetPageCount();
377 size_t nPage;
378 for ( nPage = 0; nPage < nPageCount; nPage++ )
379 if (m_pages[nPage] == page)
380 return nPage;
381 return -1;
382 }
383
384 // remove all pages
385 bool wxNotebook::DeleteAllPages()
386 {
387 m_tabView->ClearTabs(TRUE);
388
389 size_t nPageCount = GetPageCount();
390 size_t nPage;
391 for ( nPage = 0; nPage < nPageCount; nPage++ )
392 delete m_pages[nPage];
393
394 m_pages.Clear();
395
396 return TRUE;
397 }
398
399 // same as AddPage() but does it at given position
400 bool wxNotebook::InsertPage(size_t nPage,
401 wxNotebookPage *pPage,
402 const wxString& strText,
403 bool bSelect,
404 int imageId)
405 {
406 wxASSERT( pPage != NULL );
407 wxCHECK( IS_VALID_PAGE(nPage) || nPage == GetPageCount(), FALSE );
408
409 // For 16 bit integers (tabs limited to 32768)
410 #if defined (__WIN16__)
411 m_tabView->AddTab(nPage, strText);
412 #else
413 m_tabView->AddTab((int) (long) pPage, strText);
414 #endif
415 if (!bSelect)
416 pPage->Show(FALSE);
417
418 // save the pointer to the page
419 m_pages.Insert(pPage, nPage);
420
421 if (bSelect)
422 {
423 // This will cause ChangePage to be called, via OnSelPage
424 #if defined (__WIN16__)
425 m_tabView->SetTabSelection(nPage, TRUE);
426 #else
427 m_tabView->SetTabSelection((int) (long) pPage, TRUE);
428 #endif
429 }
430
431 // some page must be selected: either this one or the first one if there is
432 // still no selection
433 if ( m_nSelection == -1 )
434 ChangePage(-1, 0);
435
436 RefreshLayout(FALSE);
437
438 return TRUE;
439 }
440
441 // ----------------------------------------------------------------------------
442 // wxNotebook callbacks
443 // ----------------------------------------------------------------------------
444
445 // @@@ OnSize() is used for setting the font when it's called for the first
446 // time because doing it in ::Create() doesn't work (for unknown reasons)
447 void wxNotebook::OnSize(wxSizeEvent& event)
448 {
449 static bool s_bFirstTime = TRUE;
450 if ( s_bFirstTime ) {
451 // TODO: any first-time-size processing.
452 s_bFirstTime = FALSE;
453 }
454
455 RefreshLayout();
456
457 // Processing continues to next OnSize
458 event.Skip();
459 }
460
461 // This was supposed to cure the non-display of the notebook
462 // until the user resizes the window.
463 // What's going on?
464 void wxNotebook::OnInternalIdle()
465 {
466 wxWindow::OnInternalIdle();
467
468 #if 0
469 static bool s_bFirstTime = TRUE;
470 if ( s_bFirstTime ) {
471 /*
472 wxSize sz(GetSize());
473 sz.x ++;
474 SetSize(sz);
475 sz.x --;
476 SetSize(sz);
477 */
478
479 /*
480 wxSize sz(GetSize());
481 wxSizeEvent sizeEvent(sz, GetId());
482 sizeEvent.SetEventObject(this);
483 GetEventHandler()->ProcessEvent(sizeEvent);
484 Refresh();
485 */
486 s_bFirstTime = FALSE;
487 }
488 #endif
489 }
490
491 // Implementation: calculate the layout of the view rect
492 // and resize the children if required
493 bool wxNotebook::RefreshLayout(bool force)
494 {
495 if (m_tabView)
496 {
497 wxRect oldRect = m_tabView->GetViewRect();
498
499 int cw, ch;
500 GetClientSize(& cw, & ch);
501
502 int tabHeight = m_tabView->GetTotalTabHeight();
503 wxRect rect;
504 rect.x = 4;
505 rect.y = tabHeight + 4;
506 rect.width = cw - 8;
507 rect.height = ch - 4 - rect.y ;
508
509 m_tabView->SetViewRect(rect);
510
511 m_tabView->LayoutTabs();
512
513 // Need to do it a 2nd time to get the tab height with
514 // the new view width, since changing the view width changes the
515 // tab layout.
516 tabHeight = m_tabView->GetTotalTabHeight();
517 rect.x = 4;
518 rect.y = tabHeight + 4;
519 rect.width = cw - 8;
520 rect.height = ch - 4 - rect.y ;
521
522 m_tabView->SetViewRect(rect);
523
524 m_tabView->LayoutTabs();
525
526 if (!force && (rect == oldRect))
527 return FALSE;
528
529 // fit the notebook page to the tab control's display area
530
531 size_t nCount = m_pages.Count();
532 for ( size_t nPage = 0; nPage < nCount; nPage++ ) {
533 wxNotebookPage *pPage = m_pages[nPage];
534 wxRect clientRect = GetAvailableClientSize();
535 if (pPage->IsShown())
536 {
537 pPage->SetSize(clientRect.x, clientRect.y, clientRect.width, clientRect.height);
538 if ( pPage->GetAutoLayout() )
539 pPage->Layout();
540 }
541 }
542 Refresh();
543 }
544 return TRUE;
545 }
546
547 void wxNotebook::OnSelChange(wxNotebookEvent& event)
548 {
549 // is it our tab control?
550 if ( event.GetEventObject() == this )
551 {
552 if (event.GetSelection() != m_nSelection)
553 ChangePage(event.GetOldSelection(), event.GetSelection());
554 }
555
556 // we want to give others a chance to process this message as well
557 event.Skip();
558 }
559
560 void wxNotebook::OnSetFocus(wxFocusEvent& event)
561 {
562 // set focus to the currently selected page if any
563 if ( m_nSelection != -1 )
564 m_pages[m_nSelection]->SetFocus();
565
566 event.Skip();
567 }
568
569 void wxNotebook::OnNavigationKey(wxNavigationKeyEvent& event)
570 {
571 if ( event.IsWindowChange() ) {
572 // change pages
573 AdvanceSelection(event.GetDirection());
574 }
575 else {
576 // pass to the parent
577 if ( GetParent() ) {
578 event.SetCurrentFocus(this);
579 GetParent()->ProcessEvent(event);
580 }
581 }
582 }
583
584 // ----------------------------------------------------------------------------
585 // wxNotebook base class virtuals
586 // ----------------------------------------------------------------------------
587
588 // override these 2 functions to do nothing: everything is done in OnSize
589
590 void wxNotebook::SetConstraintSizes(bool /* recurse */)
591 {
592 // don't set the sizes of the pages - their correct size is not yet known
593 wxControl::SetConstraintSizes(FALSE);
594 }
595
596 bool wxNotebook::DoPhase(int /* nPhase */)
597 {
598 return TRUE;
599 }
600
601 void wxNotebook::Command(wxCommandEvent& WXUNUSED(event))
602 {
603 wxFAIL_MSG("wxNotebook::Command not implemented");
604 }
605
606 // ----------------------------------------------------------------------------
607 // wxNotebook helper functions
608 // ----------------------------------------------------------------------------
609
610 // hide the currently active panel and show the new one
611 void wxNotebook::ChangePage(int nOldSel, int nSel)
612 {
613 // cout << "ChangePage: " << nOldSel << ", " << nSel << "\n";
614 wxASSERT( nOldSel != nSel ); // impossible
615
616 if ( nOldSel != -1 ) {
617 m_pages[nOldSel]->Show(FALSE);
618 m_pages[nOldSel]->Lower();
619 }
620
621 wxNotebookPage *pPage = m_pages[nSel];
622
623 wxRect clientRect = GetAvailableClientSize();
624 pPage->SetSize(clientRect.x, clientRect.y, clientRect.width, clientRect.height);
625
626 Refresh();
627
628 pPage->Show(TRUE);
629 pPage->Raise();
630 pPage->SetFocus();
631
632 m_nSelection = nSel;
633 }
634
635 void wxNotebook::OnMouseEvent(wxMouseEvent& event)
636 {
637 if (m_tabView)
638 m_tabView->OnEvent(event);
639 }
640
641 void wxNotebook::OnPaint(wxPaintEvent& WXUNUSED(event) )
642 {
643 wxPaintDC dc(this);
644 if (m_tabView)
645 m_tabView->Draw(dc);
646 }
647
648 wxRect wxNotebook::GetAvailableClientSize()
649 {
650 int cw, ch;
651 GetClientSize(& cw, & ch);
652
653 int tabHeight = m_tabView->GetTotalTabHeight();
654
655 // TODO: these margins should be configurable.
656 wxRect rect;
657 rect.x = 6;
658 rect.y = tabHeight + 6;
659 rect.width = cw - 12;
660 rect.height = ch - 4 - rect.y ;
661
662 return rect;
663 }
664
665 /*
666 * wxNotebookTabView
667 */
668
669 IMPLEMENT_CLASS(wxNotebookTabView, wxTabView)
670
671 wxNotebookTabView::wxNotebookTabView(wxNotebook *notebook, long style): wxTabView(style)
672 {
673 m_notebook = notebook;
674
675 m_notebook->SetTabView(this);
676
677 SetWindow(m_notebook);
678 }
679
680 wxNotebookTabView::~wxNotebookTabView(void)
681 {
682 }
683
684 // Called when a tab is activated
685 void wxNotebookTabView::OnTabActivate(int activateId, int deactivateId)
686 {
687 if (!m_notebook)
688 return;
689
690 wxNotebookEvent event(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED, m_notebook->GetId());
691
692 #if defined (__WIN16__)
693 int activatePos = activateId;
694 int deactivatePos = deactivateId;
695 #else
696 // Translate from wxTabView's ids (which aren't position-dependent)
697 // to wxNotebook's (which are).
698 wxNotebookPage* pActive = (wxNotebookPage*) activateId;
699 wxNotebookPage* pDeactive = (wxNotebookPage*) deactivateId;
700
701 int activatePos = m_notebook->FindPagePosition(pActive);
702 int deactivatePos = m_notebook->FindPagePosition(pDeactive);
703
704 #endif
705 event.SetEventObject(m_notebook);
706 event.SetSelection(activatePos);
707 event.SetOldSelection(deactivatePos);
708 m_notebook->GetEventHandler()->ProcessEvent(event);
709 }
710
711 // Allows Vetoing
712 bool wxNotebookTabView::OnTabPreActivate(int activateId, int deactivateId)
713 {
714 bool retval = TRUE;
715
716 if (m_notebook)
717 {
718 wxNotebookEvent event(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING, m_notebook->GetId());
719
720 #if defined (__WIN16__)
721 int activatePos = activateId;
722 int deactivatePos = deactivateId;
723 #else
724 // Translate from wxTabView's ids (which aren't position-dependent)
725 // to wxNotebook's (which are).
726 wxNotebookPage* pActive = (wxNotebookPage*) activateId;
727 wxNotebookPage* pDeactive = (wxNotebookPage*) deactivateId;
728
729 int activatePos = m_notebook->FindPagePosition(pActive);
730 int deactivatePos = m_notebook->FindPagePosition(pDeactive);
731
732 #endif
733 event.SetEventObject(m_notebook);
734 event.SetSelection(activatePos);
735 event.SetOldSelection(deactivatePos);
736 if (m_notebook->GetEventHandler()->ProcessEvent(event))
737 {
738 retval = event.IsAllowed();
739 }
740 }
741 return retval;
742 }
743