Ignore deprecation warnings.
[WebKit-https.git] / Source / WebCore / platform / mac / DragImageMac.mm
1 /*
2  * Copyright (C) 2007, 2009, 2012 Apple 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  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #import "config.h"
27 #import "DragImage.h"
28
29 #if ENABLE(DRAG_SUPPORT)
30 #import "BitmapImage.h"
31 #import "CoreGraphicsSPI.h"
32 #import "FontCascade.h"
33 #import "FontDescription.h"
34 #import "FontSelector.h"
35 #import "GraphicsContext.h"
36 #import "Image.h"
37 #import "URL.h"
38 #import "ResourceResponse.h"
39 #import "StringTruncator.h"
40 #import "TextRun.h"
41 #import <wtf/NeverDestroyed.h>
42
43 namespace WebCore {
44
45 IntSize dragImageSize(RetainPtr<NSImage> image)
46 {
47     return (IntSize)[image.get() size];
48 }
49
50 void deleteDragImage(RetainPtr<NSImage>)
51 {
52     // Since this is a RetainPtr, there's nothing additional we need to do to
53     // delete it. It will be released when it falls out of scope.
54 }
55
56 RetainPtr<NSImage> scaleDragImage(RetainPtr<NSImage> image, FloatSize scale)
57 {
58     NSSize originalSize = [image.get() size];
59     NSSize newSize = NSMakeSize((originalSize.width * scale.width()), (originalSize.height * scale.height()));
60     newSize.width = roundf(newSize.width);
61     newSize.height = roundf(newSize.height);
62 #pragma clang diagnostic push
63 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
64     [image.get() setScalesWhenResized:YES];
65 #pragma clang diagnostic pop
66     [image.get() setSize:newSize];
67     return image;
68 }
69     
70 RetainPtr<NSImage> dissolveDragImageToFraction(RetainPtr<NSImage> image, float delta)
71 {
72     if (!image)
73         return nil;
74
75     RetainPtr<NSImage> dissolvedImage = adoptNS([[NSImage alloc] initWithSize:[image.get() size]]);
76     
77     [dissolvedImage.get() lockFocus];
78 #pragma clang diagnostic push
79 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
80     [image.get() drawAtPoint:NSZeroPoint fromRect:NSMakeRect(0, 0, [image size].width, [image size].height) operation:NSCompositeCopy fraction:delta];
81 #pragma clang diagnostic pop
82     [dissolvedImage.get() unlockFocus];
83
84     return dissolvedImage;
85 }
86         
87 RetainPtr<NSImage> createDragImageFromImage(Image* image, ImageOrientationDescription description)
88 {
89     FloatSize size = image->size();
90
91     if (is<BitmapImage>(*image)) {
92         ImageOrientation orientation;
93         BitmapImage& bitmapImage = downcast<BitmapImage>(*image);
94         IntSize sizeRespectingOrientation = bitmapImage.sizeRespectingOrientation(description);
95
96         if (description.respectImageOrientation() == RespectImageOrientation)
97             orientation = bitmapImage.orientationForCurrentFrame();
98
99         if (orientation != DefaultImageOrientation) {
100             // Construct a correctly-rotated copy of the image to use as the drag image.
101             FloatRect destRect(FloatPoint(), sizeRespectingOrientation);
102
103             RetainPtr<NSImage> rotatedDragImage = adoptNS([[NSImage alloc] initWithSize:(NSSize)(sizeRespectingOrientation)]);
104             [rotatedDragImage.get() lockFocus];
105
106             // ImageOrientation uses top-left coordinates, need to flip to bottom-left, apply...
107             CGAffineTransform transform = CGAffineTransformMakeTranslation(0, destRect.height());
108             transform = CGAffineTransformScale(transform, 1, -1);
109             transform = CGAffineTransformConcat(orientation.transformFromDefault(sizeRespectingOrientation), transform);
110
111             if (orientation.usesWidthAsHeight())
112                 destRect = FloatRect(destRect.x(), destRect.y(), destRect.height(), destRect.width());
113
114             // ...and flip back.
115             transform = CGAffineTransformTranslate(transform, 0, destRect.height());
116             transform = CGAffineTransformScale(transform, 1, -1);
117
118             RetainPtr<NSAffineTransform> cocoaTransform = adoptNS([[NSAffineTransform alloc] init]);
119             [cocoaTransform.get() setTransformStruct:*(NSAffineTransformStruct*)&transform];
120             [cocoaTransform.get() concat];
121
122 #pragma clang diagnostic push
123 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
124             [image->getNSImage() drawInRect:destRect fromRect:NSMakeRect(0, 0, size.width(), size.height()) operation:NSCompositeSourceOver fraction:1.0];
125 #pragma clang diagnostic pop
126             [rotatedDragImage.get() unlockFocus];
127
128             return rotatedDragImage;
129         }
130     }
131
132     RetainPtr<NSImage> dragImage = adoptNS([image->getNSImage() copy]);
133     [dragImage.get() setSize:(NSSize)size];
134     return dragImage;
135 }
136     
137 RetainPtr<NSImage> createDragImageIconForCachedImageFilename(const String& filename)
138 {
139     NSString *extension = nil;
140     size_t dotIndex = filename.reverseFind('.');
141     
142     if (dotIndex != notFound && dotIndex < (filename.length() - 1)) // require that a . exists after the first character and before the last
143         extension = filename.substring(dotIndex + 1);
144     else {
145         // It might be worth doing a further lookup to pull the extension from the MIME type.
146         extension = @"";
147     }
148     
149     return [[NSWorkspace sharedWorkspace] iconForFileType:extension];
150 }
151
152
153 const float DragLabelBorderX = 4;
154 //Keep border_y in synch with DragController::LinkDragBorderInset
155 const float DragLabelBorderY = 2;
156 const float DragLabelRadius = 5;
157 const float LabelBorderYOffset = 2;
158
159 const float MinDragLabelWidthBeforeClip = 120;
160 const float MaxDragLabelWidth = 320;
161
162 const float DragLinkLabelFontsize = 11;
163 const float DragLinkUrlFontSize = 10;
164
165 // FIXME - we should move all the functionality of NSString extras to WebCore
166     
167 static FontCascade& fontFromNSFont(NSFont *font)
168 {
169     static NSFont *currentFont;
170     static NeverDestroyed<FontCascade> currentRenderer;
171     
172     if ([font isEqual:currentFont])
173         return currentRenderer;
174     if (currentFont)
175         CFRelease(currentFont);
176     currentFont = font;
177     CFRetain(currentFont);
178     currentRenderer.get() = FontCascade(FontPlatformData(toCTFont(font), [font pointSize]));
179     return currentRenderer;
180 }
181
182 static bool canUseFastRenderer(const UniChar* buffer, unsigned length)
183 {
184     unsigned i;
185     for (i = 0; i < length; i++) {
186         UCharDirection direction = u_charDirection(buffer[i]);
187         if (direction == U_RIGHT_TO_LEFT || direction > U_OTHER_NEUTRAL)
188             return false;
189     }
190     return true;
191 }
192     
193 static float widthWithFont(NSString *string, NSFont *font)
194 {
195     unsigned length = [string length];
196     Vector<UniChar, 2048> buffer(length);
197     
198     [string getCharacters:buffer.data()];
199     
200     if (canUseFastRenderer(buffer.data(), length)) {
201         FontCascade webCoreFont(FontPlatformData(toCTFont(font), [font pointSize]));
202         TextRun run(StringView(buffer.data(), length));
203         return webCoreFont.width(run);
204     }
205     
206     return [string sizeWithAttributes:[NSDictionary dictionaryWithObjectsAndKeys:font, NSFontAttributeName, nil]].width;
207 }
208     
209 static void drawAtPoint(NSString *string, NSPoint point, NSFont *font, NSColor *textColor)
210 {
211     unsigned length = [string length];
212     Vector<UniChar, 2048> buffer(length);
213     
214     [string getCharacters:buffer.data()];
215     
216     if (canUseFastRenderer(buffer.data(), length)) {
217         // The following is a half-assed attempt to match AppKit's rounding rules for drawAtPoint.
218         // It's probably incorrect for high DPI.
219         // If you change this, be sure to test all the text drawn this way in Safari, including
220         // the status bar, bookmarks bar, tab bar, and activity window.
221         point.y = CGCeiling(point.y);
222         
223         NSGraphicsContext *nsContext = [NSGraphicsContext currentContext];
224         CGContextRef cgContext = static_cast<CGContextRef>([nsContext graphicsPort]);
225         GraphicsContext graphicsContext(cgContext);    
226         
227         // Safari doesn't flip the NSGraphicsContext before calling WebKit, yet WebCore requires a flipped graphics context.
228         BOOL flipped = [nsContext isFlipped];
229         if (!flipped)
230             CGContextScaleCTM(cgContext, 1, -1);
231             
232         FontCascade webCoreFont(FontPlatformData(toCTFont(font), [font pointSize]), Antialiased);
233         TextRun run(StringView(buffer.data(), length));
234
235         CGFloat red;
236         CGFloat green;
237         CGFloat blue;
238         CGFloat alpha;
239         [[textColor colorUsingColorSpaceName:NSDeviceRGBColorSpace] getRed:&red green:&green blue:&blue alpha:&alpha];
240         graphicsContext.setFillColor(makeRGBA(red * 255, green * 255, blue * 255, alpha * 255));
241         
242         webCoreFont.drawText(graphicsContext, run, FloatPoint(point.x, (flipped ? point.y : (-1 * point.y))));
243         
244         if (!flipped)
245             CGContextScaleCTM(cgContext, 1, -1);
246     } else {
247         // The given point is on the baseline.
248         if ([[NSView focusView] isFlipped])
249             point.y -= [font ascender];
250         else
251             point.y += [font descender];
252                 
253         [string drawAtPoint:point withAttributes:[NSDictionary dictionaryWithObjectsAndKeys:font, NSFontAttributeName, textColor, NSForegroundColorAttributeName, nil]];
254     }
255 }
256     
257 static void drawDoubledAtPoint(NSString *string, NSPoint textPoint, NSColor *topColor, NSColor *bottomColor, NSFont *font)
258 {
259         // turn off font smoothing so translucent text draws correctly (Radar 3118455)
260         drawAtPoint(string, textPoint, font, bottomColor);
261         
262         textPoint.y += 1;
263         drawAtPoint(string, textPoint, font, topColor);
264 }
265
266 DragImageRef createDragImageForLink(URL& url, const String& title, FontRenderingMode)
267 {
268     NSString *label = nsStringNilIfEmpty(title);
269     NSURL *cocoaURL = url;
270     NSString *urlString = [cocoaURL absoluteString];
271
272     BOOL drawURLString = YES;
273     BOOL clipURLString = NO;
274     BOOL clipLabelString = NO;
275
276     if (!label) {
277         drawURLString = NO;
278         label = urlString;
279     }
280
281     NSFont *labelFont = [[NSFontManager sharedFontManager] convertFont:[NSFont systemFontOfSize:DragLinkLabelFontsize]
282                                                            toHaveTrait:NSBoldFontMask];
283     NSFont *urlFont = [NSFont systemFontOfSize:DragLinkUrlFontSize];
284     NSSize labelSize;
285     labelSize.width = widthWithFont(label, labelFont);
286     labelSize.height = [labelFont ascender] - [labelFont descender];
287     if (labelSize.width > MaxDragLabelWidth){
288         labelSize.width = MaxDragLabelWidth;
289         clipLabelString = YES;
290     }
291
292     NSSize imageSize;
293     imageSize.width = labelSize.width + DragLabelBorderX * 2;
294     imageSize.height = labelSize.height + DragLabelBorderY * 2;
295     if (drawURLString) {
296         NSSize urlStringSize;
297         urlStringSize.width = widthWithFont(urlString, urlFont);
298         urlStringSize.height = [urlFont ascender] - [urlFont descender];
299         imageSize.height += urlStringSize.height;
300         if (urlStringSize.width > MaxDragLabelWidth) {
301             imageSize.width = std::max(MaxDragLabelWidth + DragLabelBorderY * 2, MinDragLabelWidthBeforeClip);
302             clipURLString = YES;
303         } else
304             imageSize.width = std::max(labelSize.width + DragLabelBorderX * 2, urlStringSize.width + DragLabelBorderX * 2);
305     }
306     NSImage *dragImage = [[[NSImage alloc] initWithSize: imageSize] autorelease];
307     [dragImage lockFocus];
308
309     [[NSColor colorWithDeviceRed: 0.7f green: 0.7f blue: 0.7f alpha: 0.8f] set];
310
311     // Drag a rectangle with rounded corners
312     NSBezierPath *path = [NSBezierPath bezierPath];
313     [path appendBezierPathWithOvalInRect: NSMakeRect(0, 0, DragLabelRadius * 2, DragLabelRadius * 2)];
314     [path appendBezierPathWithOvalInRect: NSMakeRect(0, imageSize.height - DragLabelRadius * 2, DragLabelRadius * 2, DragLabelRadius * 2)];
315     [path appendBezierPathWithOvalInRect: NSMakeRect(imageSize.width - DragLabelRadius * 2, imageSize.height - DragLabelRadius * 2, DragLabelRadius * 2, DragLabelRadius * 2)];
316     [path appendBezierPathWithOvalInRect: NSMakeRect(imageSize.width - DragLabelRadius * 2, 0, DragLabelRadius * 2, DragLabelRadius * 2)];
317
318     [path appendBezierPathWithRect: NSMakeRect(DragLabelRadius, 0, imageSize.width - DragLabelRadius * 2, imageSize.height)];
319     [path appendBezierPathWithRect: NSMakeRect(0, DragLabelRadius, DragLabelRadius + 10, imageSize.height - 2 * DragLabelRadius)];
320     [path appendBezierPathWithRect: NSMakeRect(imageSize.width - DragLabelRadius - 20, DragLabelRadius, DragLabelRadius + 20, imageSize.height - 2 * DragLabelRadius)];
321     [path fill];
322
323     NSColor *topColor = [NSColor colorWithDeviceWhite:0.0f alpha:0.75f];
324     NSColor *bottomColor = [NSColor colorWithDeviceWhite:1.0f alpha:0.5f];
325     if (drawURLString) {
326         if (clipURLString)
327             urlString = StringTruncator::centerTruncate(urlString, imageSize.width - (DragLabelBorderX * 2), fontFromNSFont(urlFont));
328
329        drawDoubledAtPoint(urlString, NSMakePoint(DragLabelBorderX, DragLabelBorderY - [urlFont descender]), topColor, bottomColor, urlFont);
330     }
331
332     if (clipLabelString)
333         label = StringTruncator::rightTruncate(label, imageSize.width - (DragLabelBorderX * 2), fontFromNSFont(labelFont));
334     drawDoubledAtPoint(label, NSMakePoint(DragLabelBorderX, imageSize.height - LabelBorderYOffset - [labelFont pointSize]), topColor, bottomColor, labelFont);
335
336     [dragImage unlockFocus];
337
338     return dragImage;
339 }
340    
341 } // namespace WebCore
342
343 #endif // ENABLE(DRAG_SUPPORT)