Committing in .
[wxWidgets.git] / src / generic / notebook.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/generic/notebook.cpp
3 // Purpose: generic 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
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
22
23 #ifdef __BORLANDC__
24 #pragma hdrstop
25 #endif
26
27 #ifndef __WXPALMOS__
28
29 #include "wx/string.h"
30 #include "wx/log.h"
31 #include "wx/settings.h"
32 #include "wx/generic/imaglist.h"
33 #include "wx/notebook.h"
34 #include "wx/dcclient.h"
35 #include "wx/generic/tabg.h"
36
37 // ----------------------------------------------------------------------------
38 // macros
39 // ----------------------------------------------------------------------------
40
41 // check that the page index is valid
42 #define IS_VALID_PAGE(nPage) ((nPage) < GetPageCount())
43
44 // ----------------------------------------------------------------------------
45 // event table
46 // ----------------------------------------------------------------------------
47
48 DEFINE_EVENT_TYPE(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED)
49 DEFINE_EVENT_TYPE(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING)
50
51 BEGIN_EVENT_TABLE(wxNotebook, wxControl)
52 EVT_NOTEBOOK_PAGE_CHANGED(wxID_ANY, wxNotebook::OnSelChange)
53 EVT_SIZE(wxNotebook::OnSize)
54 EVT_PAINT(wxNotebook::OnPaint)
55 EVT_MOUSE_EVENTS(wxNotebook::OnMouseEvent)
56 EVT_SET_FOCUS(wxNotebook::OnSetFocus)
57 EVT_NAVIGATION_KEY(wxNotebook::OnNavigationKey)
58 END_EVENT_TABLE()
59
60 IMPLEMENT_DYNAMIC_CLASS(wxNotebook, wxControl)
61 IMPLEMENT_DYNAMIC_CLASS(wxNotebookEvent, wxCommandEvent)
62
63 // ============================================================================
64 // implementation
65 // ============================================================================
66
67 // ============================================================================
68 // Private class
69 // ============================================================================
70
71 // This reuses wxTabView to draw the tabs.
72 class WXDLLEXPORT wxNotebookTabView: public wxTabView
73 {
74 DECLARE_DYNAMIC_CLASS(wxNotebookTabView)
75 public:
76 wxNotebookTabView(wxNotebook* notebook, long style = wxTAB_STYLE_DRAW_BOX | wxTAB_STYLE_COLOUR_INTERIOR);
77 ~wxNotebookTabView(void);
78
79 // Called when a tab is activated
80 virtual void OnTabActivate(int activateId, int deactivateId);
81 // Allows vetoing
82 virtual bool OnTabPreActivate(int activateId, int deactivateId);
83
84 protected:
85 wxNotebook* m_notebook;
86 };
87
88 // ----------------------------------------------------------------------------
89 // wxNotebook construction
90 // ----------------------------------------------------------------------------
91
92 // common part of all ctors
93 void wxNotebook::Init()
94 {
95 m_tabView = (wxNotebookTabView*) NULL;
96 m_nSelection = -1;
97 }
98
99 // default for dynamic class
100 wxNotebook::wxNotebook()
101 {
102 Init();
103 }
104
105 // the same arguments as for wxControl
106 wxNotebook::wxNotebook(wxWindow *parent,
107 wxWindowID id,
108 const wxPoint& pos,
109 const wxSize& size,
110 long style,
111 const wxString& name)
112 {
113 Init();
114
115 Create(parent, id, pos, size, style, name);
116 }
117
118 // Create() function
119 bool wxNotebook::Create(wxWindow *parent,
120 wxWindowID id,
121 const wxPoint& pos,
122 const wxSize& size,
123 long style,
124 const wxString& name)
125 {
126 // base init
127 SetName(name);
128
129 m_windowId = id == wxID_ANY ? NewControlId() : id;
130
131 if (!wxControl::Create(parent, id, pos, size, style|wxNO_BORDER, wxDefaultValidator, name))
132 return false;
133
134 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE));
135
136 SetTabView(new wxNotebookTabView(this));
137
138 return true;
139 }
140
141 // dtor
142 wxNotebook::~wxNotebook()
143 {
144 delete m_tabView;
145 }
146
147 // ----------------------------------------------------------------------------
148 // wxNotebook accessors
149 // ----------------------------------------------------------------------------
150 int wxNotebook::GetRowCount() const
151 {
152 // TODO
153 return 0;
154 }
155
156 int wxNotebook::SetSelection(size_t nPage)
157 {
158 wxASSERT( IS_VALID_PAGE(nPage) );
159
160 wxNotebookPage* pPage = GetPage(nPage);
161
162 m_tabView->SetTabSelection((int) (long) pPage);
163
164 // TODO
165 return 0;
166 }
167
168 #if 0
169 void wxNotebook::AdvanceSelection(bool bForward)
170 {
171 int nSel = GetSelection();
172 int nMax = GetPageCount() - 1;
173 if ( bForward )
174 SetSelection(nSel == nMax ? 0 : nSel + 1);
175 else
176 SetSelection(nSel == 0 ? nMax : nSel - 1);
177 }
178 #endif
179
180 bool wxNotebook::SetPageText(size_t nPage, const wxString& strText)
181 {
182 wxASSERT( IS_VALID_PAGE(nPage) );
183
184 wxNotebookPage* page = GetPage(nPage);
185 if (page)
186 {
187 m_tabView->SetTabText((int) (long) page, strText);
188 Refresh();
189 return true;
190 }
191
192 return false;
193 }
194
195 wxString wxNotebook::GetPageText(size_t nPage) const
196 {
197 wxASSERT( IS_VALID_PAGE(nPage) );
198
199 wxNotebookPage* page = ((wxNotebook*)this)->GetPage(nPage);
200 if (page)
201 return m_tabView->GetTabText((int) (long) page);
202 else
203 return wxEmptyString;
204 }
205
206 int wxNotebook::GetPageImage(size_t WXUNUSED_UNLESS_DEBUG(nPage)) const
207 {
208 wxASSERT( IS_VALID_PAGE(nPage) );
209
210 // TODO
211 return 0;
212 }
213
214 bool wxNotebook::SetPageImage(size_t WXUNUSED_UNLESS_DEBUG(nPage),
215 int WXUNUSED(nImage))
216 {
217 wxASSERT( IS_VALID_PAGE(nPage) );
218
219 // TODO
220 return false;
221 }
222
223 // set the size (the same for all pages)
224 void wxNotebook::SetPageSize(const wxSize& WXUNUSED(size))
225 {
226 // TODO
227 }
228
229 // set the padding between tabs (in pixels)
230 void wxNotebook::SetPadding(const wxSize& WXUNUSED(padding))
231 {
232 // TODO
233 }
234
235 // set the size of the tabs for wxNB_FIXEDWIDTH controls
236 void wxNotebook::SetTabSize(const wxSize& WXUNUSED(sz))
237 {
238 // TODO
239 }
240
241 // ----------------------------------------------------------------------------
242 // wxNotebook operations
243 // ----------------------------------------------------------------------------
244
245 // remove one page from the notebook and delete it
246 bool wxNotebook::DeletePage(size_t nPage)
247 {
248 wxCHECK( IS_VALID_PAGE(nPage), false );
249
250 if (m_nSelection != -1)
251 {
252 m_pages[m_nSelection]->Show(false);
253 m_pages[m_nSelection]->Lower();
254 }
255
256 wxNotebookPage* pPage = GetPage(nPage);
257
258 m_tabView->RemoveTab((int) (long) pPage);
259
260 m_pages.Remove(pPage);
261 delete pPage;
262
263 if (m_pages.GetCount() == 0)
264 {
265 m_nSelection = -1;
266 m_tabView->SetTabSelection(-1, false);
267 }
268 else if (m_nSelection > -1)
269 {
270 m_nSelection = -1;
271
272 m_tabView->SetTabSelection((int) (long) GetPage(0), false);
273
274 if (m_nSelection != 0)
275 ChangePage(-1, 0);
276 }
277
278 RefreshLayout(false);
279
280 return true;
281 }
282
283 bool wxNotebook::DeletePage(wxNotebookPage* page)
284 {
285 int pagePos = FindPagePosition(page);
286 if (pagePos > -1)
287 return DeletePage(pagePos);
288 else
289 return false;
290 }
291
292 bool wxNotebook::RemovePage(size_t nPage)
293 {
294 return DoRemovePage(nPage) != NULL;
295 }
296
297 // remove one page from the notebook
298 wxWindow* wxNotebook::DoRemovePage(size_t nPage)
299 {
300 wxCHECK( IS_VALID_PAGE(nPage), NULL );
301
302 m_pages[nPage]->Show(false);
303 // m_pages[nPage]->Lower();
304
305 wxNotebookPage* pPage = GetPage(nPage);
306
307 m_tabView->RemoveTab((int) (long) pPage);
308
309 m_pages.Remove(pPage);
310
311 if (m_pages.GetCount() == 0)
312 {
313 m_nSelection = -1;
314 m_tabView->SetTabSelection(-1, true);
315 }
316 else if (m_nSelection > -1)
317 {
318 // Only change the selection if the page we
319 // deleted was the selection.
320 if (nPage == (size_t)m_nSelection)
321 {
322 m_nSelection = -1;
323 // Select the first tab. Generates a ChangePage.
324 m_tabView->SetTabSelection(0, true);
325 }
326 else
327 {
328 // We must adjust which tab we think is selected.
329 // If greater than the page we deleted, it must be moved down
330 // a notch.
331 if (size_t(m_nSelection) > nPage)
332 m_nSelection -- ;
333 }
334 }
335
336 RefreshLayout(false);
337
338 return pPage;
339 }
340
341 bool wxNotebook::RemovePage(wxNotebookPage* page)
342 {
343 int pagePos = FindPagePosition(page);
344 if (pagePos > -1)
345 return RemovePage(pagePos);
346 else
347 return false;
348 }
349
350 // Find the position of the wxNotebookPage, -1 if not found.
351 int wxNotebook::FindPagePosition(wxNotebookPage* page) const
352 {
353 size_t nPageCount = GetPageCount();
354 size_t nPage;
355 for ( nPage = 0; nPage < nPageCount; nPage++ )
356 if (m_pages[nPage] == page)
357 return nPage;
358 return -1;
359 }
360
361 // remove all pages
362 bool wxNotebook::DeleteAllPages()
363 {
364 m_tabView->ClearTabs(true);
365
366 size_t nPageCount = GetPageCount();
367 size_t nPage;
368 for ( nPage = 0; nPage < nPageCount; nPage++ )
369 delete m_pages[nPage];
370
371 m_pages.Clear();
372
373 return true;
374 }
375
376 // same as AddPage() but does it at given position
377 bool wxNotebook::InsertPage(size_t nPage,
378 wxNotebookPage *pPage,
379 const wxString& strText,
380 bool bSelect,
381 int WXUNUSED(imageId))
382 {
383 wxASSERT( pPage != NULL );
384 wxCHECK( IS_VALID_PAGE(nPage) || nPage == GetPageCount(), false );
385
386 m_tabView->AddTab((int) (long) pPage, strText);
387
388 if (!bSelect)
389 pPage->Show(false);
390
391 // save the pointer to the page
392 m_pages.Insert(pPage, nPage);
393
394 if (bSelect)
395 {
396 // This will cause ChangePage to be called, via OnSelPage
397
398 m_tabView->SetTabSelection((int) (long) pPage, true);
399 }
400
401 // some page must be selected: either this one or the first one if there is
402 // still no selection
403 if ( m_nSelection == -1 )
404 ChangePage(-1, 0);
405
406 RefreshLayout(false);
407
408 return true;
409 }
410
411 // ----------------------------------------------------------------------------
412 // wxNotebook callbacks
413 // ----------------------------------------------------------------------------
414
415 // @@@ OnSize() is used for setting the font when it's called for the first
416 // time because doing it in ::Create() doesn't work (for unknown reasons)
417 void wxNotebook::OnSize(wxSizeEvent& event)
418 {
419 static bool s_bFirstTime = true;
420 if ( s_bFirstTime ) {
421 // TODO: any first-time-size processing.
422 s_bFirstTime = false;
423 }
424
425 RefreshLayout();
426
427 // Processing continues to next OnSize
428 event.Skip();
429 }
430
431 // This was supposed to cure the non-display of the notebook
432 // until the user resizes the window.
433 // What's going on?
434 void wxNotebook::OnInternalIdle()
435 {
436 wxWindow::OnInternalIdle();
437
438 #if 0
439 static bool s_bFirstTime = true;
440 if ( s_bFirstTime ) {
441 /*
442 wxSize sz(GetSize());
443 sz.x ++;
444 SetSize(sz);
445 sz.x --;
446 SetSize(sz);
447 */
448
449 /*
450 wxSize sz(GetSize());
451 wxSizeEvent sizeEvent(sz, GetId());
452 sizeEvent.SetEventObject(this);
453 GetEventHandler()->ProcessEvent(sizeEvent);
454 Refresh();
455 */
456 s_bFirstTime = false;
457 }
458 #endif
459 }
460
461 // Implementation: calculate the layout of the view rect
462 // and resize the children if required
463 bool wxNotebook::RefreshLayout(bool force)
464 {
465 if (m_tabView)
466 {
467 wxRect oldRect = m_tabView->GetViewRect();
468
469 int cw, ch;
470 GetClientSize(& cw, & ch);
471
472 int tabHeight = m_tabView->GetTotalTabHeight();
473 wxRect rect;
474 rect.x = 4;
475 rect.y = tabHeight + 4;
476 rect.width = cw - 8;
477 rect.height = ch - 4 - rect.y ;
478
479 m_tabView->SetViewRect(rect);
480
481 m_tabView->LayoutTabs();
482
483 // Need to do it a 2nd time to get the tab height with
484 // the new view width, since changing the view width changes the
485 // tab layout.
486 tabHeight = m_tabView->GetTotalTabHeight();
487 rect.x = 4;
488 rect.y = tabHeight + 4;
489 rect.width = cw - 8;
490 rect.height = ch - 4 - rect.y ;
491
492 m_tabView->SetViewRect(rect);
493
494 m_tabView->LayoutTabs();
495
496 if (!force && (rect == oldRect))
497 return false;
498
499 // fit the notebook page to the tab control's display area
500
501 size_t nCount = m_pages.Count();
502 for ( size_t nPage = 0; nPage < nCount; nPage++ ) {
503 wxNotebookPage *pPage = m_pages[nPage];
504 wxRect clientRect = GetAvailableClientSize();
505 if (pPage->IsShown())
506 {
507 pPage->SetSize(clientRect.x, clientRect.y, clientRect.width, clientRect.height);
508 if ( pPage->GetAutoLayout() )
509 pPage->Layout();
510 }
511 }
512 Refresh();
513 }
514 return true;
515 }
516
517 void wxNotebook::OnSelChange(wxNotebookEvent& event)
518 {
519 // is it our tab control?
520 if ( event.GetEventObject() == this )
521 {
522 if (event.GetSelection() != m_nSelection)
523 ChangePage(event.GetOldSelection(), event.GetSelection());
524 }
525
526 // we want to give others a chance to process this message as well
527 event.Skip();
528 }
529
530 void wxNotebook::OnSetFocus(wxFocusEvent& event)
531 {
532 // set focus to the currently selected page if any
533 if ( m_nSelection != -1 )
534 m_pages[m_nSelection]->SetFocus();
535
536 event.Skip();
537 }
538
539 void wxNotebook::OnNavigationKey(wxNavigationKeyEvent& event)
540 {
541 if ( event.IsWindowChange() ) {
542 // change pages
543 AdvanceSelection(event.GetDirection());
544 }
545 else {
546 // pass to the parent
547 if ( GetParent() ) {
548 event.SetCurrentFocus(this);
549 GetParent()->ProcessEvent(event);
550 }
551 }
552 }
553
554 // ----------------------------------------------------------------------------
555 // wxNotebook base class virtuals
556 // ----------------------------------------------------------------------------
557
558 // override these 2 functions to do nothing: everything is done in OnSize
559
560 void wxNotebook::SetConstraintSizes(bool /* recurse */)
561 {
562 // don't set the sizes of the pages - their correct size is not yet known
563 wxControl::SetConstraintSizes(false);
564 }
565
566 bool wxNotebook::DoPhase(int /* nPhase */)
567 {
568 return true;
569 }
570
571 void wxNotebook::Command(wxCommandEvent& WXUNUSED(event))
572 {
573 wxFAIL_MSG(wxT("wxNotebook::Command not implemented"));
574 }
575
576 // ----------------------------------------------------------------------------
577 // wxNotebook helper functions
578 // ----------------------------------------------------------------------------
579
580 // hide the currently active panel and show the new one
581 void wxNotebook::ChangePage(int nOldSel, int nSel)
582 {
583 // cout << "ChangePage: " << nOldSel << ", " << nSel << "\n";
584 wxASSERT( nOldSel != nSel ); // impossible
585
586 if ( nOldSel != -1 ) {
587 m_pages[nOldSel]->Show(false);
588 m_pages[nOldSel]->Lower();
589 }
590
591 wxNotebookPage *pPage = m_pages[nSel];
592
593 wxRect clientRect = GetAvailableClientSize();
594 pPage->SetSize(clientRect.x, clientRect.y, clientRect.width, clientRect.height);
595
596 Refresh();
597
598 pPage->Show(true);
599 pPage->Raise();
600 pPage->SetFocus();
601
602 m_nSelection = nSel;
603 }
604
605 void wxNotebook::OnMouseEvent(wxMouseEvent& event)
606 {
607 if (m_tabView)
608 m_tabView->OnEvent(event);
609 }
610
611 void wxNotebook::OnPaint(wxPaintEvent& WXUNUSED(event) )
612 {
613 wxPaintDC dc(this);
614 if (m_tabView)
615 m_tabView->Draw(dc);
616 }
617
618 wxSize wxNotebook::CalcSizeFromPage(const wxSize& sizePage) const
619 {
620 // MBN: since the total tab height is really a function of the
621 // width, this should really call
622 // GetTotalTabHeightPretendingWidthIs(), but the current
623 // implementation will suffice, provided the wxNotebook has been
624 // created with a sensible initial width.
625 return wxSize( sizePage.x + 12,
626 sizePage.y + m_tabView->GetTotalTabHeight() + 6 + 4 );
627 }
628
629 wxRect wxNotebook::GetAvailableClientSize()
630 {
631 int cw, ch;
632 GetClientSize(& cw, & ch);
633
634 int tabHeight = m_tabView->GetTotalTabHeight();
635
636 // TODO: these margins should be configurable.
637 wxRect rect;
638 rect.x = 6;
639 rect.y = tabHeight + 6;
640 rect.width = cw - 12;
641 rect.height = ch - 4 - rect.y ;
642
643 return rect;
644 }
645
646 /*
647 * wxNotebookTabView
648 */
649
650 IMPLEMENT_CLASS(wxNotebookTabView, wxTabView)
651
652 wxNotebookTabView::wxNotebookTabView(wxNotebook *notebook, long style): wxTabView(style)
653 {
654 m_notebook = notebook;
655
656 m_notebook->SetTabView(this);
657
658 SetWindow(m_notebook);
659 }
660
661 wxNotebookTabView::~wxNotebookTabView(void)
662 {
663 }
664
665 // Called when a tab is activated
666 void wxNotebookTabView::OnTabActivate(int activateId, int deactivateId)
667 {
668 if (!m_notebook)
669 return;
670
671 wxNotebookEvent event(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED, m_notebook->GetId());
672
673 // Translate from wxTabView's ids (which aren't position-dependent)
674 // to wxNotebook's (which are).
675 wxNotebookPage* pActive = (wxNotebookPage*) activateId;
676 wxNotebookPage* pDeactive = (wxNotebookPage*) deactivateId;
677
678 int activatePos = m_notebook->FindPagePosition(pActive);
679 int deactivatePos = m_notebook->FindPagePosition(pDeactive);
680
681 event.SetEventObject(m_notebook);
682 event.SetSelection(activatePos);
683 event.SetOldSelection(deactivatePos);
684 m_notebook->GetEventHandler()->ProcessEvent(event);
685 }
686
687 // Allows Vetoing
688 bool wxNotebookTabView::OnTabPreActivate(int activateId, int deactivateId)
689 {
690 bool retval = true;
691
692 if (m_notebook)
693 {
694 wxNotebookEvent event(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING, m_notebook->GetId());
695
696 // Translate from wxTabView's ids (which aren't position-dependent)
697 // to wxNotebook's (which are).
698 wxNotebookPage* pActive = (wxNotebookPage*) activateId;
699 wxNotebookPage* pDeactive = (wxNotebookPage*) deactivateId;
700
701 int activatePos = m_notebook->FindPagePosition(pActive);
702 int deactivatePos = m_notebook->FindPagePosition(pDeactive);
703
704 event.SetEventObject(m_notebook);
705 event.SetSelection(activatePos);
706 event.SetOldSelection(deactivatePos);
707 if (m_notebook->GetEventHandler()->ProcessEvent(event))
708 {
709 retval = event.IsAllowed();
710 }
711 }
712 return retval;
713 }
714
715 #endif // __WXPALMOS__