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