1/*
2 * Copyright (C) 2003, 2004, 2005, 2006, 2010 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 "GraphicsContext.h"
28
29#import "GraphicsContextPlatformPrivateCG.h"
30#import <AppKit/AppKit.h>
31#import <wtf/StdLibExtras.h>
32
33#import "LocalCurrentGraphicsContext.h"
34#import "WebCoreSystemInterface.h"
35
36@class NSColor;
37
38// FIXME: More of this should use CoreGraphics instead of AppKit.
39// FIXME: More of this should move into GraphicsContextCG.cpp.
40
41namespace WebCore {
42
43// NSColor, NSBezierPath, and NSGraphicsContext
44// calls in this file are all exception-safe, so we don't block
45// exceptions for those.
46
47static void drawFocusRingToContext(CGContextRef context, CGPathRef focusRingPath, CGColorRef color, int radius)
48{
49#ifdef BUILDING_ON_TIGER
50    CGContextBeginTransparencyLayer(context, 0);
51#endif
52    CGContextBeginPath(context);
53    CGContextAddPath(context, focusRingPath);
54    wkDrawFocusRing(context, color, radius);
55#ifdef BUILDING_ON_TIGER
56    CGContextEndTransparencyLayer(context);
57#endif
58}
59
60void GraphicsContext::drawFocusRing(const Path& path, int width, int /*offset*/, const Color& color)
61{
62    // FIXME: Use 'offset' for something? http://webkit.org/b/49909
63
64    if (paintingDisabled())
65        return;
66
67    int radius = (width - 1) / 2;
68    CGColorRef colorRef = color.isValid() ? cachedCGColor(color, ColorSpaceDeviceRGB) : 0;
69
70    drawFocusRingToContext(platformContext(), path.platformPath(), colorRef, radius);
71}
72
73void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int offset, const Color& color)
74{
75    if (paintingDisabled())
76        return;
77
78    int radius = (width - 1) / 2;
79    offset += radius;
80    CGColorRef colorRef = color.isValid() ? cachedCGColor(color, ColorSpaceDeviceRGB) : 0;
81
82    RetainPtr<CGMutablePathRef> focusRingPath(AdoptCF, CGPathCreateMutable());
83    unsigned rectCount = rects.size();
84    for (unsigned i = 0; i < rectCount; i++)
85        CGPathAddRect(focusRingPath.get(), 0, CGRectInset(rects[i], -offset, -offset));
86
87    drawFocusRingToContext(platformContext(), focusRingPath.get(), colorRef, radius);
88}
89
90#ifdef BUILDING_ON_TIGER // Post-Tiger's setPlatformCompositeOperation() is defined in GraphicsContextCG.cpp.
91void GraphicsContext::setPlatformCompositeOperation(CompositeOperator op)
92{
93    if (paintingDisabled())
94        return;
95    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
96    [[NSGraphicsContext graphicsContextWithGraphicsPort:platformContext() flipped:YES]
97        setCompositingOperation:(NSCompositingOperation)op];
98    [pool drain];
99}
100#endif
101
102static NSColor* createPatternColor(NSString* name, NSColor* defaultColor, bool& usingDot)
103{
104    NSImage *image = [NSImage imageNamed:name];
105    ASSERT(image); // if image is not available, we want to know
106    NSColor *color = (image ? [NSColor colorWithPatternImage:image] : nil);
107    if (color)
108        usingDot = true;
109    else
110        color = defaultColor;
111    return color;
112}
113
114// WebKit on Mac is a standard platform component, so it must use the standard platform artwork for underline.
115void GraphicsContext::drawLineForTextChecking(const FloatPoint& point, float width, TextCheckingLineStyle style)
116{
117    if (paintingDisabled())
118        return;
119
120    // These are the same for misspelling or bad grammar.
121    int patternHeight = cMisspellingLineThickness;
122    float patternWidth = cMisspellingLinePatternWidth;
123
124    bool usingDot;
125    NSColor *patternColor;
126    switch (style) {
127        case TextCheckingSpellingLineStyle:
128        {
129            // Constants for spelling pattern color.
130            static bool usingDotForSpelling = false;
131            DEFINE_STATIC_LOCAL(RetainPtr<NSColor>, spellingPatternColor, (createPatternColor(@"SpellingDot", [NSColor redColor], usingDotForSpelling)));
132            usingDot = usingDotForSpelling;
133            patternColor = spellingPatternColor.get();
134            break;
135        }
136        case TextCheckingGrammarLineStyle:
137        {
138            // Constants for grammar pattern color.
139            static bool usingDotForGrammar = false;
140            DEFINE_STATIC_LOCAL(RetainPtr<NSColor>, grammarPatternColor, (createPatternColor(@"GrammarDot", [NSColor greenColor], usingDotForGrammar)));
141            usingDot = usingDotForGrammar;
142            patternColor = grammarPatternColor.get();
143            break;
144        }
145#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD)
146        // To support correction panel.
147        case TextCheckingReplacementLineStyle:
148        {
149            // Constants for spelling pattern color.
150            static bool usingDotForSpelling = false;
151            DEFINE_STATIC_LOCAL(RetainPtr<NSColor>, spellingPatternColor, (createPatternColor(@"CorrectionDot", [NSColor blueColor], usingDotForSpelling)));
152            usingDot = usingDotForSpelling;
153            patternColor = spellingPatternColor.get();
154            break;
155        }
156#endif
157        default:
158            return;
159    }
160
161    // Make sure to draw only complete dots.
162    // NOTE: Code here used to shift the underline to the left and increase the width
163    // to make sure everything gets underlined, but that results in drawing out of
164    // bounds (e.g. when at the edge of a view) and could make it appear that the
165    // space between adjacent misspelled words was underlined.
166    if (usingDot) {
167        // allow slightly more considering that the pattern ends with a transparent pixel
168        float widthMod = fmodf(width, patternWidth);
169        if (patternWidth - widthMod > cMisspellingLinePatternGapWidth)
170            width -= widthMod;
171    }
172
173    // FIXME: This code should not use NSGraphicsContext currentContext
174    // In order to remove this requirement we will need to use CGPattern instead of NSColor
175    // FIXME: This code should not be using wkSetPatternPhaseInUserSpace, as this approach is wrong
176    // for transforms.
177
178    // Draw underline.
179    LocalCurrentGraphicsContext localContext(this);
180    NSGraphicsContext *currentContext = [NSGraphicsContext currentContext];
181    CGContextRef context = (CGContextRef)[currentContext graphicsPort];
182    CGContextSaveGState(context);
183
184    [patternColor set];
185
186    wkSetPatternPhaseInUserSpace(context, point);
187
188    NSRectFillUsingOperation(NSMakeRect(point.x(), point.y(), width, patternHeight), NSCompositeSourceOver);
189
190    CGContextRestoreGState(context);
191}
192
193}
194