]> git.saurik.com Git - wxWidgets.git/blame - src/osx/carbon/notebmac.cpp
fixing #9910
[wxWidgets.git] / src / osx / carbon / notebmac.cpp
CommitLineData
489468fe
SC
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"
1f0c8f31 27#include "wx/osx/uma.h"
489468fe
SC
28
29
30// check that the page index is valid
31#define IS_VALID_PAGE(nPage) ((nPage) < GetPageCount())
32
489468fe
SC
33BEGIN_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)
39END_EVENT_TABLE()
40
41IMPLEMENT_DYNAMIC_CLASS(wxNotebook, wxBookCtrlBase)
489468fe
SC
42
43
44// common part of all ctors
45void wxNotebook::Init()
46{
47 m_nSelection = -1;
48}
49
50// default for dynamic class
51wxNotebook::wxNotebook()
52{
53 Init();
54}
55
56// the same arguments as for wxControl
57wxNotebook::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
69bool 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
127wxNotebook::~wxNotebook()
128{
129}
130
131// ----------------------------------------------------------------------------
132// wxNotebook accessors
133// ----------------------------------------------------------------------------
134
135void wxNotebook::SetPadding(const wxSize& WXUNUSED(padding))
136{
137 // unsupported by OS
138}
139
140void wxNotebook::SetTabSize(const wxSize& WXUNUSED(sz))
141{
142 // unsupported by OS
143}
144
145void wxNotebook::SetPageSize(const wxSize& size)
146{
147 SetSize( CalcSizeFromPage( size ) );
148}
149
150wxSize wxNotebook::CalcSizeFromPage(const wxSize& sizePage) const
151{
152 return DoGetSizeFromClientSize( sizePage );
153}
154
155int 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
180bool 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
191wxString 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
200int 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
207bool 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
232wxNotebookPage* 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
254bool 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
265bool 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
320int 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//
384void 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
426wxRect 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)
439void 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
3e97a905 456void wxNotebook::OnSelChange(wxBookCtrlEvent& event)
489468fe
SC
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
466void 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
475void 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
542void 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
548bool wxNotebook::DoPhase(int WXUNUSED(nPhase))
549{
550 return true;
551}
552
553#endif // wxUSE_CONSTRAINTS
554
555void 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
565void 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
584wxInt32 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 {
3e97a905 591 wxBookCtrlEvent changing(
489468fe
SC
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 {
3e97a905 599 wxBookCtrlEvent event(
489468fe
SC
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