WebCore:
[WebKit-https.git] / WebKit / mac / WebView / WebDynamicScrollBarsView.m
1 /*
2  * Copyright (C) 2005, 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  *
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 "WebDynamicScrollBarsViewInternal.h"
30
31 #import "WebDocument.h"
32 #import "WebFrameInternal.h"
33 #import "WebFrameView.h"
34 #import "WebHTMLViewInternal.h"
35 #import <WebCore/Frame.h>
36 #import <WebCore/FrameView.h>
37 #import <WebKitSystemInterface.h>
38
39 using namespace WebCore;
40
41 // FIXME: <rdar://problem/5898985> Mail expects a constant of this name to exist.
42 const int WebCoreScrollbarAlwaysOn = ScrollbarAlwaysOn;
43
44 @implementation WebDynamicScrollBarsView
45
46 - (void)setAllowsHorizontalScrolling:(BOOL)flag
47 {
48     if (hScrollModeLocked)
49         return;
50     if (flag && hScroll == ScrollbarAlwaysOff)
51         hScroll = ScrollbarAuto;
52     else if (!flag && hScroll != ScrollbarAlwaysOff)
53         hScroll = ScrollbarAlwaysOff;
54     [self updateScrollers];
55 }
56
57 @end
58
59 @implementation WebDynamicScrollBarsView (WebInternal)
60
61 - (void)setSuppressLayout:(BOOL)flag;
62 {
63     suppressLayout = flag;
64 }
65
66 - (void)setScrollBarsSuppressed:(BOOL)suppressed repaintOnUnsuppress:(BOOL)repaint
67 {
68     suppressScrollers = suppressed;
69
70     // This code was originally changes for a Leopard performance imporvement. We decided to 
71     // ifdef it to fix correctness issues on Tiger documented in <rdar://problem/5441823>.
72 #ifndef BUILDING_ON_TIGER
73     if (suppressed) {
74         [[self verticalScroller] setNeedsDisplay:NO];
75         [[self horizontalScroller] setNeedsDisplay:NO];
76     }
77         
78     if (!suppressed && repaint)
79         [super reflectScrolledClipView:[self contentView]];
80 #else
81     if (suppressed || repaint) { 
82         [[self verticalScroller] setNeedsDisplay: !suppressed]; 
83         [[self horizontalScroller] setNeedsDisplay: !suppressed]; 
84     }
85 #endif
86 }
87
88 - (void)updateScrollers
89 {
90     // We need to do the work below twice in the case where a scroll bar disappears,
91     // making the second layout have a wider width than the first. Doing it more than
92     // twice would indicate some kind of infinite loop, so we do it at most twice.
93     // It's quite efficient to do this work twice in the normal case, so we don't bother
94     // trying to figure out of the second pass is needed or not.
95     if (inUpdateScrollers)
96         return;
97     
98     inUpdateScrollers = true;
99
100     int pass;
101     BOOL hasVerticalScroller = [self hasVerticalScroller];
102     BOOL hasHorizontalScroller = [self hasHorizontalScroller];
103     BOOL oldHasVertical = hasVerticalScroller;
104     BOOL oldHasHorizontal = hasHorizontalScroller;
105
106     for (pass = 0; pass < 2; pass++) {
107         BOOL scrollsVertically;
108         BOOL scrollsHorizontally;
109
110         if (!suppressLayout && !suppressScrollers && (hScroll == ScrollbarAuto || vScroll == ScrollbarAuto)) {
111             // Do a layout if pending, before checking if scrollbars are needed.
112             // This fixes 2969367, although may introduce a slowdown in live resize performance.
113             NSView *documentView = [self documentView];
114             if (!documentView) {
115                 scrollsHorizontally = NO;
116                 scrollsVertically = NO;
117             } else {
118                 if ((hasVerticalScroller != oldHasVertical ||
119                     hasHorizontalScroller != oldHasHorizontal || [documentView inLiveResize]) && [documentView conformsToProtocol:@protocol(WebDocumentView)]) {
120                     [(id <WebDocumentView>)documentView setNeedsLayout: YES];
121                     [(id <WebDocumentView>)documentView layout];
122                 }
123
124                 NSSize documentSize = [documentView frame].size;
125                 if ([documentView isKindOfClass:[WebHTMLView class]]) {
126                     WebHTMLView *htmlView = (WebHTMLView*)documentView;
127                     if (Frame* coreFrame = core([htmlView _frame])) {
128                         if (FrameView* coreView = coreFrame->view())
129                             documentSize = coreView->minimumContentsSize();
130                     }
131                 }
132
133                 NSSize frameSize = [self frame].size;
134
135                 scrollsVertically = (vScroll == ScrollbarAlwaysOn) ||
136                     (vScroll == ScrollbarAuto && documentSize.height > frameSize.height);
137                 if (scrollsVertically)
138                     scrollsHorizontally = (hScroll == ScrollbarAlwaysOn) ||
139                         (hScroll == ScrollbarAuto && documentSize.width + [NSScroller scrollerWidth] > frameSize.width);
140                 else {
141                     scrollsHorizontally = (hScroll == ScrollbarAlwaysOn) ||
142                         (hScroll == ScrollbarAuto && documentSize.width > frameSize.width);
143                     if (scrollsHorizontally)
144                         scrollsVertically = (vScroll == ScrollbarAlwaysOn) ||
145                             (vScroll == ScrollbarAuto && documentSize.height + [NSScroller scrollerWidth] > frameSize.height);
146                 }
147             }
148         } else {
149             scrollsHorizontally = (hScroll == ScrollbarAuto) ? hasHorizontalScroller : (hScroll == ScrollbarAlwaysOn);
150             scrollsVertically = (vScroll == ScrollbarAuto) ? hasVerticalScroller : (vScroll == ScrollbarAlwaysOn);
151         }
152
153         if (hasVerticalScroller != scrollsVertically) {
154             [self setHasVerticalScroller:scrollsVertically];
155             hasVerticalScroller = scrollsVertically;
156         }
157
158         if (hasHorizontalScroller != scrollsHorizontally) {
159             [self setHasHorizontalScroller:scrollsHorizontally];
160             hasHorizontalScroller = scrollsHorizontally;
161         }
162     }
163
164     if (suppressScrollers) {
165         [[self verticalScroller] setNeedsDisplay: NO];
166         [[self horizontalScroller] setNeedsDisplay: NO];
167     }
168
169     inUpdateScrollers = false;
170 }
171
172 // Make the horizontal and vertical scroll bars come and go as needed.
173 - (void)reflectScrolledClipView:(NSClipView *)clipView
174 {
175     if (clipView == [self contentView]) {
176         // FIXME: This hack here prevents infinite recursion that takes place when we
177         // gyrate between having a vertical scroller and not having one. A reproducible
178         // case is clicking on the "the Policy Routing text" link at
179         // http://www.linuxpowered.com/archive/howto/Net-HOWTO-8.html.
180         // The underlying cause is some problem in the NSText machinery, but I was not
181         // able to pin it down.
182         if (!inUpdateScrollers && [[NSGraphicsContext currentContext] isDrawingToScreen])
183             [self updateScrollers];
184     }
185
186     // This code was originally changed for a Leopard performance imporvement. We decided to 
187     // ifdef it to fix correctness issues on Tiger documented in <rdar://problem/5441823>.
188 #ifndef BUILDING_ON_TIGER
189     // Update the scrollers if they're not being suppressed.
190     if (!suppressScrollers)
191         [super reflectScrolledClipView:clipView];
192 #else
193     [super reflectScrolledClipView:clipView]; 
194   
195     // Validate the scrollers if they're being suppressed. 
196     if (suppressScrollers) { 
197         [[self verticalScroller] setNeedsDisplay: NO]; 
198         [[self horizontalScroller] setNeedsDisplay: NO]; 
199     }
200 #endif
201 }
202
203 - (BOOL)allowsHorizontalScrolling
204 {
205     return hScroll != ScrollbarAlwaysOff;
206 }
207
208 - (BOOL)allowsVerticalScrolling
209 {
210     return vScroll != ScrollbarAlwaysOff;
211 }
212
213 - (void)scrollingModes:(WebCore::ScrollbarMode*)hMode vertical:(WebCore::ScrollbarMode*)vMode
214 {
215     *hMode = static_cast<ScrollbarMode>(hScroll);
216     *vMode = static_cast<ScrollbarMode>(vScroll);
217 }
218
219 - (ScrollbarMode)horizontalScrollingMode
220 {
221     return static_cast<ScrollbarMode>(hScroll);
222 }
223
224 - (ScrollbarMode)verticalScrollingMode
225 {
226     return static_cast<ScrollbarMode>(vScroll);
227 }
228
229 - (void)setHorizontalScrollingMode:(ScrollbarMode)horizontalMode andLock:(BOOL)lock
230 {
231     [self setScrollingModes:horizontalMode vertical:[self verticalScrollingMode] andLock:lock];
232 }
233
234 - (void)setVerticalScrollingMode:(ScrollbarMode)verticalMode andLock:(BOOL)lock
235 {
236     [self setScrollingModes:[self horizontalScrollingMode] vertical:verticalMode andLock:lock];
237 }
238
239 // Mail uses this method, so we cannot remove it. 
240 - (void)setVerticalScrollingMode:(ScrollbarMode)verticalMode 
241
242     [self setScrollingModes:[self horizontalScrollingMode] vertical:verticalMode andLock:NO]; 
243
244
245 - (void)setScrollingModes:(ScrollbarMode)horizontalMode vertical:(ScrollbarMode)verticalMode andLock:(BOOL)lock
246 {
247     BOOL update = NO;
248     if (verticalMode != vScroll && !vScrollModeLocked) {
249         vScroll = verticalMode;
250         update = YES;
251     }
252
253     if (horizontalMode != hScroll && !hScrollModeLocked) {
254         hScroll = horizontalMode;
255         update = YES;
256     }
257
258     if (lock)
259         [self setScrollingModesLocked:YES];
260
261     if (update)
262         [self updateScrollers];
263 }
264
265 - (void)setHorizontalScrollingModeLocked:(BOOL)locked
266 {
267     hScrollModeLocked = locked;
268 }
269
270 - (void)setVerticalScrollingModeLocked:(BOOL)locked
271 {
272     vScrollModeLocked = locked;
273 }
274
275 - (void)setScrollingModesLocked:(BOOL)locked
276 {
277     hScrollModeLocked = vScrollModeLocked = locked;
278 }
279
280 - (BOOL)horizontalScrollingModeLocked
281 {
282     return hScrollModeLocked;
283 }
284
285 - (BOOL)verticalScrollingModeLocked
286 {
287     return vScrollModeLocked;
288 }
289
290 - (BOOL)autoforwardsScrollWheelEvents
291 {
292     return YES;
293 }
294
295 - (void)scrollWheel:(NSEvent *)event
296 {
297     float deltaX;
298     float deltaY;
299     BOOL isContinuous;
300     WKGetWheelEventDeltas(event, &deltaX, &deltaY, &isContinuous);
301
302     if (fabsf(deltaY) > fabsf(deltaX)) {
303         if (![self allowsVerticalScrolling]) {
304             [[self nextResponder] scrollWheel:event];
305             return;
306         }
307     } else if (![self allowsHorizontalScrolling]) {
308         [[self nextResponder] scrollWheel:event];
309         return;
310     }
311
312     [super scrollWheel:event];
313 }
314
315 - (BOOL)accessibilityIsIgnored 
316 {
317     id docView = [self documentView];
318     if ([docView isKindOfClass:[WebFrameView class]] && ![(WebFrameView *)docView allowsScrolling])
319         return YES;
320     
321     return [super accessibilityIsIgnored];
322 }
323
324 @end