Committing in .
[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 // It's like a normal window...
137 if (!wxWindow::Create(parent, id, pos, size, style|wxNO_BORDER, name))
138 return FALSE;
139
140 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE));
141
142 SetTabView(new wxNotebookTabView(this));
143
144 return TRUE;
145 }
146
147 // dtor
148 wxNotebook::~wxNotebook()
149 {
150 delete m_tabView;
151 }
152
153 // ----------------------------------------------------------------------------
154 // wxNotebook accessors
155 // ----------------------------------------------------------------------------
156 int wxNotebook::GetRowCount() const
157 {
158 // TODO
159 return 0;
160 }
161
162 int wxNotebook::SetSelection(size_t nPage)
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(size_t 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(size_t 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(size_t nPage) const
224 {
225 wxASSERT( IS_VALID_PAGE(nPage) );
226
227 // TODO
228 return 0;
229 }
230
231 bool wxNotebook::SetPageImage(size_t 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(size_t 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 bool wxNotebook::RemovePage(size_t nPage)
314 {
315 return DoRemovePage(nPage) != NULL;
316 }
317
318 // remove one page from the notebook
319 wxWindow* wxNotebook::DoRemovePage(size_t nPage)
320 {
321 wxCHECK( IS_VALID_PAGE(nPage), FALSE );
322
323 m_pages[nPage]->Show(FALSE);
324 // m_pages[nPage]->Lower();
325
326 wxNotebookPage* pPage = GetPage(nPage);
327 #if defined (__WIN16__)
328 m_tabView->RemoveTab(nPage);
329 #else
330 m_tabView->RemoveTab((int) (long) pPage);
331 #endif
332
333 m_pages.Remove(pPage);
334
335 if (m_pages.GetCount() == 0)
336 {
337 m_nSelection = -1;
338 m_tabView->SetTabSelection(-1, TRUE);
339 }
340 else if (m_nSelection > -1)
341 {
342 // Only change the selection if the page we
343 // deleted was the selection.
344 if (nPage == (size_t)m_nSelection)
345 {
346 m_nSelection = -1;
347 // Select the first tab. Generates a ChangePage.
348 m_tabView->SetTabSelection(0, TRUE);
349 }
350 else
351 {
352 // We must adjust which tab we think is selected.
353 // If greater than the page we deleted, it must be moved down
354 // a notch.
355 if (size_t(m_nSelection) > nPage)
356 m_nSelection -- ;
357 }
358 }
359
360 RefreshLayout(FALSE);
361
362 return pPage;
363 }
364
365 bool wxNotebook::RemovePage(wxNotebookPage* page)
366 {
367 int pagePos = FindPagePosition(page);
368 if (pagePos > -1)
369 return RemovePage(pagePos);
370 else
371 return FALSE;
372 }
373
374 // Find the position of the wxNotebookPage, -1 if not found.
375 int wxNotebook::FindPagePosition(wxNotebookPage* page) const
376 {
377 size_t nPageCount = GetPageCount();
378 size_t nPage;
379 for ( nPage = 0; nPage < nPageCount; nPage++ )
380 if (m_pages[nPage] == page)
381 return nPage;
382 return -1;
383 }
384
385 // remove all pages
386 bool wxNotebook::DeleteAllPages()
387 {
388 m_tabView->ClearTabs(TRUE);
389
390 size_t nPageCount = GetPageCount();
391 size_t nPage;
392 for ( nPage = 0; nPage < nPageCount; nPage++ )
393 delete m_pages[nPage];
394
395 m_pages.Clear();
396
397 return TRUE;
398 }
399
400 // same as AddPage() but does it at given position
401 bool wxNotebook::InsertPage(size_t nPage,
402 wxNotebookPage *pPage,
403 const wxString& strText,
404 bool bSelect,
405 int imageId)
406 {
407 wxASSERT( pPage != NULL );
408 wxCHECK( IS_VALID_PAGE(nPage) || nPage == GetPageCount(), FALSE );
409
410 // For 16 bit integers (tabs limited to 32768)
411 #if defined (__WIN16__)
412 m_tabView->AddTab(nPage, strText);
413 #else
414 m_tabView->AddTab((int) (long) pPage, strText);
415 #endif
416 if (!bSelect)
417 pPage->Show(FALSE);
418
419 // save the pointer to the page
420 m_pages.Insert(pPage, nPage);
421
422 if (bSelect)
423 {
424 // This will cause ChangePage to be called, via OnSelPage
425 #if defined (__WIN16__)
426 m_tabView->SetTabSelection(nPage, TRUE);
427 #else
428 m_tabView->SetTabSelection((int) (long) pPage, TRUE);
429 #endif
430 }
431
432 // some page must be selected: either this one or the first one if there is
433 // still no selection
434 if ( m_nSelection == -1 )
435 ChangePage(-1, 0);
436
437 RefreshLayout(FALSE);
438
439 return TRUE;
440 }
441
442 // ----------------------------------------------------------------------------
443 // wxNotebook callbacks
444 // ----------------------------------------------------------------------------
445
446 // @@@ OnSize() is used for setting the font when it's called for the first
447 // time because doing it in ::Create() doesn't work (for unknown reasons)
448 void wxNotebook::OnSize(wxSizeEvent& event)
449 {
450 static bool s_bFirstTime = TRUE;
451 if ( s_bFirstTime ) {
452 // TODO: any first-time-size processing.
453 s_bFirstTime = FALSE;
454 }
455
456 RefreshLayout();
457
458 // Processing continues to next OnSize
459 event.Skip();
460 }
461
462 // This was supposed to cure the non-display of the notebook
463 // until the user resizes the window.
464 // What's going on?
465 void wxNotebook::OnInternalIdle()
466 {
467 wxWindow::OnInternalIdle();
468
469 #if 0
470 static bool s_bFirstTime = TRUE;
471 if ( s_bFirstTime ) {
472 /*
473 wxSize sz(GetSize());
474 sz.x ++;
475 SetSize(sz);
476 sz.x --;
477 SetSize(sz);
478 */
479
480 /*
481 wxSize sz(GetSize());
482 wxSizeEvent sizeEvent(sz, GetId());
483 sizeEvent.SetEventObject(this);
484 GetEventHandler()->ProcessEvent(sizeEvent);
485 Refresh();
486 */
487 s_bFirstTime = FALSE;
488 }
489 #endif
490 }
491
492 // Implementation: calculate the layout of the view rect
493 // and resize the children if required
494 bool wxNotebook::RefreshLayout(bool force)
495 {
496 if (m_tabView)
497 {
498 wxRect oldRect = m_tabView->GetViewRect();
499
500 int cw, ch;
501 GetClientSize(& cw, & ch);
502
503 int tabHeight = m_tabView->GetTotalTabHeight();
504 wxRect rect;
505 rect.x = 4;
506 rect.y = tabHeight + 4;
507 rect.width = cw - 8;
508 rect.height = ch - 4 - rect.y ;
509
510 m_tabView->SetViewRect(rect);
511
512 m_tabView->LayoutTabs();
513
514 // Need to do it a 2nd time to get the tab height with
515 // the new view width, since changing the view width changes the
516 // tab layout.
517 tabHeight = m_tabView->GetTotalTabHeight();
518 rect.x = 4;
519 rect.y = tabHeight + 4;
520 rect.width = cw - 8;
521 rect.height = ch - 4 - rect.y ;
522
523 m_tabView->SetViewRect(rect);
524
525 m_tabView->LayoutTabs();
526
527 if (!force && (rect == oldRect))
528 return FALSE;
529
530 // fit the notebook page to the tab control's display area
531
532 size_t nCount = m_pages.Count();
533 for ( size_t nPage = 0; nPage < nCount; nPage++ ) {
534 wxNotebookPage *pPage = m_pages[nPage];
535 wxRect clientRect = GetAvailableClientSize();
536 if (pPage->IsShown())
537 {
538 pPage->SetSize(clientRect.x, clientRect.y, clientRect.width, clientRect.height);
539 if ( pPage->GetAutoLayout() )
540 pPage->Layout();
541 }
542 }
543 Refresh();
544 }
545 return TRUE;
546 }
547
548 void wxNotebook::OnSelChange(wxNotebookEvent& event)
549 {
550 // is it our tab control?
551 if ( event.GetEventObject() == this )
552 {
553 if (event.GetSelection() != m_nSelection)
554 ChangePage(event.GetOldSelection(), event.GetSelection());
555 }
556
557 // we want to give others a chance to process this message as well
558 event.Skip();
559 }
560
561 void wxNotebook::OnSetFocus(wxFocusEvent& event)
562 {
563 // set focus to the currently selected page if any
564 if ( m_nSelection != -1 )
565 m_pages[m_nSelection]->SetFocus();
566
567 event.Skip();
568 }
569
570 void wxNotebook::OnNavigationKey(wxNavigationKeyEvent& event)
571 {
572 if ( event.IsWindowChange() ) {
573 // change pages
574 AdvanceSelection(event.GetDirection());
575 }
576 else {
577 // pass to the parent
578 if ( GetParent() ) {
579 event.SetCurrentFocus(this);
580 GetParent()->ProcessEvent(event);
581 }
582 }
583 }
584
585 // ----------------------------------------------------------------------------
586 // wxNotebook base class virtuals
587 // ----------------------------------------------------------------------------
588
589 // override these 2 functions to do nothing: everything is done in OnSize
590
591 void wxNotebook::SetConstraintSizes(bool /* recurse */)
592 {
593 // don't set the sizes of the pages - their correct size is not yet known
594 wxControl::SetConstraintSizes(FALSE);
595 }
596
597 bool wxNotebook::DoPhase(int /* nPhase */)
598 {
599 return TRUE;
600 }
601
602 void wxNotebook::Command(wxCommandEvent& WXUNUSED(event))
603 {
604 wxFAIL_MSG("wxNotebook::Command not implemented");
605 }
606
607 // ----------------------------------------------------------------------------
608 // wxNotebook helper functions
609 // ----------------------------------------------------------------------------
610
611 // hide the currently active panel and show the new one
612 void wxNotebook::ChangePage(int nOldSel, int nSel)
613 {
614 // cout << "ChangePage: " << nOldSel << ", " << nSel << "\n";
615 wxASSERT( nOldSel != nSel ); // impossible
616
617 if ( nOldSel != -1 ) {
618 m_pages[nOldSel]->Show(FALSE);
619 m_pages[nOldSel]->Lower();
620 }
621
622 wxNotebookPage *pPage = m_pages[nSel];
623
624 wxRect clientRect = GetAvailableClientSize();
625 pPage->SetSize(clientRect.x, clientRect.y, clientRect.width, clientRect.height);
626
627 Refresh();
628
629 pPage->Show(TRUE);
630 pPage->Raise();
631 pPage->SetFocus();
632
633 m_nSelection = nSel;
634 }
635
636 void wxNotebook::OnMouseEvent(wxMouseEvent& event)
637 {
638 if (m_tabView)
639 m_tabView->OnEvent(event);
640 }
641
642 void wxNotebook::OnPaint(wxPaintEvent& WXUNUSED(event) )
643 {
644 wxPaintDC dc(this);
645 if (m_tabView)
646 m_tabView->Draw(dc);
647 }
648
649 wxRect wxNotebook::GetAvailableClientSize()
650 {
651 int cw, ch;
652 GetClientSize(& cw, & ch);
653
654 int tabHeight = m_tabView->GetTotalTabHeight();
655
656 // TODO: these margins should be configurable.
657 wxRect rect;
658 rect.x = 6;
659 rect.y = tabHeight + 6;
660 rect.width = cw - 12;
661 rect.height = ch - 4 - rect.y ;
662
663 return rect;
664 }
665
666 /*
667 * wxNotebookTabView
668 */
669
670 IMPLEMENT_CLASS(wxNotebookTabView, wxTabView)
671
672 wxNotebookTabView::wxNotebookTabView(wxNotebook *notebook, long style): wxTabView(style)
673 {
674 m_notebook = notebook;
675
676 m_notebook->SetTabView(this);
677
678 SetWindow(m_notebook);
679 }
680
681 wxNotebookTabView::~wxNotebookTabView(void)
682 {
683 }
684
685 // Called when a tab is activated
686 void wxNotebookTabView::OnTabActivate(int activateId, int deactivateId)
687 {
688 if (!m_notebook)
689 return;
690
691 wxNotebookEvent event(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED, m_notebook->GetId());
692
693 #if defined (__WIN16__)
694 int activatePos = activateId;
695 int deactivatePos = deactivateId;
696 #else
697 // Translate from wxTabView's ids (which aren't position-dependent)
698 // to wxNotebook's (which are).
699 wxNotebookPage* pActive = (wxNotebookPage*) activateId;
700 wxNotebookPage* pDeactive = (wxNotebookPage*) deactivateId;
701
702 int activatePos = m_notebook->FindPagePosition(pActive);
703 int deactivatePos = m_notebook->FindPagePosition(pDeactive);
704
705 #endif
706 event.SetEventObject(m_notebook);
707 event.SetSelection(activatePos);
708 event.SetOldSelection(deactivatePos);
709 m_notebook->GetEventHandler()->ProcessEvent(event);
710 }
711
712 // Allows Vetoing
713 bool wxNotebookTabView::OnTabPreActivate(int activateId, int deactivateId)
714 {
715 bool retval = TRUE;
716
717 if (m_notebook)
718 {
719 wxNotebookEvent event(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING, m_notebook->GetId());
720
721 #if defined (__WIN16__)
722 int activatePos = activateId;
723 int deactivatePos = deactivateId;
724 #else
725 // Translate from wxTabView's ids (which aren't position-dependent)
726 // to wxNotebook's (which are).
727 wxNotebookPage* pActive = (wxNotebookPage*) activateId;
728 wxNotebookPage* pDeactive = (wxNotebookPage*) deactivateId;
729
730 int activatePos = m_notebook->FindPagePosition(pActive);
731 int deactivatePos = m_notebook->FindPagePosition(pDeactive);
732
733 #endif
734 event.SetEventObject(m_notebook);
735 event.SetSelection(activatePos);
736 event.SetOldSelection(deactivatePos);
737 if (m_notebook->GetEventHandler()->ProcessEvent(event))
738 {
739 retval = event.IsAllowed();
740 }
741 }
742 return retval;
743 }
744