]> git.saurik.com Git - wxWidgets.git/blame - src/generic/notebook.cpp
tree ctrl sorting shouldn't crash when items don't have data
[wxWidgets.git] / src / generic / notebook.cpp
CommitLineData
1e6d9499
JS
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>
793f619f
JS
32#include <wx/settings.h>
33#include <wx/generic/imaglist.h>
1e6d9499
JS
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
49BEGIN_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)
f60d0f94 56// EVT_IDLE(wxNotebook::OnIdle)
1e6d9499
JS
57END_EVENT_TABLE()
58
59IMPLEMENT_DYNAMIC_CLASS(wxNotebook, wxControl)
60IMPLEMENT_DYNAMIC_CLASS(wxNotebookEvent, wxCommandEvent)
61#endif
62
63// ============================================================================
64// implementation
65// ============================================================================
66
67// ----------------------------------------------------------------------------
68// wxNotebook construction
69// ----------------------------------------------------------------------------
70
71// common part of all ctors
72void wxNotebook::Init()
73{
74 m_tabView = (wxNotebookTabView*) NULL;
75 m_pImageList = NULL;
76 m_nSelection = -1;
77}
78
79// default for dynamic class
80wxNotebook::wxNotebook()
81{
82 Init();
83}
84
85// the same arguments as for wxControl
86wxNotebook::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
99bool 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...
f60d0f94 112 if (!wxWindow::Create(parent, id, pos, size, style|wxNO_BORDER, name))
1e6d9499
JS
113 return FALSE;
114
f60d0f94
JS
115 SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE));
116
1e6d9499
JS
117 SetTabView(new wxNotebookTabView(this));
118
119 return TRUE;
120}
121
122// dtor
123wxNotebook::~wxNotebook()
124{
125 delete m_tabView;
126}
127
128// ----------------------------------------------------------------------------
129// wxNotebook accessors
130// ----------------------------------------------------------------------------
131int wxNotebook::GetPageCount() const
132{
133 return m_aPages.Count();
134}
135
136int wxNotebook::GetRowCount() const
137{
138 // TODO
139 return 0;
140}
141
142int wxNotebook::SetSelection(int nPage)
143{
144 if (nPage == -1)
145 return 0;
146
147 wxASSERT( IS_VALID_PAGE(nPage) );
148
aeab10d0
JS
149#if defined (__WIN16__)
150 m_tabView->SetTabSelection(nPage);
151#else
1e6d9499
JS
152 wxNotebookPage* pPage = GetPage(nPage);
153
154 m_tabView->SetTabSelection((int) (long) pPage);
aeab10d0 155#endif
1e6d9499
JS
156 // TODO
157 return 0;
158}
159
160void wxNotebook::AdvanceSelection(bool bForward)
161{
162 int nSel = GetSelection();
163 int nMax = GetPageCount() - 1;
164 if ( bForward )
165 SetSelection(nSel == nMax ? 0 : nSel + 1);
166 else
167 SetSelection(nSel == 0 ? nMax : nSel - 1);
168}
169
170bool wxNotebook::SetPageText(int nPage, const wxString& strText)
171{
172 wxASSERT( IS_VALID_PAGE(nPage) );
aeab10d0
JS
173#if defined (__WIN16__)
174 m_tabView->SetTabText(nPage, strText);
175 Refresh();
176 return TRUE;
177#else
1e6d9499
JS
178 wxNotebookPage* page = GetPage(nPage);
179 if (page)
180 {
181 m_tabView->SetTabText((int) (long) page, strText);
182 Refresh();
183 return TRUE;
184 }
aeab10d0 185#endif
1e6d9499
JS
186 return FALSE;
187}
188
189wxString wxNotebook::GetPageText(int nPage) const
190{
191 wxASSERT( IS_VALID_PAGE(nPage) );
192
aeab10d0
JS
193#if defined (__WIN16__)
194 return m_tabView->GetTabText(nPage);
195#else
1e6d9499
JS
196 wxNotebookPage* page = ((wxNotebook*)this)->GetPage(nPage);
197 if (page)
198 return m_tabView->GetTabText((int) (long) page);
199 else
200 return wxEmptyString;
aeab10d0 201#endif
1e6d9499
JS
202}
203
204int wxNotebook::GetPageImage(int nPage) const
205{
206 wxASSERT( IS_VALID_PAGE(nPage) );
207
208 // TODO
209 return 0;
210}
211
212bool wxNotebook::SetPageImage(int nPage, int nImage)
213{
214 wxASSERT( IS_VALID_PAGE(nPage) );
215
216 // TODO
217 return FALSE;
218}
219
220void wxNotebook::SetImageList(wxImageList* imageList)
221{
222 m_pImageList = imageList;
223 // TODO
224}
225
226// ----------------------------------------------------------------------------
227// wxNotebook operations
228// ----------------------------------------------------------------------------
229
230// remove one page from the notebook and delete it
231bool wxNotebook::DeletePage(int nPage)
232{
233 wxCHECK( IS_VALID_PAGE(nPage), FALSE );
234
235 if (m_nSelection != -1)
236 {
237 m_aPages[m_nSelection]->Show(FALSE);
238 m_aPages[m_nSelection]->Lower();
239 }
240
241 wxNotebookPage* pPage = GetPage(nPage);
aeab10d0
JS
242#if defined (__WIN16__)
243 m_tabView->RemoveTab(nPage);
244#else
1e6d9499 245 m_tabView->RemoveTab((int) (long) pPage);
aeab10d0 246#endif
1e6d9499
JS
247
248 delete m_aPages[nPage];
249 m_aPages.Remove(nPage);
250
251 if (m_aPages.GetCount() == 0)
252 {
253 m_nSelection = -1;
254 m_tabView->SetTabSelection(-1, FALSE);
255 }
256 else if (m_nSelection > -1)
257 {
258 m_nSelection = -1;
aeab10d0
JS
259#if defined (__WIN16__)
260 m_tabView->SetTabSelection(0, FALSE);
261#else
1e6d9499 262 m_tabView->SetTabSelection((int) (long) GetPage(0), FALSE);
aeab10d0 263#endif
1e6d9499
JS
264 if (m_nSelection != 0)
265 ChangePage(-1, 0);
266 }
267
268 RefreshLayout(FALSE);
269
270 return TRUE;
271}
272
273bool wxNotebook::DeletePage(wxNotebookPage* page)
274{
275 int pagePos = FindPagePosition(page);
276 if (pagePos > -1)
277 return DeletePage(pagePos);
278 else
279 return FALSE;
280}
281
282// remove one page from the notebook
283bool wxNotebook::RemovePage(int nPage)
284{
285 wxCHECK( IS_VALID_PAGE(nPage), FALSE );
286
287 m_aPages[nPage]->Show(FALSE);
288 // m_aPages[nPage]->Lower();
289
290 wxNotebookPage* pPage = GetPage(nPage);
aeab10d0
JS
291#if defined (__WIN16__)
292 m_tabView->RemoveTab(nPage);
293#else
1e6d9499 294 m_tabView->RemoveTab((int) (long) pPage);
aeab10d0 295#endif
1e6d9499
JS
296
297 m_aPages.Remove(nPage);
298
299 if (m_aPages.GetCount() == 0)
300 {
301 m_nSelection = -1;
302 m_tabView->SetTabSelection(-1, TRUE);
303 }
304 else if (m_nSelection > -1)
305 {
306 // Only change the selection if the page we
307 // deleted was the selection.
308 if (nPage == m_nSelection)
309 {
310 m_nSelection = -1;
311 // Select the first tab. Generates a ChangePage.
312 m_tabView->SetTabSelection((int) (long) GetPage(0), TRUE);
313 }
314 else
315 {
316 // We must adjust which tab we think is selected.
317 // If greater than the page we deleted, it must be moved down
318 // a notch.
319 if (m_nSelection > nPage)
320 m_nSelection -- ;
321 }
322 }
323
324 RefreshLayout(FALSE);
325
326 return TRUE;
327}
328
329bool wxNotebook::RemovePage(wxNotebookPage* page)
330{
331 int pagePos = FindPagePosition(page);
332 if (pagePos > -1)
333 return RemovePage(pagePos);
334 else
335 return FALSE;
336}
337
338// Find the position of the wxNotebookPage, -1 if not found.
339int wxNotebook::FindPagePosition(wxNotebookPage* page) const
340{
341 int nPageCount = GetPageCount();
342 int nPage;
343 for ( nPage = 0; nPage < nPageCount; nPage++ )
344 if (m_aPages[nPage] == page)
345 return nPage;
346 return -1;
347}
348
349// remove all pages
350bool wxNotebook::DeleteAllPages()
351{
352 m_tabView->ClearTabs(TRUE);
353
354 int nPageCount = GetPageCount();
355 int nPage;
356 for ( nPage = 0; nPage < nPageCount; nPage++ )
357 delete m_aPages[nPage];
358
359 m_aPages.Clear();
360
361 return TRUE;
362}
363
364// add a page to the notebook
365bool wxNotebook::AddPage(wxNotebookPage *pPage,
366 const wxString& strText,
367 bool bSelect,
368 int imageId)
369{
370 return InsertPage(GetPageCount(), pPage, strText, bSelect, imageId);
371}
372
373// same as AddPage() but does it at given position
374bool 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
aeab10d0
JS
383// For 16 bit integers (tabs limited to 32768)
384#if defined (__WIN16__)
385 m_tabView->AddTab(nPage, strText);
386#else
1e6d9499 387 m_tabView->AddTab((int) (long) pPage, strText);
aeab10d0 388#endif
1e6d9499
JS
389 if (!bSelect)
390 pPage->Show(FALSE);
391
392 // save the pointer to the page
393 m_aPages.Insert(pPage, nPage);
394
395 if (bSelect)
396 {
397 // This will cause ChangePage to be called, via OnSelPage
aeab10d0
JS
398#if defined (__WIN16__)
399 m_tabView->SetTabSelection(nPage, TRUE);
400#else
1e6d9499 401 m_tabView->SetTabSelection((int) (long) pPage, TRUE);
aeab10d0 402#endif
1e6d9499
JS
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)
421void 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?
438void 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
464bool 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->Layout();
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->Layout();
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_aPages.Count();
503 for ( unsigned int nPage = 0; nPage < nCount; nPage++ ) {
504 wxNotebookPage *pPage = m_aPages[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
518void 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
531void wxNotebook::OnSetFocus(wxFocusEvent& event)
532{
533 // set focus to the currently selected page if any
534 if ( m_nSelection != -1 )
535 m_aPages[m_nSelection]->SetFocus();
536
537 event.Skip();
538}
539
540void 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
561void 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
567bool wxNotebook::DoPhase(int /* nPhase */)
568{
569 return TRUE;
570}
571
572void wxNotebook::Command(wxCommandEvent& 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
582void 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_aPages[nOldSel]->Show(FALSE);
589 m_aPages[nOldSel]->Lower();
590 }
591
592 wxNotebookPage *pPage = m_aPages[nSel];
593
594 wxRect clientRect = GetAvailableClientSize();
595 pPage->SetSize(clientRect.x, clientRect.y, clientRect.width, clientRect.height);
596
597 pPage->Show(TRUE);
598 pPage->Raise();
599 pPage->SetFocus();
600
601 Refresh();
602
603 m_nSelection = nSel;
604}
605
606void wxNotebook::OnMouseEvent(wxMouseEvent& event)
607{
608 if (m_tabView)
609 m_tabView->OnEvent(event);
610}
611
612void wxNotebook::OnPaint(wxPaintEvent& WXUNUSED(event) )
613{
614 wxPaintDC dc(this);
615 if (m_tabView)
616 m_tabView->Draw(dc);
617}
618
619wxRect 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
640IMPLEMENT_CLASS(wxNotebookTabView, wxTabView)
641
642wxNotebookTabView::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
651wxNotebookTabView::~wxNotebookTabView(void)
652{
653}
654
655// Called when a tab is activated
656void wxNotebookTabView::OnTabActivate(int activateId, int deactivateId)
657{
658 if (!m_notebook)
659 return;
660
62448488
JS
661// Because of name truncation!
662#if defined(__BORLANDC__) && defined(__WIN16__)
663 wxNotebookEvent event(wxEVT_COMMAND_NB_PAGE_CHANGED, m_notebook->GetId());
664#else
1e6d9499 665 wxNotebookEvent event(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED, m_notebook->GetId());
62448488 666#endif
1e6d9499 667
aeab10d0
JS
668#if defined (__WIN16__)
669 int activatePos = activateId;
670 int deactivatePos = deactivateId;
671#else
1e6d9499
JS
672 // Translate from wxTabView's ids (which aren't position-dependent)
673 // to wxNotebook's (which are).
674 wxNotebookPage* pActive = (wxNotebookPage*) activateId;
675 wxNotebookPage* pDeactive = (wxNotebookPage*) deactivateId;
676
677 int activatePos = m_notebook->FindPagePosition(pActive);
678 int deactivatePos = m_notebook->FindPagePosition(pDeactive);
679
aeab10d0 680#endif
1e6d9499
JS
681 event.SetEventObject(m_notebook);
682 event.SetSelection(activatePos);
683 event.SetOldSelection(deactivatePos);
684 m_notebook->GetEventHandler()->ProcessEvent(event);
685}
686
687