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