Reviewed by Richard
[WebKit-https.git] / WebKit / Misc.subproj / WebNSViewExtras.m
1 /*
2         WebNSViewExtras.m
3         Copyright (c) 2002, Apple, Inc. All rights reserved.
4 */
5
6 #import <WebKit/WebNSViewExtras.h>
7
8 #import <WebKit/WebFrameView.h>
9 #import <WebKit/WebImageRenderer.h>
10 #import <WebKit/WebNSImageExtras.h>
11 #import <WebKit/WebNSPasteboardExtras.h>
12 #import <WebKit/WebNSURLExtras.h>
13
14 #import <Foundation/NSString_NSURLExtras.h>
15 #import <Foundation/NSURL_NSURLExtras.h>
16 #import <Foundation/NSURLFileTypeMappings.h>
17
18 #define WebDragStartHysteresisX                 5.0
19 #define WebDragStartHysteresisY                 5.0
20 #define WebMaxDragImageSize                     NSMakeSize(400, 400)
21 #define WebMaxOriginalImageArea                 (1500 * 1500)
22 #define WebDragIconRightInset                   7.0
23 #define WebDragIconBottomInset                  3.0
24
25 #ifdef DEBUG_VIEWS
26 @interface NSObject (Foo)
27 - (void*)_renderFramePart;
28 - (id)_frameForView: (id)aView;
29 - (id)_webView;
30 @end
31 #endif
32
33 @implementation NSView (WebExtras)
34
35 - (NSView *)_web_superviewOfClass:(Class)class stoppingAtClass:(Class)limitClass
36 {
37     NSView *view = self;
38     while ((view = [view superview]) != nil) {
39         if ([view isKindOfClass:class]) {
40             return view;
41         } else if (limitClass && [view isKindOfClass:limitClass]) {
42             break;
43         }
44     }
45
46     return nil;
47 }
48
49 - (NSView *)_web_superviewOfClass:(Class)class
50 {
51     return [self _web_superviewOfClass:class stoppingAtClass:nil];
52 }
53
54 - (WebFrameView *)_web_parentWebFrameView
55 {
56     WebFrameView *view = (WebFrameView *)[[[self superview] superview] superview];
57     
58     if ([view isKindOfClass: [WebFrameView class]])
59         return view;
60     return nil;
61 }
62
63 /* Determine whether a mouse down should turn into a drag; started as copy of NSTableView code */
64 - (BOOL)_web_dragShouldBeginFromMouseDown:(NSEvent *)mouseDownEvent
65                            withExpiration:(NSDate *)expiration
66                               xHysteresis:(float)xHysteresis
67                               yHysteresis:(float)yHysteresis
68 {
69     NSEvent *nextEvent, *firstEvent, *dragEvent, *mouseUp;
70     BOOL dragIt;
71
72     if ([mouseDownEvent type] != NSLeftMouseDown) {
73         return NO;
74     }
75
76     nextEvent = nil;
77     firstEvent = nil;
78     dragEvent = nil;
79     mouseUp = nil;
80     dragIt = NO;
81
82     while ((nextEvent = [[self window] nextEventMatchingMask:(NSLeftMouseUpMask | NSLeftMouseDraggedMask)
83                                                    untilDate:expiration
84                                                       inMode:NSEventTrackingRunLoopMode
85                                                      dequeue:YES]) != nil) {
86         if (firstEvent == nil) {
87             firstEvent = nextEvent;
88         }
89
90         if ([nextEvent type] == NSLeftMouseDragged) {
91             float deltax = ABS([nextEvent locationInWindow].x - [mouseDownEvent locationInWindow].x);
92             float deltay = ABS([nextEvent locationInWindow].y - [mouseDownEvent locationInWindow].y);
93             dragEvent = nextEvent;
94
95             if (deltax >= xHysteresis) {
96                 dragIt = YES;
97                 break;
98             }
99
100             if (deltay >= yHysteresis) {
101                 dragIt = YES;
102                 break;
103             }
104         } else if ([nextEvent type] == NSLeftMouseUp) {
105             mouseUp = nextEvent;
106             break;
107         }
108     }
109
110     // Since we've been dequeuing the events (If we don't, we'll never see the mouse up...),
111     // we need to push some of the events back on.  It makes sense to put the first and last
112     // drag events and the mouse up if there was one.
113     if (mouseUp != nil) {
114         [NSApp postEvent:mouseUp atStart:YES];
115     }
116     if (dragEvent != nil) {
117         [NSApp postEvent:dragEvent atStart:YES];
118     }
119     if (firstEvent != mouseUp && firstEvent != dragEvent) {
120         [NSApp postEvent:firstEvent atStart:YES];
121     }
122
123     return dragIt;
124 }
125
126 - (BOOL)_web_dragShouldBeginFromMouseDown:(NSEvent *)mouseDownEvent
127                            withExpiration:(NSDate *)expiration
128 {
129     return [self _web_dragShouldBeginFromMouseDown:mouseDownEvent
130                                     withExpiration:expiration
131                                        xHysteresis:WebDragStartHysteresisX
132                                        yHysteresis:WebDragStartHysteresisY];
133 }
134
135
136 - (NSDragOperation)_web_dragOperationForDraggingInfo:(id <NSDraggingInfo>)sender
137 {
138     if (![NSApp modalWindow] && 
139         ![[self window] attachedSheet] &&
140         [sender draggingSource] != self &&
141         [[sender draggingPasteboard] _web_bestURL]) {
142         return NSDragOperationCopy;
143     } else {
144         return NSDragOperationNone;
145     }
146 }
147
148 #ifdef DEBUG_VIEWS
149 - (void)_web_printViewHierarchy: (int)level
150 {
151     NSArray *subviews;
152     int _level = level, i;
153     NSRect f;
154     NSView *subview;
155     void *rfp = 0;
156     
157     subviews = [self subviews];
158     _level = level;
159     while (_level-- > 0)
160         printf (" ");
161     f = [self frame];
162     
163     if ([self respondsToSelector: @selector(_webView)]){
164         id aWebView = [self _webView];
165         id aFrame = [aWebView _frameForView: self];
166         rfp = [aFrame _renderFramePart];
167     }
168     
169     printf ("%s renderFramePart %p (%f,%f) w %f, h %f\n", [[[self class] className] cString], rfp, f.origin.x, f.origin.y, f.size.width, f.size.height);
170     for (i = 0; i < (int)[subviews count]; i++){
171         subview = [subviews objectAtIndex: i];
172         [subview _web_printViewHierarchy: level + 1];
173     }
174 }
175 #endif
176
177 - (void)_web_dragImage:(WebImageRenderer *)image
178                   rect:(NSRect)rect
179                  event:(NSEvent *)event
180             pasteboard:(NSPasteboard *)pasteboard 
181                 source:(id)source
182                 offset:(NSPoint *)dragImageOffset
183 {
184     NSPoint mouseDownPoint = [self convertPoint:[event locationInWindow] fromView:nil];
185     NSImage *dragImage;
186     NSPoint origin;
187     
188     if ([image size].height * [image size].width <= WebMaxOriginalImageArea) {
189         NSSize originalSize = rect.size;
190         origin = rect.origin;
191         
192         dragImage = [[image copy] autorelease];
193         [dragImage setScalesWhenResized:YES];
194         [dragImage setSize:originalSize];
195         
196         [dragImage _web_scaleToMaxSize:WebMaxDragImageSize];
197         NSSize newSize = [dragImage size];
198         
199         [dragImage _web_dissolveToFraction:WebDragImageAlpha];
200         
201         // Properly orient the drag image and orient it differently if it's smaller than the original
202         origin.x = mouseDownPoint.x - (((mouseDownPoint.x - origin.x) / originalSize.width) * newSize.width);
203         origin.y = origin.y + originalSize.height;
204         origin.y = mouseDownPoint.y - (((mouseDownPoint.y - origin.y) / originalSize.height) * newSize.height);
205     } else {
206         NSString *extension = [[NSURLFileTypeMappings sharedMappings] preferredExtensionForMIMEType:[image MIMEType]];
207         if (extension == nil) {
208             extension = @"";
209         }
210         dragImage = [[NSWorkspace sharedWorkspace] iconForFileType:extension];
211         NSSize offset = NSMakeSize([dragImage size].width - WebDragIconRightInset, -WebDragIconBottomInset);
212         origin = NSMakePoint(mouseDownPoint.x - offset.width, mouseDownPoint.y - offset.height);
213     }
214
215     // This is the offset from the lower left corner of the image to the mouse location.  Because we
216     // are a flipped view the calculation of Y is inverted.
217     if (dragImageOffset) {
218         dragImageOffset->x = mouseDownPoint.x - origin.x;
219         dragImageOffset->y = origin.y - mouseDownPoint.y;
220     }
221     
222     // Per kwebster, offset arg is ignored
223     [self dragImage:dragImage at:origin offset:NSZeroSize event:event pasteboard:pasteboard source:source slideBack:YES];
224 }
225
226 - (BOOL)firstResponderIsSelfOrDescendantView
227 {
228     NSResponder *responder = [[self window] firstResponder];
229     return (responder && 
230            (responder == self || 
231            ([responder isKindOfClass:[NSView class]] && [(NSView *)responder isDescendantOf:self])));
232 }
233
234 @end