2007-06-18 Mitz Pettel <mitz@webkit.org>
[WebKit-https.git] / WebKit / WebInspector / WebNodeHighlightView.m
1 /*
2  * Copyright (C) 2006 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 "WebNodeHighlight.h"
30 #import "WebNodeHighlightView.h"
31 #import <WebKitSystemInterface.h>
32
33 @implementation WebNodeHighlightView
34 - (NSBezierPath *)roundedRect:(NSRect)rect withRadius:(float)radius
35 {
36     NSBezierPath *path = [[NSBezierPath alloc] init];
37
38     NSRect irect = NSInsetRect(rect, radius, radius);
39     [path appendBezierPathWithArcWithCenter:NSMakePoint(NSMinX(irect), NSMinY(irect)) radius:radius startAngle:180.0f endAngle:270.0f];
40     [path appendBezierPathWithArcWithCenter:NSMakePoint(NSMaxX(irect), NSMinY(irect)) radius:radius startAngle:270.0f endAngle:360.0f];
41     [path appendBezierPathWithArcWithCenter:NSMakePoint(NSMaxX(irect), NSMaxY(irect)) radius:radius startAngle:0.0f endAngle:90.0f];
42     [path appendBezierPathWithArcWithCenter:NSMakePoint(NSMinX(irect), NSMaxY(irect)) radius:radius startAngle:90.0f endAngle:180.0f];
43     [path closePath];
44
45     return [path autorelease];
46 }
47
48 - (id)initWithHighlight:(WebNodeHighlight *)highlight andRects:(NSArray *)rects forView:(NSView *)view
49 {
50     if (![self init])
51         return nil;
52
53     _highlight = highlight; // don't retain, would cause a circular retain
54
55     NSRect visibleRect = [view visibleRect];
56
57     NSRect rect = NSZeroRect;
58     NSBezierPath *path = nil;
59     NSBezierPath *straightPath = nil;
60
61     if([rects count] == 1) {
62         NSValue *value = (NSValue *)[rects objectAtIndex:0];
63         rect = NSInsetRect([value rectValue], -1.0f, -1.0f);
64         rect = NSIntersectionRect(rect, visibleRect);
65         if (!NSIsEmptyRect(rect))
66             path = [[self roundedRect:rect withRadius:3.0f] retain];
67
68         // shift everything to the corner
69         NSAffineTransform *transform = [[NSAffineTransform alloc] init];
70         [transform translateXBy:(NSMinX(rect) * -1.0f) + 2.5f yBy:(NSMinY(rect) * -1.0f) + 2.5f];
71         [path transformUsingAffineTransform:transform];
72         [straightPath transformUsingAffineTransform:transform];
73         [transform release];
74     } else if ([rects count] > 1) {
75         path = [[NSBezierPath alloc] init];
76         straightPath = [path copy];
77
78         // roundedRect: returns an autoreleased path, so release them soon with a pool
79         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
80
81         NSEnumerator *enumerator = [rects objectEnumerator];
82         NSValue *value = nil;
83         while ((value = [enumerator nextObject])) {
84             rect = NSIntersectionRect([value rectValue], visibleRect);
85             if (!NSIsEmptyRect(rect)) {
86                 [straightPath appendBezierPathWithRect:rect];
87                 [path appendBezierPath:[self roundedRect:rect withRadius:3.0f]];
88             }
89         }
90
91         [pool drain];
92
93         rect = [path bounds];
94
95         [straightPath setWindingRule:NSNonZeroWindingRule];
96         [straightPath setLineJoinStyle:NSRoundLineJoinStyle];
97         [straightPath setLineCapStyle:NSRoundLineCapStyle];
98
99         // multiple rects we get from WebCore need flipped to show up correctly
100         NSAffineTransform *transform = [[NSAffineTransform alloc] init];
101         [transform scaleXBy:1.0f yBy:-1.0f];
102         [path transformUsingAffineTransform:transform];
103         [straightPath transformUsingAffineTransform:transform];
104         [transform release];
105
106         // shift everything to the corner
107         transform = [[NSAffineTransform alloc] init];
108         [transform translateXBy:(NSMinX(rect) * -1.0f) + 2.5f yBy:NSMaxY(rect) + 2.5f];
109         [path transformUsingAffineTransform:transform];
110         [straightPath transformUsingAffineTransform:transform];
111         [transform release];
112     }
113
114     if (!path || [path isEmpty]) {
115         [self release];
116         return nil;
117     }
118
119     [path setWindingRule:NSNonZeroWindingRule];
120     [path setLineJoinStyle:NSRoundLineJoinStyle];
121     [path setLineCapStyle:NSRoundLineCapStyle];
122
123     // make the drawing area larger for the focus ring blur
124     rect = [path bounds];
125     rect.size.width += 5.0f;
126     rect.size.height += 5.0f;
127     [self setFrameSize:rect.size];
128
129     // draw into an image
130     _highlightRingImage = [[NSImage alloc] initWithSize:rect.size];
131     [_highlightRingImage lockFocus];
132     [NSGraphicsContext saveGraphicsState];
133
134     if (straightPath) {
135         [[NSColor redColor] set];
136         [path setLineWidth:4.0f];
137         [path stroke];
138
139         // clear the center to eliminate thick inner strokes for overlapping rects
140         [[NSGraphicsContext currentContext] setCompositingOperation:NSCompositeClear];
141         [path fill];
142
143         // stroke the straight line path with a light color to show any inner rects
144         [[NSGraphicsContext currentContext] setCompositingOperation:NSCompositeDestinationOver];
145         [[[NSColor redColor] colorWithAlphaComponent:0.6f] set];
146         [straightPath setLineWidth:1.0f];
147         [straightPath stroke];
148     } else {
149         [[NSColor redColor] set];
150         [path setLineWidth:2.0f];
151         [path stroke];
152     }
153
154     [NSGraphicsContext restoreGraphicsState];
155     [_highlightRingImage unlockFocus];
156
157     [path release];
158     [straightPath release];
159
160     return self;
161 }
162
163 - (void)dealloc
164 {
165     [_highlightRingImage release];
166     [super dealloc];
167 }
168
169 - (BOOL)isOpaque
170 {
171     return NO;
172 }
173
174 - (void)drawRect:(NSRect)rect
175 {
176     double alpha = 1.0 - [_highlight fractionComplete];
177     if (alpha > 1.0)
178         alpha = 1.0;
179     else if (alpha < 0.0)
180         alpha = 0.0;
181
182     [_highlightRingImage drawInRect:rect fromRect:rect operation:NSCompositeCopy fraction:(float)alpha];
183 }
184 @end