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