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