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