1. made compilation with WXWIN_COMPATIBILITY_EVENT_TYPES really work
[wxWidgets.git] / src / generic / notebook.cpp
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
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"
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
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(-1, 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 // EVT_IDLE(wxNotebook::OnIdle)
59 END_EVENT_TABLE()
60
61 IMPLEMENT_DYNAMIC_CLASS(wxNotebook, wxControl)
62 IMPLEMENT_DYNAMIC_CLASS(wxNotebookEvent, wxCommandEvent)
63
64 // ============================================================================
65 // implementation
66 // ============================================================================
67
68 // ----------------------------------------------------------------------------
69 // wxNotebook construction
70 // ----------------------------------------------------------------------------
71
72 // common part of all ctors
73 void wxNotebook::Init()
74 {
75 m_tabView = (wxNotebookTabView*) NULL;
76 m_pImageList = NULL;
77 m_nSelection = -1;
78 }
79
80 // default for dynamic class
81 wxNotebook::wxNotebook()
82 {
83 Init();
84 }
85
86 // the same arguments as for wxControl
87 wxNotebook::wxNotebook(wxWindow *parent,
88 wxWindowID id,
89 const wxPoint& pos,
90 const wxSize& size,
91 long style,
92 const wxString& name)
93 {
94 Init();
95
96 Create(parent, id, pos, size, style, name);
97 }
98
99 // Create() function
100 bool wxNotebook::Create(wxWindow *parent,
101 wxWindowID id,
102 const wxPoint& pos,
103 const wxSize& size,
104 long style,
105 const wxString& name)
106 {
107 // base init
108 SetName(name);
109
110 m_windowId = id == -1 ? NewControlId() : id;
111
112 // It's like a normal window...
113 if (!wxWindow::Create(parent, id, pos, size, style|wxNO_BORDER, name))
114 return FALSE;
115
116 SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE));
117
118 SetTabView(new wxNotebookTabView(this));
119
120 return TRUE;
121 }
122
123 // dtor
124 wxNotebook::~wxNotebook()
125 {
126 delete m_tabView;
127 }
128
129 // ----------------------------------------------------------------------------
130 // wxNotebook accessors
131 // ----------------------------------------------------------------------------
132 int wxNotebook::GetPageCount() const
133 {
134 return m_aPages.Count();
135 }
136
137 int wxNotebook::GetRowCount() const
138 {
139 // TODO
140 return 0;
141 }
142
143 int wxNotebook::SetSelection(int nPage)
144 {
145 if (nPage == -1)
146 return 0;
147
148 wxASSERT( IS_VALID_PAGE(nPage) );
149
150 #if defined (__WIN16__)
151 m_tabView->SetTabSelection(nPage);
152 #else
153 wxNotebookPage* pPage = GetPage(nPage);
154
155 m_tabView->SetTabSelection((int) (long) pPage);
156 #endif
157 // TODO
158 return 0;
159 }
160
161 void wxNotebook::AdvanceSelection(bool bForward)
162 {
163 int nSel = GetSelection();
164 int nMax = GetPageCount() - 1;
165 if ( bForward )
166 SetSelection(nSel == nMax ? 0 : nSel + 1);
167 else
168 SetSelection(nSel == 0 ? nMax : nSel - 1);
169 }
170
171 bool wxNotebook::SetPageText(int nPage, const wxString& strText)
172 {
173 wxASSERT( IS_VALID_PAGE(nPage) );
174 #if defined (__WIN16__)
175 m_tabView->SetTabText(nPage, strText);
176 Refresh();
177 return TRUE;
178 #else
179 wxNotebookPage* page = GetPage(nPage);
180 if (page)
181 {
182 m_tabView->SetTabText((int) (long) page, strText);
183 Refresh();
184 return TRUE;
185 }
186 #endif
187 return FALSE;
188 }
189
190 wxString wxNotebook::GetPageText(int nPage) const
191 {
192 wxASSERT( IS_VALID_PAGE(nPage) );
193
194 #if defined (__WIN16__)
195 return m_tabView->GetTabText(nPage);
196 #else
197 wxNotebookPage* page = ((wxNotebook*)this)->GetPage(nPage);
198 if (page)
199 return m_tabView->GetTabText((int) (long) page);
200 else
201 return wxEmptyString;
202 #endif
203 }
204
205 int wxNotebook::GetPageImage(int nPage) const
206 {
207 wxASSERT( IS_VALID_PAGE(nPage) );
208
209 // TODO
210 return 0;
211 }
212
213 bool wxNotebook::SetPageImage(int nPage, int nImage)
214 {
215 wxASSERT( IS_VALID_PAGE(nPage) );
216
217 // TODO
218 return FALSE;
219 }
220
221 void wxNotebook::SetImageList(wxImageList* imageList)
222 {
223 m_pImageList = imageList;
224 // TODO
225 }
226
227 // ----------------------------------------------------------------------------
228 // wxNotebook operations
229 // ----------------------------------------------------------------------------
230
231 // remove one page from the notebook and delete it
232 bool wxNotebook::DeletePage(int nPage)
233 {
234 wxCHECK( IS_VALID_PAGE(nPage), FALSE );
235
236 if (m_nSelection != -1)
237 {
238 m_aPages[m_nSelection]->Show(FALSE);
239 m_aPages[m_nSelection]->Lower();
240 }
241
242 wxNotebookPage* pPage = GetPage(nPage);
243 #if defined (__WIN16__)
244 m_tabView->RemoveTab(nPage);
245 #else
246 m_tabView->RemoveTab((int) (long) pPage);
247 #endif
248
249 delete m_aPages[nPage];
250 m_aPages.Remove(nPage);
251
252 if (m_aPages.GetCount() == 0)
253 {
254 m_nSelection = -1;
255 m_tabView->SetTabSelection(-1, FALSE);
256 }
257 else if (m_nSelection > -1)
258 {
259 m_nSelection = -1;
260 #if defined (__WIN16__)
261 m_tabView->SetTabSelection(0, FALSE);
262 #else
263 m_tabView->SetTabSelection((int) (long) GetPage(0), FALSE);
264 #endif
265 if (m_nSelection != 0)
266 ChangePage(-1, 0);
267 }
268
269 RefreshLayout(FALSE);
270
271 return TRUE;
272 }
273
274 bool wxNotebook::DeletePage(wxNotebookPage* page)
275 {
276 int pagePos = FindPagePosition(page);
277 if (pagePos > -1)
278 return DeletePage(pagePos);
279 else
280 return FALSE;
281 }
282
283 // remove one page from the notebook
284 bool wxNotebook::RemovePage(int nPage)
285 {
286 wxCHECK( IS_VALID_PAGE(nPage), FALSE );
287
288 m_aPages[nPage]->Show(FALSE);
289 // m_aPages[nPage]->Lower();
290
291 wxNotebookPage* pPage = GetPage(nPage);
292 #if defined (__WIN16__)
293 m_tabView->RemoveTab(nPage);
294 #else
295 m_tabView->RemoveTab((int) (long) pPage);
296 #endif
297
298 m_aPages.Remove(nPage);
299
300 if (m_aPages.GetCount() == 0)
301 {
302 m_nSelection = -1;
303 m_tabView->SetTabSelection(-1, TRUE);
304 }
305 else if (m_nSelection > -1)
306 {
307 // Only change the selection if the page we
308 // deleted was the selection.
309 if (nPage == m_nSelection)
310 {
311 m_nSelection = -1;
312 // Select the first tab. Generates a ChangePage.
313 m_tabView->SetTabSelection((int) (long) GetPage(0), TRUE);
314 }
315 else
316 {
317 // We must adjust which tab we think is selected.
318 // If greater than the page we deleted, it must be moved down
319 // a notch.
320 if (m_nSelection > nPage)
321 m_nSelection -- ;
322 }
323 }
324
325 RefreshLayout(FALSE);
326
327 return TRUE;
328 }
329
330 bool wxNotebook::RemovePage(wxNotebookPage* page)
331 {
332 int pagePos = FindPagePosition(page);
333 if (pagePos > -1)
334 return RemovePage(pagePos);
335 else
336 return FALSE;
337 }
338
339 // Find the position of the wxNotebookPage, -1 if not found.
340 int wxNotebook::FindPagePosition(wxNotebookPage* page) const
341 {
342 int nPageCount = GetPageCount();
343 int nPage;
344 for ( nPage = 0; nPage < nPageCount; nPage++ )
345 if (m_aPages[nPage] == page)
346 return nPage;
347 return -1;
348 }
349
350 // remove all pages
351 bool wxNotebook::DeleteAllPages()
352 {
353 m_tabView->ClearTabs(TRUE);
354
355 int nPageCount = GetPageCount();
356 int nPage;
357 for ( nPage = 0; nPage < nPageCount; nPage++ )
358 delete m_aPages[nPage];
359
360 m_aPages.Clear();
361
362 return TRUE;
363 }
364
365 // add a page to the notebook
366 bool wxNotebook::AddPage(wxNotebookPage *pPage,
367 const wxString& strText,
368 bool bSelect,
369 int imageId)
370 {
371 return InsertPage(GetPageCount(), pPage, strText, bSelect, imageId);
372 }
373
374 // same as AddPage() but does it at given position
375 bool wxNotebook::InsertPage(int nPage,
376 wxNotebookPage *pPage,
377 const wxString& strText,
378 bool bSelect,
379 int imageId)
380 {
381 wxASSERT( pPage != NULL );
382 wxCHECK( IS_VALID_PAGE(nPage) || nPage == GetPageCount(), FALSE );
383
384 // For 16 bit integers (tabs limited to 32768)
385 #if defined (__WIN16__)
386 m_tabView->AddTab(nPage, strText);
387 #else
388 m_tabView->AddTab((int) (long) pPage, strText);
389 #endif
390 if (!bSelect)
391 pPage->Show(FALSE);
392
393 // save the pointer to the page
394 m_aPages.Insert(pPage, nPage);
395
396 if (bSelect)
397 {
398 // This will cause ChangePage to be called, via OnSelPage
399 #if defined (__WIN16__)
400 m_tabView->SetTabSelection(nPage, TRUE);
401 #else
402 m_tabView->SetTabSelection((int) (long) pPage, TRUE);
403 #endif
404 }
405
406 // some page must be selected: either this one or the first one if there is
407 // still no selection
408 if ( m_nSelection == -1 )
409 ChangePage(-1, 0);
410
411 RefreshLayout(FALSE);
412
413 return TRUE;
414 }
415
416 // ----------------------------------------------------------------------------
417 // wxNotebook callbacks
418 // ----------------------------------------------------------------------------
419
420 // @@@ OnSize() is used for setting the font when it's called for the first
421 // time because doing it in ::Create() doesn't work (for unknown reasons)
422 void wxNotebook::OnSize(wxSizeEvent& event)
423 {
424 static bool s_bFirstTime = TRUE;
425 if ( s_bFirstTime ) {
426 // TODO: any first-time-size processing.
427 s_bFirstTime = FALSE;
428 }
429
430 RefreshLayout();
431
432 // Processing continues to next OnSize
433 event.Skip();
434 }
435
436 // This was supposed to cure the non-display of the notebook
437 // until the user resizes the window.
438 // What's going on?
439 void wxNotebook::OnIdle(wxIdleEvent& event)
440 {
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 event.Skip();
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 unsigned int nCount = m_aPages.Count();
504 for ( unsigned int nPage = 0; nPage < nCount; nPage++ ) {
505 wxNotebookPage *pPage = m_aPages[nPage];
506 if (pPage->IsShown())
507 {
508 wxRect clientRect = GetAvailableClientSize();
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_aPages[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("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_aPages[nOldSel]->Show(FALSE);
590 m_aPages[nOldSel]->Lower();
591 }
592
593 wxNotebookPage *pPage = m_aPages[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 wxRect wxNotebook::GetAvailableClientSize()
621 {
622 int cw, ch;
623 GetClientSize(& cw, & ch);
624
625 int tabHeight = m_tabView->GetTotalTabHeight();
626
627 // TODO: these margins should be configurable.
628 wxRect rect;
629 rect.x = 6;
630 rect.y = tabHeight + 6;
631 rect.width = cw - 12;
632 rect.height = ch - 4 - rect.y ;
633
634 return rect;
635 }
636
637 /*
638 * wxNotebookTabView
639 */
640
641 IMPLEMENT_CLASS(wxNotebookTabView, wxTabView)
642
643 wxNotebookTabView::wxNotebookTabView(wxNotebook *notebook, long style): wxTabView(style)
644 {
645 m_notebook = notebook;
646
647 m_notebook->SetTabView(this);
648
649 SetWindow(m_notebook);
650 }
651
652 wxNotebookTabView::~wxNotebookTabView(void)
653 {
654 }
655
656 // Called when a tab is activated
657 void wxNotebookTabView::OnTabActivate(int activateId, int deactivateId)
658 {
659 if (!m_notebook)
660 return;
661
662 wxNotebookEvent event(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED, m_notebook->GetId());
663
664 #if defined (__WIN16__)
665 int activatePos = activateId;
666 int deactivatePos = deactivateId;
667 #else
668 // Translate from wxTabView's ids (which aren't position-dependent)
669 // to wxNotebook's (which are).
670 wxNotebookPage* pActive = (wxNotebookPage*) activateId;
671 wxNotebookPage* pDeactive = (wxNotebookPage*) deactivateId;
672
673 int activatePos = m_notebook->FindPagePosition(pActive);
674 int deactivatePos = m_notebook->FindPagePosition(pDeactive);
675
676 #endif
677 event.SetEventObject(m_notebook);
678 event.SetSelection(activatePos);
679 event.SetOldSelection(deactivatePos);
680 m_notebook->GetEventHandler()->ProcessEvent(event);
681 }
682
683