1/* 2 * Copyright (C) 2007, 2009 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 "CachedImage.h" 31#import "Font.h" 32#import "FontDescription.h" 33#import "FontSelector.h" 34#import "GraphicsContext.h" 35#import "Image.h" 36#import "KURL.h" 37#import "ResourceResponse.h" 38#import "Settings.h" 39#import "StringTruncator.h" 40#import "TextRun.h" 41 42namespace WebCore { 43 44IntSize dragImageSize(RetainPtr<NSImage> image) 45{ 46 return (IntSize)[image.get() size]; 47} 48 49void 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 55RetainPtr<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 [image.get() setScalesWhenResized:YES]; 62 [image.get() setSize:newSize]; 63 return image; 64} 65 66RetainPtr<NSImage> dissolveDragImageToFraction(RetainPtr<NSImage> image, float delta) 67{ 68 RetainPtr<NSImage> dissolvedImage(AdoptNS, [[NSImage alloc] initWithSize:[image.get() size]]); 69 70 NSPoint point = [image.get() isFlipped] ? NSMakePoint(0, [image.get() size].height) : NSZeroPoint; 71 72 // In this case the dragging image is always correct. 73 [dissolvedImage.get() setFlipped:[image.get() isFlipped]]; 74 75 [dissolvedImage.get() lockFocus]; 76 [image.get() dissolveToPoint:point fraction: delta]; 77 [dissolvedImage.get() unlockFocus]; 78 79 [image.get() lockFocus]; 80 [dissolvedImage.get() compositeToPoint:point operation:NSCompositeCopy]; 81 [image.get() unlockFocus]; 82 83 return image; 84} 85 86RetainPtr<NSImage> createDragImageFromImage(Image* image) 87{ 88 RetainPtr<NSImage> dragImage(AdoptNS, [image->getNSImage() copy]); 89 [dragImage.get() setSize:(NSSize)(image->size())]; 90 return dragImage; 91} 92 93RetainPtr<NSImage> createDragImageIconForCachedImage(CachedImage* image) 94{ 95 const String& filename = image->response().suggestedFilename(); 96 NSString *extension = nil; 97 size_t dotIndex = filename.reverseFind('.'); 98 99 if (dotIndex != notFound && dotIndex < (filename.length() - 1)) // require that a . exists after the first character and before the last 100 extension = filename.substring(dotIndex + 1); 101 else { 102 // It might be worth doing a further lookup to pull the extension from the MIME type. 103 extension = @""; 104 } 105 106 return [[NSWorkspace sharedWorkspace] iconForFileType:extension]; 107} 108 109 110const float DragLabelBorderX = 4; 111//Keep border_y in synch with DragController::LinkDragBorderInset 112const float DragLabelBorderY = 2; 113const float DragLabelRadius = 5; 114const float LabelBorderYOffset = 2; 115 116const float MinDragLabelWidthBeforeClip = 120; 117const float MaxDragLabelWidth = 320; 118 119const float DragLinkLabelFontsize = 11; 120const float DragLinkUrlFontSize = 10; 121 122// FIXME - we should move all the functionality of NSString extras to WebCore 123 124static Font& fontFromNSFont(NSFont *font) 125{ 126 static NSFont *currentFont; 127 DEFINE_STATIC_LOCAL(Font, currentRenderer, ()); 128 129 if ([font isEqual:currentFont]) 130 return currentRenderer; 131 if (currentFont) 132 CFRelease(currentFont); 133 currentFont = font; 134 CFRetain(currentFont); 135 FontPlatformData f(font, [font pointSize]); 136 currentRenderer = Font(f, ![[NSGraphicsContext currentContext] isDrawingToScreen]); 137 return currentRenderer; 138} 139 140static bool canUseFastRenderer(const UniChar* buffer, unsigned length) 141{ 142 unsigned i; 143 for (i = 0; i < length; i++) { 144 UCharDirection direction = u_charDirection(buffer[i]); 145 if (direction == U_RIGHT_TO_LEFT || direction > U_OTHER_NEUTRAL) 146 return false; 147 } 148 return true; 149} 150 151static float widthWithFont(NSString *string, NSFont *font) 152{ 153 unsigned length = [string length]; 154 Vector<UniChar, 2048> buffer(length); 155 156 [string getCharacters:buffer.data()]; 157 158 if (canUseFastRenderer(buffer.data(), length)) { 159 Font webCoreFont(FontPlatformData(font, [font pointSize]), ![[NSGraphicsContext currentContext] isDrawingToScreen]); 160 TextRun run(buffer.data(), length); 161 return webCoreFont.width(run); 162 } 163 164 return [string sizeWithAttributes:[NSDictionary dictionaryWithObjectsAndKeys:font, NSFontAttributeName, nil]].width; 165} 166 167static inline CGFloat webkit_CGCeiling(CGFloat value) 168{ 169 if (sizeof(value) == sizeof(float)) 170 return ceilf(value); 171 return static_cast<CGFloat>(ceil(value)); 172} 173 174static void drawAtPoint(NSString *string, NSPoint point, NSFont *font, NSColor *textColor) 175{ 176 unsigned length = [string length]; 177 Vector<UniChar, 2048> buffer(length); 178 179 [string getCharacters:buffer.data()]; 180 181 if (canUseFastRenderer(buffer.data(), length)) { 182 // The following is a half-assed attempt to match AppKit's rounding rules for drawAtPoint. 183 // It's probably incorrect for high DPI. 184 // If you change this, be sure to test all the text drawn this way in Safari, including 185 // the status bar, bookmarks bar, tab bar, and activity window. 186 point.y = webkit_CGCeiling(point.y); 187 188 NSGraphicsContext *nsContext = [NSGraphicsContext currentContext]; 189 CGContextRef cgContext = static_cast<CGContextRef>([nsContext graphicsPort]); 190 GraphicsContext graphicsContext(cgContext); 191 192 // Safari doesn't flip the NSGraphicsContext before calling WebKit, yet WebCore requires a flipped graphics context. 193 BOOL flipped = [nsContext isFlipped]; 194 if (!flipped) 195 CGContextScaleCTM(cgContext, 1, -1); 196 197 Font webCoreFont(FontPlatformData(font, [font pointSize]), ![nsContext isDrawingToScreen], Antialiased); 198 TextRun run(buffer.data(), length); 199 200 CGFloat red; 201 CGFloat green; 202 CGFloat blue; 203 CGFloat alpha; 204 [[textColor colorUsingColorSpaceName:NSDeviceRGBColorSpace] getRed:&red green:&green blue:&blue alpha:&alpha]; 205 graphicsContext.setFillColor(makeRGBA(red * 255, green * 255, blue * 255, alpha * 255), ColorSpaceDeviceRGB); 206 207 webCoreFont.drawText(&graphicsContext, run, FloatPoint(point.x, (flipped ? point.y : (-1 * point.y)))); 208 209 if (!flipped) 210 CGContextScaleCTM(cgContext, 1, -1); 211 } else { 212 // The given point is on the baseline. 213 if ([[NSView focusView] isFlipped]) 214 point.y -= [font ascender]; 215 else 216 point.y += [font descender]; 217 218 [string drawAtPoint:point withAttributes:[NSDictionary dictionaryWithObjectsAndKeys:font, NSFontAttributeName, textColor, NSForegroundColorAttributeName, nil]]; 219 } 220} 221 222static void drawDoubledAtPoint(NSString *string, NSPoint textPoint, NSColor *topColor, NSColor *bottomColor, NSFont *font) 223{ 224 // turn off font smoothing so translucent text draws correctly (Radar 3118455) 225 drawAtPoint(string, textPoint, font, bottomColor); 226 227 textPoint.y += 1; 228 drawAtPoint(string, textPoint, font, topColor); 229} 230 231DragImageRef createDragImageForLink(KURL& url, const String& title, Frame* frame) 232{ 233 if (!frame) 234 return nil; 235 NSString *label = 0; 236 if (!title.isEmpty()) 237 label = title; 238 NSURL *cocoaURL = url; 239 NSString *urlString = [cocoaURL absoluteString]; 240 241 BOOL drawURLString = YES; 242 BOOL clipURLString = NO; 243 BOOL clipLabelString = NO; 244 245 if (!label) { 246 drawURLString = NO; 247 label = urlString; 248 } 249 250 NSFont *labelFont = [[NSFontManager sharedFontManager] convertFont:[NSFont systemFontOfSize:DragLinkLabelFontsize] 251 toHaveTrait:NSBoldFontMask]; 252 NSFont *urlFont = [NSFont systemFontOfSize:DragLinkUrlFontSize]; 253 NSSize labelSize; 254 labelSize.width = widthWithFont(label, labelFont); 255 labelSize.height = [labelFont ascender] - [labelFont descender]; 256 if (labelSize.width > MaxDragLabelWidth){ 257 labelSize.width = MaxDragLabelWidth; 258 clipLabelString = YES; 259 } 260 261 NSSize imageSize; 262 imageSize.width = labelSize.width + DragLabelBorderX * 2; 263 imageSize.height = labelSize.height + DragLabelBorderY * 2; 264 if (drawURLString) { 265 NSSize urlStringSize; 266 urlStringSize.width = widthWithFont(urlString, urlFont); 267 urlStringSize.height = [urlFont ascender] - [urlFont descender]; 268 imageSize.height += urlStringSize.height; 269 if (urlStringSize.width > MaxDragLabelWidth) { 270 imageSize.width = std::max(MaxDragLabelWidth + DragLabelBorderY * 2, MinDragLabelWidthBeforeClip); 271 clipURLString = YES; 272 } else 273 imageSize.width = std::max(labelSize.width + DragLabelBorderX * 2, urlStringSize.width + DragLabelBorderX * 2); 274 } 275 NSImage *dragImage = [[[NSImage alloc] initWithSize: imageSize] autorelease]; 276 [dragImage lockFocus]; 277 278 [[NSColor colorWithDeviceRed: 0.7f green: 0.7f blue: 0.7f alpha: 0.8f] set]; 279 280 // Drag a rectangle with rounded corners 281 NSBezierPath *path = [NSBezierPath bezierPath]; 282 [path appendBezierPathWithOvalInRect: NSMakeRect(0, 0, DragLabelRadius * 2, DragLabelRadius * 2)]; 283 [path appendBezierPathWithOvalInRect: NSMakeRect(0, imageSize.height - DragLabelRadius * 2, DragLabelRadius * 2, DragLabelRadius * 2)]; 284 [path appendBezierPathWithOvalInRect: NSMakeRect(imageSize.width - DragLabelRadius * 2, imageSize.height - DragLabelRadius * 2, DragLabelRadius * 2, DragLabelRadius * 2)]; 285 [path appendBezierPathWithOvalInRect: NSMakeRect(imageSize.width - DragLabelRadius * 2, 0, DragLabelRadius * 2, DragLabelRadius * 2)]; 286 287 [path appendBezierPathWithRect: NSMakeRect(DragLabelRadius, 0, imageSize.width - DragLabelRadius * 2, imageSize.height)]; 288 [path appendBezierPathWithRect: NSMakeRect(0, DragLabelRadius, DragLabelRadius + 10, imageSize.height - 2 * DragLabelRadius)]; 289 [path appendBezierPathWithRect: NSMakeRect(imageSize.width - DragLabelRadius - 20, DragLabelRadius, DragLabelRadius + 20, imageSize.height - 2 * DragLabelRadius)]; 290 [path fill]; 291 292 NSColor *topColor = [NSColor colorWithDeviceWhite:0.0f alpha:0.75f]; 293 NSColor *bottomColor = [NSColor colorWithDeviceWhite:1.0f alpha:0.5f]; 294 if (drawURLString) { 295 if (clipURLString) 296 urlString = StringTruncator::centerTruncate(urlString, imageSize.width - (DragLabelBorderX * 2), fontFromNSFont(urlFont)); 297 298 drawDoubledAtPoint(urlString, NSMakePoint(DragLabelBorderX, DragLabelBorderY - [urlFont descender]), topColor, bottomColor, urlFont); 299 } 300 301 if (clipLabelString) 302 label = StringTruncator::rightTruncate(label, imageSize.width - (DragLabelBorderX * 2), fontFromNSFont(labelFont)); 303 drawDoubledAtPoint(label, NSMakePoint(DragLabelBorderX, imageSize.height - LabelBorderYOffset - [labelFont pointSize]), topColor, bottomColor, labelFont); 304 305 [dragImage unlockFocus]; 306 307 return dragImage; 308} 309 310} // namespace WebCore 311 312#endif // ENABLE(DRAG_SUPPORT) 313