/* * Copyright (C) 2005 Apple Computer, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef __LP64__ #import "HIWebView.h" #import "CarbonWindowAdapter.h" #import "HIViewAdapter.h" #import "WebHTMLViewInternal.h" #import "WebKit.h" #import <objc/objc-runtime.h> #import <WebKitSystemInterface.h> @interface NSWindow (AppKitSecretsHIWebViewKnows) - (void)_removeWindowRef; @end @interface NSView (AppKitSecretsHIWebViewKnows) - (void)_clearDirtyRectsForTree; @end extern "C" void HIWebViewRegisterClass(); @interface MenuItemProxy : NSObject <NSValidatedUserInterfaceItem> { int _tag; SEL _action; } - (id)initWithAction:(SEL)action; - (SEL)action; - (int)tag; @end @implementation MenuItemProxy - (id)initWithAction:(SEL)action { [super init]; if (self == nil) return nil; _action = action; return self; } - (SEL)action { return _action; } - (int)tag { return 0; } @end struct HIWebView { HIViewRef fViewRef; WebView* fWebView; NSView* fFirstResponder; CarbonWindowAdapter * fKitWindow; bool fIsComposited; CFRunLoopObserverRef fUpdateObserver; }; typedef struct HIWebView HIWebView; static const OSType NSAppKitPropertyCreator = 'akit'; /* These constants are not used. Commented out to make the compiler happy. static const OSType NSViewCarbonControlViewPropertyTag = 'view'; static const OSType NSViewCarbonControlAutodisplayPropertyTag = 'autd'; static const OSType NSViewCarbonControlFirstResponderViewPropertyTag = 'frvw'; */ static const OSType NSCarbonWindowPropertyTag = 'win '; #ifdef BUILDING_ON_TIGER const int typeByteCount = typeSInt32; #endif static SEL _NSSelectorForHICommand( const HICommand* hiCommand ); static const EventTypeSpec kEvents[] = { { kEventClassHIObject, kEventHIObjectConstruct }, { kEventClassHIObject, kEventHIObjectDestruct }, { kEventClassMouse, kEventMouseUp }, { kEventClassMouse, kEventMouseMoved }, { kEventClassMouse, kEventMouseDragged }, { kEventClassMouse, kEventMouseWheelMoved }, { kEventClassKeyboard, kEventRawKeyDown }, { kEventClassKeyboard, kEventRawKeyRepeat }, { kEventClassCommand, kEventCommandProcess }, { kEventClassCommand, kEventCommandUpdateStatus }, { kEventClassControl, kEventControlInitialize }, { kEventClassControl, kEventControlDraw }, { kEventClassControl, kEventControlHitTest }, { kEventClassControl, kEventControlGetPartRegion }, { kEventClassControl, kEventControlGetData }, { kEventClassControl, kEventControlBoundsChanged }, { kEventClassControl, kEventControlActivate }, { kEventClassControl, kEventControlDeactivate }, { kEventClassControl, kEventControlOwningWindowChanged }, { kEventClassControl, kEventControlClick }, { kEventClassControl, kEventControlContextualMenuClick }, { kEventClassControl, kEventControlSetFocusPart } }; #define kHIViewBaseClassID CFSTR( "com.apple.hiview" ) #define kHIWebViewClassID CFSTR( "com.apple.HIWebView" ) static HIWebView* HIWebViewConstructor( HIViewRef inView ); static void HIWebViewDestructor( HIWebView* view ); static OSStatus HIWebViewEventHandler( EventHandlerCallRef inCallRef, EventRef inEvent, void * inUserData ); static UInt32 GetBehaviors(); static ControlKind GetKind(); static void Draw( HIWebView* inView, RgnHandle limitRgn, CGContextRef inContext ); static ControlPartCode HitTest( HIWebView* view, const HIPoint* where ); static OSStatus GetRegion( HIWebView* view, ControlPartCode inPart, RgnHandle outRgn ); static void BoundsChanged( HIWebView* inView, UInt32 inOptions, const HIRect* inOriginalBounds, const HIRect* inCurrentBounds ); static void OwningWindowChanged( HIWebView* view, WindowRef oldWindow, WindowRef newWindow ); static void ActiveStateChanged( HIWebView* view ); static OSStatus Click( HIWebView* inView, EventRef inEvent ); static OSStatus ContextMenuClick( HIWebView* inView, EventRef inEvent ); static OSStatus MouseUp( HIWebView* inView, EventRef inEvent ); static OSStatus MouseMoved( HIWebView* inView, EventRef inEvent ); static OSStatus MouseDragged( HIWebView* inView, EventRef inEvent ); static OSStatus MouseWheelMoved( HIWebView* inView, EventRef inEvent ); static OSStatus ProcessCommand( HIWebView* inView, const HICommand* inCommand ); static OSStatus UpdateCommandStatus( HIWebView* inView, const HICommand* inCommand ); static OSStatus SetFocusPart( HIWebView* view, ControlPartCode desiredFocus, RgnHandle invalidRgn, Boolean focusEverything, ControlPartCode* actualFocus ); static NSView* AdvanceFocus( HIWebView* view, bool forward ); static void RelinquishFocus( HIWebView* view, bool inAutodisplay ); static WindowRef GetWindowRef( HIWebView* inView ); static void SyncFrame( HIWebView* inView ); static OSStatus WindowHandler( EventHandlerCallRef inCallRef, EventRef inEvent, void* inUserData ); static void StartUpdateObserver( HIWebView* view ); static void StopUpdateObserver( HIWebView* view ); static inline void HIRectToQDRect( const HIRect* inRect, Rect* outRect ) { outRect->top = (SInt16)CGRectGetMinY( *inRect ); outRect->left = (SInt16)CGRectGetMinX( *inRect ); outRect->bottom = (SInt16)CGRectGetMaxY( *inRect ); outRect->right = (SInt16)CGRectGetMaxX( *inRect ); } //---------------------------------------------------------------------------------- // HIWebViewCreate //---------------------------------------------------------------------------------- // OSStatus HIWebViewCreate(HIViewRef* outControl) { HIWebViewRegisterClass(); return HIObjectCreate(kHIWebViewClassID, NULL, (HIObjectRef*)outControl); } //---------------------------------------------------------------------------------- // HIWebViewGetWebView //---------------------------------------------------------------------------------- // WebView* HIWebViewGetWebView( HIViewRef inView ) { HIWebView* view = (HIWebView*)HIObjectDynamicCast( (HIObjectRef)inView, kHIWebViewClassID ); WebView* result = NULL; if ( view ) result = view->fWebView; return result; } //---------------------------------------------------------------------------------- // HIWebViewConstructor //---------------------------------------------------------------------------------- // static HIWebView* HIWebViewConstructor( HIViewRef inView ) { HIWebView* view = (HIWebView*)malloc( sizeof( HIWebView ) ); if ( view ) { NSRect frame = { { 0, 0 }, { 400, 400 } }; view->fViewRef = inView; WebView *webView = [[WebView alloc] initWithFrame: frame]; CFRetain(webView); [webView release]; view->fWebView = webView; [HIViewAdapter bindHIViewToNSView:inView nsView:view->fWebView]; view->fFirstResponder = NULL; view->fKitWindow = NULL; view->fIsComposited = false; view->fUpdateObserver = NULL; } return view; } //---------------------------------------------------------------------------------- // HIWebViewDestructor //---------------------------------------------------------------------------------- // static void HIWebViewDestructor( HIWebView* inView ) { [HIViewAdapter unbindNSView:inView->fWebView]; CFRelease(inView->fWebView); free(inView); } //---------------------------------------------------------------------------------- // HIWebViewRegisterClass //---------------------------------------------------------------------------------- // void HIWebViewRegisterClass() { static bool sRegistered; if ( !sRegistered ) { HIObjectRegisterSubclass( kHIWebViewClassID, kHIViewBaseClassID, 0, HIWebViewEventHandler, GetEventTypeCount( kEvents ), kEvents, 0, NULL ); sRegistered = true; } } //---------------------------------------------------------------------------------- // GetBehaviors //---------------------------------------------------------------------------------- // static UInt32 GetBehaviors() { return kControlSupportsDataAccess | kControlSupportsGetRegion | kControlGetsFocusOnClick; } //---------------------------------------------------------------------------------- // Draw //---------------------------------------------------------------------------------- // static void Draw( HIWebView* inView, RgnHandle limitRgn, CGContextRef inContext ) { HIRect bounds; Rect drawRect; HIRect hiRect; bool createdContext = false; if (!inView->fIsComposited) { GrafPtr port; Rect portRect; GetPort( &port ); GetPortBounds( port, &portRect ); CreateCGContextForPort( port, &inContext ); SyncCGContextOriginWithPort( inContext, port ); CGContextTranslateCTM( inContext, 0, (portRect.bottom - portRect.top) ); CGContextScaleCTM( inContext, 1, -1 ); createdContext = true; } HIViewGetBounds( inView->fViewRef, &bounds ); CGContextRef savedContext = WKNSWindowOverrideCGContext(inView->fKitWindow, inContext); [NSGraphicsContext setCurrentContext:[inView->fKitWindow graphicsContext]]; GetRegionBounds( limitRgn, &drawRect ); if ( !inView->fIsComposited ) OffsetRect( &drawRect, (SInt16)-bounds.origin.x, (SInt16)-bounds.origin.y ); hiRect.origin.x = drawRect.left; hiRect.origin.y = bounds.size.height - drawRect.bottom; // flip y hiRect.size.width = drawRect.right - drawRect.left; hiRect.size.height = drawRect.bottom - drawRect.top; // printf( "Drawing: drawRect is (%g %g) (%g %g)\n", hiRect.origin.x, hiRect.origin.y, // hiRect.size.width, hiRect.size.height ); // FIXME: We need to do layout before Carbon has decided what region needs drawn. // In Cocoa we make sure to do layout and invalidate any new regions before draw, so everything // can be drawn in one pass. Doing a layout here will cause new regions to be invalidated, but they // will not all be drawn in this pass since we already have a fixed rect we are going to display. NSView <WebDocumentView> *documentView = [[[inView->fWebView mainFrame] frameView] documentView]; if ([documentView isKindOfClass:[WebHTMLView class]]) [(WebHTMLView *)documentView _web_layoutIfNeededRecursive]; if ( inView->fIsComposited ) [inView->fWebView displayIfNeededInRect: *(NSRect*)&hiRect]; else [inView->fWebView displayRect:*(NSRect*)&hiRect]; WKNSWindowRestoreCGContext(inView->fKitWindow, savedContext); if ( !inView->fIsComposited ) { HIViewRef view; HIViewFindByID( HIViewGetRoot( GetControlOwner( inView->fViewRef ) ), kHIViewWindowGrowBoxID, &view ); if ( view ) { HIRect frame; HIViewGetBounds( view, &frame ); HIViewConvertRect( &frame, view, NULL ); hiRect.origin.x = drawRect.left; hiRect.origin.y = drawRect.top; hiRect.size.width = drawRect.right - drawRect.left; hiRect.size.height = drawRect.bottom - drawRect.top; HIViewConvertRect( &hiRect, inView->fViewRef, NULL ); if ( CGRectIntersectsRect( frame, hiRect ) ) HIViewSetNeedsDisplay( view, true ); } } if ( createdContext ) { CGContextSynchronize( inContext ); CGContextRelease( inContext ); } } //---------------------------------------------------------------------------------- // HitTest //---------------------------------------------------------------------------------- // static ControlPartCode HitTest( HIWebView* view, const HIPoint* where ) { HIRect bounds; HIViewGetBounds( view->fViewRef, &bounds ); if ( CGRectContainsPoint( bounds, *where ) ) return 1; else return kControlNoPart; } //---------------------------------------------------------------------------------- // GetRegion //---------------------------------------------------------------------------------- // static OSStatus GetRegion( HIWebView* inView, ControlPartCode inPart, RgnHandle outRgn ) { OSStatus err = eventNotHandledErr; if ( inPart == -3 ) // kControlOpaqueMetaPart: { if ( [inView->fWebView isOpaque] ) { HIRect bounds; Rect temp; HIViewGetBounds( inView->fViewRef, &bounds ); temp.top = (SInt16)bounds.origin.y; temp.left = (SInt16)bounds.origin.x; temp.bottom = (SInt16)CGRectGetMaxY( bounds ); temp.right = (SInt16)CGRectGetMaxX( bounds ); RectRgn( outRgn, &temp ); err = noErr; } } return err; } static WindowRef GetWindowRef( HIWebView* inView ) { return GetControlOwner( inView->fViewRef ); } //---------------------------------------------------------------------------------- // Click //---------------------------------------------------------------------------------- // static OSStatus Click(HIWebView* inView, EventRef inEvent) { NSEvent *kitEvent = WKCreateNSEventWithCarbonClickEvent(inEvent, GetWindowRef(inView)); if (!inView->fIsComposited) StartUpdateObserver(inView); [inView->fKitWindow sendEvent:kitEvent]; if (!inView->fIsComposited) StopUpdateObserver(inView); [kitEvent release]; return noErr; } //---------------------------------------------------------------------------------- // MouseUp //---------------------------------------------------------------------------------- // static OSStatus MouseUp( HIWebView* inView, EventRef inEvent ) { NSEvent* kitEvent = WKCreateNSEventWithCarbonEvent(inEvent); [inView->fKitWindow sendEvent:kitEvent]; [kitEvent release]; return noErr; } //---------------------------------------------------------------------------------- // MouseMoved //---------------------------------------------------------------------------------- // static OSStatus MouseMoved( HIWebView* inView, EventRef inEvent ) { NSEvent *kitEvent = WKCreateNSEventWithCarbonMouseMoveEvent(inEvent, inView->fKitWindow); [inView->fKitWindow sendEvent:kitEvent]; [kitEvent release]; return noErr; } //---------------------------------------------------------------------------------- // MouseDragged //---------------------------------------------------------------------------------- // static OSStatus MouseDragged( HIWebView* inView, EventRef inEvent ) { NSEvent* kitEvent = WKCreateNSEventWithCarbonEvent(inEvent); [inView->fKitWindow sendEvent:kitEvent]; [kitEvent release]; return noErr; } //---------------------------------------------------------------------------------- // MouseDragged //---------------------------------------------------------------------------------- // static OSStatus MouseWheelMoved( HIWebView* inView, EventRef inEvent ) { NSEvent* kitEvent = WKCreateNSEventWithCarbonEvent(inEvent); [inView->fKitWindow sendEvent:kitEvent]; [kitEvent release]; return noErr; } //---------------------------------------------------------------------------------- // ContextMenuClick //---------------------------------------------------------------------------------- // static OSStatus ContextMenuClick( HIWebView* inView, EventRef inEvent ) { NSView *webView = inView->fWebView; NSWindow *window = [webView window]; // Get the point out of the event. HIPoint point; GetEventParameter(inEvent, kEventParamMouseLocation, typeHIPoint, NULL, sizeof(point), NULL, &point); HIViewConvertPoint(&point, inView->fViewRef, NULL); // Flip the Y coordinate, since Carbon is flipped relative to the AppKit. NSPoint location = NSMakePoint(point.x, [window frame].size.height - point.y); // Make up an event with the point and send it to the window. NSEvent *kitEvent = [NSEvent mouseEventWithType:NSRightMouseDown location:location modifierFlags:0 timestamp:GetEventTime(inEvent) windowNumber:[window windowNumber] context:0 eventNumber:0 clickCount:1 pressure:0]; [inView->fKitWindow sendEvent:kitEvent]; return noErr; } //---------------------------------------------------------------------------------- // GetKind //---------------------------------------------------------------------------------- // static ControlKind GetKind() { const ControlKind kMyKind = { 'appl', 'wbvw' }; return kMyKind; } //---------------------------------------------------------------------------------- // BoundsChanged //---------------------------------------------------------------------------------- // static void BoundsChanged( HIWebView* inView, UInt32 inOptions, const HIRect* inOriginalBounds, const HIRect* inCurrentBounds ) { if ( inView->fWebView ) { SyncFrame( inView ); } } //---------------------------------------------------------------------------------- // OwningWindowChanged //---------------------------------------------------------------------------------- // static void OwningWindowChanged( HIWebView* view, WindowRef oldWindow, WindowRef newWindow ) { if ( newWindow ){ WindowAttributes attrs; OSStatus err = GetWindowProperty(newWindow, NSAppKitPropertyCreator, NSCarbonWindowPropertyTag, sizeof(NSWindow *), NULL, &view->fKitWindow); if ( err != noErr ) { const EventTypeSpec kWindowEvents[] = { { kEventClassWindow, kEventWindowClosed }, { kEventClassMouse, kEventMouseMoved }, { kEventClassMouse, kEventMouseUp }, { kEventClassMouse, kEventMouseDragged }, { kEventClassMouse, kEventMouseWheelMoved }, { kEventClassKeyboard, kEventRawKeyDown }, { kEventClassKeyboard, kEventRawKeyRepeat }, { kEventClassKeyboard, kEventRawKeyUp }, { kEventClassControl, kEventControlClick }, }; view->fKitWindow = [[CarbonWindowAdapter alloc] initWithCarbonWindowRef: newWindow takingOwnership: NO disableOrdering:NO carbon:YES]; SetWindowProperty(newWindow, NSAppKitPropertyCreator, NSCarbonWindowPropertyTag, sizeof(NSWindow *), &view->fKitWindow); InstallWindowEventHandler( newWindow, WindowHandler, GetEventTypeCount( kWindowEvents ), kWindowEvents, newWindow, NULL ); } [[view->fKitWindow contentView] addSubview:view->fWebView]; GetWindowAttributes( newWindow, &attrs ); view->fIsComposited = ( ( attrs & kWindowCompositingAttribute ) != 0 ); SyncFrame( view ); } else { // Be sure to detach the cocoa view, too. if ( view->fWebView ) [view->fWebView removeFromSuperview]; view->fKitWindow = NULL; // break the ties that bind } } //------------------------------------------------------------------------------------- // WindowHandler //------------------------------------------------------------------------------------- // Redirect mouse events to the views beneath them. This is required for WebKit to work // properly. We install it once per window. We also tap into window close to release // the NSWindow that shadows our Carbon window. // static OSStatus WindowHandler( EventHandlerCallRef inCallRef, EventRef inEvent, void* inUserData ) { WindowRef window = (WindowRef)inUserData; OSStatus result = eventNotHandledErr; switch( GetEventClass( inEvent ) ) { case kEventClassControl: { switch( GetEventKind( inEvent ) ) { case kEventControlClick: { CarbonWindowAdapter *kitWindow; OSStatus err; err = GetWindowProperty( window, NSAppKitPropertyCreator, NSCarbonWindowPropertyTag, sizeof(NSWindow *), NULL, &kitWindow); // We must be outside the HIWebView, relinquish focus. [kitWindow relinquishFocus]; } break; } } break; case kEventClassKeyboard: { NSWindow* kitWindow; OSStatus err; NSEvent* kitEvent; // if the first responder in the kit window is something other than the // window, we assume a subview of the webview is focused. we must send // the event to the window so that it goes through the kit's normal TSM // logic, and -- more importantly -- allows any delegates associated // with the first responder to have a chance at the event. err = GetWindowProperty( window, NSAppKitPropertyCreator, NSCarbonWindowPropertyTag, sizeof(NSWindow *), NULL, &kitWindow); if ( err == noErr ) { NSResponder* responder = [kitWindow firstResponder]; if ( responder != kitWindow ) { kitEvent = WKCreateNSEventWithCarbonEvent(inEvent); [kitWindow sendEvent:kitEvent]; [kitEvent release]; result = noErr; } } } break; case kEventClassWindow: { NSWindow* kitWindow; OSStatus err; err = GetWindowProperty( window, NSAppKitPropertyCreator, NSCarbonWindowPropertyTag, sizeof(NSWindow *), NULL, &kitWindow); if ( err == noErr ) { [kitWindow _removeWindowRef]; [kitWindow close]; } result = noErr; } break; case kEventClassMouse: switch (GetEventKind(inEvent)) { case kEventMouseMoved: { Point where; GetEventParameter(inEvent, kEventParamMouseLocation, typeQDPoint, NULL, sizeof(Point), NULL, &where); WindowRef temp; FindWindow(where, &temp); if (temp == window) { Rect bounds; GetWindowBounds(window, kWindowStructureRgn, &bounds); where.h -= bounds.left; where.v -= bounds.top; SetEventParameter(inEvent, kEventParamWindowRef, typeWindowRef, sizeof(WindowRef), &window); SetEventParameter(inEvent, kEventParamWindowMouseLocation, typeQDPoint, sizeof(Point), &where); OSStatus err = noErr; HIViewRef view = NULL; err = HIViewGetViewForMouseEvent(HIViewGetRoot(window), inEvent, &view); if (err == noErr && view && HIObjectIsOfClass((HIObjectRef)view, kHIWebViewClassID)) result = SendEventToEventTargetWithOptions(inEvent, HIObjectGetEventTarget((HIObjectRef)view), kEventTargetDontPropagate); } } break; case kEventMouseUp: case kEventMouseDragged: case kEventMouseWheelMoved: { OSStatus err = noErr; HIViewRef view = NULL; err = HIViewGetViewForMouseEvent(HIViewGetRoot(window), inEvent, &view); if (err == noErr && view && HIObjectIsOfClass((HIObjectRef)view, kHIWebViewClassID)) result = SendEventToEventTargetWithOptions(inEvent, HIObjectGetEventTarget((HIObjectRef)view), kEventTargetDontPropagate); } break; } break; } return result; } //---------------------------------------------------------------------------------- // SyncFrame //---------------------------------------------------------------------------------- // static void SyncFrame( HIWebView* inView ) { HIViewRef parent = HIViewGetSuperview( inView->fViewRef ); if ( parent ) { if ( inView->fIsComposited ) { HIRect frame; HIRect parentBounds; NSPoint origin; HIViewGetFrame( inView->fViewRef, &frame ); HIViewGetBounds( parent, &parentBounds ); origin.x = frame.origin.x; origin.y = parentBounds.size.height - CGRectGetMaxY( frame ); // printf( "syncing to (%g %g) (%g %g)\n", origin.x, origin.y, // frame.size.width, frame.size.height ); [inView->fWebView setFrameOrigin: origin]; [inView->fWebView setFrameSize: *(NSSize*)&frame.size]; } else { GrafPtr port = GetWindowPort( GetControlOwner( inView->fViewRef ) ); PixMapHandle portPix = GetPortPixMap( port ); Rect bounds; HIRect rootFrame; HIRect frame; GetControlBounds( inView->fViewRef, &bounds ); OffsetRect( &bounds, -(**portPix).bounds.left, -(**portPix).bounds.top ); // printf( "control lives at %d %d %d %d in window-coords\n", bounds.top, bounds.left, // bounds.bottom, bounds.right ); HIViewGetFrame( HIViewGetRoot( GetControlOwner( inView->fViewRef ) ), &rootFrame ); frame.origin.x = bounds.left; frame.origin.y = rootFrame.size.height - bounds.bottom; frame.size.width = bounds.right - bounds.left; frame.size.height = bounds.bottom - bounds.top; // printf( " before frame convert (%g %g) (%g %g)\n", frame.origin.x, frame.origin.y, // frame.size.width, frame.size.height ); [inView->fWebView convertRect:*(NSRect*)&frame fromView:nil]; // printf( " moving web view to (%g %g) (%g %g)\n", frame.origin.x, frame.origin.y, // frame.size.width, frame.size.height ); [inView->fWebView setFrameOrigin: *(NSPoint*)&frame.origin]; [inView->fWebView setFrameSize: *(NSSize*)&frame.size]; } } } //---------------------------------------------------------------------------------- // SetFocusPart //---------------------------------------------------------------------------------- // static OSStatus SetFocusPart( HIWebView* view, ControlPartCode desiredFocus, RgnHandle invalidRgn, Boolean focusEverything, ControlPartCode* actualFocus ) { NSView * freshlyMadeFirstResponderView; SInt32 partCodeToReturn; // Do what Carbon is telling us to do. if ( desiredFocus == kControlFocusNoPart ) { // Relinquish the keyboard focus. RelinquishFocus( view, true ); //(autodisplay ? YES : NO)); freshlyMadeFirstResponderView = nil; partCodeToReturn = kControlFocusNoPart; //NSLog(@"Relinquished the key focus because we have no choice."); } else if ( desiredFocus == kControlFocusNextPart || desiredFocus == kControlFocusPrevPart ) { BOOL goForward = (desiredFocus == kControlFocusNextPart ); // Advance the keyboard focus, maybe right off of this view. Maybe a subview of this one already has the keyboard focus, maybe not. freshlyMadeFirstResponderView = AdvanceFocus( view, goForward ); if (freshlyMadeFirstResponderView) partCodeToReturn = desiredFocus; else partCodeToReturn = kControlFocusNoPart; //NSLog(freshlyMadeFirstResponderView ? @"Advanced the key focus." : @"Relinquished the key focus."); } else { // What's this? if (desiredFocus != kControlIndicatorPart) { check(false); } freshlyMadeFirstResponderView = nil; partCodeToReturn = desiredFocus; } view->fFirstResponder = freshlyMadeFirstResponderView; *actualFocus = partCodeToReturn; // Done. return noErr; } //---------------------------------------------------------------------------------- // AdvanceFocus //---------------------------------------------------------------------------------- // static NSView* AdvanceFocus( HIWebView* view, bool forward ) { NSResponder* oldFirstResponder; NSView* currentKeyView; NSView* viewWeMadeFirstResponder; // Focus on some part (subview) of this control (view). Maybe // a subview of this one already has the keyboard focus, maybe not. oldFirstResponder = [view->fKitWindow firstResponder]; // If we tab out of our NSView, it will no longer be the responder // when we get here. We'll try this trick for now. We might need to // tag the view appropriately. if ( view->fFirstResponder && ( (NSResponder*)view->fFirstResponder != oldFirstResponder ) ) { return NULL; } if ( [oldFirstResponder isKindOfClass:[NSView class]] ) { NSView* tentativeNewKeyView; // Some view in this window already has the keyboard focus. It better at least be a subview of this one. NSView* oldFirstResponderView = (NSView *)oldFirstResponder; check( [oldFirstResponderView isDescendantOf:view->fWebView] ); if ( oldFirstResponderView != view->fFirstResponder && ![oldFirstResponderView isDescendantOf:view->fFirstResponder] ) { // Despite our efforts to record what view we made the first responder // (for use in the next paragraph) we couldn't keep up because the user // has clicked in a text field to make it the key focus, instead of using // the tab key. Find a control on which it's reasonable to invoke // -[NSView nextValidKeyView], taking into account the fact that // NSTextFields always pass on first-respondership to a temporarily- // contained NSTextView. NSView *viewBeingTested; currentKeyView = oldFirstResponderView; viewBeingTested = currentKeyView; while ( viewBeingTested != view->fWebView ) { if ( [viewBeingTested isKindOfClass:[NSTextField class]] ) { currentKeyView = viewBeingTested; break; } else { viewBeingTested = [viewBeingTested superview]; } } } else { // We recorded which view we made into the first responder the // last time the user hit the tab key, and nothing has invalidated // our recorded value since. currentKeyView = view->fFirstResponder; } // Try to move on to the next or previous key view. We use the laboriously // recorded/figured currentKeyView instead of just oldFirstResponder as the // jumping-off-point when searching for the next valid key view. This is so // we don't get fooled if we recently made some view the first responder, but // it passed on first-responder-ness to some temporary subview. // You can't put normal views in a window with Carbon-control-wrapped views. // Stuff like this would break. M.P. Notice - 12/2/00 tentativeNewKeyView = forward ? [currentKeyView nextValidKeyView] : [currentKeyView previousValidKeyView]; if ( tentativeNewKeyView && [tentativeNewKeyView isDescendantOf:view->fWebView] ) { // The user has tabbed to another subview of this control view. Change the keyboard focus. //NSLog(@"Tabbed to the next or previous key view."); [view->fKitWindow makeFirstResponder:tentativeNewKeyView]; viewWeMadeFirstResponder = tentativeNewKeyView; } else { // The user has tabbed past the subviews of this control view. The window is the first responder now. //NSLog(@"Tabbed past the first or last key view."); [view->fKitWindow makeFirstResponder:view->fKitWindow]; viewWeMadeFirstResponder = nil; } } else { // No view in this window has the keyboard focus. This view should // try to select one of its key subviews. We're not interested in // the subviews of sibling views here. //NSLog(@"No keyboard focus in window. Attempting to set..."); NSView *tentativeNewKeyView; check(oldFirstResponder==fKitWindow); if ( [view->fWebView acceptsFirstResponder] ) tentativeNewKeyView = view->fWebView; else tentativeNewKeyView = [view->fWebView nextValidKeyView]; if ( tentativeNewKeyView && [tentativeNewKeyView isDescendantOf:view->fWebView] ) { // This control view has at least one subview that can take the keyboard focus. if ( !forward ) { // The user has tabbed into this control view backwards. Find // and select the last subview of this one that can take the // keyboard focus. Watch out for loops of valid key views. NSView *firstTentativeNewKeyView = tentativeNewKeyView; NSView *nextTentativeNewKeyView = [tentativeNewKeyView nextValidKeyView]; while ( nextTentativeNewKeyView && [nextTentativeNewKeyView isDescendantOf:view->fWebView] && nextTentativeNewKeyView!=firstTentativeNewKeyView) { tentativeNewKeyView = nextTentativeNewKeyView; nextTentativeNewKeyView = [tentativeNewKeyView nextValidKeyView]; } } // Set the keyboard focus. //NSLog(@"Tabbed into the first or last key view."); [view->fKitWindow makeFirstResponder:tentativeNewKeyView]; viewWeMadeFirstResponder = tentativeNewKeyView; } else { // This control view has no subviews that can take the keyboard focus. //NSLog(@"Can't tab into this view."); viewWeMadeFirstResponder = nil; } } // Done. return viewWeMadeFirstResponder; } //---------------------------------------------------------------------------------- // RelinquishFocus //---------------------------------------------------------------------------------- // static void RelinquishFocus( HIWebView* view, bool inAutodisplay ) { NSResponder* firstResponder; // Apparently Carbon thinks that some subview of this control view has the keyboard focus, // or we wouldn't be being asked to relinquish focus. firstResponder = [view->fKitWindow firstResponder]; if ( [firstResponder isKindOfClass:[NSView class]] ) { // Some subview of this control view really is the first responder right now. check( [(NSView *)firstResponder isDescendantOf:view->fWebView] ); // Make the window the first responder, so that no view is the key view. [view->fKitWindow makeFirstResponder:view->fKitWindow]; // If this control is not allowed to do autodisplay, don't let // it autodisplay any just-changed focus rings or text on the // next go around the event loop. I'm probably clearing more // dirty rects than I have to, but it doesn't seem to hurt in // the print panel accessory view case, and I don't have time // to figure out exactly what -[NSCell _setKeyboardFocusRingNeedsDisplay] // is doing when invoked indirectly from -makeFirstResponder up above. M.P. Notice - 12/4/00 if ( !inAutodisplay ) [[view->fWebView opaqueAncestor] _clearDirtyRectsForTree]; } else { // The Cocoa first responder does not correspond to the Carbon // control that has the keyboard focus. This can happen when // you've closed a dialog by hitting return in an NSTextView // that's a subview of this one; Cocoa closed the window, and // now Carbon is telling this control to relinquish the focus // as it's being disposed. There's nothing to do. check(firstResponder==window); } } //---------------------------------------------------------------------------------- // ActiveStateChanged //---------------------------------------------------------------------------------- // static void ActiveStateChanged( HIWebView* view ) { if ( [view->fWebView respondsToSelector:@selector(setEnabled)] ) { [(NSControl*)view->fWebView setEnabled: IsControlEnabled( view->fViewRef )]; HIViewSetNeedsDisplay( view->fViewRef, true ); } } //---------------------------------------------------------------------------------- // ProcessCommand //---------------------------------------------------------------------------------- // static OSStatus ProcessCommand( HIWebView* inView, const HICommand* inCommand ) { OSStatus result = eventNotHandledErr; NSResponder* resp; resp = [inView->fKitWindow firstResponder]; if ( [resp isKindOfClass:[NSView class]] ) { NSView* respView = (NSView*)resp; if ( respView == inView->fWebView || [respView isDescendantOf: inView->fWebView] ) { switch ( inCommand->commandID ) { case kHICommandCut: case kHICommandCopy: case kHICommandPaste: case kHICommandClear: case kHICommandSelectAll: { SEL selector = _NSSelectorForHICommand( inCommand ); if ( [respView respondsToSelector:selector] ) { [respView performSelector:selector withObject:nil]; result = noErr; } } break; } } } return result; } //---------------------------------------------------------------------------------- // UpdateCommandStatus //---------------------------------------------------------------------------------- // static OSStatus UpdateCommandStatus( HIWebView* inView, const HICommand* inCommand ) { OSStatus result = eventNotHandledErr; MenuItemProxy* proxy = NULL; NSResponder* resp; resp = [inView->fKitWindow firstResponder]; if ( [resp isKindOfClass:[NSView class]] ) { NSView* respView = (NSView*)resp; if ( respView == inView->fWebView || [respView isDescendantOf: inView->fWebView] ) { if ( inCommand->attributes & kHICommandFromMenu ) { SEL selector = _NSSelectorForHICommand( inCommand ); if ( selector ) { if ( [resp respondsToSelector: selector] ) { proxy = [[MenuItemProxy alloc] initWithAction: selector]; // Can't use -performSelector:withObject: here because the method we're calling returns BOOL, while // -performSelector:withObject:'s return value is assumed to be an id. BOOL (*validationFunction)(id, SEL, id) = (BOOL (*)(id, SEL, id))objc_msgSend; if (validationFunction(resp, @selector(validateUserInterfaceItem:), proxy)) EnableMenuItem( inCommand->menu.menuRef, inCommand->menu.menuItemIndex ); else DisableMenuItem( inCommand->menu.menuRef, inCommand->menu.menuItemIndex ); result = noErr; } } } } } if ( proxy ) [proxy release]; return result; } // Blatantly stolen from AppKit and cropped a bit //---------------------------------------------------------------------------------- // _NSSelectorForHICommand //---------------------------------------------------------------------------------- // static SEL _NSSelectorForHICommand( const HICommand* inCommand ) { switch ( inCommand->commandID ) { case kHICommandUndo: return @selector(undo:); case kHICommandRedo: return @selector(redo:); case kHICommandCut : return @selector(cut:); case kHICommandCopy : return @selector(copy:); case kHICommandPaste: return @selector(paste:); case kHICommandClear: return @selector(delete:); case kHICommandSelectAll: return @selector(selectAll:); default: return NULL; } return NULL; } //----------------------------------------------------------------------------------- // HIWebViewEventHandler //----------------------------------------------------------------------------------- // Our object's virtual event handler method. I'm not sure if we need this these days. // We used to do various things with it, but those days are long gone... // static OSStatus HIWebViewEventHandler( EventHandlerCallRef inCallRef, EventRef inEvent, void * inUserData ) { OSStatus result = eventNotHandledErr; HIPoint where; OSType tag; void * ptr; Size size; UInt32 features; RgnHandle region = NULL; ControlPartCode part; HIWebView* view = (HIWebView*)inUserData; // [NSApp setWindowsNeedUpdate:YES] must be called before events so that ActivateTSMDocument is called to set an active document. // Without an active document, TSM will use a default document which uses a bottom-line input window which we don't want. [NSApp setWindowsNeedUpdate:YES]; switch ( GetEventClass( inEvent ) ) { case kEventClassHIObject: switch ( GetEventKind( inEvent ) ) { case kEventHIObjectConstruct: { HIObjectRef object; result = GetEventParameter( inEvent, kEventParamHIObjectInstance, typeHIObjectRef, NULL, sizeof( HIObjectRef ), NULL, &object ); require_noerr( result, MissingParameter ); // on entry for our construct event, we're passed the // creation proc we registered with for this class. // we use it now to create the instance, and then we // replace the instance parameter data with said instance // as type void. view = HIWebViewConstructor( (HIViewRef)object ); if ( view ) { SetEventParameter( inEvent, kEventParamHIObjectInstance, typeVoidPtr, sizeof( void * ), &view ); } } break; case kEventHIObjectDestruct: HIWebViewDestructor( view ); // result is unimportant break; } break; case kEventClassKeyboard: { NSEvent* kitEvent = WKCreateNSEventWithCarbonEvent(inEvent); [view->fKitWindow sendSuperEvent:kitEvent]; [kitEvent release]; result = noErr; } break; case kEventClassMouse: switch ( GetEventKind( inEvent ) ) { case kEventMouseUp: result = MouseUp( view, inEvent ); break; case kEventMouseWheelMoved: result = MouseWheelMoved( view, inEvent ); break; case kEventMouseMoved: result = MouseMoved( view, inEvent ); break; case kEventMouseDragged: result = MouseDragged( view, inEvent ); break; } break; case kEventClassCommand: { HICommand command; result = GetEventParameter( inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof( HICommand ), NULL, &command ); require_noerr( result, MissingParameter ); switch ( GetEventKind( inEvent ) ) { case kEventCommandProcess: result = ProcessCommand( view, &command ); break; case kEventCommandUpdateStatus: result = UpdateCommandStatus( view, &command ); break; } } break; case kEventClassControl: switch ( GetEventKind( inEvent ) ) { case kEventControlInitialize: features = GetBehaviors(); SetEventParameter( inEvent, kEventParamControlFeatures, typeUInt32, sizeof( UInt32 ), &features ); result = noErr; break; case kEventControlDraw: { CGContextRef context = NULL; GetEventParameter( inEvent, kEventParamRgnHandle, typeQDRgnHandle, NULL, sizeof( RgnHandle ), NULL, ®ion ); GetEventParameter( inEvent, kEventParamCGContextRef, typeCGContextRef, NULL, sizeof( CGContextRef ), NULL, &context ); Draw( view, region, context ); result = noErr; } break; case kEventControlHitTest: GetEventParameter( inEvent, kEventParamMouseLocation, typeHIPoint, NULL, sizeof( HIPoint ), NULL, &where ); part = HitTest( view, &where ); SetEventParameter( inEvent, kEventParamControlPart, typeControlPartCode, sizeof( ControlPartCode ), &part ); result = noErr; break; case kEventControlGetPartRegion: GetEventParameter( inEvent, kEventParamControlPart, typeControlPartCode, NULL, sizeof( ControlPartCode ), NULL, &part ); GetEventParameter( inEvent, kEventParamControlRegion, typeQDRgnHandle, NULL, sizeof( RgnHandle ), NULL, ®ion ); result = GetRegion( view, part, region ); break; case kEventControlGetData: GetEventParameter(inEvent, kEventParamControlPart, typeControlPartCode, NULL, sizeof(ControlPartCode), NULL, &part); GetEventParameter(inEvent, kEventParamControlDataTag, typeEnumeration, NULL, sizeof(OSType), NULL, &tag); GetEventParameter(inEvent, kEventParamControlDataBuffer, typePtr, NULL, sizeof(Ptr), NULL, &ptr); GetEventParameter(inEvent, kEventParamControlDataBufferSize, typeByteCount, NULL, sizeof(Size), NULL, &size); if (tag == kControlKindTag) { Size outSize; result = noErr; if (ptr) { if (size != sizeof(ControlKind)) result = errDataSizeMismatch; else (*(ControlKind *)ptr) = GetKind(); } outSize = sizeof(ControlKind); SetEventParameter(inEvent, kEventParamControlDataBufferSize, typeByteCount, sizeof(Size), &outSize); } break; case kEventControlBoundsChanged: { HIRect prevRect, currRect; UInt32 attrs; GetEventParameter( inEvent, kEventParamAttributes, typeUInt32, NULL, sizeof( UInt32 ), NULL, &attrs ); GetEventParameter( inEvent, kEventParamOriginalBounds, typeHIRect, NULL, sizeof( HIRect ), NULL, &prevRect ); GetEventParameter( inEvent, kEventParamCurrentBounds, typeHIRect, NULL, sizeof( HIRect ), NULL, &currRect ); BoundsChanged( view, attrs, &prevRect, &currRect ); result = noErr; } break; case kEventControlActivate: ActiveStateChanged( view ); result = noErr; break; case kEventControlDeactivate: ActiveStateChanged( view ); result = noErr; break; case kEventControlOwningWindowChanged: { WindowRef fromWindow, toWindow; result = GetEventParameter( inEvent, kEventParamControlOriginalOwningWindow, typeWindowRef, NULL, sizeof( WindowRef ), NULL, &fromWindow ); require_noerr( result, MissingParameter ); result = GetEventParameter( inEvent, kEventParamControlCurrentOwningWindow, typeWindowRef, NULL, sizeof( WindowRef ), NULL, &toWindow ); require_noerr( result, MissingParameter ); OwningWindowChanged( view, fromWindow, toWindow ); result = noErr; } break; case kEventControlClick: result = Click( view, inEvent ); break; case kEventControlContextualMenuClick: result = ContextMenuClick( view, inEvent ); break; case kEventControlSetFocusPart: { ControlPartCode desiredFocus; RgnHandle invalidRgn; Boolean focusEverything; ControlPartCode actualFocus; result = GetEventParameter( inEvent, kEventParamControlPart, typeControlPartCode, NULL, sizeof( ControlPartCode ), NULL, &desiredFocus ); require_noerr( result, MissingParameter ); GetEventParameter( inEvent, kEventParamControlInvalRgn, typeQDRgnHandle, NULL, sizeof( RgnHandle ), NULL, &invalidRgn ); focusEverything = false; // a good default in case the parameter doesn't exist GetEventParameter( inEvent, kEventParamControlFocusEverything, typeBoolean, NULL, sizeof( Boolean ), NULL, &focusEverything ); result = SetFocusPart( view, desiredFocus, invalidRgn, focusEverything, &actualFocus ); if ( result == noErr ) verify_noerr( SetEventParameter( inEvent, kEventParamControlPart, typeControlPartCode, sizeof( ControlPartCode ), &actualFocus ) ); } break; // some other kind of Control event default: break; } break; // some other event class default: break; } MissingParameter: return result; } static void UpdateObserver(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info); static void StartUpdateObserver( HIWebView* view ) { CFRunLoopObserverContext context; CFRunLoopObserverRef observer; CFRunLoopRef mainRunLoop; check( view->fIsComposited == false ); check( view->fUpdateObserver == NULL ); context.version = 0; context.info = view; context.retain = NULL; context.release = NULL; context.copyDescription = NULL; mainRunLoop = (CFRunLoopRef)GetCFRunLoopFromEventLoop( GetMainEventLoop() ); observer = CFRunLoopObserverCreate( NULL, kCFRunLoopEntry | kCFRunLoopBeforeWaiting, true, 0, UpdateObserver, &context ); CFRunLoopAddObserver( mainRunLoop, observer, kCFRunLoopCommonModes ); view->fUpdateObserver = observer; // printf( "Update observer started\n" ); } static void StopUpdateObserver( HIWebView* view ) { check( view->fIsComposited == false ); check( view->fUpdateObserver != NULL ); CFRunLoopObserverInvalidate( view->fUpdateObserver ); CFRelease( view->fUpdateObserver ); view->fUpdateObserver = NULL; // printf( "Update observer removed\n" ); } static void UpdateObserver( CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info ) { HIWebView* view = (HIWebView*)info; RgnHandle region = NewRgn(); // printf( "Update observer called\n" ); if ( region ) { GetWindowRegion( GetControlOwner( view->fViewRef ), kWindowUpdateRgn, region ); if ( !EmptyRgn( region ) ) { RgnHandle ourRgn = NewRgn(); Rect rect; GetWindowBounds( GetControlOwner( view->fViewRef ), kWindowStructureRgn, &rect ); // printf( "Update region is non-empty\n" ); if ( ourRgn ) { Rect rect; GrafPtr savePort, port; Point offset = { 0, 0 }; port = GetWindowPort( GetControlOwner( view->fViewRef ) ); GetPort( &savePort ); SetPort( port ); GlobalToLocal( &offset ); OffsetRgn( region, offset.h, offset.v ); GetControlBounds( view->fViewRef, &rect ); RectRgn( ourRgn, &rect ); // printf( "our control is at %d %d %d %d\n", // rect.top, rect.left, rect.bottom, rect.right ); GetRegionBounds( region, &rect ); // printf( "region is at %d %d %d %d\n", // rect.top, rect.left, rect.bottom, rect.right ); SectRgn( ourRgn, region, ourRgn ); GetRegionBounds( ourRgn, &rect ); // printf( "intersection is %d %d %d %d\n", // rect.top, rect.left, rect.bottom, rect.right ); if ( !EmptyRgn( ourRgn ) ) { RgnHandle saveVis = NewRgn(); // printf( "looks like we should draw\n" ); if ( saveVis ) { // RGBColor kRedColor = { 0xffff, 0, 0 }; GetPortVisibleRegion( GetWindowPort( GetControlOwner( view->fViewRef ) ), saveVis ); SetPortVisibleRegion( GetWindowPort( GetControlOwner( view->fViewRef ) ), ourRgn ); // RGBForeColor( &kRedColor ); // PaintRgn( ourRgn ); // QDFlushPortBuffer( port, NULL ); // Delay( 15, NULL ); Draw1Control( view->fViewRef ); ValidWindowRgn( GetControlOwner( view->fViewRef ), ourRgn ); SetPortVisibleRegion( GetWindowPort( GetControlOwner( view->fViewRef ) ), saveVis ); DisposeRgn( saveVis ); } } SetPort( savePort ); DisposeRgn( ourRgn ); } } DisposeRgn( region ); } } #endif