|
1 /* |
|
2 * Copyright (C) 2004, 2005, 2006, 2008 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 "Widget.h" |
|
28 |
|
29 #ifdef BUILDING_ON_TIGER |
|
30 #import "AutodrainedPool.h" |
|
31 #endif |
|
32 |
|
33 #import "BlockExceptions.h" |
|
34 #import "Chrome.h" |
|
35 #import "Cursor.h" |
|
36 #import "Document.h" |
|
37 #import "Font.h" |
|
38 #import "FoundationExtras.h" |
|
39 #import "Frame.h" |
|
40 #import "GraphicsContext.h" |
|
41 #import "NotImplemented.h" |
|
42 #import "Page.h" |
|
43 #import "PlatformMouseEvent.h" |
|
44 #import "ScrollView.h" |
|
45 #import "WebCoreFrameView.h" |
|
46 #import "WebCoreView.h" |
|
47 #import <wtf/RetainPtr.h> |
|
48 |
|
49 @interface NSWindow (WebWindowDetails) |
|
50 - (BOOL)_needsToResetDragMargins; |
|
51 - (void)_setNeedsToResetDragMargins:(BOOL)needs; |
|
52 @end |
|
53 |
|
54 @interface NSView (WebSetSelectedMethods) |
|
55 - (void)setIsSelected:(BOOL)isSelected; |
|
56 - (void)webPlugInSetIsSelected:(BOOL)isSelected; |
|
57 @end |
|
58 |
|
59 @interface NSView (Widget) |
|
60 - (void)visibleRectDidChange; |
|
61 @end |
|
62 |
|
63 namespace WebCore { |
|
64 |
|
65 class WidgetPrivate { |
|
66 public: |
|
67 WidgetPrivate() |
|
68 : previousVisibleRect(NSZeroRect) |
|
69 { |
|
70 } |
|
71 |
|
72 bool mustStayInWindow; |
|
73 bool removeFromSuperviewSoon; |
|
74 NSRect previousVisibleRect; |
|
75 }; |
|
76 |
|
77 static void safeRemoveFromSuperview(NSView *view) |
|
78 { |
|
79 // If the the view is the first responder, then set the window's first responder to nil so |
|
80 // we don't leave the window pointing to a view that's no longer in it. |
|
81 NSWindow *window = [view window]; |
|
82 NSResponder *firstResponder = [window firstResponder]; |
|
83 if ([firstResponder isKindOfClass:[NSView class]] && [(NSView *)firstResponder isDescendantOf:view]) |
|
84 [window makeFirstResponder:nil]; |
|
85 |
|
86 // Suppress the resetting of drag margins since we know we can't affect them. |
|
87 BOOL resetDragMargins = [window _needsToResetDragMargins]; |
|
88 [window _setNeedsToResetDragMargins:NO]; |
|
89 [view removeFromSuperview]; |
|
90 [window _setNeedsToResetDragMargins:resetDragMargins]; |
|
91 } |
|
92 |
|
93 Widget::Widget(NSView *view) |
|
94 : m_data(new WidgetPrivate) |
|
95 { |
|
96 init(view); |
|
97 m_data->mustStayInWindow = false; |
|
98 m_data->removeFromSuperviewSoon = false; |
|
99 } |
|
100 |
|
101 Widget::~Widget() |
|
102 { |
|
103 releasePlatformWidget(); |
|
104 delete m_data; |
|
105 } |
|
106 |
|
107 // FIXME: Should move this to Chrome; bad layering that this knows about Frame. |
|
108 void Widget::setFocus(bool focused) |
|
109 { |
|
110 if (!focused) |
|
111 return; |
|
112 |
|
113 Frame* frame = Frame::frameForWidget(this); |
|
114 if (!frame) |
|
115 return; |
|
116 |
|
117 BEGIN_BLOCK_OBJC_EXCEPTIONS; |
|
118 |
|
119 NSView *view = [platformWidget() _webcore_effectiveFirstResponder]; |
|
120 if (Page* page = frame->page()) |
|
121 page->chrome()->focusNSView(view); |
|
122 |
|
123 END_BLOCK_OBJC_EXCEPTIONS; |
|
124 } |
|
125 |
|
126 void Widget::setCursor(const Cursor& cursor) |
|
127 { |
|
128 ScrollView* view = root(); |
|
129 if (!view) |
|
130 return; |
|
131 view->hostWindow()->setCursor(cursor); |
|
132 } |
|
133 |
|
134 void Widget::show() |
|
135 { |
|
136 if (isSelfVisible()) |
|
137 return; |
|
138 |
|
139 setSelfVisible(true); |
|
140 |
|
141 BEGIN_BLOCK_OBJC_EXCEPTIONS; |
|
142 [getOuterView() setHidden:NO]; |
|
143 END_BLOCK_OBJC_EXCEPTIONS; |
|
144 } |
|
145 |
|
146 void Widget::hide() |
|
147 { |
|
148 if (!isSelfVisible()) |
|
149 return; |
|
150 |
|
151 setSelfVisible(false); |
|
152 |
|
153 BEGIN_BLOCK_OBJC_EXCEPTIONS; |
|
154 [getOuterView() setHidden:YES]; |
|
155 END_BLOCK_OBJC_EXCEPTIONS; |
|
156 } |
|
157 |
|
158 IntRect Widget::frameRect() const |
|
159 { |
|
160 if (!platformWidget()) |
|
161 return m_frame; |
|
162 |
|
163 BEGIN_BLOCK_OBJC_EXCEPTIONS; |
|
164 return enclosingIntRect([getOuterView() frame]); |
|
165 END_BLOCK_OBJC_EXCEPTIONS; |
|
166 |
|
167 return m_frame; |
|
168 } |
|
169 |
|
170 void Widget::setFrameRect(const IntRect& rect) |
|
171 { |
|
172 m_frame = rect; |
|
173 |
|
174 BEGIN_BLOCK_OBJC_EXCEPTIONS; |
|
175 NSView *v = getOuterView(); |
|
176 if (!v) |
|
177 return; |
|
178 |
|
179 NSRect visibleRect = [v visibleRect]; |
|
180 NSRect f = rect; |
|
181 if (!NSEqualRects(f, [v frame])) { |
|
182 [v setFrame:f]; |
|
183 [v setNeedsDisplay:NO]; |
|
184 } else if (!NSEqualRects(visibleRect, m_data->previousVisibleRect) && [v respondsToSelector:@selector(visibleRectDidChange)]) |
|
185 [v visibleRectDidChange]; |
|
186 |
|
187 m_data->previousVisibleRect = visibleRect; |
|
188 END_BLOCK_OBJC_EXCEPTIONS; |
|
189 } |
|
190 |
|
191 NSView* Widget::getOuterView() const |
|
192 { |
|
193 NSView* view = platformWidget(); |
|
194 |
|
195 // If this widget's view is a WebCoreFrameScrollView then we |
|
196 // resize its containing view, a WebFrameView. |
|
197 if ([view conformsToProtocol:@protocol(WebCoreFrameScrollView)]) { |
|
198 view = [view superview]; |
|
199 ASSERT(view); |
|
200 } |
|
201 |
|
202 return view; |
|
203 } |
|
204 |
|
205 void Widget::paint(GraphicsContext* p, const IntRect& r) |
|
206 { |
|
207 if (p->paintingDisabled()) |
|
208 return; |
|
209 NSView *view = getOuterView(); |
|
210 NSGraphicsContext *currentContext = [NSGraphicsContext currentContext]; |
|
211 if (currentContext == [[view window] graphicsContext] || ![currentContext isDrawingToScreen]) { |
|
212 // This is the common case of drawing into a window or printing. |
|
213 BEGIN_BLOCK_OBJC_EXCEPTIONS; |
|
214 [view displayRectIgnoringOpacity:[view convertRect:r fromView:[view superview]]]; |
|
215 END_BLOCK_OBJC_EXCEPTIONS; |
|
216 } else { |
|
217 // This is the case of drawing into a bitmap context other than a window backing store. It gets hit beneath |
|
218 // -cacheDisplayInRect:toBitmapImageRep:, and when painting into compositing layers. |
|
219 |
|
220 // Transparent subframes are in fact implemented with scroll views that return YES from -drawsBackground (whenever the WebView |
|
221 // itself is in drawsBackground mode). In the normal drawing code path, the scroll views are never asked to draw the background, |
|
222 // so this is not an issue, but in this code path they are, so the following code temporarily turns background drwaing off. |
|
223 NSView *innerView = platformWidget(); |
|
224 NSScrollView *scrollView = 0; |
|
225 if ([innerView conformsToProtocol:@protocol(WebCoreFrameScrollView)]) { |
|
226 ASSERT([innerView isKindOfClass:[NSScrollView class]]); |
|
227 NSScrollView *scrollView = static_cast<NSScrollView *>(innerView); |
|
228 // -copiesOnScroll will return NO whenever the content view is not fully opaque. |
|
229 if ([scrollView drawsBackground] && ![[scrollView contentView] copiesOnScroll]) |
|
230 [scrollView setDrawsBackground:NO]; |
|
231 else |
|
232 scrollView = 0; |
|
233 } |
|
234 |
|
235 CGContextRef cgContext = p->platformContext(); |
|
236 ASSERT(cgContext == [currentContext graphicsPort]); |
|
237 CGContextSaveGState(cgContext); |
|
238 |
|
239 NSRect viewFrame = [view frame]; |
|
240 NSRect viewBounds = [view bounds]; |
|
241 // Set up the translation and (flipped) orientation of the graphics context. In normal drawing, AppKit does it as it descends down |
|
242 // the view hierarchy. |
|
243 CGContextTranslateCTM(cgContext, viewFrame.origin.x - viewBounds.origin.x, viewFrame.origin.y + viewFrame.size.height + viewBounds.origin.y); |
|
244 CGContextScaleCTM(cgContext, 1, -1); |
|
245 |
|
246 BEGIN_BLOCK_OBJC_EXCEPTIONS; |
|
247 { |
|
248 #ifdef BUILDING_ON_TIGER |
|
249 AutodrainedPool pool; |
|
250 #endif |
|
251 NSGraphicsContext *nsContext = [NSGraphicsContext graphicsContextWithGraphicsPort:cgContext flipped:YES]; |
|
252 [view displayRectIgnoringOpacity:[view convertRect:r fromView:[view superview]] inContext:nsContext]; |
|
253 } |
|
254 END_BLOCK_OBJC_EXCEPTIONS; |
|
255 |
|
256 CGContextRestoreGState(cgContext); |
|
257 |
|
258 if (scrollView) |
|
259 [scrollView setDrawsBackground:YES]; |
|
260 } |
|
261 } |
|
262 |
|
263 void Widget::setIsSelected(bool isSelected) |
|
264 { |
|
265 NSView *view = platformWidget(); |
|
266 BEGIN_BLOCK_OBJC_EXCEPTIONS; |
|
267 if ([view respondsToSelector:@selector(webPlugInSetIsSelected:)]) |
|
268 [view webPlugInSetIsSelected:isSelected]; |
|
269 else if ([view respondsToSelector:@selector(setIsSelected:)]) |
|
270 [view setIsSelected:isSelected]; |
|
271 END_BLOCK_OBJC_EXCEPTIONS; |
|
272 } |
|
273 |
|
274 void Widget::removeFromSuperview() |
|
275 { |
|
276 if (m_data->mustStayInWindow) |
|
277 m_data->removeFromSuperviewSoon = true; |
|
278 else { |
|
279 m_data->removeFromSuperviewSoon = false; |
|
280 BEGIN_BLOCK_OBJC_EXCEPTIONS; |
|
281 safeRemoveFromSuperview(getOuterView()); |
|
282 END_BLOCK_OBJC_EXCEPTIONS; |
|
283 } |
|
284 } |
|
285 |
|
286 void Widget::beforeMouseDown(NSView *unusedView, Widget* widget) |
|
287 { |
|
288 if (widget) { |
|
289 ASSERT_UNUSED(unusedView, unusedView == widget->getOuterView()); |
|
290 ASSERT(!widget->m_data->mustStayInWindow); |
|
291 widget->m_data->mustStayInWindow = true; |
|
292 } |
|
293 } |
|
294 |
|
295 void Widget::afterMouseDown(NSView *view, Widget* widget) |
|
296 { |
|
297 if (!widget) { |
|
298 BEGIN_BLOCK_OBJC_EXCEPTIONS; |
|
299 safeRemoveFromSuperview(view); |
|
300 END_BLOCK_OBJC_EXCEPTIONS; |
|
301 } else { |
|
302 ASSERT(widget->m_data->mustStayInWindow); |
|
303 widget->m_data->mustStayInWindow = false; |
|
304 if (widget->m_data->removeFromSuperviewSoon) |
|
305 widget->removeFromSuperview(); |
|
306 } |
|
307 } |
|
308 |
|
309 // These are here to deal with flipped coords on Mac. |
|
310 IntRect Widget::convertFromRootToContainingWindow(const Widget* rootWidget, const IntRect& rect) |
|
311 { |
|
312 if (!rootWidget->platformWidget()) |
|
313 return rect; |
|
314 |
|
315 BEGIN_BLOCK_OBJC_EXCEPTIONS; |
|
316 return enclosingIntRect([rootWidget->platformWidget() convertRect:rect toView:nil]); |
|
317 END_BLOCK_OBJC_EXCEPTIONS; |
|
318 |
|
319 return rect; |
|
320 } |
|
321 |
|
322 IntRect Widget::convertFromContainingWindowToRoot(const Widget* rootWidget, const IntRect& rect) |
|
323 { |
|
324 if (!rootWidget->platformWidget()) |
|
325 return rect; |
|
326 |
|
327 BEGIN_BLOCK_OBJC_EXCEPTIONS; |
|
328 return enclosingIntRect([rootWidget->platformWidget() convertRect:rect fromView:nil]); |
|
329 END_BLOCK_OBJC_EXCEPTIONS; |
|
330 |
|
331 return rect; |
|
332 } |
|
333 |
|
334 IntPoint Widget::convertFromRootToContainingWindow(const Widget* rootWidget, const IntPoint& point) |
|
335 { |
|
336 if (!rootWidget->platformWidget()) |
|
337 return point; |
|
338 |
|
339 BEGIN_BLOCK_OBJC_EXCEPTIONS; |
|
340 return IntPoint([rootWidget->platformWidget() convertPoint:point toView:nil]); |
|
341 END_BLOCK_OBJC_EXCEPTIONS; |
|
342 return point; |
|
343 } |
|
344 |
|
345 IntPoint Widget::convertFromContainingWindowToRoot(const Widget* rootWidget, const IntPoint& point) |
|
346 { |
|
347 if (!rootWidget->platformWidget()) |
|
348 return point; |
|
349 |
|
350 BEGIN_BLOCK_OBJC_EXCEPTIONS; |
|
351 return IntPoint([rootWidget->platformWidget() convertPoint:point fromView:nil]); |
|
352 END_BLOCK_OBJC_EXCEPTIONS; |
|
353 |
|
354 return point; |
|
355 } |
|
356 |
|
357 void Widget::releasePlatformWidget() |
|
358 { |
|
359 HardRelease(m_widget); |
|
360 m_data->previousVisibleRect = NSZeroRect; |
|
361 } |
|
362 |
|
363 void Widget::retainPlatformWidget() |
|
364 { |
|
365 HardRetain(m_widget); |
|
366 } |
|
367 |
|
368 } // namespace WebCore |
|
369 |