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