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