Home | History | Annotate | Download | only in Misc
      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