]> git.saurik.com Git - wxWidgets.git/blob - src/generic/notebook.cpp
fixed bug #129464 (hopefully)
[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 BEGIN_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)
55 // EVT_IDLE(wxNotebook::OnIdle)
56 END_EVENT_TABLE()
57
58 IMPLEMENT_DYNAMIC_CLASS(wxNotebook, wxControl)
59 IMPLEMENT_DYNAMIC_CLASS(wxNotebookEvent, wxCommandEvent)
60
61 // ============================================================================
62 // implementation
63 // ============================================================================
64
65 // ----------------------------------------------------------------------------
66 // wxNotebook construction
67 // ----------------------------------------------------------------------------
68
69 // common part of all ctors
70 void wxNotebook::Init()
71 {
72 m_tabView = (wxNotebookTabView*) NULL;
73 m_pImageList = NULL;
74 m_nSelection = -1;
75 }
76
77 // default for dynamic class
78 wxNotebook::wxNotebook()
79 {
80 Init();
81 }
82
83 // the same arguments as for wxControl
84 wxNotebook::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
97 bool 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...
110 if (!wxWindow::Create(parent, id, pos, size, style|wxNO_BORDER, name))
111 return FALSE;
112
113 SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE));
114
115 SetTabView(new wxNotebookTabView(this));
116
117 return TRUE;
118 }
119
120 // dtor
121 wxNotebook::~wxNotebook()
122 {
123 delete m_tabView;
124 }
125
126 // ----------------------------------------------------------------------------
127 // wxNotebook accessors
128 // ----------------------------------------------------------------------------
129 int wxNotebook::GetPageCount() const
130 {
131 return m_aPages.Count();
132 }
133
134 int wxNotebook::GetRowCount() const
135 {
136 // TODO
137 return 0;
138 }
139
140 int wxNotebook::SetSelection(int nPage)
141 {
142 if (nPage == -1)
143 return 0;
144
145 wxASSERT( IS_VALID_PAGE(nPage) );
146
147 #if defined (__WIN16__)
148 m_tabView->SetTabSelection(nPage);
149 #else
150 wxNotebookPage* pPage = GetPage(nPage);
151
152 m_tabView->SetTabSelection((int) (long) pPage);
153 #endif
154 // TODO
155 return 0;
156 }
157
158 void 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
168 bool wxNotebook::SetPageText(int nPage, const wxString& strText)
169 {
170 wxASSERT( IS_VALID_PAGE(nPage) );
171 #if defined (__WIN16__)
172 m_tabView->SetTabText(nPage, strText);
173 Refresh();
174 return TRUE;
175 #else
176 wxNotebookPage* page = GetPage(nPage);
177 if (page)
178 {
179 m_tabView->SetTabText((int) (long) page, strText);
180 Refresh();
181 return TRUE;
182 }
183 #endif
184 return FALSE;
185 }
186
187 wxString wxNotebook::GetPageText(int nPage) const
188 {
189 wxASSERT( IS_VALID_PAGE(nPage) );
190
191 #if defined (__WIN16__)
192 return m_tabView->GetTabText(nPage);
193 #else
194 wxNotebookPage* page = ((wxNotebook*)this)->GetPage(nPage);
195 if (page)
196 return m_tabView->GetTabText((int) (long) page);
197 else
198 return wxEmptyString;
199 #endif
200 }
201
202 int wxNotebook::GetPageImage(int nPage) const
203 {
204 wxASSERT( IS_VALID_PAGE(nPage) );
205
206 // TODO
207 return 0;
208 }
209
210 bool wxNotebook::SetPageImage(int nPage, int nImage)
211 {
212 wxASSERT( IS_VALID_PAGE(nPage) );
213
214 // TODO
215 return FALSE;
216 }
217
218 void 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
229 bool 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);
240 #if defined (__WIN16__)
241 m_tabView->RemoveTab(nPage);
242 #else
243 m_tabView->RemoveTab((int) (long) pPage);
244 #endif
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;
257 #if defined (__WIN16__)
258 m_tabView->SetTabSelection(0, FALSE);
259 #else
260 m_tabView->SetTabSelection((int) (long) GetPage(0), FALSE);
261 #endif
262 if (m_nSelection != 0)
263 ChangePage(-1, 0);
264 }
265
266 RefreshLayout(FALSE);
267
268 return TRUE;
269 }
270
271 bool 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
281 bool 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);
289 #if defined (__WIN16__)
290 m_tabView->RemoveTab(nPage);
291 #else
292 m_tabView->RemoveTab((int) (long) pPage);
293 #endif
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
327 bool 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.
337 int 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
348 bool 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
363 bool 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
372 bool 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
381 // For 16 bit integers (tabs limited to 32768)
382 #if defined (__WIN16__)
383 m_tabView->AddTab(nPage, strText);
384 #else
385 m_tabView->AddTab((int) (long) pPage, strText);
386 #endif
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
396 #if defined (__WIN16__)
397 m_tabView->SetTabSelection(nPage, TRUE);
398 #else
399 m_tabView->SetTabSelection((int) (long) pPage, TRUE);
400 #endif
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)
419 void 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?
436 void 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
462 bool 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
480 m_tabView->LayoutTabs();
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
493 m_tabView->LayoutTabs();
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
516 void 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
529 void 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
538 void 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
559 void 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
565 bool wxNotebook::DoPhase(int /* nPhase */)
566 {
567 return TRUE;
568 }
569
570 void wxNotebook::Command(wxCommandEvent& WXUNUSED(event))
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
580 void 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
595 Refresh();
596
597 pPage->Show(TRUE);
598 pPage->Raise();
599 pPage->SetFocus();
600
601 m_nSelection = nSel;
602 }
603
604 void wxNotebook::OnMouseEvent(wxMouseEvent& event)
605 {
606 if (m_tabView)
607 m_tabView->OnEvent(event);
608 }
609
610 void wxNotebook::OnPaint(wxPaintEvent& WXUNUSED(event) )
611 {
612 wxPaintDC dc(this);
613 if (m_tabView)
614 m_tabView->Draw(dc);
615 }
616
617 wxRect 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
638 IMPLEMENT_CLASS(wxNotebookTabView, wxTabView)
639
640 wxNotebookTabView::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
649 wxNotebookTabView::~wxNotebookTabView(void)
650 {
651 }
652
653 // Called when a tab is activated
654 void wxNotebookTabView::OnTabActivate(int activateId, int deactivateId)
655 {
656 if (!m_notebook)
657 return;
658
659 // Because of name truncation!
660 #if defined(__BORLANDC__) && defined(__WIN16__)
661 wxNotebookEvent event(wxEVT_COMMAND_NB_PAGE_CHANGED, m_notebook->GetId());
662 #else
663 wxNotebookEvent event(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED, m_notebook->GetId());
664 #endif
665
666 #if defined (__WIN16__)
667 int activatePos = activateId;
668 int deactivatePos = deactivateId;
669 #else
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
678 #endif
679 event.SetEventObject(m_notebook);
680 event.SetSelection(activatePos);
681 event.SetOldSelection(deactivatePos);
682 m_notebook->GetEventHandler()->ProcessEvent(event);
683 }
684
685