]> git.saurik.com Git - wxWidgets.git/blob - src/generic/notebook.cpp
fix for a fatal bug in wxMGL's wxDir
[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_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::GetRowCount() const
132 {
133 // TODO
134 return 0;
135 }
136
137 int wxNotebook::SetSelection(int nPage)
138 {
139 if (nPage == -1)
140 return 0;
141
142 wxASSERT( IS_VALID_PAGE(nPage) );
143
144 #if defined (__WIN16__)
145 m_tabView->SetTabSelection(nPage);
146 #else
147 wxNotebookPage* pPage = GetPage(nPage);
148
149 m_tabView->SetTabSelection((int) (long) pPage);
150 #endif
151 // TODO
152 return 0;
153 }
154
155 #if 0
156 void wxNotebook::AdvanceSelection(bool bForward)
157 {
158 int nSel = GetSelection();
159 int nMax = GetPageCount() - 1;
160 if ( bForward )
161 SetSelection(nSel == nMax ? 0 : nSel + 1);
162 else
163 SetSelection(nSel == 0 ? nMax : nSel - 1);
164 }
165 #endif
166
167 bool wxNotebook::SetPageText(int nPage, const wxString& strText)
168 {
169 wxASSERT( IS_VALID_PAGE(nPage) );
170 #if defined (__WIN16__)
171 m_tabView->SetTabText(nPage, strText);
172 Refresh();
173 return TRUE;
174 #else
175 wxNotebookPage* page = GetPage(nPage);
176 if (page)
177 {
178 m_tabView->SetTabText((int) (long) page, strText);
179 Refresh();
180 return TRUE;
181 }
182 #endif
183 return FALSE;
184 }
185
186 wxString wxNotebook::GetPageText(int nPage) const
187 {
188 wxASSERT( IS_VALID_PAGE(nPage) );
189
190 #if defined (__WIN16__)
191 return m_tabView->GetTabText(nPage);
192 #else
193 wxNotebookPage* page = ((wxNotebook*)this)->GetPage(nPage);
194 if (page)
195 return m_tabView->GetTabText((int) (long) page);
196 else
197 return wxEmptyString;
198 #endif
199 }
200
201 int wxNotebook::GetPageImage(int nPage) const
202 {
203 wxASSERT( IS_VALID_PAGE(nPage) );
204
205 // TODO
206 return 0;
207 }
208
209 bool wxNotebook::SetPageImage(int nPage, int nImage)
210 {
211 wxASSERT( IS_VALID_PAGE(nPage) );
212
213 // TODO
214 return FALSE;
215 }
216
217 // set the size (the same for all pages)
218 void wxNotebook::SetPageSize(const wxSize& size)
219 {
220 // TODO
221 }
222
223 // set the padding between tabs (in pixels)
224 void wxNotebook::SetPadding(const wxSize& padding)
225 {
226 // TODO
227 }
228
229 // set the size of the tabs for wxNB_FIXEDWIDTH controls
230 void wxNotebook::SetTabSize(const wxSize& sz)
231 {
232 // TODO
233 }
234
235 // ----------------------------------------------------------------------------
236 // wxNotebook operations
237 // ----------------------------------------------------------------------------
238
239 // remove one page from the notebook and delete it
240 bool wxNotebook::DeletePage(int nPage)
241 {
242 wxCHECK( IS_VALID_PAGE(nPage), FALSE );
243
244 if (m_nSelection != -1)
245 {
246 m_pages[m_nSelection]->Show(FALSE);
247 m_pages[m_nSelection]->Lower();
248 }
249
250 wxNotebookPage* pPage = GetPage(nPage);
251 #if defined (__WIN16__)
252 m_tabView->RemoveTab(nPage);
253 #else
254 m_tabView->RemoveTab((int) (long) pPage);
255 #endif
256
257 m_pages.Remove(pPage);
258 delete pPage;
259
260 if (m_pages.GetCount() == 0)
261 {
262 m_nSelection = -1;
263 m_tabView->SetTabSelection(-1, FALSE);
264 }
265 else if (m_nSelection > -1)
266 {
267 m_nSelection = -1;
268 #if defined (__WIN16__)
269 m_tabView->SetTabSelection(0, FALSE);
270 #else
271 m_tabView->SetTabSelection((int) (long) GetPage(0), FALSE);
272 #endif
273 if (m_nSelection != 0)
274 ChangePage(-1, 0);
275 }
276
277 RefreshLayout(FALSE);
278
279 return TRUE;
280 }
281
282 bool wxNotebook::DeletePage(wxNotebookPage* page)
283 {
284 int pagePos = FindPagePosition(page);
285 if (pagePos > -1)
286 return DeletePage(pagePos);
287 else
288 return FALSE;
289 }
290
291 // remove one page from the notebook
292 bool wxNotebook::RemovePage(int nPage)
293 {
294 wxCHECK( IS_VALID_PAGE(nPage), FALSE );
295
296 m_pages[nPage]->Show(FALSE);
297 // m_pages[nPage]->Lower();
298
299 wxNotebookPage* pPage = GetPage(nPage);
300 #if defined (__WIN16__)
301 m_tabView->RemoveTab(nPage);
302 #else
303 m_tabView->RemoveTab((int) (long) pPage);
304 #endif
305
306 m_pages.Remove(pPage);
307
308 if (m_pages.GetCount() == 0)
309 {
310 m_nSelection = -1;
311 m_tabView->SetTabSelection(-1, TRUE);
312 }
313 else if (m_nSelection > -1)
314 {
315 // Only change the selection if the page we
316 // deleted was the selection.
317 if (nPage == m_nSelection)
318 {
319 m_nSelection = -1;
320 // Select the first tab. Generates a ChangePage.
321 m_tabView->SetTabSelection((int) (long) GetPage(0), TRUE);
322 }
323 else
324 {
325 // We must adjust which tab we think is selected.
326 // If greater than the page we deleted, it must be moved down
327 // a notch.
328 if (m_nSelection > nPage)
329 m_nSelection -- ;
330 }
331 }
332
333 RefreshLayout(FALSE);
334
335 return TRUE;
336 }
337
338 bool wxNotebook::RemovePage(wxNotebookPage* page)
339 {
340 int pagePos = FindPagePosition(page);
341 if (pagePos > -1)
342 return RemovePage(pagePos);
343 else
344 return FALSE;
345 }
346
347 // Find the position of the wxNotebookPage, -1 if not found.
348 int wxNotebook::FindPagePosition(wxNotebookPage* page) const
349 {
350 int nPageCount = GetPageCount();
351 int nPage;
352 for ( nPage = 0; nPage < nPageCount; nPage++ )
353 if (m_pages[nPage] == page)
354 return nPage;
355 return -1;
356 }
357
358 // remove all pages
359 bool wxNotebook::DeleteAllPages()
360 {
361 m_tabView->ClearTabs(TRUE);
362
363 int nPageCount = GetPageCount();
364 int nPage;
365 for ( nPage = 0; nPage < nPageCount; nPage++ )
366 delete m_pages[nPage];
367
368 m_pages.Clear();
369
370 return TRUE;
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_pages.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_pages.Count();
503 for ( unsigned int nPage = 0; nPage < nCount; nPage++ ) {
504 wxNotebookPage *pPage = m_pages[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_pages[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& WXUNUSED(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_pages[nOldSel]->Show(FALSE);
589 m_pages[nOldSel]->Lower();
590 }
591
592 wxNotebookPage *pPage = m_pages[nSel];
593
594 wxRect clientRect = GetAvailableClientSize();
595 pPage->SetSize(clientRect.x, clientRect.y, clientRect.width, clientRect.height);
596
597 Refresh();
598
599 pPage->Show(TRUE);
600 pPage->Raise();
601 pPage->SetFocus();
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 wxNotebookEvent event(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED, m_notebook->GetId());
662
663 #if defined (__WIN16__)
664 int activatePos = activateId;
665 int deactivatePos = deactivateId;
666 #else
667 // Translate from wxTabView's ids (which aren't position-dependent)
668 // to wxNotebook's (which are).
669 wxNotebookPage* pActive = (wxNotebookPage*) activateId;
670 wxNotebookPage* pDeactive = (wxNotebookPage*) deactivateId;
671
672 int activatePos = m_notebook->FindPagePosition(pActive);
673 int deactivatePos = m_notebook->FindPagePosition(pDeactive);
674
675 #endif
676 event.SetEventObject(m_notebook);
677 event.SetSelection(activatePos);
678 event.SetOldSelection(deactivatePos);
679 m_notebook->GetEventHandler()->ProcessEvent(event);
680 }
681
682