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