/* * 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 "KWQLineEdit.h" #import "KWQButton.h" #import "KWQExceptions.h" #import "KWQKHTMLPart.h" #import "KWQLogging.h" #import "KWQTextField.h" #import "WebCoreBridge.h" #import "WebCoreTextRenderer.h" #import "WebCoreTextRendererFactory.h" #import "WebCoreViewFactory.h" @interface NSSearchField (SearchFieldSecrets) - (void)_addStringToRecentSearches:(NSString *)string; @end QLineEdit::QLineEdit(Type type) : m_returnPressed(this, SIGNAL(returnPressed())) , m_textChanged(this, SIGNAL(textChanged(const QString &))) , m_clicked(this, SIGNAL(clicked())) , m_performSearch(this, SIGNAL(performSearch())) , m_type(type) { KWQ_BLOCK_EXCEPTIONS; id view = nil; switch (type) { case Normal: view = [KWQTextField alloc]; break; case Password: view = [KWQSecureTextField alloc]; break; case Search: view = [KWQSearchField alloc]; break; } ASSERT(view); [view initWithQLineEdit:this]; m_controller = [view controller]; setView(view); [view release]; [view setSelectable:YES]; // must do this explicitly so setEditable:NO does not make it NO KWQ_UNBLOCK_EXCEPTIONS; } QLineEdit::~QLineEdit() { KWQ_BLOCK_EXCEPTIONS; [m_controller detachQLineEdit]; KWQ_UNBLOCK_EXCEPTIONS; } void QLineEdit::setCursorPosition(int) { // Don't do anything here. } int QLineEdit::cursorPosition() const { // Not needed. We ignore setCursorPosition(). return 0; } void QLineEdit::setFont(const QFont &font) { QWidget::setFont(font); if (m_type == Search) { const NSControlSize size = KWQNSControlSizeForFont(font); NSControl * const searchField = static_cast<NSControl *>(getView()); [[searchField cell] setControlSize:size]; [searchField setFont:[NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:size]]]; } else { NSTextField *textField = (NSTextField *)getView(); KWQ_BLOCK_EXCEPTIONS; [textField setFont:font.getNSFont()]; KWQ_UNBLOCK_EXCEPTIONS; } } void QLineEdit::setPalette(const QPalette &palette) { QWidget::setPalette(palette); NSTextField *textField = (NSTextField *)getView(); KWQ_BLOCK_EXCEPTIONS; // Below we've added a special case that maps any completely transparent color to white. This is a workaround for the following // AppKit problems: <rdar://problem/3142730> and <rdar://problem/3036580>. Without this special case we have black // backgrounds on some text fields as described in <rdar://problem/3854383>. Text fields will still not be able to display // transparent and translucent backgrounds, which will need to be fixed in the future. See <rdar://problem/3865114>. [textField setTextColor:palette.foreground().getNSColor()]; QColor background = palette.background(); if (!background.isValid() || background.alpha() == 0) background = Qt::white; [textField setBackgroundColor:background.getNSColor()]; KWQ_UNBLOCK_EXCEPTIONS; } void QLineEdit::setText(const QString &s) { NSTextField *textField = (NSTextField *)getView(); KWQ_BLOCK_EXCEPTIONS; [textField setStringValue:s.getNSString()]; KWQ_UNBLOCK_EXCEPTIONS; } QString QLineEdit::text() const { KWQ_BLOCK_EXCEPTIONS; return QString::fromNSString([m_controller string]); KWQ_UNBLOCK_EXCEPTIONS; return QString(); } void QLineEdit::setMaxLength(int len) { [m_controller setMaximumLength:len]; } bool QLineEdit::isReadOnly() const { NSTextField *textField = (NSTextField *)getView(); KWQ_BLOCK_EXCEPTIONS; return ![textField isEditable]; KWQ_UNBLOCK_EXCEPTIONS; return true; } void QLineEdit::setReadOnly(bool flag) { NSTextField *textField = (NSTextField *)getView(); KWQ_BLOCK_EXCEPTIONS; [textField setEditable:!flag]; KWQ_UNBLOCK_EXCEPTIONS; } int QLineEdit::maxLength() const { return [m_controller maximumLength]; } void QLineEdit::selectAll() { if (!hasFocus()) { // Do the makeFirstResponder ourselves explicitly (by calling setFocus) // so WebHTMLView will know it's programmatic and not the user clicking. setFocus(); } else { KWQ_BLOCK_EXCEPTIONS; NSTextField *textField = (NSTextField *)getView(); [textField selectText:nil]; KWQ_UNBLOCK_EXCEPTIONS; } } bool QLineEdit::edited() const { return [m_controller edited]; } void QLineEdit::setEdited(bool flag) { [m_controller setEdited:flag]; } static const NSSize textFieldMargins = { 8, 6 }; QSize QLineEdit::sizeForCharacterWidth(int numCharacters) const { // Figure out how big a text field needs to be for a given number of characters // (using "0" as the nominal character). NSTextField *textField = (NSTextField *)getView(); ASSERT(numCharacters > 0); // We empirically determined these dimensions. // It would be better to get this info from AppKit somehow, but bug 3711080 shows we can't yet. // Note: baselinePosition below also has the height computation. NSSize size = textFieldMargins; KWQ_BLOCK_EXCEPTIONS; NSFont *font = [textField font]; size.height += [font defaultLineHeightForFont]; id <WebCoreTextRenderer> renderer = [[WebCoreTextRendererFactory sharedFactory] rendererWithFont:font usingPrinterFont:![NSGraphicsContext currentContextDrawingToScreen]]; WebCoreTextStyle style; WebCoreInitializeEmptyTextStyle(&style); style.applyRunRounding = NO; style.applyWordRounding = NO; const UniChar zero = '0'; WebCoreTextRun run; WebCoreInitializeTextRun(&run, &zero, 1, 0, 1); size.width += ceilf([renderer floatWidthForRun:&run style:&style widths:0] * numCharacters); KWQ_UNBLOCK_EXCEPTIONS; return QSize(size); } int QLineEdit::baselinePosition(int height) const { NSTextField *textField = (NSTextField *)getView(); KWQ_BLOCK_EXCEPTIONS; NSFont *font = [textField font]; float lineHeight = [font defaultLineHeightForFont]; NSRect bounds = NSMakeRect(0, 0, 100, textFieldMargins.height + lineHeight); // bounds width is arbitrary, height same as what sizeForCharacterWidth returns NSRect drawingRect = [[textField cell] drawingRectForBounds:bounds]; return static_cast<int>(ceilf(drawingRect.origin.y - bounds.origin.y + lineHeight + [font descender])); KWQ_UNBLOCK_EXCEPTIONS; return 0; } void QLineEdit::clicked() { m_clicked.call(); } void QLineEdit::setAlignment(AlignmentFlags alignment) { KWQ_BLOCK_EXCEPTIONS; NSTextField *textField = (NSTextField *)getView(); [textField setAlignment:KWQNSTextAlignmentForAlignmentFlags(alignment)]; KWQ_UNBLOCK_EXCEPTIONS; } void QLineEdit::setWritingDirection(QPainter::TextDirection direction) { KWQ_BLOCK_EXCEPTIONS; [m_controller setBaseWritingDirection:(direction == QPainter::RTL ? NSWritingDirectionRightToLeft : NSWritingDirectionLeftToRight)]; KWQ_UNBLOCK_EXCEPTIONS; } QWidget::FocusPolicy QLineEdit::focusPolicy() const { FocusPolicy policy = QWidget::focusPolicy(); return policy == TabFocus ? StrongFocus : policy; } bool QLineEdit::checksDescendantsForFocus() const { return true; } NSTextAlignment KWQNSTextAlignmentForAlignmentFlags(Qt::AlignmentFlags a) { switch (a) { default: ERROR("unsupported alignment"); case Qt::AlignLeft: return NSLeftTextAlignment; case Qt::AlignRight: return NSRightTextAlignment; case Qt::AlignHCenter: return NSCenterTextAlignment; } } void QLineEdit::setLiveSearch(bool liveSearch) { if (m_type != Search) return; NSSearchField *searchField = (NSSearchField *)getView(); [[searchField cell] setSendsWholeSearchString:!liveSearch]; } void QLineEdit::setAutoSaveName(const QString& name) { if (m_type != Search) return; QString autosave; if (!name.isEmpty()) autosave = "com.apple.WebKit.searchField:" + name; NSSearchField *searchField = (NSSearchField *)getView(); [searchField setRecentsAutosaveName:autosave.getNSString()]; } void QLineEdit::setMaxResults(int maxResults) { if (m_type != Search) return; NSSearchField *searchField = (NSSearchField *)getView(); id searchCell = [searchField cell]; if (maxResults == -1) { [searchCell setSearchButtonCell:nil]; [searchCell setSearchMenuTemplate:nil]; } else { NSMenu* cellMenu = [searchCell searchMenuTemplate]; NSButtonCell* buttonCell = [searchCell searchButtonCell]; if (!buttonCell) [searchCell resetSearchButtonCell]; if (cellMenu && !maxResults) [searchCell setSearchMenuTemplate:nil]; else if (!cellMenu && maxResults) [searchCell setSearchMenuTemplate:[[WebCoreViewFactory sharedFactory] cellMenuForSearchField]]; } [searchCell setMaximumRecents:maxResults]; } void QLineEdit::setPlaceholderString(const QString& placeholder) { NSTextField *textField = (NSTextField *)getView(); [[textField cell] setPlaceholderString:placeholder.getNSString()]; } void QLineEdit::addSearchResult() { if (m_type != Search) return; NSSearchField *searchField = (NSSearchField *)getView(); [[searchField cell] _addStringToRecentSearches:[searchField stringValue]]; }