Reviewed by Chris Blumenberg.
[WebKit-https.git] / WebKit / WebView.subproj / WebImageView.m
1 /*
2  * Copyright (C) 2005 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/WebImageView.h>
30
31 #import <WebKit/WebAssertions.h>
32 #import <WebKit/WebDataSource.h>
33 #import <WebKit/WebDocument.h>
34 #import <WebKit/WebFrameView.h>
35 #import <WebKit/WebImageRenderer.h>
36 #import <WebKit/WebImageRendererFactory.h>
37 #import <WebKit/WebImageRepresentation.h>
38 #import <WebKit/WebNSFileManagerExtras.h>
39 #import <WebKit/WebNSObjectExtras.h>
40 #import <WebKit/WebNSPasteboardExtras.h>
41 #import <WebKit/WebNSViewExtras.h>
42 #import <WebKit/WebViewPrivate.h>
43 #import <WebKit/WebUIDelegatePrivate.h>
44
45 #import <WebCore/WebCoreImageRenderer.h>
46
47 @implementation WebImageView
48
49 + (void)initialize
50 {
51     [NSApp registerServicesMenuSendTypes:[NSArray arrayWithObject:NSTIFFPboardType] returnTypes:nil];
52 }
53
54 + (NSArray *)supportedImageMIMETypes
55 {
56     static NSMutableArray *imageMIMETypes = nil;
57     if (imageMIMETypes == nil) {
58         imageMIMETypes = [[[WebImageRendererFactory sharedFactory] supportedMIMETypes] mutableCopy];
59         [imageMIMETypes removeObject:@"application/pdf"];
60         [imageMIMETypes removeObject:@"application/postscript"];
61     }
62     return imageMIMETypes;
63 }
64
65 - (id)initWithFrame:(NSRect)frame
66 {
67     self = [super initWithFrame:frame];
68     if (!self)
69         return nil;
70     [self setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
71     return self;
72 }
73
74 - (void)dealloc
75 {
76     [[rep image] stopAnimation];
77     [rep release];
78     [mouseDownEvent release];
79     
80     [super dealloc];
81 }
82
83 - (void)finalize
84 {
85     [[rep image] stopAnimation];
86     [super finalize];
87 }
88
89 - (BOOL)haveCompleteImage
90 {
91     NSSize imageSize = [[rep image] size];
92     return [rep doneLoading] && imageSize.width > 0 && imageSize.width > 0;
93 }
94
95 - (BOOL)isFlipped 
96 {
97     return YES;
98 }
99
100 - (BOOL)acceptsFirstResponder
101 {
102     // Being first responder is useful for scrolling from the keyboard at least.
103     return YES;
104 }
105
106 - (NSRect)drawingRect
107 {
108     NSSize imageSize = [[rep image] size];
109     return NSMakeRect(0, 0, imageSize.width, imageSize.height);
110 }
111
112 - (void)drawRect:(NSRect)rect
113 {
114     if (needsLayout) {
115         [self layout];
116     }
117     
118     if ([[self _webView] drawsBackground]) {
119         [[NSColor whiteColor] set];
120         NSRectFill(rect);
121     }
122     
123     NSRect drawingRect = [self drawingRect];
124     [[rep image] drawImageInRect:drawingRect fromRect:drawingRect];
125 }
126
127 - (void)adjustFrameSize
128 {
129     NSSize size = [[rep image] size];
130     
131     // When drawing on screen, ensure that the view always fills the content area 
132     // (so we draw over the entire previous page), and that the view is at least 
133     // as large as the image.. Otherwise we're printing, and we want the image to 
134     // fill the view so that the printed size doesn't depend on the window size.
135     if ([NSGraphicsContext currentContextDrawingToScreen]) {
136         NSSize clipViewSize = [[self _web_superviewOfClass:[NSClipView class]] frame].size;
137         size.width = MAX(size.width, clipViewSize.width);
138         size.height = MAX(size.height, clipViewSize.height);
139     }
140     
141     [super setFrameSize:size];
142 }
143
144 - (void)setFrameSize:(NSSize)size
145 {
146     [self adjustFrameSize];
147 }
148
149 - (void)layout
150 {
151     [self adjustFrameSize];    
152     needsLayout = NO;
153 }
154
155 - (void)setDataSource:(WebDataSource *)dataSource
156 {
157     ASSERT(!rep);
158     rep = [[dataSource representation] retain];
159 }
160
161 - (void)dataSourceUpdated:(WebDataSource *)dataSource
162 {
163     NSSize imageSize = [[rep image] size];
164     if (imageSize.width > 0 && imageSize.height > 0) {
165         [self setNeedsLayout:YES];
166         [self setNeedsDisplay:YES];
167     }
168 }
169
170 - (void)setNeedsLayout: (BOOL)flag
171 {
172     needsLayout = flag;
173 }
174
175 - (void)viewWillMoveToHostWindow:(NSWindow *)hostWindow
176 {
177 }
178
179 - (void)viewDidMoveToHostWindow
180 {
181 }
182
183 - (void)viewDidMoveToWindow
184 {
185     if (![self window]){
186         [[rep image] stopAnimation];
187     }
188     
189     [super viewDidMoveToWindow];
190 }
191
192 - (WebView *)webView
193 {
194     return [self _web_parentWebView];
195 }
196
197 - (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item
198 {
199     if ([item action] == @selector(copy:)){
200         return [self haveCompleteImage];
201     }
202
203     return YES;
204 }
205
206 - (id)validRequestorForSendType:(NSString *)sendType returnType:(NSString *)returnType
207 {
208     if (sendType && [sendType isEqualToString:NSTIFFPboardType]){
209         return self;
210     }
211
212     return [super validRequestorForSendType:sendType returnType:returnType];
213 }
214
215 - (BOOL)writeImageToPasteboard:(NSPasteboard *)pasteboard types:(NSArray *)types
216 {    
217     if ([self haveCompleteImage]) {
218         [pasteboard _web_writeImage:[rep image] URL:[rep URL] title:nil archive:[rep archive] types:types];
219         return YES;
220     }
221     
222     return NO;
223 }
224
225 - (void)copy:(id)sender
226 {
227     [self writeImageToPasteboard:[NSPasteboard generalPasteboard] types:[NSPasteboard _web_writableTypesForImageIncludingArchive:([rep archive] != nil)]];
228 }
229
230 - (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pasteboard types:(NSArray *)types
231 {
232     return [self writeImageToPasteboard:pasteboard types:types];
233 }
234
235 - (NSDictionary *)elementAtPoint:(NSPoint)point
236 {
237     WebFrame *frame = [[self _web_parentWebFrameView] webFrame];
238     ASSERT(frame);
239     
240     return [NSDictionary dictionaryWithObjectsAndKeys:
241         [rep image],                            WebElementImageKey,
242         [NSValue valueWithRect:[self bounds]],  WebElementImageRectKey,
243         [rep URL],                              WebElementImageURLKey,
244         [NSNumber numberWithBool:NO],           WebElementIsSelectedKey,
245         frame,                                  WebElementFrameKey, nil];
246 }
247
248 - (NSMenu *)menuForEvent:(NSEvent *)theEvent
249 {
250     WebView *webView = [self webView];
251     ASSERT(webView);
252     return [webView _menuForElement:[self elementAtPoint:NSZeroPoint] defaultItems:nil];
253 }
254
255 - (void)mouseDown:(NSEvent *)event
256 {
257     ignoringMouseDraggedEvents = NO;
258     [mouseDownEvent release];
259     mouseDownEvent = [event retain];
260     
261     WebView *webView = [self webView];
262     NSPoint point = [webView convertPoint:[mouseDownEvent locationInWindow] fromView:nil];
263     dragSourceActionMask = [[webView _UIDelegateForwarder] webView:webView dragSourceActionMaskForPoint:point];
264     
265     [super mouseDown:event];
266 }
267
268 - (void)mouseDragged:(NSEvent *)mouseDraggedEvent
269 {
270     if (ignoringMouseDraggedEvents || ![self haveCompleteImage] || !(dragSourceActionMask & WebDragSourceActionImage)) {
271         return;
272     }
273     
274     NSPasteboard *pasteboard = [NSPasteboard pasteboardWithName:NSDragPboard];
275     id source = [pasteboard _web_declareAndWriteDragImage:[rep image]
276                                                       URL:[rep URL]
277                                                     title:nil
278                                                   archive:[rep archive]
279                                                    source:self];
280     
281     WebView *webView = [self webView];
282     NSPoint point = [webView convertPoint:[mouseDownEvent locationInWindow] fromView:nil];
283     [[webView _UIDelegateForwarder] webView:webView willPerformDragSourceAction:WebDragSourceActionImage fromPoint:point withPasteboard:pasteboard];
284     
285     [[self webView] _setInitiatedDrag:YES];
286     
287     // Retain this view during the drag because it may be released before the drag ends.
288     [self retain];
289     
290     [self _web_dragImage:[rep image]
291                     rect:[self drawingRect]
292                    event:mouseDraggedEvent
293               pasteboard:pasteboard
294                   source:source
295                   offset:NULL];
296 }
297
298 - (NSArray *)namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination
299 {
300     // FIXME: Report an error if we fail to create a file.
301     NSString *path = [[dropDestination path] stringByAppendingPathComponent:[rep filename]];
302     path = [[NSFileManager defaultManager] _webkit_pathWithUniqueFilenameForPath:path];
303     [[rep data] writeToFile:path atomically:NO];
304     return [NSArray arrayWithObject:[path lastPathComponent]];
305 }
306
307 - (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation
308 {
309     // Prevent queued mouseDragged events from coming after the drag which can cause a double drag.
310     ignoringMouseDraggedEvents = YES;
311     
312     [[self webView] _setInitiatedDrag:NO];
313
314     // Balance the previous retain from when the drag started.
315     [self release];
316 }
317
318 - (NSImage *)image
319 {
320     return [[rep image] image];
321 }
322
323 #pragma mark PRINTING
324
325 - (void)drawPageBorderWithSize:(NSSize)borderSize
326 {
327     ASSERT(NSEqualSizes(borderSize, [[[NSPrintOperation currentOperation] printInfo] paperSize]));
328     // FIXME: How to determine the number of pages required to print the whole image?
329     [[self webView] _drawHeaderAndFooter];
330 }
331
332 - (void)beginDocument
333 {
334     [self adjustFrameSize];
335     [[self webView] _adjustPrintingMarginsForHeaderAndFooter];
336     [super beginDocument];
337 }
338
339 - (void)endDocument
340 {
341     [super endDocument];
342     [self adjustFrameSize];
343 }
344
345 - (BOOL)canPrintHeadersAndFooters
346 {
347     return YES;
348 }
349
350 @end