LayoutTests:
[WebKit-https.git] / WebCore / platform / mac / TextFieldMac.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 "config.h"
27 #import "TextField.h"
28
29 #import "BlockExceptions.h"
30 #import "Color.h"
31 #import "Font.h"
32 #import "FontData.h"
33 #import "IntSize.h"
34 #import "Logging.h"
35 #import "RenderView.h"
36 #import "RenderWidget.h"
37 #import "TextStyle.h"
38 #import "WebCoreTextField.h"
39 #import "WebCoreViewFactory.h"
40 #import "WidgetClient.h"
41
42 @interface NSSearchFieldCell (SearchFieldSecrets)
43 - (void)_addStringToRecentSearches:(NSString *)string;
44 @end
45
46 namespace WebCore {
47
48 NSControlSize ControlSizeForFont(const Font& f)
49 {
50     const int fontSize = f.pixelSize();
51     if (fontSize >= 16)
52         return NSRegularControlSize;
53     if (fontSize >= 11)
54         return NSSmallControlSize;
55     return NSMiniControlSize;
56 }
57
58 TextField::TextField()
59 {
60     BEGIN_BLOCK_OBJC_EXCEPTIONS;
61     id view = [WebCoreSearchField alloc];
62     [view initWithWidget:this];
63     m_controller = [view controller];
64     setView((NSView *)view);
65     [view release];
66     [view setSelectable:YES]; // must do this explicitly so setEditable:NO does not make it NO
67     END_BLOCK_OBJC_EXCEPTIONS;
68 }
69
70 TextField::~TextField()
71 {
72     BEGIN_BLOCK_OBJC_EXCEPTIONS;
73     [m_controller detachWidget];
74     END_BLOCK_OBJC_EXCEPTIONS;
75 }
76
77 void TextField::setCursorPosition(int pos)
78 {
79     BEGIN_BLOCK_OBJC_EXCEPTIONS;
80     NSRange tempRange = {pos, 0}; // 4213314
81     [m_controller setSelectedRange:tempRange];
82     END_BLOCK_OBJC_EXCEPTIONS;
83 }
84
85 int TextField::cursorPosition() const
86 {
87     BEGIN_BLOCK_OBJC_EXCEPTIONS;
88     return [m_controller selectedRange].location;
89     END_BLOCK_OBJC_EXCEPTIONS;
90     return 0;
91 }
92
93 void TextField::setFont(const Font &font)
94 {
95     Widget::setFont(font);
96     const NSControlSize size = ControlSizeForFont(font);    
97     NSControl * const searchField = static_cast<NSControl *>(getView());
98     [[searchField cell] setControlSize:size];
99     [searchField setFont:[NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:size]]];
100 }
101
102 void TextField::setColors(const Color& background, const Color& foreground)
103 {
104     NSTextField *textField = (NSTextField *)getView();
105
106     BEGIN_BLOCK_OBJC_EXCEPTIONS;
107
108     // Below we've added a special case that maps any completely transparent color to white.  This is a workaround for the following
109     // AppKit problems: <rdar://problem/3142730> and <rdar://problem/3036580>.  Without this special case we have black
110     // backgrounds on some text fields as described in <rdar://problem/3854383>.  Text fields will still not be able to display
111     // transparent and translucent backgrounds, which will need to be fixed in the future.  See  <rdar://problem/3865114>.
112         
113     [textField setTextColor:nsColor(foreground)];
114
115     Color bg = background;
116     if (!bg.isValid() || bg.alpha() == 0)
117         bg = Color::white;
118     [textField setBackgroundColor:nsColor(bg)];
119
120     END_BLOCK_OBJC_EXCEPTIONS;
121 }
122
123 void TextField::setText(const String& s)
124 {
125     NSTextField *textField = (NSTextField *)getView();
126     BEGIN_BLOCK_OBJC_EXCEPTIONS;
127     [textField setStringValue:s];
128     END_BLOCK_OBJC_EXCEPTIONS;
129 }
130
131 String TextField::text() const
132 {
133     BEGIN_BLOCK_OBJC_EXCEPTIONS;
134     return String([m_controller string]);
135     END_BLOCK_OBJC_EXCEPTIONS;
136     return String();
137 }
138
139 void TextField::setMaxLength(int len)
140 {
141     [m_controller setMaximumLength:len];
142 }
143
144 bool TextField::isReadOnly() const
145 {
146     NSTextField *textField = (NSTextField *)getView();
147
148     BEGIN_BLOCK_OBJC_EXCEPTIONS;
149     return ![textField isEditable];
150     END_BLOCK_OBJC_EXCEPTIONS;
151
152     return true;
153 }
154
155 void TextField::setReadOnly(bool flag)
156 {
157     NSTextField *textField = (NSTextField *)getView();
158     BEGIN_BLOCK_OBJC_EXCEPTIONS;
159     [textField setEditable:!flag];
160     END_BLOCK_OBJC_EXCEPTIONS;
161 }
162
163 int TextField::maxLength() const
164 {
165     return [m_controller maximumLength];
166 }
167
168
169 void TextField::selectAll()
170 {
171     if (!hasFocus()) {
172         // Do the makeFirstResponder ourselves explicitly (by calling setFocus)
173         // so WebHTMLView will know it's programmatic and not the user clicking.
174         setFocus();
175     } else {
176         BEGIN_BLOCK_OBJC_EXCEPTIONS;
177         NSTextField *textField = (NSTextField *)getView();
178         [textField selectText:nil];
179         END_BLOCK_OBJC_EXCEPTIONS;
180     }
181 }
182
183 int TextField::selectionStart() const
184 {
185     BEGIN_BLOCK_OBJC_EXCEPTIONS;
186     if ([m_controller hasSelection]) {
187         return [m_controller selectedRange].location;
188     }
189     END_BLOCK_OBJC_EXCEPTIONS;
190     return -1;
191 }
192
193 String TextField::selectedText() const
194 {
195     BEGIN_BLOCK_OBJC_EXCEPTIONS;
196     NSRange range = [m_controller selectedRange];
197     NSString *str = [m_controller string];
198     return [str substringWithRange:range];
199     END_BLOCK_OBJC_EXCEPTIONS;
200     return String();
201 }
202
203 void TextField::setSelection(int start, int length)
204 {
205     BEGIN_BLOCK_OBJC_EXCEPTIONS;
206     NSRange tempRange = {start, length}; // 4213314
207     [m_controller setSelectedRange:tempRange];
208     END_BLOCK_OBJC_EXCEPTIONS;
209 }
210
211 bool TextField::hasSelectedText() const
212 {
213     return [m_controller hasSelection];
214 }
215
216 bool TextField::edited() const
217 {
218     return [m_controller edited];
219 }
220
221 void TextField::setEdited(bool flag)
222 {
223     [m_controller setEdited:flag];
224 }
225
226 static const NSSize textFieldMargins = { 8, 6 };
227
228 IntSize TextField::sizeForCharacterWidth(int numCharacters) const
229 {
230     // Figure out how big a text field needs to be for a given number of characters
231     // (using "0" as the nominal character).
232
233     NSTextField *textField = (NSTextField *)getView();
234
235     ASSERT(numCharacters > 0);
236
237     // We empirically determined these dimensions.
238     // It would be better to get this info from AppKit somehow, but bug 3711080 shows we can't yet.
239     // Note: baselinePosition below also has the height computation.
240     NSSize size = textFieldMargins;
241
242     BEGIN_BLOCK_OBJC_EXCEPTIONS;
243
244     RenderWidget *client = static_cast<RenderWidget *>(Widget::client());
245     FontPlatformData font([textField font]);
246     Font renderer(font, client->view()->printingMode());
247
248     NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
249     size.height += [layoutManager defaultLineHeightForFont:font.font];
250     [layoutManager release];
251
252     TextStyle style;
253     style.disableRoundingHacks();
254
255     const UniChar zero = '0';
256     TextRun run(&zero, 1);
257
258     size.width += ceilf(renderer.floatWidth(run, style) * numCharacters);
259
260     return IntSize(size);
261     END_BLOCK_OBJC_EXCEPTIONS;
262     return IntSize(0, 0);
263 }
264
265 int TextField::baselinePosition(int height) const
266 {
267     NSTextField *textField = (NSTextField *)getView();
268
269     BEGIN_BLOCK_OBJC_EXCEPTIONS;
270     NSFont *font = [textField font];
271     NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
272     float lineHeight = [layoutManager defaultLineHeightForFont:font];
273     [layoutManager release];
274     NSRect bounds = NSMakeRect(0, 0, 100, textFieldMargins.height + lineHeight); // bounds width is arbitrary, height same as what sizeForCharacterWidth returns
275     NSRect drawingRect = [[textField cell] drawingRectForBounds:bounds];
276     return static_cast<int>(ceilf(drawingRect.origin.y - bounds.origin.y + lineHeight + [font descender]));
277     END_BLOCK_OBJC_EXCEPTIONS;
278
279     return 0;
280 }
281
282 void TextField::setAlignment(HorizontalAlignment alignment)
283 {
284     BEGIN_BLOCK_OBJC_EXCEPTIONS;
285
286     NSTextField *textField = (NSTextField *)getView();
287     [textField setAlignment:TextAlignment(alignment)];
288
289     END_BLOCK_OBJC_EXCEPTIONS;
290 }
291
292 void TextField::setWritingDirection(TextDirection direction)
293 {
294     BEGIN_BLOCK_OBJC_EXCEPTIONS;
295     [m_controller setBaseWritingDirection:(direction == RTL ? NSWritingDirectionRightToLeft : NSWritingDirectionLeftToRight)];
296     END_BLOCK_OBJC_EXCEPTIONS;
297 }
298
299 Widget::FocusPolicy TextField::focusPolicy() const
300 {
301     FocusPolicy policy = Widget::focusPolicy();
302     return policy == TabFocus ? StrongFocus : policy;
303 }
304
305 bool TextField::checksDescendantsForFocus() const
306 {
307     return true;
308 }
309
310 NSTextAlignment TextAlignment(HorizontalAlignment a)
311 {
312     switch (a) {
313         case AlignLeft:
314             return NSLeftTextAlignment;
315         case AlignRight:
316             return NSRightTextAlignment;
317         case AlignHCenter:
318             return NSCenterTextAlignment;
319     }
320     LOG_ERROR("unsupported alignment");
321     return NSLeftTextAlignment;
322 }
323
324 void TextField::setLiveSearch(bool liveSearch)
325 {
326     NSSearchField *searchField = (NSSearchField *)getView();
327     [[searchField cell] setSendsWholeSearchString:!liveSearch];
328 }
329
330 void TextField::setAutoSaveName(const String& name)
331 {
332     String autosave;
333     if (!name.isEmpty())
334         autosave = "com.apple.WebKit.searchField:" + name;
335     
336     NSSearchField *searchField = (NSSearchField *)getView();
337     [searchField setRecentsAutosaveName:autosave];
338 }
339
340 void TextField::setMaxResults(int maxResults)
341 {
342     NSSearchField *searchField = (NSSearchField *)getView();
343     id searchCell = [searchField cell];
344     if (maxResults == -1) {
345         [searchCell setSearchButtonCell:nil];
346         [searchCell setSearchMenuTemplate:nil];
347     }
348     else {
349         NSMenu* cellMenu = [searchCell searchMenuTemplate];
350         NSButtonCell* buttonCell = [searchCell searchButtonCell];
351         if (!buttonCell)
352             [searchCell resetSearchButtonCell];
353         if (cellMenu && !maxResults)
354             [searchCell setSearchMenuTemplate:nil];
355         else if (!cellMenu && maxResults)
356             [searchCell setSearchMenuTemplate:[[WebCoreViewFactory sharedFactory] cellMenuForSearchField]];
357     }
358     
359     [searchCell setMaximumRecents:maxResults];
360 }
361
362 void TextField::setPlaceholderString(const String& placeholder)
363 {
364     NSTextField *textField = (NSTextField *)getView();
365     [[textField cell] setPlaceholderString:placeholder];
366 }
367
368 void TextField::addSearchResult()
369 {
370     NSSearchField *searchField = (NSSearchField *)getView();
371     [[searchField cell] _addStringToRecentSearches:[searchField stringValue]];
372 }
373
374 }