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