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