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