removed Win16 code (patch 905241), last version with remains of Win16 support tagged...
[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 wxNotebookPage* pPage = GetPage(nPage);
166
167 m_tabView->SetTabSelection((int) (long) pPage);
168
169 // TODO
170 return 0;
171 }
172
173 #if 0
174 void wxNotebook::AdvanceSelection(bool bForward)
175 {
176 int nSel = GetSelection();
177 int nMax = GetPageCount() - 1;
178 if ( bForward )
179 SetSelection(nSel == nMax ? 0 : nSel + 1);
180 else
181 SetSelection(nSel == 0 ? nMax : nSel - 1);
182 }
183 #endif
184
185 bool wxNotebook::SetPageText(size_t nPage, const wxString& strText)
186 {
187 wxASSERT( IS_VALID_PAGE(nPage) );
188
189 wxNotebookPage* page = GetPage(nPage);
190 if (page)
191 {
192 m_tabView->SetTabText((int) (long) page, strText);
193 Refresh();
194 return TRUE;
195 }
196
197 return FALSE;
198 }
199
200 wxString wxNotebook::GetPageText(size_t nPage) const
201 {
202 wxASSERT( IS_VALID_PAGE(nPage) );
203
204 wxNotebookPage* page = ((wxNotebook*)this)->GetPage(nPage);
205 if (page)
206 return m_tabView->GetTabText((int) (long) page);
207 else
208 return wxEmptyString;
209 }
210
211 int wxNotebook::GetPageImage(size_t nPage) const
212 {
213 wxASSERT( IS_VALID_PAGE(nPage) );
214
215 // TODO
216 return 0;
217 }
218
219 bool wxNotebook::SetPageImage(size_t nPage, int nImage)
220 {
221 wxASSERT( IS_VALID_PAGE(nPage) );
222
223 // TODO
224 return FALSE;
225 }
226
227 // set the size (the same for all pages)
228 void wxNotebook::SetPageSize(const wxSize& size)
229 {
230 // TODO
231 }
232
233 // set the padding between tabs (in pixels)
234 void wxNotebook::SetPadding(const wxSize& padding)
235 {
236 // TODO
237 }
238
239 // set the size of the tabs for wxNB_FIXEDWIDTH controls
240 void wxNotebook::SetTabSize(const wxSize& sz)
241 {
242 // TODO
243 }
244
245 // ----------------------------------------------------------------------------
246 // wxNotebook operations
247 // ----------------------------------------------------------------------------
248
249 // remove one page from the notebook and delete it
250 bool wxNotebook::DeletePage(size_t nPage)
251 {
252 wxCHECK( IS_VALID_PAGE(nPage), FALSE );
253
254 if (m_nSelection != -1)
255 {
256 m_pages[m_nSelection]->Show(FALSE);
257 m_pages[m_nSelection]->Lower();
258 }
259
260 wxNotebookPage* pPage = GetPage(nPage);
261
262 m_tabView->RemoveTab((int) (long) pPage);
263
264 m_pages.Remove(pPage);
265 delete pPage;
266
267 if (m_pages.GetCount() == 0)
268 {
269 m_nSelection = -1;
270 m_tabView->SetTabSelection(-1, FALSE);
271 }
272 else if (m_nSelection > -1)
273 {
274 m_nSelection = -1;
275
276 m_tabView->SetTabSelection((int) (long) GetPage(0), FALSE);
277
278 if (m_nSelection != 0)
279 ChangePage(-1, 0);
280 }
281
282 RefreshLayout(FALSE);
283
284 return TRUE;
285 }
286
287 bool wxNotebook::DeletePage(wxNotebookPage* page)
288 {
289 int pagePos = FindPagePosition(page);
290 if (pagePos > -1)
291 return DeletePage(pagePos);
292 else
293 return FALSE;
294 }
295
296 bool wxNotebook::RemovePage(size_t nPage)
297 {
298 return DoRemovePage(nPage) != NULL;
299 }
300
301 // remove one page from the notebook
302 wxWindow* wxNotebook::DoRemovePage(size_t nPage)
303 {
304 wxCHECK( IS_VALID_PAGE(nPage), FALSE );
305
306 m_pages[nPage]->Show(FALSE);
307 // m_pages[nPage]->Lower();
308
309 wxNotebookPage* pPage = GetPage(nPage);
310
311 m_tabView->RemoveTab((int) (long) pPage);
312
313 m_pages.Remove(pPage);
314
315 if (m_pages.GetCount() == 0)
316 {
317 m_nSelection = -1;
318 m_tabView->SetTabSelection(-1, TRUE);
319 }
320 else if (m_nSelection > -1)
321 {
322 // Only change the selection if the page we
323 // deleted was the selection.
324 if (nPage == (size_t)m_nSelection)
325 {
326 m_nSelection = -1;
327 // Select the first tab. Generates a ChangePage.
328 m_tabView->SetTabSelection(0, TRUE);
329 }
330 else
331 {
332 // We must adjust which tab we think is selected.
333 // If greater than the page we deleted, it must be moved down
334 // a notch.
335 if (size_t(m_nSelection) > nPage)
336 m_nSelection -- ;
337 }
338 }
339
340 RefreshLayout(FALSE);
341
342 return pPage;
343 }
344
345 bool wxNotebook::RemovePage(wxNotebookPage* page)
346 {
347 int pagePos = FindPagePosition(page);
348 if (pagePos > -1)
349 return RemovePage(pagePos);
350 else
351 return FALSE;
352 }
353
354 // Find the position of the wxNotebookPage, -1 if not found.
355 int wxNotebook::FindPagePosition(wxNotebookPage* page) const
356 {
357 size_t nPageCount = GetPageCount();
358 size_t nPage;
359 for ( nPage = 0; nPage < nPageCount; nPage++ )
360 if (m_pages[nPage] == page)
361 return nPage;
362 return -1;
363 }
364
365 // remove all pages
366 bool wxNotebook::DeleteAllPages()
367 {
368 m_tabView->ClearTabs(TRUE);
369
370 size_t nPageCount = GetPageCount();
371 size_t nPage;
372 for ( nPage = 0; nPage < nPageCount; nPage++ )
373 delete m_pages[nPage];
374
375 m_pages.Clear();
376
377 return TRUE;
378 }
379
380 // same as AddPage() but does it at given position
381 bool wxNotebook::InsertPage(size_t nPage,
382 wxNotebookPage *pPage,
383 const wxString& strText,
384 bool bSelect,
385 int imageId)
386 {
387 wxASSERT( pPage != NULL );
388 wxCHECK( IS_VALID_PAGE(nPage) || nPage == GetPageCount(), FALSE );
389
390 m_tabView->AddTab((int) (long) pPage, strText);
391
392 if (!bSelect)
393 pPage->Show(FALSE);
394
395 // save the pointer to the page
396 m_pages.Insert(pPage, nPage);
397
398 if (bSelect)
399 {
400 // This will cause ChangePage to be called, via OnSelPage
401
402 m_tabView->SetTabSelection((int) (long) pPage, TRUE);
403 }
404
405 // some page must be selected: either this one or the first one if there is
406 // still no selection
407 if ( m_nSelection == -1 )
408 ChangePage(-1, 0);
409
410 RefreshLayout(FALSE);
411
412 return TRUE;
413 }
414
415 // ----------------------------------------------------------------------------
416 // wxNotebook callbacks
417 // ----------------------------------------------------------------------------
418
419 // @@@ OnSize() is used for setting the font when it's called for the first
420 // time because doing it in ::Create() doesn't work (for unknown reasons)
421 void wxNotebook::OnSize(wxSizeEvent& event)
422 {
423 static bool s_bFirstTime = TRUE;
424 if ( s_bFirstTime ) {
425 // TODO: any first-time-size processing.
426 s_bFirstTime = FALSE;
427 }
428
429 RefreshLayout();
430
431 // Processing continues to next OnSize
432 event.Skip();
433 }
434
435 // This was supposed to cure the non-display of the notebook
436 // until the user resizes the window.
437 // What's going on?
438 void wxNotebook::OnInternalIdle()
439 {
440 wxWindow::OnInternalIdle();
441
442 #if 0
443 static bool s_bFirstTime = TRUE;
444 if ( s_bFirstTime ) {
445 /*
446 wxSize sz(GetSize());
447 sz.x ++;
448 SetSize(sz);
449 sz.x --;
450 SetSize(sz);
451 */
452
453 /*
454 wxSize sz(GetSize());
455 wxSizeEvent sizeEvent(sz, GetId());
456 sizeEvent.SetEventObject(this);
457 GetEventHandler()->ProcessEvent(sizeEvent);
458 Refresh();
459 */
460 s_bFirstTime = FALSE;
461 }
462 #endif
463 }
464
465 // Implementation: calculate the layout of the view rect
466 // and resize the children if required
467 bool wxNotebook::RefreshLayout(bool force)
468 {
469 if (m_tabView)
470 {
471 wxRect oldRect = m_tabView->GetViewRect();
472
473 int cw, ch;
474 GetClientSize(& cw, & ch);
475
476 int tabHeight = m_tabView->GetTotalTabHeight();
477 wxRect rect;
478 rect.x = 4;
479 rect.y = tabHeight + 4;
480 rect.width = cw - 8;
481 rect.height = ch - 4 - rect.y ;
482
483 m_tabView->SetViewRect(rect);
484
485 m_tabView->LayoutTabs();
486
487 // Need to do it a 2nd time to get the tab height with
488 // the new view width, since changing the view width changes the
489 // tab layout.
490 tabHeight = m_tabView->GetTotalTabHeight();
491 rect.x = 4;
492 rect.y = tabHeight + 4;
493 rect.width = cw - 8;
494 rect.height = ch - 4 - rect.y ;
495
496 m_tabView->SetViewRect(rect);
497
498 m_tabView->LayoutTabs();
499
500 if (!force && (rect == oldRect))
501 return FALSE;
502
503 // fit the notebook page to the tab control's display area
504
505 size_t nCount = m_pages.Count();
506 for ( size_t nPage = 0; nPage < nCount; nPage++ ) {
507 wxNotebookPage *pPage = m_pages[nPage];
508 wxRect clientRect = GetAvailableClientSize();
509 if (pPage->IsShown())
510 {
511 pPage->SetSize(clientRect.x, clientRect.y, clientRect.width, clientRect.height);
512 if ( pPage->GetAutoLayout() )
513 pPage->Layout();
514 }
515 }
516 Refresh();
517 }
518 return TRUE;
519 }
520
521 void wxNotebook::OnSelChange(wxNotebookEvent& event)
522 {
523 // is it our tab control?
524 if ( event.GetEventObject() == this )
525 {
526 if (event.GetSelection() != m_nSelection)
527 ChangePage(event.GetOldSelection(), event.GetSelection());
528 }
529
530 // we want to give others a chance to process this message as well
531 event.Skip();
532 }
533
534 void wxNotebook::OnSetFocus(wxFocusEvent& event)
535 {
536 // set focus to the currently selected page if any
537 if ( m_nSelection != -1 )
538 m_pages[m_nSelection]->SetFocus();
539
540 event.Skip();
541 }
542
543 void wxNotebook::OnNavigationKey(wxNavigationKeyEvent& event)
544 {
545 if ( event.IsWindowChange() ) {
546 // change pages
547 AdvanceSelection(event.GetDirection());
548 }
549 else {
550 // pass to the parent
551 if ( GetParent() ) {
552 event.SetCurrentFocus(this);
553 GetParent()->ProcessEvent(event);
554 }
555 }
556 }
557
558 // ----------------------------------------------------------------------------
559 // wxNotebook base class virtuals
560 // ----------------------------------------------------------------------------
561
562 // override these 2 functions to do nothing: everything is done in OnSize
563
564 void wxNotebook::SetConstraintSizes(bool /* recurse */)
565 {
566 // don't set the sizes of the pages - their correct size is not yet known
567 wxControl::SetConstraintSizes(FALSE);
568 }
569
570 bool wxNotebook::DoPhase(int /* nPhase */)
571 {
572 return TRUE;
573 }
574
575 void wxNotebook::Command(wxCommandEvent& WXUNUSED(event))
576 {
577 wxFAIL_MSG(wxT("wxNotebook::Command not implemented"));
578 }
579
580 // ----------------------------------------------------------------------------
581 // wxNotebook helper functions
582 // ----------------------------------------------------------------------------
583
584 // hide the currently active panel and show the new one
585 void wxNotebook::ChangePage(int nOldSel, int nSel)
586 {
587 // cout << "ChangePage: " << nOldSel << ", " << nSel << "\n";
588 wxASSERT( nOldSel != nSel ); // impossible
589
590 if ( nOldSel != -1 ) {
591 m_pages[nOldSel]->Show(FALSE);
592 m_pages[nOldSel]->Lower();
593 }
594
595 wxNotebookPage *pPage = m_pages[nSel];
596
597 wxRect clientRect = GetAvailableClientSize();
598 pPage->SetSize(clientRect.x, clientRect.y, clientRect.width, clientRect.height);
599
600 Refresh();
601
602 pPage->Show(TRUE);
603 pPage->Raise();
604 pPage->SetFocus();
605
606 m_nSelection = nSel;
607 }
608
609 void wxNotebook::OnMouseEvent(wxMouseEvent& event)
610 {
611 if (m_tabView)
612 m_tabView->OnEvent(event);
613 }
614
615 void wxNotebook::OnPaint(wxPaintEvent& WXUNUSED(event) )
616 {
617 wxPaintDC dc(this);
618 if (m_tabView)
619 m_tabView->Draw(dc);
620 }
621
622 wxRect wxNotebook::GetAvailableClientSize()
623 {
624 int cw, ch;
625 GetClientSize(& cw, & ch);
626
627 int tabHeight = m_tabView->GetTotalTabHeight();
628
629 // TODO: these margins should be configurable.
630 wxRect rect;
631 rect.x = 6;
632 rect.y = tabHeight + 6;
633 rect.width = cw - 12;
634 rect.height = ch - 4 - rect.y ;
635
636 return rect;
637 }
638
639 /*
640 * wxNotebookTabView
641 */
642
643 IMPLEMENT_CLASS(wxNotebookTabView, wxTabView)
644
645 wxNotebookTabView::wxNotebookTabView(wxNotebook *notebook, long style): wxTabView(style)
646 {
647 m_notebook = notebook;
648
649 m_notebook->SetTabView(this);
650
651 SetWindow(m_notebook);
652 }
653
654 wxNotebookTabView::~wxNotebookTabView(void)
655 {
656 }
657
658 // Called when a tab is activated
659 void wxNotebookTabView::OnTabActivate(int activateId, int deactivateId)
660 {
661 if (!m_notebook)
662 return;
663
664 wxNotebookEvent event(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED, m_notebook->GetId());
665
666 // Translate from wxTabView's ids (which aren't position-dependent)
667 // to wxNotebook's (which are).
668 wxNotebookPage* pActive = (wxNotebookPage*) activateId;
669 wxNotebookPage* pDeactive = (wxNotebookPage*) deactivateId;
670
671 int activatePos = m_notebook->FindPagePosition(pActive);
672 int deactivatePos = m_notebook->FindPagePosition(pDeactive);
673
674 event.SetEventObject(m_notebook);
675 event.SetSelection(activatePos);
676 event.SetOldSelection(deactivatePos);
677 m_notebook->GetEventHandler()->ProcessEvent(event);
678 }
679
680 // Allows Vetoing
681 bool wxNotebookTabView::OnTabPreActivate(int activateId, int deactivateId)
682 {
683 bool retval = TRUE;
684
685 if (m_notebook)
686 {
687 wxNotebookEvent event(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING, m_notebook->GetId());
688
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 event.SetEventObject(m_notebook);
698 event.SetSelection(activatePos);
699 event.SetOldSelection(deactivatePos);
700 if (m_notebook->GetEventHandler()->ProcessEvent(event))
701 {
702 retval = event.IsAllowed();
703 }
704 }
705 return retval;
706 }
707