/* * Copyright (C) 2004 Apple Computer, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #import "KWQButton.h" #import "KWQAssertions.h" #import "KWQCheckBox.h" #import "KWQExceptions.h" #import "KWQKHTMLPart.h" #import "KWQNSViewExtras.h" #import "KWQView.h" #import "WebCoreBridge.h" #import "render_form.h" @interface NSCell (KWQButtonKnowsAppKitSecrets) - (NSMutableDictionary *)_textAttributes; @end @interface KWQButton : NSButton <KWQWidgetHolder> { QButton *button; BOOL needToSendConsumedMouseUp; BOOL inNextValidKeyView; } - (id)initWithQButton:(QButton *)b; - (void)detachQButton; - (void)sendConsumedMouseUpIfNeeded; @end @interface KWQButtonCell : NSButtonCell { NSWritingDirection baseWritingDirection; } - (void)setBaseWritingDirection:(NSWritingDirection)direction; - (NSWritingDirection)baseWritingDirection; @end @implementation KWQButton + (Class)cellClass { return [KWQButtonCell class]; } - (id)initWithQButton:(QButton *)b { self = [self init]; button = b; [self setTarget:self]; [self setAction:@selector(action:)]; return self; } - (void)detachQButton { button = 0; [self setTarget:nil]; } - (void)action:(id)sender { button->clicked(); } - (void)sendConsumedMouseUpIfNeeded { if (needToSendConsumedMouseUp) { needToSendConsumedMouseUp = NO; if (button) { button->sendConsumedMouseUp(); } } } -(void)mouseDown:(NSEvent *)event { needToSendConsumedMouseUp = YES; QWidget::beforeMouseDown(self); [super mouseDown:event]; QWidget::afterMouseDown(self); [self sendConsumedMouseUpIfNeeded]; } - (QWidget *)widget { return button; } - (BOOL)becomeFirstResponder { BOOL become = [super becomeFirstResponder]; if (become && button) { if (!KWQKHTMLPart::currentEventIsMouseDownInWidget(button)) { [self _KWQ_scrollFrameToVisible]; } if (button) { QFocusEvent event(QEvent::FocusIn); const_cast<QObject *>(button->eventFilterObject())->eventFilter(button, &event); } } return become; } - (BOOL)resignFirstResponder { BOOL resign = [super resignFirstResponder]; if (resign && button) { QFocusEvent event(QEvent::FocusOut); const_cast<QObject *>(button->eventFilterObject())->eventFilter(button, &event); } return resign; } -(NSView *)nextKeyView { NSView *view = nil; if (button && inNextValidKeyView) { // resign so we send a blur before setting focus on // the next widget, otherwise the blur for this // widget will remove focus from the widget after // we tab to it [self resignFirstResponder]; if (button) { view = KWQKHTMLPart::nextKeyViewForWidget(button, KWQSelectingNext); } else { view = [super nextKeyView]; } } else { view = [super nextKeyView]; } return view; } -(NSView *)previousKeyView { NSView *view = nil; if (button && inNextValidKeyView) { // resign so we send a blur before setting focus on // the next widget, otherwise the blur for this // widget will remove focus from the widget after // we tab to it [self resignFirstResponder]; if (button) { view = KWQKHTMLPart::nextKeyViewForWidget(button, KWQSelectingPrevious); } else { view = [super previousKeyView]; } } else { view = [super previousKeyView]; } return view; } - (BOOL)canBecomeKeyView { // Simplified method from NSView; overridden to replace NSView's way of checking // for full keyboard access with ours. if (button && !KWQKHTMLPart::partForWidget(button)->tabsToAllControls()) { return NO; } return ([self window] != nil) && ![self isHiddenOrHasHiddenAncestor] && [self acceptsFirstResponder]; } -(NSView *)nextValidKeyView { inNextValidKeyView = YES; NSView *view = [super nextValidKeyView]; inNextValidKeyView = NO; return view; } -(NSView *)previousValidKeyView { inNextValidKeyView = YES; NSView *view = [super previousValidKeyView]; inNextValidKeyView = NO; return view; } @end @implementation KWQButtonCell - (NSWritingDirection)baseWritingDirection { return baseWritingDirection; } - (void)setBaseWritingDirection:(NSWritingDirection)direction { baseWritingDirection = direction; } - (NSMutableDictionary *)_textAttributes { NSMutableDictionary *attributes = [super _textAttributes]; NSParagraphStyle *style = [attributes objectForKey:NSParagraphStyleAttributeName]; ASSERT(style != nil); if ([style baseWritingDirection] != baseWritingDirection) { NSMutableParagraphStyle *mutableStyle = [style mutableCopy]; [mutableStyle setBaseWritingDirection:baseWritingDirection]; [attributes setObject:mutableStyle forKey:NSParagraphStyleAttributeName]; [mutableStyle release]; } return attributes; } @end QButton::QButton() : m_clicked(this, SIGNAL(clicked())) { KWQ_BLOCK_EXCEPTIONS; KWQButton *button = [[KWQButton alloc] initWithQButton:this]; setView(button); [button release]; [button setTitle:@""]; [[button cell] setControlSize:NSSmallControlSize]; [button setFont:[NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]]]; KWQ_UNBLOCK_EXCEPTIONS; } QButton::~QButton() { KWQ_BLOCK_EXCEPTIONS; KWQButton *button = (KWQButton *)getView(); [button detachQButton]; KWQ_UNBLOCK_EXCEPTIONS; } void QButton::setText(const QString &s) { KWQ_BLOCK_EXCEPTIONS; NSButton *button = (NSButton *)getView(); [button setTitle:s.getNSString()]; KWQ_UNBLOCK_EXCEPTIONS; } QString QButton::text() const { QString result; KWQ_BLOCK_EXCEPTIONS; NSButton *button = (NSButton *)getView(); result = QString::fromNSString([button title]); KWQ_UNBLOCK_EXCEPTIONS; return result; } void QButton::clicked() { // Order of signals is: // 1) signals in subclasses (stateChanged, not sure if there are any others) // 2) mouse up // 3) clicked // Proper behavior of check boxes, at least, depends on this order. KWQ_BLOCK_EXCEPTIONS; KWQButton *button = (KWQButton *)getView(); [button sendConsumedMouseUpIfNeeded]; // Don't call clicked if the button was destroyed inside of sendConsumedMouseUpIfNeeded. if ([button target]) { m_clicked.call(); } KWQ_UNBLOCK_EXCEPTIONS; } void QButton::click(bool sendMouseEvents) { KWQ_BLOCK_EXCEPTIONS; KWQButton *button = (KWQButton *)getView(); [button performClick:nil]; KWQ_UNBLOCK_EXCEPTIONS; } void QButton::setFont(const QFont &f) { KWQ_BLOCK_EXCEPTIONS; QWidget::setFont(f); const NSControlSize size = KWQNSControlSizeForFont(f); NSControl * const button = static_cast<NSControl *>(getView()); [[button cell] setControlSize:size]; [button setFont:[NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:size]]]; KWQ_UNBLOCK_EXCEPTIONS; } NSControlSize KWQNSControlSizeForFont(const QFont &f) { const int fontSize = f.pixelSize(); if (fontSize >= 16) { return NSRegularControlSize; } if (fontSize >= 11) { return NSSmallControlSize; } return NSMiniControlSize; } QWidget::FocusPolicy QButton::focusPolicy() const { KWQ_BLOCK_EXCEPTIONS; WebCoreBridge *bridge = KWQKHTMLPart::bridgeForWidget(this); if (!bridge || ![bridge part] || ![bridge part]->tabsToAllControls()) { return NoFocus; } KWQ_UNBLOCK_EXCEPTIONS; return QWidget::focusPolicy(); } void QButton::setWritingDirection(QPainter::TextDirection direction) { KWQ_BLOCK_EXCEPTIONS; KWQButton *button = getView(); KWQButtonCell *cell = [button cell]; NSWritingDirection d = direction == QPainter::RTL ? NSWritingDirectionRightToLeft : NSWritingDirectionLeftToRight; if ([cell baseWritingDirection] != d) { [cell setBaseWritingDirection:d]; [button setNeedsDisplay:YES]; } KWQ_UNBLOCK_EXCEPTIONS; }