1/*
2 * Copyright (C) 2004, 2006, 2007, 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 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 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 "ScrollView.h"
28
29#import "BlockExceptions.h"
30#import "FloatRect.h"
31#import "FloatSize.h"
32#import "IntRect.h"
33#import "Logging.h"
34#import "NotImplemented.h"
35#import "WebCoreFrameView.h"
36
37@interface NSScrollView (Details)
38- (NSEdgeInsets)contentInsets;
39@property BOOL automaticallyAdjustsContentInsets;
40@end
41
42@interface NSWindow (WebWindowDetails)
43- (BOOL)_needsToResetDragMargins;
44- (void)_setNeedsToResetDragMargins:(BOOL)needs;
45@end
46
47namespace WebCore {
48
49inline NSScrollView<WebCoreFrameScrollView> *ScrollView::scrollView() const
50{
51    ASSERT(!platformWidget() || [platformWidget() isKindOfClass:[NSScrollView class]]);
52    ASSERT(!platformWidget() || [platformWidget() conformsToProtocol:@protocol(WebCoreFrameScrollView)]);
53    return static_cast<NSScrollView<WebCoreFrameScrollView> *>(platformWidget());
54}
55
56NSView *ScrollView::documentView() const
57{
58    BEGIN_BLOCK_OBJC_EXCEPTIONS;
59    return [scrollView() documentView];
60    END_BLOCK_OBJC_EXCEPTIONS;
61    return nil;
62}
63
64void ScrollView::platformAddChild(Widget* child)
65{
66    BEGIN_BLOCK_OBJC_EXCEPTIONS;
67    NSView *parentView = documentView();
68    NSView *childView = child->getOuterView();
69    ASSERT(![parentView isDescendantOf:childView]);
70
71    // Suppress the resetting of drag margins since we know we can't affect them.
72    NSWindow *window = [parentView window];
73    BOOL resetDragMargins = [window _needsToResetDragMargins];
74    [window _setNeedsToResetDragMargins:NO];
75    if ([childView superview] != parentView)
76        [parentView addSubview:childView];
77    [window _setNeedsToResetDragMargins:resetDragMargins];
78    END_BLOCK_OBJC_EXCEPTIONS;
79}
80
81void ScrollView::platformRemoveChild(Widget* child)
82{
83    child->removeFromSuperview();
84}
85
86void ScrollView::platformSetScrollbarModes()
87{
88    BEGIN_BLOCK_OBJC_EXCEPTIONS;
89    [scrollView() setScrollingModes:m_horizontalScrollbarMode vertical:m_verticalScrollbarMode andLock:NO];
90    END_BLOCK_OBJC_EXCEPTIONS;
91}
92
93void ScrollView::platformScrollbarModes(ScrollbarMode& horizontal, ScrollbarMode& vertical) const
94{
95    BEGIN_BLOCK_OBJC_EXCEPTIONS;
96    [scrollView() scrollingModes:&horizontal vertical:&vertical];
97    END_BLOCK_OBJC_EXCEPTIONS;
98}
99
100void ScrollView::platformSetCanBlitOnScroll(bool canBlitOnScroll)
101{
102    BEGIN_BLOCK_OBJC_EXCEPTIONS;
103    [[scrollView() contentView] setCopiesOnScroll:canBlitOnScroll];
104    END_BLOCK_OBJC_EXCEPTIONS;
105}
106
107bool ScrollView::platformCanBlitOnScroll() const
108{
109    return [[scrollView() contentView] copiesOnScroll];
110}
111
112float ScrollView::platformTopContentInset() const
113{
114    BEGIN_BLOCK_OBJC_EXCEPTIONS;
115#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
116    return scrollView().contentInsets.top;
117#endif
118    END_BLOCK_OBJC_EXCEPTIONS;
119
120    return 0;
121}
122
123void ScrollView::platformSetTopContentInset(float topContentInset)
124{
125    BEGIN_BLOCK_OBJC_EXCEPTIONS;
126#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
127    if (topContentInset)
128        scrollView().automaticallyAdjustsContentInsets = NO;
129    else
130        scrollView().automaticallyAdjustsContentInsets = YES;
131
132    NSEdgeInsets contentInsets = scrollView().contentInsets;
133    contentInsets.top = topContentInset;
134    scrollView().contentInsets = contentInsets;
135#else
136    UNUSED_PARAM(topContentInset);
137#endif
138    END_BLOCK_OBJC_EXCEPTIONS;
139}
140
141IntRect ScrollView::platformVisibleContentRect(bool includeScrollbars) const
142{
143    BEGIN_BLOCK_OBJC_EXCEPTIONS;
144    IntRect visibleContentRect = platformVisibleContentRectIncludingObscuredArea(includeScrollbars);
145
146#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 10100
147    visibleContentRect.move(scrollView().contentInsets.left, scrollView().contentInsets.top);
148    visibleContentRect.contract(scrollView().contentInsets.left + scrollView().contentInsets.right, scrollView().contentInsets.top + scrollView().contentInsets.bottom);
149#endif
150
151    return visibleContentRect;
152    END_BLOCK_OBJC_EXCEPTIONS;
153
154    return IntRect();
155}
156
157IntSize ScrollView::platformVisibleContentSize(bool includeScrollbars) const
158{
159    return platformVisibleContentRect(includeScrollbars).size();
160}
161
162IntRect ScrollView::platformVisibleContentRectIncludingObscuredArea(bool includeScrollbars) const
163{
164    BEGIN_BLOCK_OBJC_EXCEPTIONS;
165    IntRect visibleContentRectIncludingObscuredArea = enclosingIntRect([scrollView() documentVisibleRect]);
166
167    if (includeScrollbars) {
168        IntSize frameSize = IntSize([scrollView() frame].size);
169        visibleContentRectIncludingObscuredArea.setSize(frameSize);
170    }
171
172    return visibleContentRectIncludingObscuredArea;
173    END_BLOCK_OBJC_EXCEPTIONS;
174
175    return IntRect();
176}
177
178IntSize ScrollView::platformVisibleContentSizeIncludingObscuredArea(bool includeScrollbars) const
179{
180    return platformVisibleContentRectIncludingObscuredArea(includeScrollbars).size();
181}
182
183void ScrollView::platformSetContentsSize()
184{
185    BEGIN_BLOCK_OBJC_EXCEPTIONS;
186    int w = m_contentsSize.width();
187    int h = m_contentsSize.height();
188    LOG(Frames, "%p %@ at w %d h %d\n", documentView(), [(id)[documentView() class] className], w, h);
189    [documentView() setFrameSize:NSMakeSize(std::max(0, w), std::max(0, h))];
190    END_BLOCK_OBJC_EXCEPTIONS;
191}
192
193void ScrollView::platformSetScrollbarsSuppressed(bool repaintOnUnsuppress)
194{
195    BEGIN_BLOCK_OBJC_EXCEPTIONS;
196    [scrollView() setScrollBarsSuppressed:m_scrollbarsSuppressed
197                      repaintOnUnsuppress:repaintOnUnsuppress];
198    END_BLOCK_OBJC_EXCEPTIONS;
199}
200
201void ScrollView::platformSetScrollPosition(const IntPoint& scrollPoint)
202{
203    BEGIN_BLOCK_OBJC_EXCEPTIONS;
204    NSPoint floatPoint = scrollPoint;
205    NSPoint tempPoint = { std::max(-[scrollView() scrollOrigin].x, floatPoint.x), std::max(-[scrollView() scrollOrigin].y, floatPoint.y) };  // Don't use NSMakePoint to work around 4213314.
206
207#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
208    // AppKit has the inset factored into all of its scroll positions. In WebCore, we use positions that ignore
209    // the insets so that they are equivalent whether or not there is an inset.
210    tempPoint.x = tempPoint.x - scrollView().contentInsets.left;
211    tempPoint.y = tempPoint.y - scrollView().contentInsets.top;
212#endif
213
214    [documentView() scrollPoint:tempPoint];
215    END_BLOCK_OBJC_EXCEPTIONS;
216}
217
218bool ScrollView::platformScroll(ScrollDirection, ScrollGranularity)
219{
220    // FIXME: It would be nice to implement this so that all of the code in WebFrameView could go away.
221    notImplemented();
222    return false;
223}
224
225void ScrollView::platformRepaintContentRectangle(const IntRect& rect)
226{
227    BEGIN_BLOCK_OBJC_EXCEPTIONS;
228    NSView *view = documentView();
229    [view setNeedsDisplayInRect:rect];
230
231    END_BLOCK_OBJC_EXCEPTIONS;
232}
233
234// "Containing Window" means the NSWindow's coord system, which is origin lower left
235
236IntRect ScrollView::platformContentsToScreen(const IntRect& rect) const
237{
238    BEGIN_BLOCK_OBJC_EXCEPTIONS;
239    if (NSView* documentView = this->documentView()) {
240        NSRect tempRect = rect;
241        tempRect = [documentView convertRect:tempRect toView:nil];
242#pragma clang diagnostic push
243#pragma clang diagnostic ignored "-Wdeprecated-declarations"
244        tempRect.origin = [[documentView window] convertBaseToScreen:tempRect.origin];
245#pragma clang diagnostic pop
246        return enclosingIntRect(tempRect);
247    }
248    END_BLOCK_OBJC_EXCEPTIONS;
249    return IntRect();
250}
251
252IntPoint ScrollView::platformScreenToContents(const IntPoint& point) const
253{
254    BEGIN_BLOCK_OBJC_EXCEPTIONS;
255    if (NSView* documentView = this->documentView()) {
256#pragma clang diagnostic push
257#pragma clang diagnostic ignored "-Wdeprecated-declarations"
258        NSPoint windowCoord = [[documentView window] convertScreenToBase: point];
259#pragma clang diagnostic pop
260        return IntPoint([documentView convertPoint:windowCoord fromView:nil]);
261    }
262    END_BLOCK_OBJC_EXCEPTIONS;
263    return IntPoint();
264}
265
266bool ScrollView::platformIsOffscreen() const
267{
268    return ![platformWidget() window] || ![[platformWidget() window] isVisible];
269}
270
271static inline NSScrollerKnobStyle toNSScrollerKnobStyle(ScrollbarOverlayStyle style)
272{
273    switch (style) {
274    case ScrollbarOverlayStyleDark:
275        return NSScrollerKnobStyleDark;
276    case ScrollbarOverlayStyleLight:
277        return NSScrollerKnobStyleLight;
278    default:
279        return NSScrollerKnobStyleDefault;
280    }
281}
282
283void ScrollView::platformSetScrollbarOverlayStyle(ScrollbarOverlayStyle overlayStyle)
284{
285    [scrollView() setScrollerKnobStyle:toNSScrollerKnobStyle(overlayStyle)];
286}
287
288void ScrollView::platformSetScrollOrigin(const IntPoint& origin, bool updatePositionAtAll, bool updatePositionSynchronously)
289{
290    BEGIN_BLOCK_OBJC_EXCEPTIONS;
291    [scrollView() setScrollOrigin:origin updatePositionAtAll:updatePositionAtAll immediately:updatePositionSynchronously];
292    END_BLOCK_OBJC_EXCEPTIONS;
293}
294
295} // namespace WebCore
296