]> git.saurik.com Git - wxWidgets.git/blob - src/generic/notebook.cpp
fix for bug introduced in GetNextChild() yesterday
[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 // MBN: this is probably just hiding a problem under the carpet,
537 // but: with OpenMotif 2.2 (not Lesstif), not moving the window
538 // may cause the tabs to be not clickable.
539 else
540 {
541 pPage->Move(clientRect.x, clientRect.y);
542 }
543 }
544 Refresh();
545 }
546 return TRUE;
547 }
548
549 void wxNotebook::OnSelChange(wxNotebookEvent& event)
550 {
551 // is it our tab control?
552 if ( event.GetEventObject() == this )
553 {
554 if (event.GetSelection() != m_nSelection)
555 ChangePage(event.GetOldSelection(), event.GetSelection());
556 }
557
558 // we want to give others a chance to process this message as well
559 event.Skip();
560 }
561
562 void wxNotebook::OnSetFocus(wxFocusEvent& event)
563 {
564 // set focus to the currently selected page if any
565 if ( m_nSelection != -1 )
566 m_pages[m_nSelection]->SetFocus();
567
568 event.Skip();
569 }
570
571 void wxNotebook::OnNavigationKey(wxNavigationKeyEvent& event)
572 {
573 if ( event.IsWindowChange() ) {
574 // change pages
575 AdvanceSelection(event.GetDirection());
576 }
577 else {
578 // pass to the parent
579 if ( GetParent() ) {
580 event.SetCurrentFocus(this);
581 GetParent()->ProcessEvent(event);
582 }
583 }
584 }
585
586 // ----------------------------------------------------------------------------
587 // wxNotebook base class virtuals
588 // ----------------------------------------------------------------------------
589
590 // override these 2 functions to do nothing: everything is done in OnSize
591
592 void wxNotebook::SetConstraintSizes(bool /* recurse */)
593 {
594 // don't set the sizes of the pages - their correct size is not yet known
595 wxControl::SetConstraintSizes(FALSE);
596 }
597
598 bool wxNotebook::DoPhase(int /* nPhase */)
599 {
600 return TRUE;
601 }
602
603 void wxNotebook::Command(wxCommandEvent& WXUNUSED(event))
604 {
605 wxFAIL_MSG("wxNotebook::Command not implemented");
606 }
607
608 // ----------------------------------------------------------------------------
609 // wxNotebook helper functions
610 // ----------------------------------------------------------------------------
611
612 // hide the currently active panel and show the new one
613 void wxNotebook::ChangePage(int nOldSel, int nSel)
614 {
615 // cout << "ChangePage: " << nOldSel << ", " << nSel << "\n";
616 wxASSERT( nOldSel != nSel ); // impossible
617
618 if ( nOldSel != -1 ) {
619 m_pages[nOldSel]->Show(FALSE);
620 m_pages[nOldSel]->Lower();
621 }
622
623 wxNotebookPage *pPage = m_pages[nSel];
624
625 wxRect clientRect = GetAvailableClientSize();
626 pPage->SetSize(clientRect.x, clientRect.y, clientRect.width, clientRect.height);
627
628 Refresh();
629
630 pPage->Show(TRUE);
631 pPage->Raise();
632 pPage->SetFocus();
633
634 m_nSelection = nSel;
635 }
636
637 void wxNotebook::OnMouseEvent(wxMouseEvent& event)
638 {
639 if (m_tabView)
640 m_tabView->OnEvent(event);
641 }
642
643 void wxNotebook::OnPaint(wxPaintEvent& WXUNUSED(event) )
644 {
645 wxPaintDC dc(this);
646 if (m_tabView)
647 m_tabView->Draw(dc);
648 }
649
650 wxRect wxNotebook::GetAvailableClientSize()
651 {
652 int cw, ch;
653 GetClientSize(& cw, & ch);
654
655 int tabHeight = m_tabView->GetTotalTabHeight();
656
657 // TODO: these margins should be configurable.
658 wxRect rect;
659 rect.x = 6;
660 rect.y = tabHeight + 6;
661 rect.width = cw - 12;
662 rect.height = ch - 4 - rect.y ;
663
664 return rect;
665 }
666
667 /*
668 * wxNotebookTabView
669 */
670
671 IMPLEMENT_CLASS(wxNotebookTabView, wxTabView)
672
673 wxNotebookTabView::wxNotebookTabView(wxNotebook *notebook, long style): wxTabView(style)
674 {
675 m_notebook = notebook;
676
677 m_notebook->SetTabView(this);
678
679 SetWindow(m_notebook);
680 }
681
682 wxNotebookTabView::~wxNotebookTabView(void)
683 {
684 }
685
686 // Called when a tab is activated
687 void wxNotebookTabView::OnTabActivate(int activateId, int deactivateId)
688 {
689 if (!m_notebook)
690 return;
691
692 wxNotebookEvent event(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED, m_notebook->GetId());
693
694 #if defined (__WIN16__)
695 int activatePos = activateId;
696 int deactivatePos = deactivateId;
697 #else
698 // Translate from wxTabView's ids (which aren't position-dependent)
699 // to wxNotebook's (which are).
700 wxNotebookPage* pActive = (wxNotebookPage*) activateId;
701 wxNotebookPage* pDeactive = (wxNotebookPage*) deactivateId;
702
703 int activatePos = m_notebook->FindPagePosition(pActive);
704 int deactivatePos = m_notebook->FindPagePosition(pDeactive);
705
706 #endif
707 event.SetEventObject(m_notebook);
708 event.SetSelection(activatePos);
709 event.SetOldSelection(deactivatePos);
710 m_notebook->GetEventHandler()->ProcessEvent(event);
711 }
712
713 // Allows Vetoing
714 bool wxNotebookTabView::OnTabPreActivate(int activateId, int deactivateId)
715 {
716 bool retval = TRUE;
717
718 if (m_notebook)
719 {
720 wxNotebookEvent event(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING, m_notebook->GetId());
721
722 #if defined (__WIN16__)
723 int activatePos = activateId;
724 int deactivatePos = deactivateId;
725 #else
726 // Translate from wxTabView's ids (which aren't position-dependent)
727 // to wxNotebook's (which are).
728 wxNotebookPage* pActive = (wxNotebookPage*) activateId;
729 wxNotebookPage* pDeactive = (wxNotebookPage*) deactivateId;
730
731 int activatePos = m_notebook->FindPagePosition(pActive);
732 int deactivatePos = m_notebook->FindPagePosition(pDeactive);
733
734 #endif
735 event.SetEventObject(m_notebook);
736 event.SetSelection(activatePos);
737 event.SetOldSelection(deactivatePos);
738 if (m_notebook->GetEventHandler()->ProcessEvent(event))
739 {
740 retval = event.IsAllowed();
741 }
742 }
743 return retval;
744 }
745