]> git.saurik.com Git - wxWidgets.git/blob - src/osx/carbon/notebmac.cpp
common implementation files
[wxWidgets.git] / src / osx / carbon / notebmac.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/mac/carbon/notebmac.cpp
3 // Purpose: implementation of wxNotebook
4 // Author: Stefan Csomor
5 // Modified by:
6 // Created: 1998-01-01
7 // RCS-ID: $Id$
8 // Copyright: (c) Stefan Csomor
9 // Licence: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
11
12 #include "wx/wxprec.h"
13
14 #if wxUSE_NOTEBOOK
15
16 #include "wx/notebook.h"
17
18 #ifndef WX_PRECOMP
19 #include "wx/string.h"
20 #include "wx/log.h"
21 #include "wx/app.h"
22 #include "wx/image.h"
23 #endif
24
25 #include "wx/string.h"
26 #include "wx/imaglist.h"
27 #include "wx/osx/uma.h"
28
29
30 // check that the page index is valid
31 #define IS_VALID_PAGE(nPage) ((nPage) < GetPageCount())
32
33 BEGIN_EVENT_TABLE(wxNotebook, wxBookCtrlBase)
34 EVT_NOTEBOOK_PAGE_CHANGED(wxID_ANY, wxNotebook::OnSelChange)
35
36 EVT_SIZE(wxNotebook::OnSize)
37 EVT_SET_FOCUS(wxNotebook::OnSetFocus)
38 EVT_NAVIGATION_KEY(wxNotebook::OnNavigationKey)
39 END_EVENT_TABLE()
40
41 IMPLEMENT_DYNAMIC_CLASS(wxNotebook, wxBookCtrlBase)
42
43
44 // common part of all ctors
45 void wxNotebook::Init()
46 {
47 m_nSelection = -1;
48 }
49
50 // default for dynamic class
51 wxNotebook::wxNotebook()
52 {
53 Init();
54 }
55
56 // the same arguments as for wxControl
57 wxNotebook::wxNotebook( wxWindow *parent,
58 wxWindowID id,
59 const wxPoint& pos,
60 const wxSize& size,
61 long style,
62 const wxString& name )
63 {
64 Init();
65
66 Create( parent, id, pos, size, style, name );
67 }
68
69 bool wxNotebook::Create( wxWindow *parent,
70 wxWindowID id,
71 const wxPoint& pos,
72 const wxSize& size,
73 long style,
74 const wxString& name )
75 {
76 m_macIsUserPane = false ;
77
78 if (! (style & wxBK_ALIGN_MASK))
79 style |= wxBK_TOP;
80
81 if ( !wxNotebookBase::Create( parent, id, pos, size, style, name ) )
82 return false;
83
84 Rect bounds = wxMacGetBoundsForControl( this, pos, size );
85
86 if ( bounds.right <= bounds.left )
87 bounds.right = bounds.left + 100;
88 if ( bounds.bottom <= bounds.top )
89 bounds.bottom = bounds.top + 100;
90
91 UInt16 tabstyle = kControlTabDirectionNorth;
92 if ( HasFlag(wxBK_LEFT) )
93 tabstyle = kControlTabDirectionWest;
94 else if ( HasFlag( wxBK_RIGHT ) )
95 tabstyle = kControlTabDirectionEast;
96 else if ( HasFlag( wxBK_BOTTOM ) )
97 tabstyle = kControlTabDirectionSouth;
98
99 ControlTabSize tabsize;
100 switch (GetWindowVariant())
101 {
102 case wxWINDOW_VARIANT_MINI:
103 tabsize = 3 ;
104 break;
105
106 case wxWINDOW_VARIANT_SMALL:
107 tabsize = kControlTabSizeSmall;
108 break;
109
110 default:
111 tabsize = kControlTabSizeLarge;
112 break;
113 }
114
115 m_peer = new wxMacControl( this );
116 OSStatus err = CreateTabsControl(
117 MAC_WXHWND(parent->MacGetTopLevelWindowRef()), &bounds,
118 tabsize, tabstyle, 0, NULL, m_peer->GetControlRefAddr() );
119 verify_noerr( err );
120
121 MacPostControlCreate( pos, size );
122
123 return true ;
124 }
125
126 // dtor
127 wxNotebook::~wxNotebook()
128 {
129 }
130
131 // ----------------------------------------------------------------------------
132 // wxNotebook accessors
133 // ----------------------------------------------------------------------------
134
135 void wxNotebook::SetPadding(const wxSize& WXUNUSED(padding))
136 {
137 // unsupported by OS
138 }
139
140 void wxNotebook::SetTabSize(const wxSize& WXUNUSED(sz))
141 {
142 // unsupported by OS
143 }
144
145 void wxNotebook::SetPageSize(const wxSize& size)
146 {
147 SetSize( CalcSizeFromPage( size ) );
148 }
149
150 wxSize wxNotebook::CalcSizeFromPage(const wxSize& sizePage) const
151 {
152 return DoGetSizeFromClientSize( sizePage );
153 }
154
155 int wxNotebook::DoSetSelection(size_t nPage, int flags)
156 {
157 wxCHECK_MSG( IS_VALID_PAGE(nPage), wxNOT_FOUND, wxT("DoSetSelection: invalid notebook page") );
158
159 if ( m_nSelection == wxNOT_FOUND || nPage != (size_t)m_nSelection )
160 {
161 if ( flags & SetSelection_SendEvent )
162 {
163 if ( !SendPageChangingEvent(nPage) )
164 {
165 // vetoed by program
166 return m_nSelection;
167 }
168 //else: program allows the page change
169
170 SendPageChangedEvent(m_nSelection, nPage);
171 }
172
173 ChangePage(m_nSelection, nPage);
174 }
175 //else: no change
176
177 return m_nSelection;
178 }
179
180 bool wxNotebook::SetPageText(size_t nPage, const wxString& strText)
181 {
182 wxCHECK_MSG( IS_VALID_PAGE(nPage), false, wxT("SetPageText: invalid notebook page") );
183
184 wxNotebookPage *page = m_pages[nPage];
185 page->SetLabel(wxStripMenuCodes(strText));
186 MacSetupTabs();
187
188 return true;
189 }
190
191 wxString wxNotebook::GetPageText(size_t nPage) const
192 {
193 wxCHECK_MSG( IS_VALID_PAGE(nPage), wxEmptyString, wxT("GetPageText: invalid notebook page") );
194
195 wxNotebookPage *page = m_pages[nPage];
196
197 return page->GetLabel();
198 }
199
200 int wxNotebook::GetPageImage(size_t nPage) const
201 {
202 wxCHECK_MSG( IS_VALID_PAGE(nPage), wxNOT_FOUND, wxT("GetPageImage: invalid notebook page") );
203
204 return m_images[nPage];
205 }
206
207 bool wxNotebook::SetPageImage(size_t nPage, int nImage)
208 {
209 wxCHECK_MSG( IS_VALID_PAGE(nPage), false,
210 wxT("SetPageImage: invalid notebook page") );
211 wxCHECK_MSG( m_imageList && nImage < m_imageList->GetImageCount(), false,
212 wxT("SetPageImage: invalid image index") );
213
214 if ( nImage != m_images[nPage] )
215 {
216 // if the item didn't have an icon before or, on the contrary, did have
217 // it but has lost it now, its size will change - but if the icon just
218 // changes, it won't
219 m_images[nPage] = nImage;
220
221 MacSetupTabs() ;
222 }
223
224 return true;
225 }
226
227 // ----------------------------------------------------------------------------
228 // wxNotebook operations
229 // ----------------------------------------------------------------------------
230
231 // remove one page from the notebook, without deleting the window
232 wxNotebookPage* wxNotebook::DoRemovePage(size_t nPage)
233 {
234 wxCHECK_MSG( IS_VALID_PAGE(nPage), NULL,
235 wxT("DoRemovePage: invalid notebook page") );
236
237 wxNotebookPage* page = m_pages[nPage] ;
238 m_pages.RemoveAt(nPage);
239
240 MacSetupTabs();
241
242 if (m_nSelection >= (int)GetPageCount())
243 m_nSelection = GetPageCount() - 1;
244
245 if (m_nSelection >= 0)
246 m_pages[m_nSelection]->Show(true);
247
248 InvalidateBestSize();
249
250 return page;
251 }
252
253 // remove all pages
254 bool wxNotebook::DeleteAllPages()
255 {
256 WX_CLEAR_ARRAY(m_pages) ;
257 MacSetupTabs();
258 m_nSelection = -1 ;
259 InvalidateBestSize();
260
261 return true;
262 }
263
264 // same as AddPage() but does it at given position
265 bool wxNotebook::InsertPage(size_t nPage,
266 wxNotebookPage *pPage,
267 const wxString& strText,
268 bool bSelect,
269 int imageId )
270 {
271 if ( !wxNotebookBase::InsertPage( nPage, pPage, strText, bSelect, imageId ) )
272 return false;
273
274 wxASSERT_MSG( pPage->GetParent() == this, wxT("notebook pages must have notebook as parent") );
275
276 // don't show pages by default (we'll need to adjust their size first)
277 pPage->Show( false ) ;
278
279 pPage->SetLabel( wxStripMenuCodes(strText) );
280
281 m_images.Insert( imageId, nPage );
282
283 MacSetupTabs();
284
285 wxRect rect = GetPageRect() ;
286 pPage->SetSize( rect );
287 if ( pPage->GetAutoLayout() )
288 pPage->Layout();
289
290 // now deal with the selection
291 // ---------------------------
292
293 // if the inserted page is before the selected one, we must update the
294 // index of the selected page
295
296 if ( int(nPage) <= m_nSelection )
297 {
298 m_nSelection++;
299
300 // while this still is the same page showing, we need to update the tabs
301 m_peer->SetValue( m_nSelection + 1 ) ;
302 }
303
304 // some page should be selected: either this one or the first one if there
305 // is still no selection
306 int selNew = -1;
307 if ( bSelect )
308 selNew = nPage;
309 else if ( m_nSelection == -1 )
310 selNew = 0;
311
312 if ( selNew != -1 )
313 SetSelection( selNew );
314
315 InvalidateBestSize();
316
317 return true;
318 }
319
320 int wxNotebook::HitTest(const wxPoint& pt, long * flags) const
321 {
322 int resultV = wxNOT_FOUND;
323
324 const int countPages = GetPageCount();
325
326 // we have to convert from Client to Window relative coordinates
327 wxPoint adjustedPt = pt + GetClientAreaOrigin();
328 // and now to HIView native ones
329 adjustedPt.x -= MacGetLeftBorderSize() ;
330 adjustedPt.y -= MacGetTopBorderSize() ;
331
332 HIPoint hipoint= { adjustedPt.x , adjustedPt.y } ;
333 HIViewPartCode outPart = 0 ;
334 OSStatus err = HIViewGetPartHit( m_peer->GetControlRef(), &hipoint, &outPart );
335
336 int max = m_peer->GetMaximum() ;
337 if ( outPart == 0 && max > 0 )
338 {
339 // this is a hack, as unfortunately a hit on an already selected tab returns 0,
340 // so we have to go some extra miles to make sure we select something different
341 // and try again ..
342 int val = m_peer->GetValue() ;
343 int maxval = max ;
344 if ( max == 1 )
345 {
346 m_peer->SetMaximum( 2 ) ;
347 maxval = 2 ;
348 }
349
350 if ( val == 1 )
351 m_peer->SetValue( maxval ) ;
352 else
353 m_peer->SetValue( 1 ) ;
354
355 err = HIViewGetPartHit( m_peer->GetControlRef(), &hipoint, &outPart );
356
357 m_peer->SetValue( val ) ;
358 if ( max == 1 )
359 m_peer->SetMaximum( 1 ) ;
360 }
361
362 if ( outPart >= 1 && outPart <= countPages )
363 resultV = outPart - 1 ;
364
365 if (flags != NULL)
366 {
367 *flags = 0;
368
369 // we cannot differentiate better
370 if (resultV >= 0)
371 *flags |= wxBK_HITTEST_ONLABEL;
372 else
373 *flags |= wxBK_HITTEST_NOWHERE;
374 }
375
376 return resultV;
377 }
378
379 // Added by Mark Newsam
380 // When a page is added or deleted to the notebook this function updates
381 // information held in the control so that it matches the order
382 // the user would expect.
383 //
384 void wxNotebook::MacSetupTabs()
385 {
386 m_peer->SetMaximum( GetPageCount() ) ;
387
388 wxNotebookPage *page;
389 ControlTabInfoRecV1 info;
390
391 const size_t countPages = GetPageCount();
392 for (size_t ii = 0; ii < countPages; ii++)
393 {
394 page = m_pages[ii];
395 info.version = kControlTabInfoVersionOne;
396 info.iconSuiteID = 0;
397 wxCFStringRef cflabel( page->GetLabel(), GetFont().GetEncoding() ) ;
398 info.name = cflabel ;
399 m_peer->SetData<ControlTabInfoRecV1>( ii + 1, kControlTabInfoTag, &info ) ;
400
401 if ( GetImageList() && GetPageImage(ii) >= 0 )
402 {
403 const wxBitmap bmap = GetImageList()->GetBitmap( GetPageImage( ii ) ) ;
404 if ( bmap.Ok() )
405 {
406 ControlButtonContentInfo info ;
407
408 wxMacCreateBitmapButton( &info, bmap ) ;
409
410 OSStatus err = m_peer->SetData<ControlButtonContentInfo>( ii + 1, kControlTabImageContentTag, &info );
411 if ( err != noErr )
412 {
413 wxFAIL_MSG("Error when setting icon on tab");
414 }
415
416 wxMacReleaseBitmapButton( &info ) ;
417 }
418 }
419
420 m_peer->SetTabEnabled( ii + 1, true ) ;
421 }
422
423 Refresh();
424 }
425
426 wxRect wxNotebook::GetPageRect() const
427 {
428 wxSize size = GetClientSize() ;
429
430 return wxRect( 0 , 0 , size.x , size.y ) ;
431 }
432
433 // ----------------------------------------------------------------------------
434 // wxNotebook callbacks
435 // ----------------------------------------------------------------------------
436
437 // @@@ OnSize() is used for setting the font when it's called for the first
438 // time because doing it in ::Create() doesn't work (for unknown reasons)
439 void wxNotebook::OnSize(wxSizeEvent& event)
440 {
441 unsigned int nCount = m_pages.Count();
442 wxRect rect = GetPageRect() ;
443
444 for ( unsigned int nPage = 0; nPage < nCount; nPage++ )
445 {
446 wxNotebookPage *pPage = m_pages[nPage];
447 pPage->SetSize(rect);
448 if ( pPage->GetAutoLayout() )
449 pPage->Layout();
450 }
451
452 // Processing continues to next OnSize
453 event.Skip();
454 }
455
456 void wxNotebook::OnSelChange(wxBookCtrlEvent& event)
457 {
458 // is it our tab control?
459 if ( event.GetEventObject() == this )
460 ChangePage(event.GetOldSelection(), event.GetSelection());
461
462 // we want to give others a chance to process this message as well
463 event.Skip();
464 }
465
466 void wxNotebook::OnSetFocus(wxFocusEvent& event)
467 {
468 // set focus to the currently selected page if any
469 if ( m_nSelection != -1 )
470 m_pages[m_nSelection]->SetFocus();
471
472 event.Skip();
473 }
474
475 void wxNotebook::OnNavigationKey(wxNavigationKeyEvent& event)
476 {
477 if ( event.IsWindowChange() )
478 {
479 // change pages
480 AdvanceSelection( event.GetDirection() );
481 }
482 else
483 {
484 // we get this event in 2 cases
485 //
486 // a) one of our pages might have generated it because the user TABbed
487 // out from it in which case we should propagate the event upwards and
488 // our parent will take care of setting the focus to prev/next sibling
489 //
490 // or
491 //
492 // b) the parent panel wants to give the focus to us so that we
493 // forward it to our selected page. We can't deal with this in
494 // OnSetFocus() because we don't know which direction the focus came
495 // from in this case and so can't choose between setting the focus to
496 // first or last panel child
497 wxWindow *parent = GetParent();
498
499 // the cast is here to fix a GCC ICE
500 if ( ((wxWindow*)event.GetEventObject()) == parent )
501 {
502 // no, it doesn't come from child, case (b): forward to a page
503 if ( m_nSelection != -1 )
504 {
505 // so that the page knows that the event comes from it's parent
506 // and is being propagated downwards
507 event.SetEventObject( this );
508
509 wxWindow *page = m_pages[m_nSelection];
510 if ( !page->HandleWindowEvent( event ) )
511 {
512 page->SetFocus();
513 }
514 //else: page manages focus inside it itself
515 }
516 else
517 {
518 // we have no pages - still have to give focus to _something_
519 SetFocus();
520 }
521 }
522 else
523 {
524 // it comes from our child, case (a), pass to the parent
525 if ( parent )
526 {
527 event.SetCurrentFocus( this );
528 parent->HandleWindowEvent( event );
529 }
530 }
531 }
532 }
533
534 // ----------------------------------------------------------------------------
535 // wxNotebook base class virtuals
536 // ----------------------------------------------------------------------------
537
538 #if wxUSE_CONSTRAINTS
539
540 // override these 2 functions to do nothing: everything is done in OnSize
541
542 void wxNotebook::SetConstraintSizes(bool WXUNUSED(recurse))
543 {
544 // don't set the sizes of the pages - their correct size is not yet known
545 wxControl::SetConstraintSizes( false );
546 }
547
548 bool wxNotebook::DoPhase(int WXUNUSED(nPhase))
549 {
550 return true;
551 }
552
553 #endif // wxUSE_CONSTRAINTS
554
555 void wxNotebook::Command(wxCommandEvent& WXUNUSED(event))
556 {
557 wxFAIL_MSG(wxT("wxNotebook::Command not implemented"));
558 }
559
560 // ----------------------------------------------------------------------------
561 // wxNotebook helper functions
562 // ----------------------------------------------------------------------------
563
564 // hide the currently active panel and show the new one
565 void wxNotebook::ChangePage(int nOldSel, int nSel)
566 {
567 if (nOldSel == nSel)
568 return;
569
570 if ( nOldSel != -1 )
571 m_pages[nOldSel]->Show( false );
572
573 if ( nSel != -1 )
574 {
575 wxNotebookPage *pPage = m_pages[nSel];
576 pPage->Show( true );
577 pPage->SetFocus();
578 }
579
580 m_nSelection = nSel;
581 m_peer->SetValue( m_nSelection + 1 ) ;
582 }
583
584 wxInt32 wxNotebook::MacControlHit(WXEVENTHANDLERREF WXUNUSED(handler) , WXEVENTREF WXUNUSED(event) )
585 {
586 OSStatus status = eventNotHandledErr ;
587
588 SInt32 newSel = m_peer->GetValue() - 1 ;
589 if ( newSel != m_nSelection )
590 {
591 wxBookCtrlEvent changing(
592 wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING, m_windowId,
593 newSel , m_nSelection );
594 changing.SetEventObject( this );
595 HandleWindowEvent( changing );
596
597 if ( changing.IsAllowed() )
598 {
599 wxBookCtrlEvent event(
600 wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED, m_windowId,
601 newSel, m_nSelection );
602 event.SetEventObject( this );
603 HandleWindowEvent( event );
604 }
605 else
606 {
607 m_peer->SetValue( m_nSelection + 1 ) ;
608 }
609
610 status = noErr ;
611 }
612
613 return (wxInt32)status ;
614 }
615
616 #endif