]> git.saurik.com Git - wxWidgets.git/blob - src/generic/notebook.cpp
Don't send wxEVT_COMMAND_SPINCTRLDOUBLE_UPDATED when nothing changed.
[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 #if wxUSE_NOTEBOOK
28
29 #include "wx/notebook.h"
30
31 #ifndef WX_PRECOMP
32 #include "wx/string.h"
33 #include "wx/log.h"
34 #include "wx/dcclient.h"
35 #include "wx/settings.h"
36 #endif
37
38 #include "wx/imaglist.h"
39 #include "wx/generic/tabg.h"
40
41 // ----------------------------------------------------------------------------
42 // macros
43 // ----------------------------------------------------------------------------
44
45 // check that the page index is valid
46 #define IS_VALID_PAGE(nPage) ((nPage) < GetPageCount())
47
48 // ----------------------------------------------------------------------------
49 // event table
50 // ----------------------------------------------------------------------------
51
52 BEGIN_EVENT_TABLE(wxNotebook, wxBookCtrlBase)
53 EVT_NOTEBOOK_PAGE_CHANGED(wxID_ANY, 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, wxBookCtrlBase)
62
63 // ============================================================================
64 // implementation
65 // ============================================================================
66
67 // ============================================================================
68 // Private class
69 // ============================================================================
70
71 WX_DECLARE_HASH_MAP(int, wxNotebookPage*, wxIntegerHash, wxIntegerEqual,
72 wxIntToNotebookPageHashMap);
73
74 WX_DECLARE_HASH_MAP(wxNotebookPage*, int, wxPointerHash, wxPointerEqual,
75 wxNotebookPageToIntHashMap);
76
77 // This reuses wxTabView to draw the tabs.
78 class WXDLLEXPORT wxNotebookTabView: public wxTabView
79 {
80 DECLARE_DYNAMIC_CLASS(wxNotebookTabView)
81 public:
82 wxNotebookTabView(wxNotebook* notebook, long style = wxTAB_STYLE_DRAW_BOX | wxTAB_STYLE_COLOUR_INTERIOR);
83 virtual ~wxNotebookTabView(void);
84
85 // Called when a tab is activated
86 virtual void OnTabActivate(int activateId, int deactivateId);
87 // Allows vetoing
88 virtual bool OnTabPreActivate(int activateId, int deactivateId);
89
90 // map integer ids used by wxTabView to wxNotebookPage pointers
91 int GetId(wxNotebookPage *page);
92 wxNotebookPage *GetPage(int id) { return m_idToPage[id]; }
93
94 protected:
95 wxNotebook* m_notebook;
96
97 private:
98 wxIntToNotebookPageHashMap m_idToPage;
99 wxNotebookPageToIntHashMap m_pageToId;
100 int m_nextid;
101 };
102
103 static int GetPageId(wxTabView *tabview, wxNotebookPage *page)
104 {
105 return static_cast<wxNotebookTabView*>(tabview)->GetId(page);
106 }
107
108 // ----------------------------------------------------------------------------
109 // wxNotebook construction
110 // ----------------------------------------------------------------------------
111
112 // common part of all ctors
113 void wxNotebook::Init()
114 {
115 m_tabView = NULL;
116 m_nSelection = -1;
117 }
118
119 // default for dynamic class
120 wxNotebook::wxNotebook()
121 {
122 Init();
123 }
124
125 // the same arguments as for wxControl
126 wxNotebook::wxNotebook(wxWindow *parent,
127 wxWindowID id,
128 const wxPoint& pos,
129 const wxSize& size,
130 long style,
131 const wxString& name)
132 {
133 Init();
134
135 Create(parent, id, pos, size, style, name);
136 }
137
138 // Create() function
139 bool wxNotebook::Create(wxWindow *parent,
140 wxWindowID id,
141 const wxPoint& pos,
142 const wxSize& size,
143 long style,
144 const wxString& name)
145 {
146 // base init
147 SetName(name);
148
149 if ( (style & wxBK_ALIGN_MASK) == wxBK_DEFAULT )
150 style |= wxBK_TOP;
151
152 m_windowId = id == wxID_ANY ? NewControlId() : id;
153
154 if (!wxControl::Create(parent, id, pos, size, style|wxNO_BORDER, wxDefaultValidator, name))
155 return false;
156
157 SetTabView(new wxNotebookTabView(this));
158
159 return true;
160 }
161
162 // dtor
163 wxNotebook::~wxNotebook()
164 {
165 delete m_tabView;
166 }
167
168 // ----------------------------------------------------------------------------
169 // wxNotebook accessors
170 // ----------------------------------------------------------------------------
171 int wxNotebook::GetRowCount() const
172 {
173 // TODO
174 return 0;
175 }
176
177 int wxNotebook::SetSelection(size_t nPage)
178 {
179 wxASSERT( IS_VALID_PAGE(nPage) );
180
181 wxNotebookPage* pPage = GetPage(nPage);
182
183 m_tabView->SetTabSelection(GetPageId(m_tabView, pPage));
184
185 // TODO
186 return 0;
187 }
188
189 int wxNotebook::ChangeSelection(size_t nPage)
190 {
191 // FIXME: currently it does generate events too
192 return SetSelection(nPage);
193 }
194
195 #if 0
196 void wxNotebook::AdvanceSelection(bool bForward)
197 {
198 int nSel = GetSelection();
199 int nMax = GetPageCount() - 1;
200 if ( bForward )
201 SetSelection(nSel == nMax ? 0 : nSel + 1);
202 else
203 SetSelection(nSel == 0 ? nMax : nSel - 1);
204 }
205 #endif
206
207 bool wxNotebook::SetPageText(size_t nPage, const wxString& strText)
208 {
209 wxASSERT( IS_VALID_PAGE(nPage) );
210
211 wxNotebookPage* page = GetPage(nPage);
212 if (page)
213 {
214 m_tabView->SetTabText(GetPageId(m_tabView, page), strText);
215 Refresh();
216 return true;
217 }
218
219 return false;
220 }
221
222 wxString wxNotebook::GetPageText(size_t nPage) const
223 {
224 wxASSERT( IS_VALID_PAGE(nPage) );
225
226 wxNotebookPage* page = ((wxNotebook*)this)->GetPage(nPage);
227 if (page)
228 return m_tabView->GetTabText(GetPageId(m_tabView, page));
229 else
230 return wxEmptyString;
231 }
232
233 int wxNotebook::GetPageImage(size_t WXUNUSED_UNLESS_DEBUG(nPage)) const
234 {
235 wxASSERT( IS_VALID_PAGE(nPage) );
236
237 // TODO
238 return 0;
239 }
240
241 bool wxNotebook::SetPageImage(size_t WXUNUSED_UNLESS_DEBUG(nPage),
242 int WXUNUSED(nImage))
243 {
244 wxASSERT( IS_VALID_PAGE(nPage) );
245
246 // TODO
247 return false;
248 }
249
250 // set the size (the same for all pages)
251 void wxNotebook::SetPageSize(const wxSize& WXUNUSED(size))
252 {
253 // TODO
254 }
255
256 // set the padding between tabs (in pixels)
257 void wxNotebook::SetPadding(const wxSize& WXUNUSED(padding))
258 {
259 // TODO
260 }
261
262 // set the size of the tabs for wxNB_FIXEDWIDTH controls
263 void wxNotebook::SetTabSize(const wxSize& WXUNUSED(sz))
264 {
265 // TODO
266 }
267
268 // ----------------------------------------------------------------------------
269 // wxNotebook operations
270 // ----------------------------------------------------------------------------
271
272 // remove one page from the notebook and delete it
273 bool wxNotebook::DeletePage(size_t nPage)
274 {
275 wxCHECK( IS_VALID_PAGE(nPage), false );
276
277 if (m_nSelection != -1)
278 {
279 m_pages[m_nSelection]->Show(false);
280 m_pages[m_nSelection]->Lower();
281 }
282
283 wxNotebookPage* pPage = GetPage(nPage);
284
285 m_tabView->RemoveTab(GetPageId(m_tabView, pPage));
286
287 m_pages.Remove(pPage);
288 delete pPage;
289
290 if (m_pages.GetCount() == 0)
291 {
292 m_nSelection = -1;
293 m_tabView->SetTabSelection(-1, false);
294 }
295 else if (m_nSelection > -1)
296 {
297 m_nSelection = -1;
298
299 m_tabView->SetTabSelection(GetPageId(m_tabView, GetPage(0)), false);
300
301 if (m_nSelection != 0)
302 ChangePage(-1, 0);
303 }
304
305 RefreshLayout(false);
306
307 return true;
308 }
309
310 bool wxNotebook::DeletePage(wxNotebookPage* page)
311 {
312 int pagePos = FindPagePosition(page);
313 if (pagePos > -1)
314 return DeletePage(pagePos);
315 else
316 return false;
317 }
318
319 bool wxNotebook::RemovePage(size_t nPage)
320 {
321 return DoRemovePage(nPage) != NULL;
322 }
323
324 // remove one page from the notebook
325 wxWindow* wxNotebook::DoRemovePage(size_t nPage)
326 {
327 wxCHECK( IS_VALID_PAGE(nPage), NULL );
328
329 m_pages[nPage]->Show(false);
330 // m_pages[nPage]->Lower();
331
332 wxNotebookPage* pPage = GetPage(nPage);
333
334 m_tabView->RemoveTab(GetPageId(m_tabView, pPage));
335
336 m_pages.Remove(pPage);
337
338 if (m_pages.GetCount() == 0)
339 {
340 m_nSelection = -1;
341 m_tabView->SetTabSelection(-1, true);
342 }
343 else if (m_nSelection > -1)
344 {
345 // Only change the selection if the page we
346 // deleted was the selection.
347 if (nPage == (size_t)m_nSelection)
348 {
349 m_nSelection = -1;
350 // Select the first tab. Generates a ChangePage.
351 m_tabView->SetTabSelection(0, true);
352 }
353 else
354 {
355 // We must adjust which tab we think is selected.
356 // If greater than the page we deleted, it must be moved down
357 // a notch.
358 if (size_t(m_nSelection) > nPage)
359 m_nSelection -- ;
360 }
361 }
362
363 RefreshLayout(false);
364
365 return pPage;
366 }
367
368 bool wxNotebook::RemovePage(wxNotebookPage* page)
369 {
370 int pagePos = FindPagePosition(page);
371 if (pagePos > -1)
372 return RemovePage(pagePos);
373 else
374 return false;
375 }
376
377 // Find the position of the wxNotebookPage, -1 if not found.
378 int wxNotebook::FindPagePosition(wxNotebookPage* page) const
379 {
380 size_t nPageCount = GetPageCount();
381 size_t nPage;
382 for ( nPage = 0; nPage < nPageCount; nPage++ )
383 if (m_pages[nPage] == page)
384 return nPage;
385 return -1;
386 }
387
388 // remove all pages
389 bool wxNotebook::DeleteAllPages()
390 {
391 m_tabView->ClearTabs(true);
392
393 size_t nPageCount = GetPageCount();
394 size_t nPage;
395 for ( nPage = 0; nPage < nPageCount; nPage++ )
396 delete m_pages[nPage];
397
398 m_pages.Clear();
399
400 return true;
401 }
402
403 // same as AddPage() but does it at given position
404 bool wxNotebook::InsertPage(size_t nPage,
405 wxNotebookPage *pPage,
406 const wxString& strText,
407 bool bSelect,
408 int WXUNUSED(imageId))
409 {
410 wxASSERT( pPage != NULL );
411 wxCHECK( IS_VALID_PAGE(nPage) || nPage == GetPageCount(), false );
412
413 m_tabView->AddTab(GetPageId(m_tabView, pPage), strText);
414
415 if (!bSelect)
416 pPage->Show(false);
417
418 // save the pointer to the page
419 m_pages.Insert(pPage, nPage);
420
421 if (bSelect)
422 {
423 // This will cause ChangePage to be called, via OnSelPage
424
425 m_tabView->SetTabSelection(GetPageId(m_tabView, pPage), true);
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(wxBookCtrlEvent& 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 {
570 // change pages
571 AdvanceSelection(event.GetDirection());
572 }
573 else {
574 // pass to the parent
575 if ( GetParent() )
576 {
577 event.SetCurrentFocus(this);
578 GetParent()->ProcessWindowEvent(event);
579 }
580 }
581 }
582
583 // ----------------------------------------------------------------------------
584 // wxNotebook base class virtuals
585 // ----------------------------------------------------------------------------
586
587 // override these 2 functions to do nothing: everything is done in OnSize
588
589 void wxNotebook::SetConstraintSizes(bool /* recurse */)
590 {
591 // don't set the sizes of the pages - their correct size is not yet known
592 wxControl::SetConstraintSizes(false);
593 }
594
595 bool wxNotebook::DoPhase(int /* nPhase */)
596 {
597 return true;
598 }
599
600 void wxNotebook::Command(wxCommandEvent& WXUNUSED(event))
601 {
602 wxFAIL_MSG(wxT("wxNotebook::Command not implemented"));
603 }
604
605 // ----------------------------------------------------------------------------
606 // wxNotebook helper functions
607 // ----------------------------------------------------------------------------
608
609 // hide the currently active panel and show the new one
610 void wxNotebook::ChangePage(int nOldSel, int nSel)
611 {
612 // cout << "ChangePage: " << nOldSel << ", " << nSel << "\n";
613 wxASSERT( nOldSel != nSel ); // impossible
614
615 if ( nOldSel != -1 ) {
616 m_pages[nOldSel]->Show(false);
617 m_pages[nOldSel]->Lower();
618 }
619
620 wxNotebookPage *pPage = m_pages[nSel];
621
622 wxRect clientRect = GetAvailableClientSize();
623 pPage->SetSize(clientRect.x, clientRect.y, clientRect.width, clientRect.height);
624
625 Refresh();
626
627 pPage->Show(true);
628 pPage->Raise();
629 pPage->SetFocus();
630
631 m_nSelection = nSel;
632 }
633
634 void wxNotebook::OnMouseEvent(wxMouseEvent& event)
635 {
636 if (m_tabView)
637 m_tabView->OnEvent(event);
638 }
639
640 void wxNotebook::OnPaint(wxPaintEvent& WXUNUSED(event) )
641 {
642 wxPaintDC dc(this);
643 if (m_tabView)
644 m_tabView->Draw(dc);
645 }
646
647 wxSize wxNotebook::CalcSizeFromPage(const wxSize& sizePage) const
648 {
649 // MBN: since the total tab height is really a function of the
650 // width, this should really call
651 // GetTotalTabHeightPretendingWidthIs(), but the current
652 // implementation will suffice, provided the wxNotebook has been
653 // created with a sensible initial width.
654 return wxSize( sizePage.x + 12,
655 sizePage.y + m_tabView->GetTotalTabHeight() + 6 + 4 );
656 }
657
658 wxRect wxNotebook::GetAvailableClientSize()
659 {
660 int cw, ch;
661 GetClientSize(& cw, & ch);
662
663 int tabHeight = m_tabView->GetTotalTabHeight();
664
665 // TODO: these margins should be configurable.
666 wxRect rect;
667 rect.x = 6;
668 rect.y = tabHeight + 6;
669 rect.width = cw - 12;
670 rect.height = ch - 4 - rect.y ;
671
672 return rect;
673 }
674
675 /*
676 * wxNotebookTabView
677 */
678
679 IMPLEMENT_CLASS(wxNotebookTabView, wxTabView)
680
681 wxNotebookTabView::wxNotebookTabView(wxNotebook *notebook, long style)
682 : wxTabView(style), m_nextid(1)
683 {
684 m_notebook = notebook;
685
686 m_notebook->SetTabView(this);
687
688 SetWindow(m_notebook);
689 }
690
691 wxNotebookTabView::~wxNotebookTabView(void)
692 {
693 }
694
695 int wxNotebookTabView::GetId(wxNotebookPage *page)
696 {
697 int& id = m_pageToId[page];
698
699 if (!id)
700 {
701 id = m_nextid++;
702 m_idToPage[id] = page;
703 }
704
705 return id;
706 }
707
708 // Called when a tab is activated
709 void wxNotebookTabView::OnTabActivate(int activateId, int deactivateId)
710 {
711 if (!m_notebook)
712 return;
713
714 wxBookCtrlEvent event(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED, m_notebook->GetId());
715
716 // Translate from wxTabView's ids (which aren't position-dependent)
717 // to wxNotebook's (which are).
718 wxNotebookPage* pActive = GetPage(activateId);
719 wxNotebookPage* pDeactive = GetPage(deactivateId);
720
721 int activatePos = m_notebook->FindPagePosition(pActive);
722 int deactivatePos = m_notebook->FindPagePosition(pDeactive);
723
724 event.SetEventObject(m_notebook);
725 event.SetSelection(activatePos);
726 event.SetOldSelection(deactivatePos);
727 m_notebook->GetEventHandler()->ProcessEvent(event);
728 }
729
730 // Allows Vetoing
731 bool wxNotebookTabView::OnTabPreActivate(int activateId, int deactivateId)
732 {
733 bool retval = true;
734
735 if (m_notebook)
736 {
737 wxBookCtrlEvent event(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING, m_notebook->GetId());
738
739 // Translate from wxTabView's ids (which aren't position-dependent)
740 // to wxNotebook's (which are).
741 wxNotebookPage* pActive = GetPage(activateId);
742 wxNotebookPage* pDeactive = GetPage(deactivateId);
743
744 int activatePos = m_notebook->FindPagePosition(pActive);
745 int deactivatePos = m_notebook->FindPagePosition(pDeactive);
746
747 event.SetEventObject(m_notebook);
748 event.SetSelection(activatePos);
749 event.SetOldSelection(deactivatePos);
750 if (m_notebook->GetEventHandler()->ProcessEvent(event))
751 {
752 retval = event.IsAllowed();
753 }
754 }
755 return retval;
756 }
757
758 #endif // wxUSE_NOTEBOOK