Rename DEFINE_STATIC_LOCAL to DEPRECATED_DEFINE_STATIC_LOCAL
[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 COMPUTER, 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 COMPUTER, 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 "Font.h"
32 #import "FontCache.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
42 namespace WebCore {
43
44 IntSize dragImageSize(RetainPtr<NSImage> image)
45 {
46     return (IntSize)[image.get() size];
47 }
48
49 void deleteDragImage(RetainPtr<NSImage>)
50 {
51     // Since this is a RetainPtr, there's nothing additional we need to do to
52     // delete it. It will be released when it falls out of scope.
53 }
54
55 RetainPtr<NSImage> scaleDragImage(RetainPtr<NSImage> image, FloatSize scale)
56 {
57     NSSize originalSize = [image.get() size];
58     NSSize newSize = NSMakeSize((originalSize.width * scale.width()), (originalSize.height * scale.height()));
59     newSize.width = roundf(newSize.width);
60     newSize.height = roundf(newSize.height);
61 #pragma clang diagnostic push
62 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
63     [image.get() setScalesWhenResized:YES];
64 #pragma clang diagnostic pop
65     [image.get() setSize:newSize];
66     return image;
67 }
68     
69 RetainPtr<NSImage> dissolveDragImageToFraction(RetainPtr<NSImage> image, float delta)
70 {
71     if (!image)
72         return nil;
73
74     RetainPtr<NSImage> dissolvedImage = adoptNS([[NSImage alloc] initWithSize:[image.get() size]]);
75     
76     [dissolvedImage.get() lockFocus];
77     [image.get() drawAtPoint:NSZeroPoint fromRect:NSMakeRect(0, 0, [image size].width, [image size].height) operation:NSCompositeCopy fraction:delta];
78     [dissolvedImage.get() unlockFocus];
79
80     return dissolvedImage;
81 }
82         
83 RetainPtr<NSImage> createDragImageFromImage(Image* image, ImageOrientationDescription description)
84 {
85     IntSize size = image->size();
86
87     if (image->isBitmapImage()) {
88         ImageOrientation orientation;
89         BitmapImage* bitmapImage = toBitmapImage(image);
90         IntSize sizeRespectingOrientation = bitmapImage->sizeRespectingOrientation(description);
91
92         if (description.respectImageOrientation() == RespectImageOrientation)
93             orientation = bitmapImage->orientationForCurrentFrame();
94
95         if (orientation != DefaultImageOrientation) {
96             // Construct a correctly-rotated copy of the image to use as the drag image.
97             FloatRect destRect(FloatPoint(), sizeRespectingOrientation);
98
99             RetainPtr<NSImage> rotatedDragImage = adoptNS([[NSImage alloc] initWithSize:(NSSize)(sizeRespectingOrientation)]);
100             [rotatedDragImage.get() lockFocus];
101
102             // ImageOrientation uses top-left coordinates, need to flip to bottom-left, apply...
103             CGAffineTransform transform = CGAffineTransformMakeTranslation(0, destRect.height());
104             transform = CGAffineTransformScale(transform, 1, -1);
105             transform = CGAffineTransformConcat(orientation.transformFromDefault(sizeRespectingOrientation), transform);
106
107             if (orientation.usesWidthAsHeight())
108                 destRect = FloatRect(destRect.x(), destRect.y(), destRect.height(), destRect.width());
109
110             // ...and flip back.
111             transform = CGAffineTransformTranslate(transform, 0, destRect.height());
112             transform = CGAffineTransformScale(transform, 1, -1);
113
114             RetainPtr<NSAffineTransform> cocoaTransform = adoptNS([[NSAffineTransform alloc] init]);
115             [cocoaTransform.get() setTransformStruct:*(NSAffineTransformStruct*)&transform];
116             [cocoaTransform.get() concat];
117
118             [image->getNSImage() drawInRect:destRect fromRect:NSMakeRect(0, 0, size.width(), size.height()) operation:NSCompositeSourceOver fraction:1.0];
119             [rotatedDragImage.get() unlockFocus];
120
121             return rotatedDragImage;
122         }
123     }
124
125     RetainPtr<NSImage> dragImage = adoptNS([image->getNSImage() copy]);
126     [dragImage.get() setSize:(NSSize)size];
127     return dragImage;
128 }
129     
130 RetainPtr<NSImage> createDragImageIconForCachedImageFilename(const String& filename)
131 {
132     NSString *extension = nil;
133     size_t dotIndex = filename.reverseFind('.');
134     
135     if (dotIndex != notFound && dotIndex < (filename.length() - 1)) // require that a . exists after the first character and before the last
136         extension = filename.substring(dotIndex + 1);
137     else {
138         // It might be worth doing a further lookup to pull the extension from the MIME type.
139         extension = @"";
140     }
141     
142     return [[NSWorkspace sharedWorkspace] iconForFileType:extension];
143 }
144
145
146 const float DragLabelBorderX = 4;
147 //Keep border_y in synch with DragController::LinkDragBorderInset
148 const float DragLabelBorderY = 2;
149 const float DragLabelRadius = 5;
150 const float LabelBorderYOffset = 2;
151
152 const float MinDragLabelWidthBeforeClip = 120;
153 const float MaxDragLabelWidth = 320;
154
155 const float DragLinkLabelFontsize = 11;
156 const float DragLinkUrlFontSize = 10;
157
158 // FIXME - we should move all the functionality of NSString extras to WebCore
159     
160 static Font& fontFromNSFont(NSFont *font)
161 {
162     static NSFont *currentFont;
163     DEPRECATED_DEFINE_STATIC_LOCAL(Font, currentRenderer, ());
164     
165     if ([font isEqual:currentFont])
166         return currentRenderer;
167     if (currentFont)
168         CFRelease(currentFont);
169     currentFont = font;
170     CFRetain(currentFont);
171     FontPlatformData f(font, [font pointSize]);
172     currentRenderer = Font(f, ![[NSGraphicsContext currentContext] isDrawingToScreen]);
173     return currentRenderer;
174 }
175
176 static bool canUseFastRenderer(const UniChar* buffer, unsigned length)
177 {
178     unsigned i;
179     for (i = 0; i < length; i++) {
180         UCharDirection direction = u_charDirection(buffer[i]);
181         if (direction == U_RIGHT_TO_LEFT || direction > U_OTHER_NEUTRAL)
182             return false;
183     }
184     return true;
185 }
186     
187 static float widthWithFont(NSString *string, NSFont *font)
188 {
189     unsigned length = [string length];
190     Vector<UniChar, 2048> buffer(length);
191     
192     [string getCharacters:buffer.data()];
193     
194     if (canUseFastRenderer(buffer.data(), length)) {
195         FontCachePurgePreventer fontCachePurgePreventer;
196
197         Font webCoreFont(FontPlatformData(font, [font pointSize]), ![[NSGraphicsContext currentContext] isDrawingToScreen]);
198         TextRun run(buffer.data(), length);
199         run.disableRoundingHacks();
200         return webCoreFont.width(run);
201     }
202     
203     return [string sizeWithAttributes:[NSDictionary dictionaryWithObjectsAndKeys:font, NSFontAttributeName, nil]].width;
204 }
205
206 static inline CGFloat webkit_CGCeiling(CGFloat value)
207 {
208     if (sizeof(value) == sizeof(float))
209         return ceilf(value);
210     return static_cast<CGFloat>(ceil(value));
211 }
212     
213 static void drawAtPoint(NSString *string, NSPoint point, NSFont *font, NSColor *textColor)
214 {
215     unsigned length = [string length];
216     Vector<UniChar, 2048> buffer(length);
217     
218     [string getCharacters:buffer.data()];
219     
220     if (canUseFastRenderer(buffer.data(), length)) {
221         FontCachePurgePreventer fontCachePurgePreventer;
222
223         // The following is a half-assed attempt to match AppKit's rounding rules for drawAtPoint.
224         // It's probably incorrect for high DPI.
225         // If you change this, be sure to test all the text drawn this way in Safari, including
226         // the status bar, bookmarks bar, tab bar, and activity window.
227         point.y = webkit_CGCeiling(point.y);
228         
229         NSGraphicsContext *nsContext = [NSGraphicsContext currentContext];
230         CGContextRef cgContext = static_cast<CGContextRef>([nsContext graphicsPort]);
231         GraphicsContext graphicsContext(cgContext);    
232         
233         // Safari doesn't flip the NSGraphicsContext before calling WebKit, yet WebCore requires a flipped graphics context.
234         BOOL flipped = [nsContext isFlipped];
235         if (!flipped)
236             CGContextScaleCTM(cgContext, 1, -1);
237             
238         Font webCoreFont(FontPlatformData(font, [font pointSize]), ![nsContext isDrawingToScreen], Antialiased);
239         TextRun run(buffer.data(), length);
240         run.disableRoundingHacks();
241
242         CGFloat red;
243         CGFloat green;
244         CGFloat blue;
245         CGFloat alpha;
246         [[textColor colorUsingColorSpaceName:NSDeviceRGBColorSpace] getRed:&red green:&green blue:&blue alpha:&alpha];
247         graphicsContext.setFillColor(makeRGBA(red * 255, green * 255, blue * 255, alpha * 255), ColorSpaceDeviceRGB);
248         
249         webCoreFont.drawText(&graphicsContext, run, FloatPoint(point.x, (flipped ? point.y : (-1 * point.y))));
250         
251         if (!flipped)
252             CGContextScaleCTM(cgContext, 1, -1);
253     } else {
254         // The given point is on the baseline.
255         if ([[NSView focusView] isFlipped])
256             point.y -= [font ascender];
257         else
258             point.y += [font descender];
259                 
260         [string drawAtPoint:point withAttributes:[NSDictionary dictionaryWithObjectsAndKeys:font, NSFontAttributeName, textColor, NSForegroundColorAttributeName, nil]];
261     }
262 }
263     
264 static void drawDoubledAtPoint(NSString *string, NSPoint textPoint, NSColor *topColor, NSColor *bottomColor, NSFont *font)
265 {
266         // turn off font smoothing so translucent text draws correctly (Radar 3118455)
267         drawAtPoint(string, textPoint, font, bottomColor);
268         
269         textPoint.y += 1;
270         drawAtPoint(string, textPoint, font, topColor);
271 }
272
273 DragImageRef createDragImageForLink(URL& url, const String& title, FontRenderingMode)
274 {
275     NSString *label = nsStringNilIfEmpty(title);
276     NSURL *cocoaURL = url;
277     NSString *urlString = [cocoaURL absoluteString];
278
279     BOOL drawURLString = YES;
280     BOOL clipURLString = NO;
281     BOOL clipLabelString = NO;
282
283     if (!label) {
284         drawURLString = NO;
285         label = urlString;
286     }
287
288     NSFont *labelFont = [[NSFontManager sharedFontManager] convertFont:[NSFont systemFontOfSize:DragLinkLabelFontsize]
289                                                            toHaveTrait:NSBoldFontMask];
290     NSFont *urlFont = [NSFont systemFontOfSize:DragLinkUrlFontSize];
291     NSSize labelSize;
292     labelSize.width = widthWithFont(label, labelFont);
293     labelSize.height = [labelFont ascender] - [labelFont descender];
294     if (labelSize.width > MaxDragLabelWidth){
295         labelSize.width = MaxDragLabelWidth;
296         clipLabelString = YES;
297     }
298
299     NSSize imageSize;
300     imageSize.width = labelSize.width + DragLabelBorderX * 2;
301     imageSize.height = labelSize.height + DragLabelBorderY * 2;
302     if (drawURLString) {
303         NSSize urlStringSize;
304         urlStringSize.width = widthWithFont(urlString, urlFont);
305         urlStringSize.height = [urlFont ascender] - [urlFont descender];
306         imageSize.height += urlStringSize.height;
307         if (urlStringSize.width > MaxDragLabelWidth) {
308             imageSize.width = std::max(MaxDragLabelWidth + DragLabelBorderY * 2, MinDragLabelWidthBeforeClip);
309             clipURLString = YES;
310         } else
311             imageSize.width = std::max(labelSize.width + DragLabelBorderX * 2, urlStringSize.width + DragLabelBorderX * 2);
312     }
313     NSImage *dragImage = [[[NSImage alloc] initWithSize: imageSize] autorelease];
314     [dragImage lockFocus];
315
316     [[NSColor colorWithDeviceRed: 0.7f green: 0.7f blue: 0.7f alpha: 0.8f] set];
317
318     // Drag a rectangle with rounded corners
319     NSBezierPath *path = [NSBezierPath bezierPath];
320     [path appendBezierPathWithOvalInRect: NSMakeRect(0, 0, DragLabelRadius * 2, DragLabelRadius * 2)];
321     [path appendBezierPathWithOvalInRect: NSMakeRect(0, imageSize.height - DragLabelRadius * 2, DragLabelRadius * 2, DragLabelRadius * 2)];
322     [path appendBezierPathWithOvalInRect: NSMakeRect(imageSize.width - DragLabelRadius * 2, imageSize.height - DragLabelRadius * 2, DragLabelRadius * 2, DragLabelRadius * 2)];
323     [path appendBezierPathWithOvalInRect: NSMakeRect(imageSize.width - DragLabelRadius * 2, 0, DragLabelRadius * 2, DragLabelRadius * 2)];
324
325     [path appendBezierPathWithRect: NSMakeRect(DragLabelRadius, 0, imageSize.width - DragLabelRadius * 2, imageSize.height)];
326     [path appendBezierPathWithRect: NSMakeRect(0, DragLabelRadius, DragLabelRadius + 10, imageSize.height - 2 * DragLabelRadius)];
327     [path appendBezierPathWithRect: NSMakeRect(imageSize.width - DragLabelRadius - 20, DragLabelRadius, DragLabelRadius + 20, imageSize.height - 2 * DragLabelRadius)];
328     [path fill];
329
330     NSColor *topColor = [NSColor colorWithDeviceWhite:0.0f alpha:0.75f];
331     NSColor *bottomColor = [NSColor colorWithDeviceWhite:1.0f alpha:0.5f];
332     if (drawURLString) {
333         if (clipURLString)
334             urlString = StringTruncator::centerTruncate(urlString, imageSize.width - (DragLabelBorderX * 2), fontFromNSFont(urlFont));
335
336        drawDoubledAtPoint(urlString, NSMakePoint(DragLabelBorderX, DragLabelBorderY - [urlFont descender]), topColor, bottomColor, urlFont);
337     }
338
339     if (clipLabelString)
340         label = StringTruncator::rightTruncate(label, imageSize.width - (DragLabelBorderX * 2), fontFromNSFont(labelFont));
341     drawDoubledAtPoint(label, NSMakePoint(DragLabelBorderX, imageSize.height - LabelBorderYOffset - [labelFont pointSize]), topColor, bottomColor, labelFont);
342
343     [dragImage unlockFocus];
344
345     return dragImage;
346 }
347    
348 } // namespace WebCore
349
350 #endif // ENABLE(DRAG_SUPPORT)