LayoutTests:
[WebKit-https.git] / WebCore / platform / mac / SliderMac.mm
1 /*
2  * Copyright (C) 2004 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  * 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 "Slider.h"
28
29 #import "BlockExceptions.h"
30 #import "TextField.h"
31 #import "FrameMac.h"
32 #import "KeyboardEvent.h"
33 #import "WebCoreFrameBridge.h"
34 #import "WebCoreWidgetHolder.h"
35 #import "WidgetClient.h"
36
37 using namespace std;
38 using namespace WebCore;
39
40 @interface WebCoreSlider : NSSlider <WebCoreWidgetHolder>
41 {
42     Slider* slider;
43     BOOL inNextValidKeyView;
44 }
45
46 - (id)initWithSlider:(Slider*)s;
47 - (void)detachSlider;
48
49 @end
50
51 @implementation WebCoreSlider
52
53 - (id)initWithSlider:(Slider*)s
54 {
55     self = [self init];
56
57     slider = s;
58
59     [self setTarget:self];
60     [self setAction:@selector(slide:)];
61     [self setContinuous:YES]; // Our sliders are always continuous by default.
62     [self setMinValue:0.0];
63     [self setMaxValue:100.0];
64     [self setDoubleValue:50.0];
65
66     return self;
67 }
68
69 - (void)detachSlider
70 {
71     [self setTarget:nil];
72     slider = 0;
73 }
74
75 - (void)mouseDown:(NSEvent *)event
76 {
77     Widget::beforeMouseDown(self);
78     [super mouseDown:event];
79     Widget::afterMouseDown(self);
80     if (slider)
81         slider->sendConsumedMouseUp();
82     if (slider && slider->client())
83         slider->client()->clicked(slider);
84 }
85
86 - (IBAction)slide:(NSSlider*)sender
87 {
88     if (slider)
89         slider->sliderValueChanged();
90 }
91
92 - (Widget *)widget
93 {
94     return slider;
95 }
96
97 // FIXME: All the firstResponder and keyView code here is replicated in KWQButton and
98 // other KWQ classes. We should find a way to share this code.
99 - (BOOL)becomeFirstResponder
100 {
101     BOOL become = [super becomeFirstResponder];
102     if (become && slider && slider->client()) {
103         if (!FrameMac::currentEventIsMouseDownInWidget(slider))
104             slider->client()->scrollToVisible(slider);
105         slider->client()->focusIn(slider);
106         [FrameMac::bridgeForWidget(slider) formControlIsBecomingFirstResponder:self];
107     }
108     return become;
109 }
110
111 - (BOOL)resignFirstResponder
112 {
113     BOOL resign = [super resignFirstResponder];
114     if (resign && slider && slider->client()) {
115         slider->client()->focusOut(slider);
116         [FrameMac::bridgeForWidget(slider) formControlIsResigningFirstResponder:self];
117     }
118     return resign;
119 }
120
121 -(NSView *)nextKeyView
122 {
123     NSView *view = nil;
124     if (slider && inNextValidKeyView) {
125         // resign so we send a blur before setting focus on
126         // the next widget, otherwise the blur for this
127         // widget will remove focus from the widget after
128         // we tab to it
129         [self resignFirstResponder];
130         if (slider) {
131             view = FrameMac::nextKeyViewForWidget(slider, SelectingNext);
132         } else {
133             view = [super nextKeyView];
134         }
135     } else { 
136         view = [super nextKeyView];
137     }
138     return view;
139 }
140
141 -(NSView *)previousKeyView
142 {
143     NSView *view = nil;
144     if (slider && inNextValidKeyView) {
145         // resign so we send a blur before setting focus on
146         // the next widget, otherwise the blur for this
147         // widget will remove focus from the widget after
148         // we tab to it
149         [self resignFirstResponder];
150         if (slider) {
151             view = FrameMac::nextKeyViewForWidget(slider, SelectingPrevious);
152         } else {
153             view = [super previousKeyView];
154         }
155     } else { 
156         view = [super previousKeyView];
157     }
158     return view;
159 }
160
161 - (BOOL)canBecomeKeyView
162 {
163     // Simplified method from NSView; overridden to replace NSView's way of checking
164     // for full keyboard access with ours.
165     if (slider) {
166         FrameMac* frame = Mac(Frame::frameForWidget(slider));
167         if (!frame->tabsToAllControls(frame->currentKeyboardEvent().get()))
168             return NO;
169     }
170     return [self window] != nil && ![self isHiddenOrHasHiddenAncestor] && [self acceptsFirstResponder];
171 }
172
173 -(NSView *)nextValidKeyView
174 {
175     inNextValidKeyView = YES;
176     NSView *view = [super nextValidKeyView];
177     inNextValidKeyView = NO;
178     return view;
179 }
180
181 -(NSView *)previousValidKeyView
182 {
183     inNextValidKeyView = YES;
184     NSView *view = [super previousValidKeyView];
185     inNextValidKeyView = NO;
186     return view;
187 }
188
189 @end
190
191 enum {
192     dimWidth,
193     dimHeight
194 };
195
196 Slider::Slider()
197     : m_minVal(0.0), m_maxVal(100.0), m_val(50.0)
198 {
199     BEGIN_BLOCK_OBJC_EXCEPTIONS;
200     WebCoreSlider* slider = [[WebCoreSlider alloc] initWithSlider:this];
201     [[slider cell] setControlSize:NSSmallControlSize];
202     setView(slider);
203     [slider release];
204     END_BLOCK_OBJC_EXCEPTIONS;
205 }
206
207 Slider::~Slider()
208 {
209     WebCoreSlider* slider = (WebCoreSlider*)getView();
210     [slider detachSlider];
211 }
212
213 void Slider::setFont(const Font& f)
214 {
215     BEGIN_BLOCK_OBJC_EXCEPTIONS;
216     
217     Widget::setFont(f);
218     
219     const NSControlSize size = ControlSizeForFont(f);    
220     NSControl * const slider = static_cast<NSControl *>(getView());
221     [[slider cell] setControlSize:size];
222     [slider setFont:[NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:size]]];
223     
224     END_BLOCK_OBJC_EXCEPTIONS;
225 }
226
227 Widget::FocusPolicy Slider::focusPolicy() const
228 {
229     BEGIN_BLOCK_OBJC_EXCEPTIONS;
230     
231     FrameMac* frame = Mac(Frame::frameForWidget(this));
232     if (!frame || !frame->tabsToAllControls(frame->currentKeyboardEvent().get()))
233         return NoFocus;
234     
235     END_BLOCK_OBJC_EXCEPTIONS;
236     
237     return Widget::focusPolicy();
238 }
239
240 IntSize Slider::sizeHint() const 
241 {
242     return IntSize(dimensions()[dimWidth], dimensions()[dimHeight]);
243 }
244
245 void Slider::setValue(double v)
246 {
247     double val = max(m_minVal, min(v, m_maxVal));
248     
249     WebCoreSlider* slider = (WebCoreSlider*)getView();
250     [slider setDoubleValue: val];
251     m_val = val;
252 }
253
254 void Slider::setMinValue(double v)
255 {
256     if (v == m_minVal) return;
257
258     WebCoreSlider* slider = (WebCoreSlider*)getView();
259     [slider setMinValue: v];
260     m_minVal = v;
261 }
262
263 void Slider::setMaxValue(double v)
264 {
265     if (v == m_maxVal) return;
266
267     WebCoreSlider* slider = (WebCoreSlider*)getView();
268     [slider setMaxValue: v];
269     m_maxVal = v;
270 }
271
272 double Slider::value() const
273 {
274     return m_val;
275 }
276
277 double Slider::minValue() const
278 {
279     return m_minVal;
280 }
281
282 double Slider::maxValue() const
283 {
284     return m_maxVal;
285 }
286
287 void Slider::sliderValueChanged()
288 {
289     WebCoreSlider* slider = (WebCoreSlider*)getView();
290     double v = [slider doubleValue];
291     if (m_val != v) {
292         m_val = v;
293         if (client())
294             client()->valueChanged(this);
295     }
296 }
297
298 const int* Slider::dimensions() const
299 {
300     // We empirically determined these dimensions.
301     // It would be better to get this info from AppKit somehow.
302     static const int w[3][2] = {
303         { 129, 21 },
304         { 129, 15 },
305         { 129, 12 },
306     };
307     NSControl * const slider = static_cast<NSControl *>(getView());
308     
309     BEGIN_BLOCK_OBJC_EXCEPTIONS;
310     return w[[[slider cell] controlSize]];
311     END_BLOCK_OBJC_EXCEPTIONS;
312     
313     return w[NSSmallControlSize];
314 }