Fix for 3936571, placeholder attribute should work for normal inputs for Dashboard.
[WebKit-https.git] / WebCore / kwq / KWQLineEdit.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 "KWQLineEdit.h"
27
28 #import "KWQButton.h"
29 #import "KWQExceptions.h"
30 #import "KWQKHTMLPart.h"
31 #import "KWQLogging.h"
32 #import "KWQTextField.h"
33 #import "WebCoreBridge.h"
34 #import "WebCoreTextRenderer.h"
35 #import "WebCoreTextRendererFactory.h"
36 #import "WebCoreViewFactory.h"
37
38 @interface NSSearchField (SearchFieldSecrets)
39 - (void)_addStringToRecentSearches:(NSString *)string;
40 @end
41
42 QLineEdit::QLineEdit(Type type)
43     : m_returnPressed(this, SIGNAL(returnPressed()))
44     , m_textChanged(this, SIGNAL(textChanged(const QString &)))
45     , m_clicked(this, SIGNAL(clicked()))
46     , m_performSearch(this, SIGNAL(performSearch()))
47     , m_type(type)
48 {
49     KWQ_BLOCK_EXCEPTIONS;
50     id view = nil;
51     switch (type) {
52         case Normal:
53             view = [KWQTextField alloc];
54             break;
55         case Password:
56             view = [KWQSecureTextField alloc];
57             break;
58         case Search:
59             view = [KWQSearchField alloc];
60             break;
61     }
62     ASSERT(view);
63     [view initWithQLineEdit:this];
64     m_controller = [view controller];
65     setView(view);
66     [view release];
67     [view setSelectable:YES]; // must do this explicitly so setEditable:NO does not make it NO
68     KWQ_UNBLOCK_EXCEPTIONS;
69 }
70
71 QLineEdit::~QLineEdit()
72 {
73     KWQ_BLOCK_EXCEPTIONS;
74     [m_controller detachQLineEdit];
75     KWQ_UNBLOCK_EXCEPTIONS;
76 }
77
78 void QLineEdit::setCursorPosition(int)
79 {
80     // Don't do anything here.
81 }
82
83 int QLineEdit::cursorPosition() const
84 {
85     // Not needed.  We ignore setCursorPosition().
86     return 0;
87 }
88
89 void QLineEdit::setFont(const QFont &font)
90 {
91     QWidget::setFont(font);
92     if (m_type == Search) {
93         const NSControlSize size = KWQNSControlSizeForFont(font);    
94         NSControl * const searchField = static_cast<NSControl *>(getView());
95         [[searchField cell] setControlSize:size];
96         [searchField setFont:[NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:size]]];
97     }
98     else {
99         NSTextField *textField = (NSTextField *)getView();
100         KWQ_BLOCK_EXCEPTIONS;
101         [textField setFont:font.getNSFont()];
102         KWQ_UNBLOCK_EXCEPTIONS;
103     }
104 }
105
106 void QLineEdit::setPalette(const QPalette &palette)
107 {
108     QWidget::setPalette(palette);
109
110     NSTextField *textField = (NSTextField *)getView();
111
112     KWQ_BLOCK_EXCEPTIONS;
113
114     // Below we've added a special case that maps any completely transparent color to white.  This is a workaround for the following
115     // AppKit problems: <rdar://problem/3142730> and <rdar://problem/3036580>.  Without this special case we have black
116     // backgrounds on some text fields as described in <rdar://problem/3854383>.  Text fields will still not be able to display
117     // transparent and translucent backgrounds, which will need to be fixed in the future.  See  <rdar://problem/3865114>.
118         
119     [textField setTextColor:palette.foreground().getNSColor()];
120
121     QColor background = palette.background();
122     if (!background.isValid() || background.alpha() == 0)
123         background = Qt::white;
124     [textField setBackgroundColor:background.getNSColor()];
125
126     KWQ_UNBLOCK_EXCEPTIONS;
127 }
128
129 void QLineEdit::setText(const QString &s)
130 {
131     NSTextField *textField = (NSTextField *)getView();
132     KWQ_BLOCK_EXCEPTIONS;
133     [textField setStringValue:s.getNSString()];
134     KWQ_UNBLOCK_EXCEPTIONS;
135 }
136
137 QString QLineEdit::text() const
138 {
139     NSTextField *textField = (NSTextField *)getView();
140
141     KWQ_BLOCK_EXCEPTIONS;
142     NSMutableString *text = [[[textField stringValue] mutableCopy] autorelease];
143     [text replaceOccurrencesOfString:@"\r\n" withString:@"\n" options:NSLiteralSearch range:NSMakeRange(0, [text length])];
144     [text replaceOccurrencesOfString:@"\r" withString:@"\n" options:NSLiteralSearch range:NSMakeRange(0, [text length])];
145     return QString::fromNSString(text);
146     KWQ_UNBLOCK_EXCEPTIONS;
147
148     return QString();
149 }
150
151 void QLineEdit::setMaxLength(int len)
152 {
153     [m_controller setMaximumLength:len];
154 }
155
156 bool QLineEdit::isReadOnly() const
157 {
158     NSTextField *textField = (NSTextField *)getView();
159
160     KWQ_BLOCK_EXCEPTIONS;
161     return ![textField isEditable];
162     KWQ_UNBLOCK_EXCEPTIONS;
163
164     return true;
165 }
166
167 void QLineEdit::setReadOnly(bool flag)
168 {
169     NSTextField *textField = (NSTextField *)getView();
170     KWQ_BLOCK_EXCEPTIONS;
171     [textField setEditable:!flag];
172     KWQ_UNBLOCK_EXCEPTIONS;
173 }
174
175 int QLineEdit::maxLength() const
176 {
177     return [m_controller maximumLength];
178 }
179
180 void QLineEdit::selectAll()
181 {
182     KWQ_BLOCK_EXCEPTIONS;
183
184     // Do the makeFirstResponder ourselves explicitly (by calling setFocus)
185     // so WebHTMLView will know it's programmatic and not the user clicking.
186     setFocus();
187
188     NSTextField *textField = (NSTextField *)getView();
189     [textField selectText:nil];
190
191     KWQ_UNBLOCK_EXCEPTIONS;
192 }
193
194 bool QLineEdit::edited() const
195 {
196     return [m_controller edited];
197 }
198
199 void QLineEdit::setEdited(bool flag)
200 {
201     [m_controller setEdited:flag];
202 }
203
204 QSize QLineEdit::sizeForCharacterWidth(int numCharacters) const
205 {
206     // Figure out how big a text field needs to be for a given number of characters
207     // (using "0" as the nominal character).
208
209     NSTextField *textField = (NSTextField *)getView();
210
211     ASSERT(numCharacters > 0);
212
213     // We empirically determined these dimensions.
214     // It would be better to get this info from AppKit somehow, but bug 3711080 shows we can't yet.
215     NSSize size = { 8, 6 };
216
217     KWQ_BLOCK_EXCEPTIONS;
218
219     NSFont *font = [textField font];
220
221     size.height += [font defaultLineHeightForFont];
222
223     id <WebCoreTextRenderer> renderer = [[WebCoreTextRendererFactory sharedFactory]
224         rendererWithFont:font usingPrinterFont:![NSGraphicsContext currentContextDrawingToScreen]];
225
226     WebCoreTextStyle style;
227     WebCoreInitializeEmptyTextStyle(&style);
228     style.applyRunRounding = NO;
229     style.applyWordRounding = NO;
230
231     const UniChar zero = '0';
232     WebCoreTextRun run;
233     WebCoreInitializeTextRun(&run, &zero, 1, 0, 1);
234
235     size.width += ceilf([renderer floatWidthForRun:&run style:&style widths:0] * numCharacters);
236
237     KWQ_UNBLOCK_EXCEPTIONS;
238
239     return QSize(size);
240 }
241
242 int QLineEdit::baselinePosition(int height) const
243 {
244     NSTextField *textField = (NSTextField *)getView();
245
246     KWQ_BLOCK_EXCEPTIONS;
247     NSRect bounds = [textField bounds];
248     NSFont *font = [textField font];
249     return static_cast<int>(ceilf([[textField cell] drawingRectForBounds:bounds].origin.y - bounds.origin.y
250         + [font defaultLineHeightForFont] + [font descender]));
251     KWQ_UNBLOCK_EXCEPTIONS;
252
253     return 0;
254 }
255
256 void QLineEdit::clicked()
257 {
258     m_clicked.call();
259 }
260
261 void QLineEdit::setAlignment(AlignmentFlags alignment)
262 {
263     KWQ_BLOCK_EXCEPTIONS;
264
265     NSTextField *textField = (NSTextField *)getView();
266     [textField setAlignment:KWQNSTextAlignmentForAlignmentFlags(alignment)];
267
268     KWQ_UNBLOCK_EXCEPTIONS;
269 }
270
271 void QLineEdit::setWritingDirection(QPainter::TextDirection direction)
272 {
273     KWQ_BLOCK_EXCEPTIONS;
274     [m_controller setBaseWritingDirection:(direction == QPainter::RTL ? NSWritingDirectionRightToLeft : NSWritingDirectionLeftToRight)];
275     KWQ_UNBLOCK_EXCEPTIONS;
276 }
277
278 QWidget::FocusPolicy QLineEdit::focusPolicy() const
279 {
280     FocusPolicy policy = QWidget::focusPolicy();
281     return policy == TabFocus ? StrongFocus : policy;
282 }
283
284 bool QLineEdit::checksDescendantsForFocus() const
285 {
286     return true;
287 }
288
289 NSTextAlignment KWQNSTextAlignmentForAlignmentFlags(Qt::AlignmentFlags a)
290 {
291     switch (a) {
292         default:
293             ERROR("unsupported alignment");
294         case Qt::AlignLeft:
295             return NSLeftTextAlignment;
296         case Qt::AlignRight:
297             return NSRightTextAlignment;
298         case Qt::AlignHCenter:
299             return NSCenterTextAlignment;
300     }
301 }
302
303 void QLineEdit::setLiveSearch(bool liveSearch)
304 {
305     if (m_type != Search)
306         return;
307     
308     NSSearchField *searchField = (NSSearchField *)getView();
309     [[searchField cell] setSendsWholeSearchString:!liveSearch];
310 }
311
312 void QLineEdit::setAutoSaveName(const QString& name)
313 {
314     if (m_type != Search)
315         return;
316     
317     QString autosave;
318     if (!name.isEmpty())
319         autosave = "com.apple.WebKit.searchField:" + name;
320     
321     NSSearchField *searchField = (NSSearchField *)getView();
322     [searchField setRecentsAutosaveName:autosave.getNSString()];
323 }
324
325 void QLineEdit::setMaxResults(int maxResults)
326 {
327     if (m_type != Search)
328         return;
329     
330     NSSearchField *searchField = (NSSearchField *)getView();
331     id searchCell = [searchField cell];
332     if (!maxResults) {
333         [searchCell setSearchButtonCell:nil];
334         [searchCell setSearchMenuTemplate:nil];
335     }
336     else {
337         NSMenu* cellMenu = [searchCell searchMenuTemplate];
338         NSButtonCell* buttonCell = [searchCell searchButtonCell];
339         if (!buttonCell)
340             [searchCell resetSearchButtonCell];
341         if (!cellMenu)
342             [searchCell setSearchMenuTemplate:[[WebCoreViewFactory sharedFactory] cellMenuForSearchField]];
343     }
344     
345     [searchCell setMaximumRecents:maxResults];
346 }
347
348 void QLineEdit::setPlaceholderString(const QString& placeholder)
349 {
350     NSTextField *textField = (NSTextField *)getView();
351     [[textField cell] setPlaceholderString:placeholder.getNSString()];
352 }
353
354 void QLineEdit::addSearchResult()
355 {
356     if (m_type != Search)
357         return;
358     
359     NSSearchField *searchField = (NSSearchField *)getView();
360     [[searchField cell] _addStringToRecentSearches:[searchField stringValue]];
361 }
362