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