1/* 2 * Copyright (C) 2005, 2006 Apple Computer, Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 14 * its contributors may be used to endorse or promote products derived 15 * from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29#import <WebKit/WebNSViewExtras.h> 30 31#import <WebKit/DOMExtensions.h> 32#import <WebKit/WebDataSource.h> 33#import <WebKit/WebFramePrivate.h> 34#import <WebKit/WebFrameViewInternal.h> 35#import <WebKit/WebNSImageExtras.h> 36#import <WebKit/WebNSPasteboardExtras.h> 37#import <WebKit/WebNSURLExtras.h> 38#import <WebKit/WebView.h> 39 40#define WebDragStartHysteresisX 5.0f 41#define WebDragStartHysteresisY 5.0f 42#define WebMaxDragImageSize NSMakeSize(400.0f, 400.0f) 43#define WebMaxOriginalImageArea (1500.0f * 1500.0f) 44#define WebDragIconRightInset 7.0f 45#define WebDragIconBottomInset 3.0f 46 47@implementation NSView (WebExtras) 48 49- (NSView *)_web_superviewOfClass:(Class)class 50{ 51 NSView *view = [self superview]; 52 while (view && ![view isKindOfClass:class]) 53 view = [view superview]; 54 return view; 55} 56 57- (WebFrameView *)_web_parentWebFrameView 58{ 59 return (WebFrameView *)[self _web_superviewOfClass:[WebFrameView class]]; 60} 61 62// FIXME: Mail is the only client of _webView, remove this method once no versions of Mail need it. 63- (WebView *)_webView 64{ 65 return (WebView *)[self _web_superviewOfClass:[WebView class]]; 66} 67 68/* Determine whether a mouse down should turn into a drag; started as copy of NSTableView code */ 69- (BOOL)_web_dragShouldBeginFromMouseDown:(NSEvent *)mouseDownEvent 70 withExpiration:(NSDate *)expiration 71 xHysteresis:(float)xHysteresis 72 yHysteresis:(float)yHysteresis 73{ 74 NSEvent *nextEvent, *firstEvent, *dragEvent, *mouseUp; 75 BOOL dragIt; 76 77 if ([mouseDownEvent type] != NSLeftMouseDown) { 78 return NO; 79 } 80 81 nextEvent = nil; 82 firstEvent = nil; 83 dragEvent = nil; 84 mouseUp = nil; 85 dragIt = NO; 86 87 while ((nextEvent = [[self window] nextEventMatchingMask:(NSLeftMouseUpMask | NSLeftMouseDraggedMask) 88 untilDate:expiration 89 inMode:NSEventTrackingRunLoopMode 90 dequeue:YES]) != nil) { 91 if (firstEvent == nil) { 92 firstEvent = nextEvent; 93 } 94 95 if ([nextEvent type] == NSLeftMouseDragged) { 96 float deltax = ABS([nextEvent locationInWindow].x - [mouseDownEvent locationInWindow].x); 97 float deltay = ABS([nextEvent locationInWindow].y - [mouseDownEvent locationInWindow].y); 98 dragEvent = nextEvent; 99 100 if (deltax >= xHysteresis) { 101 dragIt = YES; 102 break; 103 } 104 105 if (deltay >= yHysteresis) { 106 dragIt = YES; 107 break; 108 } 109 } else if ([nextEvent type] == NSLeftMouseUp) { 110 mouseUp = nextEvent; 111 break; 112 } 113 } 114 115 // Since we've been dequeuing the events (If we don't, we'll never see the mouse up...), 116 // we need to push some of the events back on. It makes sense to put the first and last 117 // drag events and the mouse up if there was one. 118 if (mouseUp != nil) { 119 [NSApp postEvent:mouseUp atStart:YES]; 120 } 121 if (dragEvent != nil) { 122 [NSApp postEvent:dragEvent atStart:YES]; 123 } 124 if (firstEvent != mouseUp && firstEvent != dragEvent) { 125 [NSApp postEvent:firstEvent atStart:YES]; 126 } 127 128 return dragIt; 129} 130 131- (BOOL)_web_dragShouldBeginFromMouseDown:(NSEvent *)mouseDownEvent 132 withExpiration:(NSDate *)expiration 133{ 134 return [self _web_dragShouldBeginFromMouseDown:mouseDownEvent 135 withExpiration:expiration 136 xHysteresis:WebDragStartHysteresisX 137 yHysteresis:WebDragStartHysteresisY]; 138} 139 140 141- (NSDragOperation)_web_dragOperationForDraggingInfo:(id <NSDraggingInfo>)sender 142{ 143 if (![NSApp modalWindow] && 144 ![[self window] attachedSheet] && 145 [sender draggingSource] != self && 146 [[sender draggingPasteboard] _web_bestURL]) { 147 148 return NSDragOperationCopy; 149 } 150 151 return NSDragOperationNone; 152} 153 154- (void)_web_DragImageForElement:(DOMElement *)element 155 rect:(NSRect)rect 156 event:(NSEvent *)event 157 pasteboard:(NSPasteboard *)pasteboard 158 source:(id)source 159 offset:(NSPoint *)dragImageOffset 160{ 161 NSPoint mouseDownPoint = [self convertPoint:[event locationInWindow] fromView:nil]; 162 NSImage *dragImage; 163 NSPoint origin; 164 165 NSImage *image = [element image]; 166 if (image != nil && [image size].height * [image size].width <= WebMaxOriginalImageArea) { 167 NSSize originalSize = rect.size; 168 origin = rect.origin; 169 170 dragImage = [[image copy] autorelease]; 171 [dragImage setScalesWhenResized:YES]; 172 [dragImage setSize:originalSize]; 173 174 [dragImage _web_scaleToMaxSize:WebMaxDragImageSize]; 175 NSSize newSize = [dragImage size]; 176 177 [dragImage _web_dissolveToFraction:WebDragImageAlpha]; 178 179 // Properly orient the drag image and orient it differently if it's smaller than the original 180 origin.x = mouseDownPoint.x - (((mouseDownPoint.x - origin.x) / originalSize.width) * newSize.width); 181 origin.y = origin.y + originalSize.height; 182 origin.y = mouseDownPoint.y - (((mouseDownPoint.y - origin.y) / originalSize.height) * newSize.height); 183 } else { 184 // FIXME: This has been broken for a while. 185 // There's no way to get the MIME type for the image from a DOM element. 186 // The old code used WKGetPreferredExtensionForMIMEType([image MIMEType]); 187 NSString *extension = @""; 188 dragImage = [[NSWorkspace sharedWorkspace] iconForFileType:extension]; 189 NSSize offset = NSMakeSize([dragImage size].width - WebDragIconRightInset, -WebDragIconBottomInset); 190 origin = NSMakePoint(mouseDownPoint.x - offset.width, mouseDownPoint.y - offset.height); 191 } 192 193 // This is the offset from the lower left corner of the image to the mouse location. Because we 194 // are a flipped view the calculation of Y is inverted. 195 if (dragImageOffset) { 196 dragImageOffset->x = mouseDownPoint.x - origin.x; 197 dragImageOffset->y = origin.y - mouseDownPoint.y; 198 } 199 200 // Per kwebster, offset arg is ignored 201 [self dragImage:dragImage at:origin offset:NSZeroSize event:event pasteboard:pasteboard source:source slideBack:YES]; 202} 203 204- (BOOL)_web_firstResponderIsSelfOrDescendantView 205{ 206 NSResponder *responder = [[self window] firstResponder]; 207 return (responder && 208 (responder == self || 209 ([responder isKindOfClass:[NSView class]] && [(NSView *)responder isDescendantOf:self]))); 210} 211 212- (NSRect)_web_convertRect:(NSRect)aRect toView:(NSView *)aView 213{ 214 // Converting to this view's window; let -convertRect:toView: handle it 215 if (aView == nil) 216 return [self convertRect:aRect toView:nil]; 217 218 // This view must be in a window. Do whatever weird thing -convertRect:toView: does in this situation. 219 NSWindow *thisWindow = [self window]; 220 if (!thisWindow) 221 return [self convertRect:aRect toView:aView]; 222 223 // The other view must be in a window, too. 224 NSWindow *otherWindow = [aView window]; 225 if (!otherWindow) 226 return [self convertRect:aRect toView:aView]; 227 228 // Convert to this window's coordinates 229 NSRect convertedRect = [self convertRect:aRect toView:nil]; 230 231 // Convert to screen coordinates 232 convertedRect.origin = [thisWindow convertBaseToScreen:convertedRect.origin]; 233 234 // Convert to other window's coordinates 235 convertedRect.origin = [otherWindow convertScreenToBase:convertedRect.origin]; 236 237 // Convert to other view's coordinates 238 convertedRect = [aView convertRect:convertedRect fromView:nil]; 239 240 return convertedRect; 241} 242 243@end 244