2 * Copyright (C) 2003 Apple Computer, Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
26 #import "KWQComboBox.h"
29 #import "KWQExceptions.h"
30 #import "KWQKHTMLPart.h"
32 #import "KWQKHTMLPart.h"
33 #import "KWQNSViewExtras.h"
34 #import "WebCoreBridge.h"
36 #import "render_replaced.h"
38 using khtml::RenderWidget;
40 @interface NSCell (KWQComboBoxKnowsAppKitSecrets)
41 - (NSMutableDictionary *)_textAttributes;
50 widthNotIncludingText,
54 @interface KWQComboBoxAdapter : NSObject
58 - initWithQComboBox:(QComboBox *)b;
59 - (void)action:(id)sender;
62 @interface KWQPopUpButtonCell : NSPopUpButtonCell <KWQWidgetHolder>
65 NSWritingDirection baseWritingDirection;
68 - (id)initWithWidget:(QWidget *)widget;
69 - (void)setBaseWritingDirection:(NSWritingDirection)direction;
70 - (NSWritingDirection)baseWritingDirection;
74 @interface KWQPopUpButton : NSPopUpButton <KWQWidgetHolder>
76 BOOL inNextValidKeyView;
80 QComboBox::QComboBox()
83 , _activated(this, SIGNAL(activated(int)))
87 _adapter = [[KWQComboBoxAdapter alloc] initWithQComboBox:this];
88 KWQPopUpButton *button = [[KWQPopUpButton alloc] init];
92 KWQPopUpButtonCell *cell = [[KWQPopUpButtonCell alloc] initWithWidget:this];
93 [button setCell:cell];
96 [button setTarget:_adapter];
97 [button setAction:@selector(action:)];
99 [[button cell] setControlSize:NSSmallControlSize];
100 [button setFont:[NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]]];
104 KWQ_UNBLOCK_EXCEPTIONS;
107 QComboBox::~QComboBox()
109 KWQ_BLOCK_EXCEPTIONS;
111 KWQPopUpButton *button = (KWQPopUpButton *)getView();
112 [button setTarget:nil];
115 KWQ_UNBLOCK_EXCEPTIONS;
118 void QComboBox::insertItem(const QString &text, int i)
120 KWQ_BLOCK_EXCEPTIONS;
124 KWQPopUpButton *button = (KWQPopUpButton *)getView();
125 int numItems = [button numberOfItems];
129 while (index >= numItems) {
130 [button addItemWithTitle:@""];
133 // It's convenient that we added the item with an empty title,
134 // because addItemWithTitle will not allow multiple items with the
135 // same title. But this way, we can have such duplicate items.
136 [[button itemAtIndex:index] setTitle:text.getNSString()];
141 KWQ_UNBLOCK_EXCEPTIONS;
144 QSize QComboBox::sizeHint() const
148 KWQ_BLOCK_EXCEPTIONS;
150 KWQPopUpButton *button = (KWQPopUpButton *)getView();
157 NSDictionary *attributes = [NSDictionary dictionaryWithObject:[button font] forKey:NSFontAttributeName];
158 NSEnumerator *e = [[button itemTitles] objectEnumerator];
160 while ((text = [e nextObject])) {
161 NSSize size = [text sizeWithAttributes:attributes];
162 width = MAX(width, size.width);
164 _width = ceil(width);
165 if (_width < dimensions()[minimumTextWidth]) {
166 _width = dimensions()[minimumTextWidth];
171 size = [[button cell] cellSize];
173 KWQ_UNBLOCK_EXCEPTIONS;
175 return QSize((int)_width + dimensions()[widthNotIncludingText],
176 (int)size.height - (dimensions()[topMargin] + dimensions()[bottomMargin]));
179 QRect QComboBox::frameGeometry() const
181 QRect r = QWidget::frameGeometry();
182 return QRect(r.x() + dimensions()[leftMargin], r.y() + dimensions()[topMargin],
183 r.width() - (dimensions()[leftMargin] + dimensions()[rightMargin]),
184 r.height() - (dimensions()[topMargin] + dimensions()[bottomMargin]));
187 void QComboBox::setFrameGeometry(const QRect &r)
189 QWidget::setFrameGeometry(QRect(-dimensions()[leftMargin] + r.x(), -dimensions()[topMargin] + r.y(),
190 dimensions()[leftMargin] + r.width() + dimensions()[rightMargin],
191 dimensions()[topMargin] + r.height() + dimensions()[bottomMargin]));
194 int QComboBox::baselinePosition(int height) const
196 // Menu text is at the top.
197 KWQPopUpButton *button = (KWQPopUpButton *)getView();
198 return (int)ceil(-dimensions()[topMargin] + dimensions()[baselineFudgeFactor] + [[button font] ascender]);
201 void QComboBox::clear()
203 KWQPopUpButton *button = (KWQPopUpButton *)getView();
204 [button removeAllItems];
209 void QComboBox::setCurrentItem(int index)
211 KWQ_BLOCK_EXCEPTIONS;
213 KWQPopUpButton *button = (KWQPopUpButton *)getView();
214 [button selectItemAtIndex:index];
216 KWQ_UNBLOCK_EXCEPTIONS;
221 bool QComboBox::updateCurrentItem() const
223 KWQPopUpButton *button = (KWQPopUpButton *)getView();
225 KWQ_BLOCK_EXCEPTIONS;
226 int i = [button indexOfSelectedItem];
228 if (_currentItem == i) {
232 KWQ_UNBLOCK_EXCEPTIONS;
237 void QComboBox::itemSelected()
239 if (updateCurrentItem()) {
240 _activated.call(_currentItem);
244 void QComboBox::setFont(const QFont &f)
248 const NSControlSize size = KWQNSControlSizeForFont(f);
249 NSControl * const button = static_cast<NSControl *>(getView());
251 KWQ_BLOCK_EXCEPTIONS;
253 if (size != [[button cell] controlSize]) {
254 [[button cell] setControlSize:size];
255 [button setFont:[NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:size]]];
259 KWQ_UNBLOCK_EXCEPTIONS;
262 const int *QComboBox::dimensions() const
264 // We empirically determined these dimensions.
265 // It would be better to get this info from AppKit somehow.
266 static const int w[3][7] = {
267 { 2, 3, 3, 3, 4, 34, 9 },
268 { 1, 3, 3, 3, 3, 31, 5 },
269 { 0, 0, 1, 1, 2, 32, 0 }
271 NSControl * const button = static_cast<NSControl *>(getView());
273 KWQ_BLOCK_EXCEPTIONS;
274 return w[[[button cell] controlSize]];
275 KWQ_UNBLOCK_EXCEPTIONS;
277 return w[NSSmallControlSize];
280 QWidget::FocusPolicy QComboBox::focusPolicy() const
282 KWQ_BLOCK_EXCEPTIONS;
284 // Add an additional check here.
285 // For now, selects are only focused when full
286 // keyboard access is turned on.
287 unsigned keyboardUIMode = [KWQKHTMLPart::bridgeForWidget(this) keyboardUIMode];
288 if ((keyboardUIMode & WebCoreKeyboardAccessFull) == 0)
291 KWQ_UNBLOCK_EXCEPTIONS;
293 return QWidget::focusPolicy();
296 void QComboBox::setWritingDirection(QPainter::TextDirection direction)
298 KWQ_BLOCK_EXCEPTIONS;
300 KWQPopUpButton *button = getView();
301 KWQPopUpButtonCell *cell = [button cell];
302 NSWritingDirection d = direction == QPainter::RTL ? NSWritingDirectionRightToLeft : NSWritingDirectionLeftToRight;
303 if ([cell baseWritingDirection] != d) {
304 [cell setBaseWritingDirection:d];
305 [button setNeedsDisplay:YES];
308 KWQ_UNBLOCK_EXCEPTIONS;
311 @implementation KWQComboBoxAdapter
313 - initWithQComboBox:(QComboBox *)b
319 - (void)action:(id)sender
326 @implementation KWQPopUpButtonCell
328 - initWithWidget:(QWidget *)w
335 - (BOOL)trackMouse:(NSEvent *)event inRect:(NSRect)rect ofView:(NSView *)view untilMouseUp:(BOOL)flag
337 WebCoreBridge *bridge = [KWQKHTMLPart::bridgeForWidget(widget) retain];
338 BOOL result = [super trackMouse:event inRect:rect ofView:view untilMouseUp:flag];
340 // Give KHTML a chance to fix up its event state, since the popup eats all the
341 // events during tracking. [NSApp currentEvent] is still the original mouseDown
343 [bridge part]->sendFakeEventsAfterWidgetTracking(event);
354 - (void)setBaseWritingDirection:(NSWritingDirection)direction
356 baseWritingDirection = direction;
359 - (NSWritingDirection)baseWritingDirection
361 return baseWritingDirection;
364 - (NSMutableDictionary *)_textAttributes
366 NSMutableDictionary *attributes = [super _textAttributes];
367 NSParagraphStyle *style = [attributes objectForKey:NSParagraphStyleAttributeName];
368 ASSERT(style != nil);
369 if ([style baseWritingDirection] != baseWritingDirection) {
370 NSMutableParagraphStyle *mutableStyle = [style mutableCopy];
371 [mutableStyle setBaseWritingDirection:baseWritingDirection];
372 [attributes setObject:mutableStyle forKey:NSParagraphStyleAttributeName];
373 [mutableStyle release];
380 @implementation KWQPopUpButton
384 return [(KWQPopUpButtonCell *)[self cell] widget];
387 - (BOOL)becomeFirstResponder
389 BOOL become = [super becomeFirstResponder];
391 QWidget *widget = [self widget];
392 if (!KWQKHTMLPart::currentEventIsMouseDownInWidget(widget)) {
393 [self _KWQ_scrollFrameToVisible];
395 QFocusEvent event(QEvent::FocusIn);
396 const_cast<QObject *>(widget->eventFilterObject())->eventFilter(widget, &event);
401 - (BOOL)resignFirstResponder
403 BOOL resign = [super resignFirstResponder];
405 QWidget *widget = [self widget];
406 QFocusEvent event(QEvent::FocusOut);
407 const_cast<QObject *>(widget->eventFilterObject())->eventFilter(widget, &event);
412 -(NSView *)nextKeyView
414 QWidget *widget = [self widget];
415 return widget && inNextValidKeyView
416 ? KWQKHTMLPart::nextKeyViewForWidget(widget, KWQSelectingNext)
417 : [super nextKeyView];
420 -(NSView *)previousKeyView
422 QWidget *widget = [self widget];
423 return widget && inNextValidKeyView
424 ? KWQKHTMLPart::nextKeyViewForWidget(widget, KWQSelectingPrevious)
425 : [super previousKeyView];
428 -(NSView *)nextValidKeyView
430 inNextValidKeyView = YES;
431 NSView *view = [super nextValidKeyView];
432 inNextValidKeyView = NO;
436 -(NSView *)previousValidKeyView
438 inNextValidKeyView = YES;
439 NSView *view = [super previousValidKeyView];
440 inNextValidKeyView = NO;