1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/osx/carbon/window.cpp
3 // Purpose: wxWindowMac
4 // Author: Stefan Csomor
8 // Copyright: (c) Stefan Csomor
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 #include "wx/wxprec.h"
14 #include "wx/window.h"
23 #include "wx/dcclient.h"
24 #include "wx/button.h"
26 #include "wx/dialog.h"
27 #include "wx/settings.h"
28 #include "wx/msgdlg.h"
29 #include "wx/scrolbar.h"
30 #include "wx/statbox.h"
31 #include "wx/textctrl.h"
32 #include "wx/toolbar.h"
33 #include "wx/layout.h"
34 #include "wx/statusbr.h"
35 #include "wx/menuitem.h"
36 #include "wx/treectrl.h"
37 #include "wx/listctrl.h"
40 #include "wx/tooltip.h"
41 #include "wx/spinctrl.h"
42 #include "wx/geometry.h"
45 #include "wx/listctrl.h"
49 #include "wx/treectrl.h"
57 #include "wx/popupwin.h"
60 #if wxUSE_DRAG_AND_DROP
65 #include "wx/osx/uma.h"
67 #include "wx/osx/private.h"
69 #include <Carbon/Carbon.h>
72 #define MAC_SCROLLBAR_SIZE 15
73 #define MAC_SMALL_SCROLLBAR_SIZE 11
77 #define wxMAC_DEBUG_REDRAW 0
78 #ifndef wxMAC_DEBUG_REDRAW
79 #define wxMAC_DEBUG_REDRAW 0
82 // ---------------------------------------------------------------------------
84 // ---------------------------------------------------------------------------
86 static const EventTypeSpec eventList
[] =
88 { kEventClassCommand
, kEventProcessCommand
} ,
89 { kEventClassCommand
, kEventCommandUpdateStatus
} ,
91 { kEventClassControl
, kEventControlGetClickActivation
} ,
92 { kEventClassControl
, kEventControlHit
} ,
94 { kEventClassTextInput
, kEventTextInputUnicodeForKeyEvent
} ,
95 { kEventClassTextInput
, kEventTextInputUpdateActiveInputArea
} ,
97 { kEventClassControl
, kEventControlDraw
} ,
99 { kEventClassControl
, kEventControlVisibilityChanged
} ,
100 { kEventClassControl
, kEventControlEnabledStateChanged
} ,
101 { kEventClassControl
, kEventControlHiliteChanged
} ,
103 { kEventClassControl
, kEventControlActivate
} ,
104 { kEventClassControl
, kEventControlDeactivate
} ,
106 { kEventClassControl
, kEventControlSetFocusPart
} ,
107 { kEventClassControl
, kEventControlFocusPartChanged
} ,
109 { kEventClassService
, kEventServiceGetTypes
},
110 { kEventClassService
, kEventServiceCopy
},
111 { kEventClassService
, kEventServicePaste
},
113 // { kEventClassControl , kEventControlInvalidateForSizeChange } , // 10.3 only
114 // { kEventClassControl , kEventControlBoundsChanged } ,
117 static pascal OSStatus
wxMacWindowControlEventHandler( EventHandlerCallRef handler
, EventRef event
, void *data
)
119 OSStatus result
= eventNotHandledErr
;
120 static wxWindowMac
* targetFocusWindow
= NULL
;
121 static wxWindowMac
* formerFocusWindow
= NULL
;
123 wxMacCarbonEvent
cEvent( event
) ;
125 ControlRef controlRef
;
126 wxWindowMac
* thisWindow
= (wxWindowMac
*) data
;
128 cEvent
.GetParameter( kEventParamDirectObject
, &controlRef
) ;
130 switch ( GetEventKind( event
) )
132 case kEventControlDraw
:
134 RgnHandle updateRgn
= NULL
;
135 RgnHandle allocatedRgn
= NULL
;
136 wxRegion visRegion
= thisWindow
->MacGetVisibleRegion() ;
138 if ( cEvent
.GetParameter
<RgnHandle
>(kEventParamRgnHandle
, &updateRgn
) != noErr
)
140 HIShapeGetAsQDRgn( visRegion
.GetWXHRGN(), updateRgn
);
144 if ( thisWindow
->MacGetLeftBorderSize() != 0 || thisWindow
->MacGetTopBorderSize() != 0 )
146 // as this update region is in native window locals we must adapt it to wx window local
147 allocatedRgn
= NewRgn() ;
148 CopyRgn( updateRgn
, allocatedRgn
) ;
150 // hide the given region by the new region that must be shifted
151 OffsetRgn( allocatedRgn
, thisWindow
->MacGetLeftBorderSize() , thisWindow
->MacGetTopBorderSize() ) ;
152 updateRgn
= allocatedRgn
;
156 #if wxMAC_DEBUG_REDRAW
157 if ( thisWindow
->MacIsUserPane() )
159 static float color
= 0.5 ;
160 static int channel
= 0 ;
162 CGContextRef cgContext
= cEvent
.GetParameter
<CGContextRef
>(kEventParamCGContextRef
) ;
164 HIViewGetBounds( controlRef
, &bounds
);
165 CGContextSetRGBFillColor( cgContext
, channel
== 0 ? color
: 0.5 ,
166 channel
== 1 ? color
: 0.5 , channel
== 2 ? color
: 0.5 , 1 );
167 CGContextFillRect( cgContext
, bounds
);
180 bool created
= false ;
181 CGContextRef cgContext
= NULL
;
182 OSStatus err
= cEvent
.GetParameter
<CGContextRef
>(kEventParamCGContextRef
, &cgContext
) ;
185 wxFAIL_MSG("Unable to retrieve CGContextRef");
188 thisWindow
->MacSetCGContextRef( cgContext
) ;
191 wxMacCGContextStateSaver
sg( cgContext
) ;
192 CGFloat alpha
= (CGFloat
)1.0 ;
194 wxWindow
* iter
= thisWindow
;
197 alpha
*= (CGFloat
)( iter
->GetTransparent()/255.0 ) ;
198 if ( iter
->IsTopLevel() )
201 iter
= iter
->GetParent() ;
204 CGContextSetAlpha( cgContext
, alpha
) ;
206 if ( thisWindow
->GetBackgroundStyle() == wxBG_STYLE_TRANSPARENT
)
209 HIViewGetBounds( controlRef
, &bounds
);
210 CGContextClearRect( cgContext
, bounds
);
215 if ( thisWindow
->MacDoRedraw( updateRgn
, cEvent
.GetTicks() ) )
218 thisWindow
->MacSetCGContextRef( NULL
) ;
222 CGContextRelease( cgContext
) ;
226 DisposeRgn( allocatedRgn
) ;
230 case kEventControlVisibilityChanged
:
231 // we might have two native controls attributed to the same wxWindow instance
232 // eg a scrollview and an embedded textview, make sure we only fire for the 'outer'
233 // control, as otherwise native and wx visibility are different
234 if ( thisWindow
->GetPeer() != NULL
&& thisWindow
->GetPeer()->GetControlRef() == controlRef
)
236 thisWindow
->MacVisibilityChanged() ;
240 case kEventControlEnabledStateChanged
:
241 thisWindow
->MacEnabledStateChanged();
244 case kEventControlHiliteChanged
:
245 thisWindow
->MacHiliteChanged() ;
248 case kEventControlActivate
:
249 case kEventControlDeactivate
:
250 // FIXME: we should have a virtual function for this!
252 if ( thisWindow
->IsKindOf( CLASSINFO( wxTreeCtrl
) ) )
253 thisWindow
->Refresh();
256 if ( thisWindow
->IsKindOf( CLASSINFO( wxListCtrl
) ) )
257 thisWindow
->Refresh();
263 // different handling on OS X
266 case kEventControlFocusPartChanged
:
267 // the event is emulated by wxmac for systems lower than 10.5
269 if ( UMAGetSystemVersion() < 0x1050 )
271 // as it is synthesized here, we have to manually avoid propagation
274 ControlPartCode previousControlPart
= cEvent
.GetParameter
<ControlPartCode
>(kEventParamControlPreviousPart
, typeControlPartCode
);
275 ControlPartCode currentControlPart
= cEvent
.GetParameter
<ControlPartCode
>(kEventParamControlCurrentPart
, typeControlPartCode
);
277 if ( thisWindow
->MacGetTopLevelWindow() && thisWindow
->GetPeer()->NeedsFocusRect() )
279 thisWindow
->MacInvalidateBorders();
282 if ( currentControlPart
== 0 )
286 if ( thisWindow
->GetCaret() )
287 thisWindow
->GetCaret()->OnKillFocus();
290 wxLogTrace(_T("Focus"), _T("focus lost(%p)"), static_cast<void*>(thisWindow
));
292 // remove this as soon as posting the synthesized event works properly
293 static bool inKillFocusEvent
= false ;
295 if ( !inKillFocusEvent
)
297 inKillFocusEvent
= true ;
298 wxFocusEvent
event( wxEVT_KILL_FOCUS
, thisWindow
->GetId());
299 event
.SetEventObject(thisWindow
);
300 event
.SetWindow(targetFocusWindow
);
301 thisWindow
->HandleWindowEvent(event
) ;
302 inKillFocusEvent
= false ;
303 targetFocusWindow
= NULL
;
306 else if ( previousControlPart
== 0 )
309 // panel wants to track the window which was the last to have focus in it
310 wxLogTrace(_T("Focus"), _T("focus set(%p)"), static_cast<void*>(thisWindow
));
311 wxChildFocusEvent
eventFocus((wxWindow
*)thisWindow
);
312 thisWindow
->HandleWindowEvent(eventFocus
);
315 if ( thisWindow
->GetCaret() )
316 thisWindow
->GetCaret()->OnSetFocus();
319 wxFocusEvent
event(wxEVT_SET_FOCUS
, thisWindow
->GetId());
320 event
.SetEventObject(thisWindow
);
321 event
.SetWindow(formerFocusWindow
);
322 thisWindow
->HandleWindowEvent(event
) ;
323 formerFocusWindow
= NULL
;
327 case kEventControlSetFocusPart
:
329 Boolean focusEverything
= false ;
330 if ( cEvent
.GetParameter
<Boolean
>(kEventParamControlFocusEverything
, &focusEverything
) == noErr
)
332 // put a breakpoint here to catch focus everything events
334 ControlPartCode controlPart
= cEvent
.GetParameter
<ControlPartCode
>(kEventParamControlPart
, typeControlPartCode
);
335 if ( controlPart
!= kControlFocusNoPart
)
337 targetFocusWindow
= thisWindow
;
338 wxLogTrace(_T("Focus"), _T("focus to be set(%p)"), static_cast<void*>(thisWindow
));
342 formerFocusWindow
= thisWindow
;
343 wxLogTrace(_T("Focus"), _T("focus to be lost(%p)"), static_cast<void*>(thisWindow
));
346 ControlPartCode previousControlPart
= 0;
347 verify_noerr( HIViewGetFocusPart(controlRef
, &previousControlPart
));
349 if ( thisWindow
->MacIsUserPane() )
351 if ( controlPart
!= kControlFocusNoPart
)
352 cEvent
.SetParameter
<ControlPartCode
>( kEventParamControlPart
, typeControlPartCode
, 1 ) ;
356 result
= CallNextEventHandler(handler
, event
);
358 if ( UMAGetSystemVersion() < 0x1050 )
360 // set back to 0 if problems arise
362 if ( result
== noErr
)
364 ControlPartCode currentControlPart
= cEvent
.GetParameter
<ControlPartCode
>(kEventParamControlPart
, typeControlPartCode
);
365 // synthesize the event focus changed event
366 EventRef evRef
= NULL
;
368 OSStatus err
= MacCreateEvent(
369 NULL
, kEventClassControl
, kEventControlFocusPartChanged
, TicksToEventTime( TickCount() ) ,
370 kEventAttributeUserEvent
, &evRef
);
373 wxMacCarbonEvent
iEvent( evRef
) ;
374 iEvent
.SetParameter
<ControlRef
>( kEventParamDirectObject
, controlRef
);
375 iEvent
.SetParameter
<EventTargetRef
>( kEventParamPostTarget
, typeEventTargetRef
, GetControlEventTarget( controlRef
) );
376 iEvent
.SetParameter
<ControlPartCode
>( kEventParamControlPreviousPart
, typeControlPartCode
, previousControlPart
);
377 iEvent
.SetParameter
<ControlPartCode
>( kEventParamControlCurrentPart
, typeControlPartCode
, currentControlPart
);
380 // TODO test this first, avoid double posts etc...
381 PostEventToQueue( GetMainEventQueue(), evRef
, kEventPriorityHigh
);
383 wxMacWindowControlEventHandler( NULL
, evRef
, data
) ;
385 ReleaseEvent( evRef
) ;
388 // old implementation, to be removed if the new one works
389 if ( controlPart
== kControlFocusNoPart
)
392 if ( thisWindow
->GetCaret() )
393 thisWindow
->GetCaret()->OnKillFocus();
396 wxLogTrace(_T("Focus"), _T("focus lost(%p)"), static_cast<void*>(thisWindow
));
398 static bool inKillFocusEvent
= false ;
400 if ( !inKillFocusEvent
)
402 inKillFocusEvent
= true ;
403 wxFocusEvent
event( wxEVT_KILL_FOCUS
, thisWindow
->GetId());
404 event
.SetEventObject(thisWindow
);
405 thisWindow
->HandleWindowEvent(event
) ;
406 inKillFocusEvent
= false ;
411 // panel wants to track the window which was the last to have focus in it
412 wxLogTrace(_T("Focus"), _T("focus set(%p)"), static_cast<void*>(thisWindow
));
413 wxChildFocusEvent
eventFocus((wxWindow
*)thisWindow
);
414 thisWindow
->HandleWindowEvent(eventFocus
);
417 if ( thisWindow
->GetCaret() )
418 thisWindow
->GetCaret()->OnSetFocus();
421 wxFocusEvent
event(wxEVT_SET_FOCUS
, thisWindow
->GetId());
422 event
.SetEventObject(thisWindow
);
423 thisWindow
->HandleWindowEvent(event
) ;
430 case kEventControlHit
:
431 result
= thisWindow
->MacControlHit( handler
, event
) ;
434 case kEventControlGetClickActivation
:
436 // fix to always have a proper activation for DataBrowser controls (stay in bkgnd otherwise)
437 WindowRef owner
= cEvent
.GetParameter
<WindowRef
>(kEventParamWindowRef
);
438 if ( !IsWindowActive(owner
) )
440 cEvent
.SetParameter(kEventParamClickActivation
,typeClickActivationResult
, (UInt32
) kActivateAndIgnoreClick
) ;
453 static pascal OSStatus
454 wxMacWindowServiceEventHandler(EventHandlerCallRef
WXUNUSED(handler
),
458 OSStatus result
= eventNotHandledErr
;
460 wxMacCarbonEvent
cEvent( event
) ;
462 ControlRef controlRef
;
463 wxWindowMac
* thisWindow
= (wxWindowMac
*) data
;
464 wxTextCtrl
* textCtrl
= wxDynamicCast( thisWindow
, wxTextCtrl
) ;
465 cEvent
.GetParameter( kEventParamDirectObject
, &controlRef
) ;
467 switch ( GetEventKind( event
) )
469 case kEventServiceGetTypes
:
473 textCtrl
->GetSelection( &from
, &to
) ;
475 CFMutableArrayRef copyTypes
= 0 , pasteTypes
= 0;
477 copyTypes
= cEvent
.GetParameter
< CFMutableArrayRef
>( kEventParamServiceCopyTypes
, typeCFMutableArrayRef
) ;
478 if ( textCtrl
->IsEditable() )
479 pasteTypes
= cEvent
.GetParameter
< CFMutableArrayRef
>( kEventParamServicePasteTypes
, typeCFMutableArrayRef
) ;
481 static const OSType textDataTypes
[] = { kTXNTextData
/* , 'utxt', 'PICT', 'MooV', 'AIFF' */ };
482 for ( size_t i
= 0 ; i
< WXSIZEOF(textDataTypes
) ; ++i
)
484 CFStringRef typestring
= CreateTypeStringWithOSType(textDataTypes
[i
]);
488 CFArrayAppendValue(copyTypes
, typestring
) ;
490 CFArrayAppendValue(pasteTypes
, typestring
) ;
492 CFRelease( typestring
) ;
500 case kEventServiceCopy
:
505 textCtrl
->GetSelection( &from
, &to
) ;
506 wxString val
= textCtrl
->GetValue() ;
507 val
= val
.Mid( from
, to
- from
) ;
508 PasteboardRef pasteboard
= cEvent
.GetParameter
<PasteboardRef
>( kEventParamPasteboardRef
, typePasteboardRef
);
509 verify_noerr( PasteboardClear( pasteboard
) ) ;
510 PasteboardSynchronize( pasteboard
);
511 // TODO add proper conversion
512 CFDataRef data
= CFDataCreate( kCFAllocatorDefault
, (const UInt8
*)val
.c_str(), val
.length() );
513 PasteboardPutItemFlavor( pasteboard
, (PasteboardItemID
) 1, CFSTR("com.apple.traditional-mac-plain-text"), data
, 0);
519 case kEventServicePaste
:
522 PasteboardRef pasteboard
= cEvent
.GetParameter
<PasteboardRef
>( kEventParamPasteboardRef
, typePasteboardRef
);
523 PasteboardSynchronize( pasteboard
);
525 verify_noerr( PasteboardGetItemCount( pasteboard
, &itemCount
) );
526 for( UInt32 itemIndex
= 1; itemIndex
<= itemCount
; itemIndex
++ )
528 PasteboardItemID itemID
;
529 if ( PasteboardGetItemIdentifier( pasteboard
, itemIndex
, &itemID
) == noErr
)
531 CFDataRef flavorData
= NULL
;
532 if ( PasteboardCopyItemFlavorData( pasteboard
, itemID
, CFSTR("com.apple.traditional-mac-plain-text"), &flavorData
) == noErr
)
534 CFIndex flavorDataSize
= CFDataGetLength( flavorData
);
535 char *content
= new char[flavorDataSize
+1] ;
536 memcpy( content
, CFDataGetBytePtr( flavorData
), flavorDataSize
);
537 content
[flavorDataSize
]=0;
538 CFRelease( flavorData
);
540 textCtrl
->WriteText( wxString( content
, wxConvLocal
) );
542 textCtrl
->WriteText( wxString( content
) ) ;
560 pascal OSStatus
wxMacUnicodeTextEventHandler( EventHandlerCallRef handler
, EventRef event
, void *data
)
562 OSStatus result
= eventNotHandledErr
;
563 wxWindowMac
* focus
= (wxWindowMac
*) data
;
565 wchar_t* uniChars
= NULL
;
566 UInt32 when
= EventTimeToTicks( GetEventTime( event
) ) ;
568 UniChar
* charBuf
= NULL
;
569 ByteCount dataSize
= 0 ;
572 if ( GetEventParameter( event
, kEventParamTextInputSendText
, typeUnicodeText
, NULL
, 0 , &dataSize
, NULL
) == noErr
)
574 numChars
= dataSize
/ sizeof( UniChar
) + 1;
577 if ( (size_t) numChars
* 2 > sizeof(buf
) )
578 charBuf
= new UniChar
[ numChars
] ;
582 uniChars
= new wchar_t[ numChars
] ;
583 GetEventParameter( event
, kEventParamTextInputSendText
, typeUnicodeText
, NULL
, dataSize
, NULL
, charBuf
) ;
584 charBuf
[ numChars
- 1 ] = 0;
585 #if SIZEOF_WCHAR_T == 2
586 uniChars
= (wchar_t*) charBuf
;
587 /* memcpy( uniChars , charBuf , numChars * 2 ) ;*/ // is there any point in copying charBuf over itself? (in fact, memcpy isn't even guaranteed to work correctly if the source and destination ranges overlap...)
589 // the resulting string will never have more chars than the utf16 version, so this is safe
590 wxMBConvUTF16 converter
;
591 numChars
= converter
.MB2WC( uniChars
, (const char*)charBuf
, numChars
) ;
595 switch ( GetEventKind( event
) )
597 case kEventTextInputUpdateActiveInputArea
:
599 // An IME input event may return several characters, but we need to send one char at a time to
601 for (int pos
=0 ; pos
< numChars
; pos
++)
603 WXEVENTREF formerEvent
= wxTheApp
->MacGetCurrentEvent() ;
604 WXEVENTHANDLERCALLREF formerHandler
= wxTheApp
->MacGetCurrentEventHandlerCallRef() ;
605 wxTheApp
->MacSetCurrentEvent( event
, handler
) ;
607 UInt32 message
= uniChars
[pos
] < 128 ? (char)uniChars
[pos
] : '?';
609 NB: faking a charcode here is problematic. The kEventTextInputUpdateActiveInputArea event is sent
610 multiple times to update the active range during inline input, so this handler will often receive
611 uncommited text, which should usually not trigger side effects. It might be a good idea to check the
612 kEventParamTextInputSendFixLen parameter and verify if input is being confirmed (see CarbonEvents.h).
613 On the other hand, it can be useful for some applications to react to uncommitted text (for example,
614 to update a status display), as long as it does not disrupt the inline input session. Ideally, wx
615 should add new event types to support advanced text input. For now, I would keep things as they are.
617 However, the code that was being used caused additional problems:
618 UInt32 message = (0 << 8) + ((char)uniChars[pos] );
619 Since it simply truncated the unichar to the last byte, it ended up causing weird bugs with inline
620 input, such as switching to another field when one attempted to insert the character U+4E09 (the kanji
621 for "three"), because it was truncated to 09 (kTabCharCode), which was later "converted" to WXK_TAB
622 (still 09) in wxMacTranslateKey; or triggering the default button when one attempted to insert U+840D
623 (the kanji for "name"), which got truncated to 0D and interpreted as a carriage return keypress.
624 Note that even single-byte characters could have been misinterpreted, since MacRoman charcodes only
625 overlap with Unicode within the (7-bit) ASCII range.
626 But simply passing a NUL charcode would disable text updated events, because wxTextCtrl::OnChar checks
627 for codes within a specific range. Therefore I went for the solution seen above, which keeps ASCII
628 characters as they are and replaces the rest with '?', ensuring that update events are triggered.
629 It would be better to change wxTextCtrl::OnChar to look at the actual unicode character instead, but
630 I don't have time to look into that right now.
633 if ( wxTheApp
->MacSendCharEvent(
634 focus
, message
, 0 , when
, 0 , 0 , uniChars
[pos
] ) )
639 wxTheApp
->MacSetCurrentEvent( formerEvent
, formerHandler
) ;
643 case kEventTextInputUnicodeForKeyEvent
:
645 UInt32 keyCode
, modifiers
;
648 unsigned char charCode
;
650 GetEventParameter( event
, kEventParamTextInputSendKeyboardEvent
, typeEventRef
, NULL
, sizeof(rawEvent
), NULL
, &rawEvent
) ;
651 GetEventParameter( rawEvent
, kEventParamKeyMacCharCodes
, typeChar
, NULL
, sizeof(char), NULL
, &charCode
);
652 GetEventParameter( rawEvent
, kEventParamKeyCode
, typeUInt32
, NULL
, sizeof(UInt32
), NULL
, &keyCode
);
653 GetEventParameter( rawEvent
, kEventParamKeyModifiers
, typeUInt32
, NULL
, sizeof(UInt32
), NULL
, &modifiers
);
654 GetEventParameter( rawEvent
, kEventParamMouseLocation
, typeQDPoint
, NULL
, sizeof(Point
), NULL
, &point
);
656 UInt32 message
= (keyCode
<< 8) + charCode
;
658 // An IME input event may return several characters, but we need to send one char at a time to
660 for (int pos
=0 ; pos
< numChars
; pos
++)
662 WXEVENTREF formerEvent
= wxTheApp
->MacGetCurrentEvent() ;
663 WXEVENTHANDLERCALLREF formerHandler
= wxTheApp
->MacGetCurrentEventHandlerCallRef() ;
664 wxTheApp
->MacSetCurrentEvent( event
, handler
) ;
666 if ( wxTheApp
->MacSendCharEvent(
667 focus
, message
, modifiers
, when
, point
.h
, point
.v
, uniChars
[pos
] ) )
672 wxTheApp
->MacSetCurrentEvent( formerEvent
, formerHandler
) ;
681 if ( charBuf
!= buf
)
687 static pascal OSStatus
688 wxMacWindowCommandEventHandler(EventHandlerCallRef
WXUNUSED(handler
),
692 OSStatus result
= eventNotHandledErr
;
693 wxWindowMac
* focus
= (wxWindowMac
*) data
;
697 wxMacCarbonEvent
cEvent( event
) ;
698 cEvent
.GetParameter
<HICommand
>(kEventParamDirectObject
,typeHICommand
,&command
) ;
700 wxMenuItem
* item
= NULL
;
701 wxMenu
* itemMenu
= wxFindMenuFromMacCommand( command
, item
) ;
705 wxASSERT( itemMenu
!= NULL
) ;
707 switch ( cEvent
.GetKind() )
709 case kEventProcessCommand
:
710 if ( itemMenu
->HandleCommandProcess( item
, focus
) )
714 case kEventCommandUpdateStatus
:
715 if ( itemMenu
->HandleCommandUpdateStatus( item
, focus
) )
726 pascal OSStatus
wxMacWindowEventHandler( EventHandlerCallRef handler
, EventRef event
, void *data
)
728 EventRef formerEvent
= (EventRef
) wxTheApp
->MacGetCurrentEvent() ;
729 EventHandlerCallRef formerEventHandlerCallRef
= (EventHandlerCallRef
) wxTheApp
->MacGetCurrentEventHandlerCallRef() ;
730 wxTheApp
->MacSetCurrentEvent( event
, handler
) ;
731 OSStatus result
= eventNotHandledErr
;
733 switch ( GetEventClass( event
) )
735 case kEventClassCommand
:
736 result
= wxMacWindowCommandEventHandler( handler
, event
, data
) ;
739 case kEventClassControl
:
740 result
= wxMacWindowControlEventHandler( handler
, event
, data
) ;
743 case kEventClassService
:
744 result
= wxMacWindowServiceEventHandler( handler
, event
, data
) ;
747 case kEventClassTextInput
:
748 result
= wxMacUnicodeTextEventHandler( handler
, event
, data
) ;
755 wxTheApp
->MacSetCurrentEvent( formerEvent
, formerEventHandlerCallRef
) ;
760 DEFINE_ONE_SHOT_HANDLER_GETTER( wxMacWindowEventHandler
)
762 // ---------------------------------------------------------------------------
763 // Scrollbar Tracking for all
764 // ---------------------------------------------------------------------------
766 pascal void wxMacLiveScrollbarActionProc( ControlRef control
, ControlPartCode partCode
) ;
767 pascal void wxMacLiveScrollbarActionProc( ControlRef control
, ControlPartCode partCode
)
771 wxWindow
* wx
= wxFindWindowFromWXWidget( (WXWidget
) control
) ;
773 wx
->MacHandleControlClick( (WXWidget
) control
, partCode
, true /* stillDown */ ) ;
776 wxMAC_DEFINE_PROC_GETTER( ControlActionUPP
, wxMacLiveScrollbarActionProc
) ;
778 wxWidgetImplType
* wxWidgetImpl::CreateUserPane( wxWindowMac
* wxpeer
, wxWindowMac
* parent
, wxWindowID id
, const wxPoint
& pos
, const wxSize
& size
,
779 long style
, long extraStyle
)
781 OSStatus err
= noErr
;
782 Rect bounds
= wxMacGetBoundsForControl( wxpeer
, pos
, size
) ;
783 wxMacControl
* c
= new wxMacControl(wxpeer
) ;
785 | kControlSupportsEmbedding
786 | kControlSupportsLiveFeedback
787 | kControlGetsFocusOnClick
788 // | kControlHasSpecialBackground
789 // | kControlSupportsCalcBestRect
790 | kControlHandlesTracking
791 | kControlSupportsFocus
792 | kControlWantsActivate
793 | kControlWantsIdle
;
795 err
=::CreateUserPaneControl( MAC_WXHWND(wxpeer
->GetParent()->MacGetTopLevelWindowRef()) , &bounds
, features
, c
->GetControlRefAddr() );
801 void wxMacControl::InstallEventHandler( WXWidget control
)
803 wxWidgetImpl::Associate( control
? control
: (WXWidget
) m_controlRef
, this ) ;
804 ::InstallControlEventHandler( control
? (ControlRef
) control
: m_controlRef
, GetwxMacWindowEventHandlerUPP(),
805 GetEventTypeCount(eventList
), eventList
, GetWXPeer(), NULL
);
808 IMPLEMENT_DYNAMIC_CLASS( wxMacControl
, wxWidgetImpl
)
810 wxMacControl::wxMacControl()
815 wxMacControl::wxMacControl(wxWindowMac
* peer
, bool isRootControl
) :
816 wxWidgetImpl( peer
, isRootControl
)
821 wxMacControl::~wxMacControl()
823 if ( m_controlRef
&& !IsRootControl() )
825 wxASSERT_MSG( m_controlRef
!= NULL
, wxT("Control Handle already NULL, Dispose called twice ?") );
826 wxASSERT_MSG( IsValidControlHandle(m_controlRef
) , wxT("Invalid Control Handle (maybe already released) in Dispose") );
828 wxWidgetImpl::RemoveAssociations( this ) ;
829 // we cannot check the ref count here anymore, as autorelease objects might delete their refs later
830 // we can have situations when being embedded, where the control gets deleted behind our back, so only
831 // CFRelease if we are safe
832 if ( IsValidControlHandle(m_controlRef
) )
833 CFRelease(m_controlRef
);
838 void wxMacControl::Init()
841 m_macControlEventHandler
= NULL
;
844 void wxMacControl::RemoveFromParent()
846 // nothing to do here for carbon
847 HIViewRemoveFromSuperview(m_controlRef
);
850 void wxMacControl::Embed( wxWidgetImpl
*parent
)
852 HIViewAddSubview((ControlRef
)parent
->GetWXWidget(), m_controlRef
);
855 void wxMacControl::SetNeedsDisplay( const wxRect
* rect
)
862 HIRect updatearea
= CGRectMake( rect
->x
, rect
->y
, rect
->width
, rect
->height
);
863 HIViewSetNeedsDisplayInRect( m_controlRef
, &updatearea
, true );
866 HIViewSetNeedsDisplay( m_controlRef
, true );
869 void wxMacControl::Raise()
871 verify_noerr( HIViewSetZOrder( m_controlRef
, kHIViewZOrderAbove
, NULL
) );
874 void wxMacControl::Lower()
876 verify_noerr( HIViewSetZOrder( m_controlRef
, kHIViewZOrderBelow
, NULL
) );
879 void wxMacControl::GetContentArea(int &left
, int &top
, int &width
, int &height
) const
881 RgnHandle rgn
= NewRgn() ;
883 if ( GetControlRegion( m_controlRef
, kControlContentMetaPart
, rgn
) == noErr
)
884 GetRegionBounds( rgn
, &content
) ;
887 GetControlBounds( m_controlRef
, &content
);
888 content
.right
-= content
.left
;
890 content
.bottom
-= content
.top
;
898 width
= content
.right
- content
.left
;
899 height
= content
.bottom
- content
.top
;
902 void wxMacControl::Move(int x
, int y
, int width
, int height
)
904 HIRect hir
= CGRectMake(x
,y
,width
,height
);
905 HIViewSetFrame ( m_controlRef
, &hir
);
908 void wxMacControl::GetPosition( int &x
, int &y
) const
911 GetControlBounds( m_controlRef
, &r
);
916 void wxMacControl::GetSize( int &width
, int &height
) const
919 GetControlBounds( m_controlRef
, &r
);
920 width
= r
.right
- r
.left
;
921 height
= r
.bottom
- r
.top
;
924 void wxMacControl::SetControlSize( wxWindowVariant variant
)
929 case wxWINDOW_VARIANT_NORMAL
:
930 size
= kControlSizeNormal
;
933 case wxWINDOW_VARIANT_SMALL
:
934 size
= kControlSizeSmall
;
937 case wxWINDOW_VARIANT_MINI
:
938 // not always defined in the headers
942 case wxWINDOW_VARIANT_LARGE
:
943 size
= kControlSizeLarge
;
947 wxFAIL_MSG(_T("unexpected window variant"));
951 SetData
<ControlSize
>(kControlEntireControl
, kControlSizeTag
, &size
) ;
954 void wxMacControl::ScrollRect( const wxRect
*rect
, int dx
, int dy
)
956 if (GetNeedsDisplay() )
958 // because HIViewScrollRect does not scroll the already invalidated area we have two options:
959 // in case there is already a pending redraw on that area
960 // either immediate redraw or full invalidate
962 // is the better overall solution, as it does not slow down scrolling
965 // this would be the preferred version for fast drawing controls
966 HIViewRender(GetControlRef()) ;
970 // note there currently is a bug in OSX (10.3 +?) which makes inefficient refreshes in case an entire control
971 // area is scrolled, this does not occur if width and height are 2 pixels less,
972 // TODO: write optimal workaround
974 HIRect scrollarea
= CGRectMake( rect
->x
, rect
->y
, rect
->width
, rect
->height
);
975 HIViewScrollRect ( m_controlRef
, &scrollarea
, dx
,dy
);
978 // this would be the preferred version for fast drawing controls
979 HIViewRender(GetControlRef()) ;
983 bool wxMacControl::CanFocus() const
985 // TODO : evaluate performance hits by looking up this value, eventually cache the results for a 1 sec or so
986 // CAUTION : the value returned currently is 0 or 2, I've also found values of 1 having the same meaning,
987 // but the value range is nowhere documented
988 Boolean keyExistsAndHasValidFormat
;
989 CFIndex fullKeyboardAccess
= CFPreferencesGetAppIntegerValue( CFSTR("AppleKeyboardUIMode" ) ,
990 kCFPreferencesCurrentApplication
, &keyExistsAndHasValidFormat
);
992 if ( keyExistsAndHasValidFormat
&& fullKeyboardAccess
> 0 )
998 UInt32 features
= 0 ;
999 GetControlFeatures( m_controlRef
, &features
) ;
1001 return features
& ( kControlSupportsFocus
| kControlGetsFocusOnClick
) ;
1005 bool wxMacControl::GetNeedsDisplay() const
1007 return HIViewGetNeedsDisplay( m_controlRef
);
1010 void wxWidgetImpl::Convert( wxPoint
*pt
, wxWidgetImpl
*from
, wxWidgetImpl
*to
)
1016 HIViewConvertPoint( &hiPoint
, (ControlRef
) from
->GetWXWidget() , (ControlRef
) to
->GetWXWidget() );
1017 pt
->x
= (int)hiPoint
.x
;
1018 pt
->y
= (int)hiPoint
.y
;
1021 bool wxMacControl::SetFocus()
1023 // as we cannot rely on the control features to find out whether we are in full keyboard mode,
1024 // we can only leave in case of an error
1026 OSStatus err
= SetKeyboardFocus( GetControlOwner( m_controlRef
), m_controlRef
, kControlFocusNextPart
);
1027 if ( err
== errCouldntSetFocus
)
1029 SetUserFocusWindow(GetControlOwner( m_controlRef
) );
1034 bool wxMacControl::HasFocus() const
1037 GetKeyboardFocus( GetUserFocusWindow() , &control
);
1038 return control
== m_controlRef
;
1041 void wxMacControl::SetCursor(const wxCursor
& cursor
)
1043 wxWindowMac
*mouseWin
= 0 ;
1044 WindowRef window
= GetControlOwner( m_controlRef
) ;
1046 wxNonOwnedWindow
* tlwwx
= wxNonOwnedWindow::GetFromWXWindow( (WXWindow
) window
) ;
1047 if ( tlwwx
!= NULL
)
1049 ControlPartCode part
;
1050 ControlRef control
;
1052 #if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
1054 HIGetMousePosition(kHICoordSpaceWindow
, window
, &hiPoint
);
1058 GetGlobalMouse( &pt
);
1061 tlwwx
->ScreenToClient(&x
, &y
);
1065 control
= FindControlUnderMouse( pt
, window
, &part
) ;
1067 mouseWin
= wxFindWindowFromWXWidget( (WXWidget
) control
) ;
1070 if ( mouseWin
== tlwwx
&& !wxIsBusy() )
1071 cursor
.MacInstall() ;
1074 void wxMacControl::CaptureMouse()
1078 void wxMacControl::ReleaseMouse()
1083 // subclass specifics
1086 OSStatus
wxMacControl::GetData(ControlPartCode inPartCode
, ResType inTag
, Size inBufferSize
, void * inOutBuffer
, Size
* outActualSize
) const
1088 return ::GetControlData( m_controlRef
, inPartCode
, inTag
, inBufferSize
, inOutBuffer
, outActualSize
);
1091 OSStatus
wxMacControl::GetDataSize(ControlPartCode inPartCode
, ResType inTag
, Size
* outActualSize
) const
1093 return ::GetControlDataSize( m_controlRef
, inPartCode
, inTag
, outActualSize
);
1096 OSStatus
wxMacControl::SetData(ControlPartCode inPartCode
, ResType inTag
, Size inSize
, const void * inData
)
1098 return ::SetControlData( m_controlRef
, inPartCode
, inTag
, inSize
, inData
);
1101 OSStatus
wxMacControl::SendEvent( EventRef event
, OptionBits inOptions
)
1103 return SendEventToEventTargetWithOptions( event
,
1104 HIObjectGetEventTarget( (HIObjectRef
) m_controlRef
), inOptions
);
1107 OSStatus
wxMacControl::SendHICommand( HICommand
&command
, OptionBits inOptions
)
1109 wxMacCarbonEvent
event( kEventClassCommand
, kEventCommandProcess
);
1111 event
.SetParameter
<HICommand
>(kEventParamDirectObject
,command
);
1113 return SendEvent( event
, inOptions
);
1116 OSStatus
wxMacControl::SendHICommand( UInt32 commandID
, OptionBits inOptions
)
1120 memset( &command
, 0 , sizeof(command
) );
1121 command
.commandID
= commandID
;
1122 return SendHICommand( command
, inOptions
);
1125 void wxMacControl::PerformClick()
1127 HIViewSimulateClick (m_controlRef
, kControlButtonPart
, 0, NULL
);
1130 wxInt32
wxMacControl::GetValue() const
1132 return ::GetControl32BitValue( m_controlRef
);
1135 SInt32
wxMacControl::GetMaximum() const
1137 return ::GetControl32BitMaximum( m_controlRef
);
1141 wxInt32 wxMacControl::GetMinimum() const
1143 return ::GetControl32BitMinimum( m_controlRef );
1147 void wxMacControl::SetValue( wxInt32 v
)
1149 ::SetControl32BitValue( m_controlRef
, v
);
1152 void wxMacControl::SetMinimum( wxInt32 v
)
1154 ::SetControl32BitMinimum( m_controlRef
, v
);
1157 void wxMacControl::SetMaximum( wxInt32 v
)
1159 ::SetControl32BitMaximum( m_controlRef
, v
);
1162 void wxMacControl::SetValueAndRange( SInt32 value
, SInt32 minimum
, SInt32 maximum
)
1164 ::SetControl32BitMinimum( m_controlRef
, minimum
);
1165 ::SetControl32BitMaximum( m_controlRef
, maximum
);
1166 ::SetControl32BitValue( m_controlRef
, value
);
1169 void wxMacControl::VisibilityChanged(bool WXUNUSED(shown
))
1173 void wxMacControl::SuperChangedPosition()
1177 void wxMacControl::SetFont( const wxFont
& font
, const wxColour
& foreground
, long windowStyle
, bool ignoreBlack
)
1180 #if wxOSX_USE_CORE_TEXT
1181 if ( UMAGetSystemVersion() >= 0x1050 )
1183 HIViewPartCode part
= 0;
1184 HIThemeTextHorizontalFlush flush
= kHIThemeTextHorizontalFlushDefault
;
1185 if ( ( windowStyle
& wxALIGN_MASK
) & wxALIGN_CENTER_HORIZONTAL
)
1186 flush
= kHIThemeTextHorizontalFlushCenter
;
1187 else if ( ( windowStyle
& wxALIGN_MASK
) & wxALIGN_RIGHT
)
1188 flush
= kHIThemeTextHorizontalFlushRight
;
1189 HIViewSetTextFont( m_controlRef
, part
, (CTFontRef
) font
.MacGetCTFont() );
1190 HIViewSetTextHorizontalFlush( m_controlRef
, part
, flush
);
1192 if ( foreground
!= *wxBLACK
|| ignoreBlack
== false )
1194 ControlFontStyleRec fontStyle
;
1195 foreground
.GetRGBColor( &fontStyle
.foreColor
);
1196 fontStyle
.flags
= kControlUseForeColorMask
;
1197 ::SetControlFontStyle( m_controlRef
, &fontStyle
);
1201 #if wxOSX_USE_ATSU_TEXT
1202 ControlFontStyleRec fontStyle
;
1203 if ( font
.MacGetThemeFontID() != kThemeCurrentPortFont
)
1205 switch ( font
.MacGetThemeFontID() )
1207 case kThemeSmallSystemFont
:
1208 fontStyle
.font
= kControlFontSmallSystemFont
;
1211 case 109 : // mini font
1212 fontStyle
.font
= -5;
1215 case kThemeSystemFont
:
1216 fontStyle
.font
= kControlFontBigSystemFont
;
1220 fontStyle
.font
= kControlFontBigSystemFont
;
1224 fontStyle
.flags
= kControlUseFontMask
;
1228 fontStyle
.font
= font
.MacGetFontNum();
1229 fontStyle
.style
= font
.MacGetFontStyle();
1230 fontStyle
.size
= font
.MacGetFontSize();
1231 fontStyle
.flags
= kControlUseFontMask
| kControlUseFaceMask
| kControlUseSizeMask
;
1234 fontStyle
.just
= teJustLeft
;
1235 fontStyle
.flags
|= kControlUseJustMask
;
1236 if ( ( windowStyle
& wxALIGN_MASK
) & wxALIGN_CENTER_HORIZONTAL
)
1237 fontStyle
.just
= teJustCenter
;
1238 else if ( ( windowStyle
& wxALIGN_MASK
) & wxALIGN_RIGHT
)
1239 fontStyle
.just
= teJustRight
;
1242 // we only should do this in case of a non-standard color, as otherwise 'disabled' controls
1243 // won't get grayed out by the system anymore
1245 if ( foreground
!= *wxBLACK
|| ignoreBlack
== false )
1247 foreground
.GetRGBColor( &fontStyle
.foreColor
);
1248 fontStyle
.flags
|= kControlUseForeColorMask
;
1251 ::SetControlFontStyle( m_controlRef
, &fontStyle
);
1255 void wxMacControl::SetBackgroundColour( const wxColour
&WXUNUSED(col
) )
1257 // HITextViewSetBackgroundColor( m_textView , color );
1260 void wxMacControl::SetRange( SInt32 minimum
, SInt32 maximum
)
1262 ::SetControl32BitMinimum( m_controlRef
, minimum
);
1263 ::SetControl32BitMaximum( m_controlRef
, maximum
);
1266 short wxMacControl::HandleKey( SInt16 keyCode
, SInt16 charCode
, EventModifiers modifiers
)
1268 return HandleControlKey( m_controlRef
, keyCode
, charCode
, modifiers
);
1271 void wxMacControl::SetActionProc( ControlActionUPP actionProc
)
1273 SetControlAction( m_controlRef
, actionProc
);
1276 SInt32
wxMacControl::GetViewSize() const
1278 return GetControlViewSize( m_controlRef
);
1281 bool wxMacControl::IsVisible() const
1283 return IsControlVisible( m_controlRef
);
1286 void wxMacControl::SetVisibility( bool visible
)
1288 SetControlVisibility( m_controlRef
, visible
, true );
1291 bool wxMacControl::IsEnabled() const
1293 return IsControlEnabled( m_controlRef
);
1296 bool wxMacControl::IsActive() const
1298 return IsControlActive( m_controlRef
);
1301 void wxMacControl::Enable( bool enable
)
1304 EnableControl( m_controlRef
);
1306 DisableControl( m_controlRef
);
1309 void wxMacControl::SetDrawingEnabled( bool enable
)
1311 HIViewSetDrawingEnabled( m_controlRef
, enable
);
1314 void wxMacControl::GetRectInWindowCoords( Rect
*r
)
1316 GetControlBounds( m_controlRef
, r
) ;
1318 WindowRef tlwref
= GetControlOwner( m_controlRef
) ;
1320 wxNonOwnedWindow
* tlwwx
= wxNonOwnedWindow::GetFromWXWindow( (WXWindow
) tlwref
) ;
1321 if ( tlwwx
!= NULL
)
1323 ControlRef rootControl
= tlwwx
->GetPeer()->GetControlRef() ;
1324 HIPoint hiPoint
= CGPointMake( 0 , 0 ) ;
1325 HIViewConvertPoint( &hiPoint
, HIViewGetSuperview(m_controlRef
) , rootControl
) ;
1326 OffsetRect( r
, (short) hiPoint
.x
, (short) hiPoint
.y
) ;
1330 void wxMacControl::GetBestRect( wxRect
*rect
) const
1332 short baselineoffset
;
1335 GetBestControlRect( m_controlRef
, &r
, &baselineoffset
);
1336 *rect
= wxRect( r
.left
, r
.top
, r
.right
- r
.left
, r
.bottom
-r
.top
);
1339 void wxMacControl::GetBestRect( Rect
*r
) const
1341 short baselineoffset
;
1342 GetBestControlRect( m_controlRef
, r
, &baselineoffset
);
1345 void wxMacControl::SetLabel( const wxString
&title
, wxFontEncoding encoding
)
1347 SetControlTitleWithCFString( m_controlRef
, wxCFStringRef( title
, encoding
) );
1350 void wxMacControl::GetFeatures( UInt32
* features
)
1352 GetControlFeatures( m_controlRef
, features
);
1355 OSStatus
wxMacControl::GetRegion( ControlPartCode partCode
, RgnHandle region
)
1357 OSStatus err
= GetControlRegion( m_controlRef
, partCode
, region
);
1361 void wxMacControl::PulseGauge()
1365 // SetNeedsDisplay would not invalidate the children
1366 static void InvalidateControlAndChildren( HIViewRef control
)
1368 HIViewSetNeedsDisplay( control
, true );
1369 UInt16 childrenCount
= 0;
1370 OSStatus err
= CountSubControls( control
, &childrenCount
);
1371 if ( err
== errControlIsNotEmbedder
)
1374 wxASSERT_MSG( err
== noErr
, wxT("Unexpected error when accessing subcontrols") );
1376 for ( UInt16 i
= childrenCount
; i
>=1; --i
)
1380 err
= GetIndexedSubControl( control
, i
, & child
);
1381 if ( err
== errControlIsNotEmbedder
)
1384 InvalidateControlAndChildren( child
);
1388 void wxMacControl::InvalidateWithChildren()
1390 InvalidateControlAndChildren( m_controlRef
);
1393 OSType wxMacCreator
= 'WXMC';
1394 OSType wxMacControlProperty
= 'MCCT';
1396 void wxMacControl::SetReferenceInNativeControl()
1399 verify_noerr( SetControlProperty ( m_controlRef
,
1400 wxMacCreator
,wxMacControlProperty
, sizeof(data
), &data
) );
1403 wxMacControl
* wxMacControl::GetReferenceFromNativeControl(ControlRef control
)
1405 wxMacControl
* ctl
= NULL
;
1406 ByteCount actualSize
;
1407 if ( GetControlProperty( control
,wxMacCreator
,wxMacControlProperty
, sizeof(ctl
) ,
1408 &actualSize
, &ctl
) == noErr
)
1415 void wxMacControl::SetBitmap( const wxBitmap
& WXUNUSED(bmp
) )
1417 // implemented in the respective subclasses
1420 void wxMacControl::SetScrollThumb( wxInt32
WXUNUSED(pos
), wxInt32
WXUNUSED(viewsize
) )
1422 // implemented in respective subclass
1429 OSStatus
wxMacControl::SetTabEnabled( SInt16 tabNo
, bool enable
)
1431 return ::SetTabEnabled( m_controlRef
, tabNo
, enable
);
1438 wxWidgetImplType
* wxWidgetImpl::CreateContentView( wxNonOwnedWindow
* now
)
1440 // There is a bug in 10.2.X for ::GetRootControl returning the window view instead of
1441 // the content view, so we have to retrieve it explicitly
1443 wxMacControl
* contentview
= new wxMacControl(now
, true /*isRootControl*/);
1444 HIViewFindByID( HIViewGetRoot( (WindowRef
) now
->GetWXWindow() ) , kHIViewWindowContentID
,
1445 contentview
->GetControlRefAddr() ) ;
1446 if ( !contentview
->IsOk() )
1448 // compatibility mode fallback
1449 GetRootControl( (WindowRef
) now
->GetWXWindow() , contentview
->GetControlRefAddr() ) ;
1452 // the root control level handler
1453 contentview
->InstallEventHandler() ;