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