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