Files
wxWidgets/src/osx/carbon/window.cpp
Stefan Csomor f1c40652a0 reworked font handling for osx
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@59644 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
2009-03-20 13:56:57 +00:00

1492 lines
51 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// Name: src/osx/carbon/window.cpp
// Purpose: wxWindowMac
// Author: Stefan Csomor
// Modified by:
// Created: 1998-01-01
// RCS-ID: $Id$
// Copyright: (c) Stefan Csomor
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
#include "wx/wxprec.h"
#include "wx/window.h"
#ifndef WX_PRECOMP
#include "wx/log.h"
#include "wx/app.h"
#include "wx/utils.h"
#include "wx/panel.h"
#include "wx/frame.h"
#include "wx/dc.h"
#include "wx/dcclient.h"
#include "wx/button.h"
#include "wx/menu.h"
#include "wx/dialog.h"
#include "wx/settings.h"
#include "wx/msgdlg.h"
#include "wx/scrolbar.h"
#include "wx/statbox.h"
#include "wx/textctrl.h"
#include "wx/toolbar.h"
#include "wx/layout.h"
#include "wx/statusbr.h"
#include "wx/menuitem.h"
#include "wx/treectrl.h"
#include "wx/listctrl.h"
#endif
#include "wx/tooltip.h"
#include "wx/spinctrl.h"
#include "wx/geometry.h"
#if wxUSE_LISTCTRL
#include "wx/listctrl.h"
#endif
#if wxUSE_TREECTRL
#include "wx/treectrl.h"
#endif
#if wxUSE_CARET
#include "wx/caret.h"
#endif
#if wxUSE_POPUPWIN
#include "wx/popupwin.h"
#endif
#if wxUSE_DRAG_AND_DROP
#include "wx/dnd.h"
#endif
#if wxOSX_USE_CARBON
#include "wx/osx/uma.h"
#else
#include "wx/osx/private.h"
// bring in themeing
#include <Carbon/Carbon.h>
#endif
#define MAC_SCROLLBAR_SIZE 15
#define MAC_SMALL_SCROLLBAR_SIZE 11
#include <string.h>
#define wxMAC_DEBUG_REDRAW 0
#ifndef wxMAC_DEBUG_REDRAW
#define wxMAC_DEBUG_REDRAW 0
#endif
// ---------------------------------------------------------------------------
// Carbon Events
// ---------------------------------------------------------------------------
static const EventTypeSpec eventList[] =
{
{ kEventClassCommand, kEventProcessCommand } ,
{ kEventClassCommand, kEventCommandUpdateStatus } ,
{ kEventClassControl , kEventControlGetClickActivation } ,
{ kEventClassControl , kEventControlHit } ,
{ kEventClassTextInput, kEventTextInputUnicodeForKeyEvent } ,
{ kEventClassTextInput, kEventTextInputUpdateActiveInputArea } ,
{ kEventClassControl , kEventControlDraw } ,
{ kEventClassControl , kEventControlVisibilityChanged } ,
{ kEventClassControl , kEventControlEnabledStateChanged } ,
{ kEventClassControl , kEventControlHiliteChanged } ,
{ kEventClassControl , kEventControlActivate } ,
{ kEventClassControl , kEventControlDeactivate } ,
{ kEventClassControl , kEventControlSetFocusPart } ,
{ kEventClassControl , kEventControlFocusPartChanged } ,
{ kEventClassService , kEventServiceGetTypes },
{ kEventClassService , kEventServiceCopy },
{ kEventClassService , kEventServicePaste },
// { kEventClassControl , kEventControlInvalidateForSizeChange } , // 10.3 only
// { kEventClassControl , kEventControlBoundsChanged } ,
} ;
static pascal OSStatus wxMacWindowControlEventHandler( EventHandlerCallRef handler , EventRef event , void *data )
{
OSStatus result = eventNotHandledErr ;
static wxWindowMac* targetFocusWindow = NULL;
static wxWindowMac* formerFocusWindow = NULL;
wxMacCarbonEvent cEvent( event ) ;
ControlRef controlRef ;
wxWindowMac* thisWindow = (wxWindowMac*) data ;
cEvent.GetParameter( kEventParamDirectObject , &controlRef ) ;
switch ( GetEventKind( event ) )
{
case kEventControlDraw :
{
HIShapeRef updateRgn = NULL ;
HIMutableShapeRef allocatedRgn = NULL ;
wxRegion visRegion = thisWindow->MacGetVisibleRegion() ;
// according to the docs: redraw entire control if param not present
if ( cEvent.GetParameter<HIShapeRef>(kEventParamShape, &updateRgn) != noErr )
{
updateRgn = visRegion.GetWXHRGN();
}
else
{
if ( thisWindow->MacGetLeftBorderSize() != 0 || thisWindow->MacGetTopBorderSize() != 0 )
{
// as this update region is in native window locals we must adapt it to wx window local
allocatedRgn = HIShapeCreateMutableCopy(updateRgn);
HIShapeOffset(allocatedRgn, thisWindow->MacGetLeftBorderSize() , thisWindow->MacGetTopBorderSize());
// hide the given region by the new region that must be shifted
updateRgn = allocatedRgn ;
}
}
#if wxMAC_DEBUG_REDRAW
if ( thisWindow->MacIsUserPane() )
{
static float color = 0.5 ;
static int channel = 0 ;
HIRect bounds;
CGContextRef cgContext = cEvent.GetParameter<CGContextRef>(kEventParamCGContextRef) ;
HIViewGetBounds( controlRef, &bounds );
CGContextSetRGBFillColor( cgContext, channel == 0 ? color : 0.5 ,
channel == 1 ? color : 0.5 , channel == 2 ? color : 0.5 , 1 );
CGContextFillRect( cgContext, bounds );
color += 0.1 ;
if ( color > 0.9 )
{
color = 0.5 ;
channel++ ;
if ( channel == 3 )
channel = 0 ;
}
}
#endif
{
bool created = false ;
CGContextRef cgContext = NULL ;
OSStatus err = cEvent.GetParameter<CGContextRef>(kEventParamCGContextRef, &cgContext) ;
if ( err != noErr )
{
wxFAIL_MSG("Unable to retrieve CGContextRef");
}
thisWindow->MacSetCGContextRef( cgContext ) ;
{
wxMacCGContextStateSaver sg( cgContext ) ;
CGFloat alpha = (CGFloat)1.0 ;
{
wxWindow* iter = thisWindow ;
while ( iter )
{
alpha *= (CGFloat)( iter->GetTransparent()/255.0 ) ;
if ( iter->IsTopLevel() )
iter = NULL ;
else
iter = iter->GetParent() ;
}
}
CGContextSetAlpha( cgContext , alpha ) ;
if ( thisWindow->GetBackgroundStyle() == wxBG_STYLE_TRANSPARENT )
{
HIRect bounds;
HIViewGetBounds( controlRef, &bounds );
CGContextClearRect( cgContext, bounds );
}
if ( !HIShapeIsEmpty(updateRgn) )
{
// refcount increase because wxRegion constructor takes ownership of the native region
CFRetain(updateRgn);
thisWindow->GetUpdateRegion() = wxRegion(updateRgn);
if ( !thisWindow->MacDoRedraw( cEvent.GetTicks() ) )
{
// for native controls: call their native paint method
if ( !thisWindow->MacIsUserPane() ||
( thisWindow->IsTopLevel() && thisWindow->GetBackgroundStyle() == wxBG_STYLE_SYSTEM ) )
{
if ( thisWindow->GetBackgroundStyle() != wxBG_STYLE_TRANSPARENT )
CallNextEventHandler( handler ,event ) ;
}
}
thisWindow->MacPaintChildrenBorders();
}
thisWindow->MacSetCGContextRef( NULL ) ;
}
if ( created )
CGContextRelease( cgContext ) ;
}
if ( allocatedRgn )
CFRelease( allocatedRgn ) ;
}
break ;
case kEventControlVisibilityChanged :
// we might have two native controls attributed to the same wxWindow instance
// eg a scrollview and an embedded textview, make sure we only fire for the 'outer'
// control, as otherwise native and wx visibility are different
if ( thisWindow->GetPeer() != NULL && thisWindow->GetPeer()->GetControlRef() == controlRef )
{
thisWindow->MacVisibilityChanged() ;
}
break ;
case kEventControlEnabledStateChanged :
thisWindow->MacEnabledStateChanged();
break ;
case kEventControlHiliteChanged :
thisWindow->MacHiliteChanged() ;
break ;
case kEventControlActivate :
case kEventControlDeactivate :
// FIXME: we should have a virtual function for this!
#if wxUSE_TREECTRL
if ( thisWindow->IsKindOf( CLASSINFO( wxTreeCtrl ) ) )
thisWindow->Refresh();
#endif
#if wxUSE_LISTCTRL
if ( thisWindow->IsKindOf( CLASSINFO( wxListCtrl ) ) )
thisWindow->Refresh();
#endif
break ;
//
// focus handling
// different handling on OS X
//
case kEventControlFocusPartChanged :
// the event is emulated by wxmac for systems lower than 10.5
{
if ( UMAGetSystemVersion() < 0x1050 )
{
// as it is synthesized here, we have to manually avoid propagation
result = noErr;
}
ControlPartCode previousControlPart = cEvent.GetParameter<ControlPartCode>(kEventParamControlPreviousPart , typeControlPartCode );
ControlPartCode currentControlPart = cEvent.GetParameter<ControlPartCode>(kEventParamControlCurrentPart , typeControlPartCode );
if ( thisWindow->MacGetTopLevelWindow() && thisWindow->GetPeer()->NeedsFocusRect() )
{
thisWindow->MacInvalidateBorders();
}
if ( currentControlPart == 0 )
{
// kill focus
#if wxUSE_CARET
if ( thisWindow->GetCaret() )
thisWindow->GetCaret()->OnKillFocus();
#endif
wxLogTrace(_T("Focus"), _T("focus lost(%p)"), static_cast<void*>(thisWindow));
// remove this as soon as posting the synthesized event works properly
static bool inKillFocusEvent = false ;
if ( !inKillFocusEvent )
{
inKillFocusEvent = true ;
wxFocusEvent event( wxEVT_KILL_FOCUS, thisWindow->GetId());
event.SetEventObject(thisWindow);
event.SetWindow(targetFocusWindow);
thisWindow->HandleWindowEvent(event) ;
inKillFocusEvent = false ;
targetFocusWindow = NULL;
}
}
else if ( previousControlPart == 0 )
{
// set focus
// panel wants to track the window which was the last to have focus in it
wxLogTrace(_T("Focus"), _T("focus set(%p)"), static_cast<void*>(thisWindow));
wxChildFocusEvent eventFocus((wxWindow*)thisWindow);
thisWindow->HandleWindowEvent(eventFocus);
#if wxUSE_CARET
if ( thisWindow->GetCaret() )
thisWindow->GetCaret()->OnSetFocus();
#endif
wxFocusEvent event(wxEVT_SET_FOCUS, thisWindow->GetId());
event.SetEventObject(thisWindow);
event.SetWindow(formerFocusWindow);
thisWindow->HandleWindowEvent(event) ;
formerFocusWindow = NULL;
}
}
break;
case kEventControlSetFocusPart :
{
Boolean focusEverything = false ;
if ( cEvent.GetParameter<Boolean>(kEventParamControlFocusEverything , &focusEverything ) == noErr )
{
// put a breakpoint here to catch focus everything events
}
ControlPartCode controlPart = cEvent.GetParameter<ControlPartCode>(kEventParamControlPart , typeControlPartCode );
if ( controlPart != kControlFocusNoPart )
{
targetFocusWindow = thisWindow;
wxLogTrace(_T("Focus"), _T("focus to be set(%p)"), static_cast<void*>(thisWindow));
}
else
{
formerFocusWindow = thisWindow;
wxLogTrace(_T("Focus"), _T("focus to be lost(%p)"), static_cast<void*>(thisWindow));
}
ControlPartCode previousControlPart = 0;
verify_noerr( HIViewGetFocusPart(controlRef, &previousControlPart));
if ( thisWindow->MacIsUserPane() )
{
if ( controlPart != kControlFocusNoPart )
cEvent.SetParameter<ControlPartCode>( kEventParamControlPart, typeControlPartCode, 1 ) ;
result = noErr ;
}
else
result = CallNextEventHandler(handler, event);
if ( UMAGetSystemVersion() < 0x1050 )
{
// set back to 0 if problems arise
#if 1
if ( result == noErr )
{
ControlPartCode currentControlPart = cEvent.GetParameter<ControlPartCode>(kEventParamControlPart , typeControlPartCode );
// synthesize the event focus changed event
EventRef evRef = NULL ;
OSStatus err = MacCreateEvent(
NULL , kEventClassControl , kEventControlFocusPartChanged , TicksToEventTime( TickCount() ) ,
kEventAttributeUserEvent , &evRef );
verify_noerr( err );
wxMacCarbonEvent iEvent( evRef ) ;
iEvent.SetParameter<ControlRef>( kEventParamDirectObject , controlRef );
iEvent.SetParameter<EventTargetRef>( kEventParamPostTarget, typeEventTargetRef, GetControlEventTarget( controlRef ) );
iEvent.SetParameter<ControlPartCode>( kEventParamControlPreviousPart, typeControlPartCode, previousControlPart );
iEvent.SetParameter<ControlPartCode>( kEventParamControlCurrentPart, typeControlPartCode, currentControlPart );
#if 1
// TODO test this first, avoid double posts etc...
PostEventToQueue( GetMainEventQueue(), evRef , kEventPriorityHigh );
#else
wxMacWindowControlEventHandler( NULL , evRef , data ) ;
#endif
ReleaseEvent( evRef ) ;
}
#else
// old implementation, to be removed if the new one works
if ( controlPart == kControlFocusNoPart )
{
#if wxUSE_CARET
if ( thisWindow->GetCaret() )
thisWindow->GetCaret()->OnKillFocus();
#endif
wxLogTrace(_T("Focus"), _T("focus lost(%p)"), static_cast<void*>(thisWindow));
static bool inKillFocusEvent = false ;
if ( !inKillFocusEvent )
{
inKillFocusEvent = true ;
wxFocusEvent event( wxEVT_KILL_FOCUS, thisWindow->GetId());
event.SetEventObject(thisWindow);
thisWindow->HandleWindowEvent(event) ;
inKillFocusEvent = false ;
}
}
else
{
// panel wants to track the window which was the last to have focus in it
wxLogTrace(_T("Focus"), _T("focus set(%p)"), static_cast<void*>(thisWindow));
wxChildFocusEvent eventFocus((wxWindow*)thisWindow);
thisWindow->HandleWindowEvent(eventFocus);
#if wxUSE_CARET
if ( thisWindow->GetCaret() )
thisWindow->GetCaret()->OnSetFocus();
#endif
wxFocusEvent event(wxEVT_SET_FOCUS, thisWindow->GetId());
event.SetEventObject(thisWindow);
thisWindow->HandleWindowEvent(event) ;
}
#endif
}
}
break ;
case kEventControlHit :
result = thisWindow->MacControlHit( handler , event ) ;
break ;
case kEventControlGetClickActivation :
{
// fix to always have a proper activation for DataBrowser controls (stay in bkgnd otherwise)
WindowRef owner = cEvent.GetParameter<WindowRef>(kEventParamWindowRef);
if ( !IsWindowActive(owner) )
{
cEvent.SetParameter(kEventParamClickActivation,typeClickActivationResult, (UInt32) kActivateAndIgnoreClick) ;
result = noErr ;
}
}
break ;
default :
break ;
}
return result ;
}
static pascal OSStatus
wxMacWindowServiceEventHandler(EventHandlerCallRef WXUNUSED(handler),
EventRef event,
void *data)
{
OSStatus result = eventNotHandledErr ;
wxMacCarbonEvent cEvent( event ) ;
ControlRef controlRef ;
wxWindowMac* thisWindow = (wxWindowMac*) data ;
wxTextCtrl* textCtrl = wxDynamicCast( thisWindow , wxTextCtrl ) ;
cEvent.GetParameter( kEventParamDirectObject , &controlRef ) ;
switch ( GetEventKind( event ) )
{
case kEventServiceGetTypes :
if ( textCtrl )
{
long from, to ;
textCtrl->GetSelection( &from , &to ) ;
CFMutableArrayRef copyTypes = 0 , pasteTypes = 0;
if ( from != to )
copyTypes = cEvent.GetParameter< CFMutableArrayRef >( kEventParamServiceCopyTypes , typeCFMutableArrayRef ) ;
if ( textCtrl->IsEditable() )
pasteTypes = cEvent.GetParameter< CFMutableArrayRef >( kEventParamServicePasteTypes , typeCFMutableArrayRef ) ;
static const OSType textDataTypes[] = { kTXNTextData /* , 'utxt', 'PICT', 'MooV', 'AIFF' */ };
for ( size_t i = 0 ; i < WXSIZEOF(textDataTypes) ; ++i )
{
CFStringRef typestring = CreateTypeStringWithOSType(textDataTypes[i]);
if ( typestring )
{
if ( copyTypes )
CFArrayAppendValue(copyTypes, typestring) ;
if ( pasteTypes )
CFArrayAppendValue(pasteTypes, typestring) ;
CFRelease( typestring ) ;
}
}
result = noErr ;
}
break ;
case kEventServiceCopy :
if ( textCtrl )
{
long from, to ;
textCtrl->GetSelection( &from , &to ) ;
wxString val = textCtrl->GetValue() ;
val = val.Mid( from , to - from ) ;
PasteboardRef pasteboard = cEvent.GetParameter<PasteboardRef>( kEventParamPasteboardRef, typePasteboardRef );
verify_noerr( PasteboardClear( pasteboard ) ) ;
PasteboardSynchronize( pasteboard );
// TODO add proper conversion
CFDataRef data = CFDataCreate( kCFAllocatorDefault, (const UInt8*)val.c_str(), val.length() );
PasteboardPutItemFlavor( pasteboard, (PasteboardItemID) 1, CFSTR("com.apple.traditional-mac-plain-text"), data, 0);
CFRelease( data );
result = noErr ;
}
break ;
case kEventServicePaste :
if ( textCtrl )
{
PasteboardRef pasteboard = cEvent.GetParameter<PasteboardRef>( kEventParamPasteboardRef, typePasteboardRef );
PasteboardSynchronize( pasteboard );
ItemCount itemCount;
verify_noerr( PasteboardGetItemCount( pasteboard, &itemCount ) );
for( UInt32 itemIndex = 1; itemIndex <= itemCount; itemIndex++ )
{
PasteboardItemID itemID;
if ( PasteboardGetItemIdentifier( pasteboard, itemIndex, &itemID ) == noErr )
{
CFDataRef flavorData = NULL;
if ( PasteboardCopyItemFlavorData( pasteboard, itemID, CFSTR("com.apple.traditional-mac-plain-text"), &flavorData ) == noErr )
{
CFIndex flavorDataSize = CFDataGetLength( flavorData );
char *content = new char[flavorDataSize+1] ;
memcpy( content, CFDataGetBytePtr( flavorData ), flavorDataSize );
content[flavorDataSize]=0;
CFRelease( flavorData );
#if wxUSE_UNICODE
textCtrl->WriteText( wxString( content , wxConvLocal ) );
#else
textCtrl->WriteText( wxString( content ) ) ;
#endif
delete[] content ;
result = noErr ;
}
}
}
}
break ;
default:
break ;
}
return result ;
}
pascal OSStatus wxMacUnicodeTextEventHandler( EventHandlerCallRef handler , EventRef event , void *data )
{
OSStatus result = eventNotHandledErr ;
wxWindowMac* focus = (wxWindowMac*) data ;
wchar_t* uniChars = NULL ;
UInt32 when = EventTimeToTicks( GetEventTime( event ) ) ;
UniChar* charBuf = NULL;
ByteCount dataSize = 0 ;
int numChars = 0 ;
UniChar buf[2] ;
if ( GetEventParameter( event, kEventParamTextInputSendText, typeUnicodeText, NULL, 0 , &dataSize, NULL ) == noErr )
{
numChars = dataSize / sizeof( UniChar) + 1;
charBuf = buf ;
if ( (size_t) numChars * 2 > sizeof(buf) )
charBuf = new UniChar[ numChars ] ;
else
charBuf = buf ;
uniChars = new wchar_t[ numChars ] ;
GetEventParameter( event, kEventParamTextInputSendText, typeUnicodeText, NULL, dataSize , NULL , charBuf ) ;
charBuf[ numChars - 1 ] = 0;
#if SIZEOF_WCHAR_T == 2
uniChars = (wchar_t*) charBuf ;
/* 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...)
#else
// the resulting string will never have more chars than the utf16 version, so this is safe
wxMBConvUTF16 converter ;
numChars = converter.MB2WC( uniChars , (const char*)charBuf , numChars ) ;
#endif
}
switch ( GetEventKind( event ) )
{
case kEventTextInputUpdateActiveInputArea :
{
// An IME input event may return several characters, but we need to send one char at a time to
// EVT_CHAR
for (int pos=0 ; pos < numChars ; pos++)
{
WXEVENTREF formerEvent = wxTheApp->MacGetCurrentEvent() ;
WXEVENTHANDLERCALLREF formerHandler = wxTheApp->MacGetCurrentEventHandlerCallRef() ;
wxTheApp->MacSetCurrentEvent( event , handler ) ;
UInt32 message = uniChars[pos] < 128 ? (char)uniChars[pos] : '?';
/*
NB: faking a charcode here is problematic. The kEventTextInputUpdateActiveInputArea event is sent
multiple times to update the active range during inline input, so this handler will often receive
uncommited text, which should usually not trigger side effects. It might be a good idea to check the
kEventParamTextInputSendFixLen parameter and verify if input is being confirmed (see CarbonEvents.h).
On the other hand, it can be useful for some applications to react to uncommitted text (for example,
to update a status display), as long as it does not disrupt the inline input session. Ideally, wx
should add new event types to support advanced text input. For now, I would keep things as they are.
However, the code that was being used caused additional problems:
UInt32 message = (0 << 8) + ((char)uniChars[pos] );
Since it simply truncated the unichar to the last byte, it ended up causing weird bugs with inline
input, such as switching to another field when one attempted to insert the character U+4E09 (the kanji
for "three"), because it was truncated to 09 (kTabCharCode), which was later "converted" to WXK_TAB
(still 09) in wxMacTranslateKey; or triggering the default button when one attempted to insert U+840D
(the kanji for "name"), which got truncated to 0D and interpreted as a carriage return keypress.
Note that even single-byte characters could have been misinterpreted, since MacRoman charcodes only
overlap with Unicode within the (7-bit) ASCII range.
But simply passing a NUL charcode would disable text updated events, because wxTextCtrl::OnChar checks
for codes within a specific range. Therefore I went for the solution seen above, which keeps ASCII
characters as they are and replaces the rest with '?', ensuring that update events are triggered.
It would be better to change wxTextCtrl::OnChar to look at the actual unicode character instead, but
I don't have time to look into that right now.
-- CL
*/
if ( wxTheApp->MacSendCharEvent(
focus , message , 0 , when , 0 , 0 , uniChars[pos] ) )
{
result = noErr ;
}
wxTheApp->MacSetCurrentEvent( formerEvent , formerHandler ) ;
}
}
break ;
case kEventTextInputUnicodeForKeyEvent :
{
UInt32 keyCode, modifiers ;
Point point ;
EventRef rawEvent ;
unsigned char charCode ;
GetEventParameter( event, kEventParamTextInputSendKeyboardEvent, typeEventRef, NULL, sizeof(rawEvent), NULL, &rawEvent ) ;
GetEventParameter( rawEvent, kEventParamKeyMacCharCodes, typeChar, NULL, sizeof(char), NULL, &charCode );
GetEventParameter( rawEvent, kEventParamKeyCode, typeUInt32, NULL, sizeof(UInt32), NULL, &keyCode );
GetEventParameter( rawEvent, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(UInt32), NULL, &modifiers );
GetEventParameter( rawEvent, kEventParamMouseLocation, typeQDPoint, NULL, sizeof(Point), NULL, &point );
UInt32 message = (keyCode << 8) + charCode;
// An IME input event may return several characters, but we need to send one char at a time to
// EVT_CHAR
for (int pos=0 ; pos < numChars ; pos++)
{
WXEVENTREF formerEvent = wxTheApp->MacGetCurrentEvent() ;
WXEVENTHANDLERCALLREF formerHandler = wxTheApp->MacGetCurrentEventHandlerCallRef() ;
wxTheApp->MacSetCurrentEvent( event , handler ) ;
if ( wxTheApp->MacSendCharEvent(
focus , message , modifiers , when , point.h , point.v , uniChars[pos] ) )
{
result = noErr ;
}
wxTheApp->MacSetCurrentEvent( formerEvent , formerHandler ) ;
}
}
break;
default:
break ;
}
delete [] uniChars ;
if ( charBuf != buf )
delete [] charBuf ;
return result ;
}
static pascal OSStatus
wxMacWindowCommandEventHandler(EventHandlerCallRef WXUNUSED(handler),
EventRef event,
void *data)
{
OSStatus result = eventNotHandledErr ;
wxWindowMac* focus = (wxWindowMac*) data ;
HICommand command ;
wxMacCarbonEvent cEvent( event ) ;
cEvent.GetParameter<HICommand>(kEventParamDirectObject,typeHICommand,&command) ;
wxMenuItem* item = NULL ;
wxMenu* itemMenu = wxFindMenuFromMacCommand( command , item ) ;
if ( item )
{
wxASSERT( itemMenu != NULL ) ;
switch ( cEvent.GetKind() )
{
case kEventProcessCommand :
if ( itemMenu->HandleCommandProcess( item, focus ) )
result = noErr;
break ;
case kEventCommandUpdateStatus:
if ( itemMenu->HandleCommandUpdateStatus( item, focus ) )
result = noErr;
break ;
default :
break ;
}
}
return result ;
}
pascal OSStatus wxMacWindowEventHandler( EventHandlerCallRef handler , EventRef event , void *data )
{
EventRef formerEvent = (EventRef) wxTheApp->MacGetCurrentEvent() ;
EventHandlerCallRef formerEventHandlerCallRef = (EventHandlerCallRef) wxTheApp->MacGetCurrentEventHandlerCallRef() ;
wxTheApp->MacSetCurrentEvent( event , handler ) ;
OSStatus result = eventNotHandledErr ;
switch ( GetEventClass( event ) )
{
case kEventClassCommand :
result = wxMacWindowCommandEventHandler( handler , event , data ) ;
break ;
case kEventClassControl :
result = wxMacWindowControlEventHandler( handler, event, data ) ;
break ;
case kEventClassService :
result = wxMacWindowServiceEventHandler( handler, event , data ) ;
break ;
case kEventClassTextInput :
result = wxMacUnicodeTextEventHandler( handler , event , data ) ;
break ;
default :
break ;
}
wxTheApp->MacSetCurrentEvent( formerEvent, formerEventHandlerCallRef ) ;
return result ;
}
DEFINE_ONE_SHOT_HANDLER_GETTER( wxMacWindowEventHandler )
// ---------------------------------------------------------------------------
// Scrollbar Tracking for all
// ---------------------------------------------------------------------------
pascal void wxMacLiveScrollbarActionProc( ControlRef control , ControlPartCode partCode ) ;
pascal void wxMacLiveScrollbarActionProc( ControlRef control , ControlPartCode partCode )
{
if ( partCode != 0)
{
wxWindow* wx = wxFindWindowFromWXWidget( (WXWidget) control ) ;
if ( wx )
{
wxEventType scrollEvent = wxEVT_NULL;
switch ( partCode )
{
case kControlUpButtonPart:
scrollEvent = wxEVT_SCROLL_LINEUP;
break;
case kControlDownButtonPart:
scrollEvent = wxEVT_SCROLL_LINEDOWN;
break;
case kControlPageUpPart:
scrollEvent = wxEVT_SCROLL_PAGEUP;
break;
case kControlPageDownPart:
scrollEvent = wxEVT_SCROLL_PAGEDOWN;
break;
case kControlIndicatorPart:
scrollEvent = wxEVT_SCROLL_THUMBTRACK;
// when this is called as a live proc, mouse is always still down
// so no need for thumbrelease
// scrollEvent = wxEVT_SCROLL_THUMBRELEASE;
break;
}
wx->TriggerScrollEvent(scrollEvent) ;
}
}
}
wxMAC_DEFINE_PROC_GETTER( ControlActionUPP , wxMacLiveScrollbarActionProc ) ;
wxWidgetImplType* wxWidgetImpl::CreateUserPane( wxWindowMac* wxpeer, wxWindowMac* parent, wxWindowID id, const wxPoint& pos, const wxSize& size,
long style, long extraStyle)
{
OSStatus err = noErr;
Rect bounds = wxMacGetBoundsForControl( wxpeer , pos , size ) ;
wxMacControl* c = new wxMacControl(wxpeer) ;
UInt32 features = 0
| kControlSupportsEmbedding
| kControlSupportsLiveFeedback
| kControlGetsFocusOnClick
// | kControlHasSpecialBackground
// | kControlSupportsCalcBestRect
| kControlHandlesTracking
| kControlSupportsFocus
| kControlWantsActivate
| kControlWantsIdle ;
err =::CreateUserPaneControl( MAC_WXHWND(wxpeer->GetParent()->MacGetTopLevelWindowRef()) , &bounds, features , c->GetControlRefAddr() );
verify_noerr( err );
return c;
}
void wxMacControl::InstallEventHandler( WXWidget control )
{
wxWidgetImpl::Associate( control ? control : (WXWidget) m_controlRef , this ) ;
::InstallControlEventHandler( control ? (ControlRef) control : m_controlRef , GetwxMacWindowEventHandlerUPP(),
GetEventTypeCount(eventList), eventList, GetWXPeer(), NULL);
}
IMPLEMENT_DYNAMIC_CLASS( wxMacControl , wxWidgetImpl )
wxMacControl::wxMacControl()
{
Init();
}
wxMacControl::wxMacControl(wxWindowMac* peer , bool isRootControl ) :
wxWidgetImpl( peer, isRootControl )
{
Init();
}
wxMacControl::~wxMacControl()
{
if ( m_controlRef && !IsRootControl() )
{
wxASSERT_MSG( m_controlRef != NULL , wxT("Control Handle already NULL, Dispose called twice ?") );
wxASSERT_MSG( IsValidControlHandle(m_controlRef) , wxT("Invalid Control Handle (maybe already released) in Dispose") );
wxWidgetImpl::RemoveAssociations( this ) ;
// we cannot check the ref count here anymore, as autorelease objects might delete their refs later
// we can have situations when being embedded, where the control gets deleted behind our back, so only
// CFRelease if we are safe
if ( IsValidControlHandle(m_controlRef) )
CFRelease(m_controlRef);
}
m_controlRef = NULL;
}
void wxMacControl::Init()
{
m_controlRef = NULL;
m_macControlEventHandler = NULL;
}
void wxMacControl::RemoveFromParent()
{
// nothing to do here for carbon
HIViewRemoveFromSuperview(m_controlRef);
}
void wxMacControl::Embed( wxWidgetImpl *parent )
{
HIViewAddSubview((ControlRef)parent->GetWXWidget(), m_controlRef);
}
void wxMacControl::SetNeedsDisplay( const wxRect* rect )
{
if ( !IsVisible() )
return;
if ( rect != NULL )
{
HIRect updatearea = CGRectMake( rect->x , rect->y , rect->width , rect->height);
HIViewSetNeedsDisplayInRect( m_controlRef, &updatearea, true );
}
else
HIViewSetNeedsDisplay( m_controlRef , true );
}
void wxMacControl::Raise()
{
verify_noerr( HIViewSetZOrder( m_controlRef, kHIViewZOrderAbove, NULL ) );
}
void wxMacControl::Lower()
{
verify_noerr( HIViewSetZOrder( m_controlRef, kHIViewZOrderBelow, NULL ) );
}
void wxMacControl::GetContentArea(int &left , int &top , int &width , int &height) const
{
HIShapeRef rgn = NULL;
Rect content ;
if ( HIViewCopyShape(m_controlRef, kHIViewContentMetaPart, &rgn) == noErr)
{
CGRect cgrect;
HIShapeGetBounds(rgn, &cgrect);
content = (Rect){ cgrect.origin.y, cgrect.origin.x, cgrect.origin.y+cgrect.size.height, cgrect.origin.x+cgrect.size.width };
CFRelease(rgn);
}
else
{
GetControlBounds(m_controlRef, &content);
content.right -= content.left;
content.left = 0;
content.bottom -= content.top;
content.top = 0;
}
left = content.left;
top = content.top;
width = content.right - content.left ;
height = content.bottom - content.top ;
}
void wxMacControl::Move(int x, int y, int width, int height)
{
HIRect hir = CGRectMake(x,y,width,height);
HIViewSetFrame ( m_controlRef , &hir );
}
void wxMacControl::GetPosition( int &x, int &y ) const
{
Rect r;
GetControlBounds( m_controlRef , &r );
x = r.left;
y = r.top;
}
void wxMacControl::GetSize( int &width, int &height ) const
{
Rect r;
GetControlBounds( m_controlRef , &r );
width = r.right - r.left;
height = r.bottom - r.top;
}
void wxMacControl::SetControlSize( wxWindowVariant variant )
{
ControlSize size ;
switch ( variant )
{
case wxWINDOW_VARIANT_NORMAL :
size = kControlSizeNormal;
break ;
case wxWINDOW_VARIANT_SMALL :
size = kControlSizeSmall;
break ;
case wxWINDOW_VARIANT_MINI :
// not always defined in the headers
size = 3 ;
break ;
case wxWINDOW_VARIANT_LARGE :
size = kControlSizeLarge;
break ;
default:
wxFAIL_MSG(_T("unexpected window variant"));
break ;
}
SetData<ControlSize>(kControlEntireControl, kControlSizeTag, &size ) ;
}
void wxMacControl::ScrollRect( const wxRect *rect, int dx, int dy )
{
if (GetNeedsDisplay() )
{
// because HIViewScrollRect does not scroll the already invalidated area we have two options:
// in case there is already a pending redraw on that area
// either immediate redraw or full invalidate
#if 1
// is the better overall solution, as it does not slow down scrolling
SetNeedsDisplay() ;
#else
// this would be the preferred version for fast drawing controls
HIViewRender(GetControlRef()) ;
#endif
}
// note there currently is a bug in OSX (10.3 +?) which makes inefficient refreshes in case an entire control
// area is scrolled, this does not occur if width and height are 2 pixels less,
// TODO: write optimal workaround
HIRect scrollarea = CGRectMake( rect->x , rect->y , rect->width , rect->height);
HIViewScrollRect ( m_controlRef , &scrollarea , dx ,dy );
#if 0
// this would be the preferred version for fast drawing controls
HIViewRender(GetControlRef()) ;
#endif
}
bool wxMacControl::CanFocus() const
{
// TODO : evaluate performance hits by looking up this value, eventually cache the results for a 1 sec or so
// CAUTION : the value returned currently is 0 or 2, I've also found values of 1 having the same meaning,
// but the value range is nowhere documented
Boolean keyExistsAndHasValidFormat ;
CFIndex fullKeyboardAccess = CFPreferencesGetAppIntegerValue( CFSTR("AppleKeyboardUIMode" ) ,
kCFPreferencesCurrentApplication, &keyExistsAndHasValidFormat );
if ( keyExistsAndHasValidFormat && fullKeyboardAccess > 0 )
{
return true ;
}
else
{
UInt32 features = 0 ;
GetControlFeatures( m_controlRef, &features ) ;
return features & ( kControlSupportsFocus | kControlGetsFocusOnClick ) ;
}
}
bool wxMacControl::GetNeedsDisplay() const
{
return HIViewGetNeedsDisplay( m_controlRef );
}
void wxWidgetImpl::Convert( wxPoint *pt , wxWidgetImpl *from , wxWidgetImpl *to )
{
HIPoint hiPoint;
hiPoint.x = pt->x;
hiPoint.y = pt->y;
HIViewConvertPoint( &hiPoint , (ControlRef) from->GetWXWidget() , (ControlRef) to->GetWXWidget() );
pt->x = (int)hiPoint.x;
pt->y = (int)hiPoint.y;
}
bool wxMacControl::SetFocus()
{
// as we cannot rely on the control features to find out whether we are in full keyboard mode,
// we can only leave in case of an error
OSStatus err = SetKeyboardFocus( GetControlOwner( m_controlRef ), m_controlRef, kControlFocusNextPart );
if ( err == errCouldntSetFocus )
return false ;
SetUserFocusWindow(GetControlOwner( m_controlRef ) );
return true;
}
bool wxMacControl::HasFocus() const
{
ControlRef control;
GetKeyboardFocus( GetUserFocusWindow() , &control );
return control == m_controlRef;
}
void wxMacControl::SetCursor(const wxCursor& cursor)
{
wxWindowMac *mouseWin = 0 ;
WindowRef window = GetControlOwner( m_controlRef ) ;
wxNonOwnedWindow* tlwwx = wxNonOwnedWindow::GetFromWXWindow( (WXWindow) window ) ;
if ( tlwwx != NULL )
{
ControlPartCode part ;
ControlRef control ;
Point pt ;
#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
HIPoint hiPoint ;
HIGetMousePosition(kHICoordSpaceWindow, window, &hiPoint);
pt.h = hiPoint.x;
pt.v = hiPoint.y;
#else
GetGlobalMouse( &pt );
int x = pt.h;
int y = pt.v;
tlwwx->ScreenToClient(&x, &y);
pt.h = x;
pt.v = y;
#endif
control = FindControlUnderMouse( pt , window , &part ) ;
if ( control )
mouseWin = wxFindWindowFromWXWidget( (WXWidget) control ) ;
}
if ( mouseWin == tlwwx && !wxIsBusy() )
cursor.MacInstall() ;
}
void wxMacControl::CaptureMouse()
{
}
void wxMacControl::ReleaseMouse()
{
}
//
// subclass specifics
//
OSStatus wxMacControl::GetData(ControlPartCode inPartCode , ResType inTag , Size inBufferSize , void * inOutBuffer , Size * outActualSize ) const
{
return ::GetControlData( m_controlRef , inPartCode , inTag , inBufferSize , inOutBuffer , outActualSize );
}
OSStatus wxMacControl::GetDataSize(ControlPartCode inPartCode , ResType inTag , Size * outActualSize ) const
{
return ::GetControlDataSize( m_controlRef , inPartCode , inTag , outActualSize );
}
OSStatus wxMacControl::SetData(ControlPartCode inPartCode , ResType inTag , Size inSize , const void * inData)
{
return ::SetControlData( m_controlRef , inPartCode , inTag , inSize , inData );
}
OSStatus wxMacControl::SendEvent( EventRef event , OptionBits inOptions )
{
return SendEventToEventTargetWithOptions( event,
HIObjectGetEventTarget( (HIObjectRef) m_controlRef ), inOptions );
}
OSStatus wxMacControl::SendHICommand( HICommand &command , OptionBits inOptions )
{
wxMacCarbonEvent event( kEventClassCommand , kEventCommandProcess );
event.SetParameter<HICommand>(kEventParamDirectObject,command);
return SendEvent( event , inOptions );
}
OSStatus wxMacControl::SendHICommand( UInt32 commandID , OptionBits inOptions )
{
HICommand command;
memset( &command, 0 , sizeof(command) );
command.commandID = commandID;
return SendHICommand( command , inOptions );
}
void wxMacControl::PerformClick()
{
HIViewSimulateClick (m_controlRef, kControlButtonPart, 0, NULL );
}
wxInt32 wxMacControl::GetValue() const
{
return ::GetControl32BitValue( m_controlRef );
}
wxInt32 wxMacControl::GetMaximum() const
{
return ::GetControl32BitMaximum( m_controlRef );
}
wxInt32 wxMacControl::GetMinimum() const
{
return ::GetControl32BitMinimum( m_controlRef );
}
void wxMacControl::SetValue( wxInt32 v )
{
::SetControl32BitValue( m_controlRef , v );
}
void wxMacControl::SetMinimum( wxInt32 v )
{
::SetControl32BitMinimum( m_controlRef , v );
}
void wxMacControl::SetMaximum( wxInt32 v )
{
::SetControl32BitMaximum( m_controlRef , v );
}
void wxMacControl::SetValueAndRange( SInt32 value , SInt32 minimum , SInt32 maximum )
{
::SetControl32BitMinimum( m_controlRef , minimum );
::SetControl32BitMaximum( m_controlRef , maximum );
::SetControl32BitValue( m_controlRef , value );
}
void wxMacControl::VisibilityChanged(bool WXUNUSED(shown))
{
}
void wxMacControl::SuperChangedPosition()
{
}
void wxMacControl::SetFont( const wxFont & font , const wxColour& foreground , long windowStyle, bool ignoreBlack )
{
m_font = font;
#if wxOSX_USE_CORE_TEXT
if ( UMAGetSystemVersion() >= 0x1050 )
{
HIViewPartCode part = 0;
HIThemeTextHorizontalFlush flush = kHIThemeTextHorizontalFlushDefault;
if ( ( windowStyle & wxALIGN_MASK ) & wxALIGN_CENTER_HORIZONTAL )
flush = kHIThemeTextHorizontalFlushCenter;
else if ( ( windowStyle & wxALIGN_MASK ) & wxALIGN_RIGHT )
flush = kHIThemeTextHorizontalFlushRight;
HIViewSetTextFont( m_controlRef , part , (CTFontRef) font.GetCTFont() );
HIViewSetTextHorizontalFlush( m_controlRef, part, flush );
if ( foreground != *wxBLACK || ignoreBlack == false )
{
ControlFontStyleRec fontStyle;
foreground.GetRGBColor( &fontStyle.foreColor );
fontStyle.flags = kControlUseForeColorMask;
::SetControlFontStyle( m_controlRef , &fontStyle );
}
}
#endif
#if wxOSX_USE_ATSU_TEXT
ControlFontStyleRec fontStyle;
if ( font.MacGetThemeFontID() != kThemeCurrentPortFont )
{
switch ( font.MacGetThemeFontID() )
{
case kThemeSmallSystemFont :
fontStyle.font = kControlFontSmallSystemFont;
break;
case 109 : // mini font
fontStyle.font = -5;
break;
case kThemeSystemFont :
fontStyle.font = kControlFontBigSystemFont;
break;
default :
fontStyle.font = kControlFontBigSystemFont;
break;
}
fontStyle.flags = kControlUseFontMask;
}
else
{
fontStyle.font = font.MacGetFontNum();
fontStyle.style = font.MacGetFontStyle();
fontStyle.size = font.GetPointSize();
fontStyle.flags = kControlUseFontMask | kControlUseFaceMask | kControlUseSizeMask;
}
fontStyle.just = teJustLeft;
fontStyle.flags |= kControlUseJustMask;
if ( ( windowStyle & wxALIGN_MASK ) & wxALIGN_CENTER_HORIZONTAL )
fontStyle.just = teJustCenter;
else if ( ( windowStyle & wxALIGN_MASK ) & wxALIGN_RIGHT )
fontStyle.just = teJustRight;
// we only should do this in case of a non-standard color, as otherwise 'disabled' controls
// won't get grayed out by the system anymore
if ( foreground != *wxBLACK || ignoreBlack == false )
{
foreground.GetRGBColor( &fontStyle.foreColor );
fontStyle.flags |= kControlUseForeColorMask;
}
::SetControlFontStyle( m_controlRef , &fontStyle );
#endif
}
void wxMacControl::SetBackgroundColour( const wxColour &WXUNUSED(col) )
{
// HITextViewSetBackgroundColor( m_textView , color );
}
void wxMacControl::SetRange( SInt32 minimum , SInt32 maximum )
{
::SetControl32BitMinimum( m_controlRef , minimum );
::SetControl32BitMaximum( m_controlRef , maximum );
}
short wxMacControl::HandleKey( SInt16 keyCode, SInt16 charCode, EventModifiers modifiers )
{
return HandleControlKey( m_controlRef , keyCode , charCode , modifiers );
}
void wxMacControl::SetActionProc( ControlActionUPP actionProc )
{
SetControlAction( m_controlRef , actionProc );
}
SInt32 wxMacControl::GetViewSize() const
{
return GetControlViewSize( m_controlRef );
}
bool wxMacControl::IsVisible() const
{
return IsControlVisible( m_controlRef );
}
void wxMacControl::SetVisibility( bool visible )
{
SetControlVisibility( m_controlRef , visible, true );
}
bool wxMacControl::IsEnabled() const
{
return IsControlEnabled( m_controlRef );
}
bool wxMacControl::IsActive() const
{
return IsControlActive( m_controlRef );
}
void wxMacControl::Enable( bool enable )
{
if ( enable )
EnableControl( m_controlRef );
else
DisableControl( m_controlRef );
}
void wxMacControl::SetDrawingEnabled( bool enable )
{
HIViewSetDrawingEnabled( m_controlRef , enable );
}
void wxMacControl::GetRectInWindowCoords( Rect *r )
{
GetControlBounds( m_controlRef , r ) ;
WindowRef tlwref = GetControlOwner( m_controlRef ) ;
wxNonOwnedWindow* tlwwx = wxNonOwnedWindow::GetFromWXWindow( (WXWindow) tlwref ) ;
if ( tlwwx != NULL )
{
ControlRef rootControl = tlwwx->GetPeer()->GetControlRef() ;
HIPoint hiPoint = CGPointMake( 0 , 0 ) ;
HIViewConvertPoint( &hiPoint , HIViewGetSuperview(m_controlRef) , rootControl ) ;
OffsetRect( r , (short) hiPoint.x , (short) hiPoint.y ) ;
}
}
void wxMacControl::GetBestRect( wxRect *rect ) const
{
short baselineoffset;
Rect r = {0,0,0,0};
GetBestControlRect( m_controlRef , &r , &baselineoffset );
*rect = wxRect( r.left, r.top, r.right - r.left, r.bottom-r.top );
}
void wxMacControl::GetBestRect( Rect *r ) const
{
short baselineoffset;
GetBestControlRect( m_controlRef , r , &baselineoffset );
}
void wxMacControl::SetLabel( const wxString &title , wxFontEncoding encoding)
{
SetControlTitleWithCFString( m_controlRef , wxCFStringRef( title , encoding ) );
}
void wxMacControl::GetFeatures( UInt32 * features )
{
GetControlFeatures( m_controlRef , features );
}
void wxMacControl::PulseGauge()
{
}
// SetNeedsDisplay would not invalidate the children
static void InvalidateControlAndChildren( HIViewRef control )
{
HIViewSetNeedsDisplay( control , true );
UInt16 childrenCount = 0;
OSStatus err = CountSubControls( control , &childrenCount );
if ( err == errControlIsNotEmbedder )
return;
wxASSERT_MSG( err == noErr , wxT("Unexpected error when accessing subcontrols") );
for ( UInt16 i = childrenCount; i >=1; --i )
{
HIViewRef child;
err = GetIndexedSubControl( control , i , & child );
if ( err == errControlIsNotEmbedder )
return;
InvalidateControlAndChildren( child );
}
}
void wxMacControl::InvalidateWithChildren()
{
InvalidateControlAndChildren( m_controlRef );
}
OSType wxMacCreator = 'WXMC';
OSType wxMacControlProperty = 'MCCT';
void wxMacControl::SetReferenceInNativeControl()
{
void * data = this;
verify_noerr( SetControlProperty ( m_controlRef ,
wxMacCreator,wxMacControlProperty, sizeof(data), &data ) );
}
wxMacControl* wxMacControl::GetReferenceFromNativeControl(ControlRef control)
{
wxMacControl* ctl = NULL;
ByteCount actualSize;
if ( GetControlProperty( control ,wxMacCreator,wxMacControlProperty, sizeof(ctl) ,
&actualSize , &ctl ) == noErr )
{
return ctl;
}
return NULL;
}
void wxMacControl::SetBitmap( const wxBitmap& WXUNUSED(bmp) )
{
// implemented in the respective subclasses
}
void wxMacControl::SetScrollThumb( wxInt32 WXUNUSED(pos), wxInt32 WXUNUSED(viewsize) )
{
// implemented in respective subclass
}
//
// Tab Control
//
OSStatus wxMacControl::SetTabEnabled( SInt16 tabNo , bool enable )
{
return ::SetTabEnabled( m_controlRef , tabNo , enable );
}
// Control Factory
wxWidgetImplType* wxWidgetImpl::CreateContentView( wxNonOwnedWindow* now )
{
// There is a bug in 10.2.X for ::GetRootControl returning the window view instead of
// the content view, so we have to retrieve it explicitly
wxMacControl* contentview = new wxMacControl(now , true /*isRootControl*/);
HIViewFindByID( HIViewGetRoot( (WindowRef) now->GetWXWindow() ) , kHIViewWindowContentID ,
contentview->GetControlRefAddr() ) ;
if ( !contentview->IsOk() )
{
// compatibility mode fallback
GetRootControl( (WindowRef) now->GetWXWindow() , contentview->GetControlRefAddr() ) ;
}
// the root control level handler
contentview->InstallEventHandler() ;
return contentview;
}