2 * Copyright (C) 2004 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.
29 #import "BlockExceptions.h"
35 #import "RenderView.h"
36 #import "RenderWidget.h"
38 #import "WebCoreTextField.h"
39 #import "WebCoreViewFactory.h"
40 #import "WidgetClient.h"
42 @interface NSSearchFieldCell (SearchFieldSecrets)
43 - (void)_addStringToRecentSearches:(NSString *)string;
48 NSControlSize ControlSizeForFont(const Font& f)
50 const int fontSize = f.pixelSize();
52 return NSRegularControlSize;
54 return NSSmallControlSize;
55 return NSMiniControlSize;
58 TextField::TextField()
60 BEGIN_BLOCK_OBJC_EXCEPTIONS;
61 id view = [WebCoreSearchField alloc];
62 [view initWithWidget:this];
63 m_controller = [view controller];
64 setView((NSView *)view);
66 [view setSelectable:YES]; // must do this explicitly so setEditable:NO does not make it NO
67 END_BLOCK_OBJC_EXCEPTIONS;
70 TextField::~TextField()
72 BEGIN_BLOCK_OBJC_EXCEPTIONS;
73 [m_controller detachWidget];
74 END_BLOCK_OBJC_EXCEPTIONS;
77 void TextField::setCursorPosition(int pos)
79 BEGIN_BLOCK_OBJC_EXCEPTIONS;
80 NSRange tempRange = {pos, 0}; // 4213314
81 [m_controller setSelectedRange:tempRange];
82 END_BLOCK_OBJC_EXCEPTIONS;
85 int TextField::cursorPosition() const
87 BEGIN_BLOCK_OBJC_EXCEPTIONS;
88 return [m_controller selectedRange].location;
89 END_BLOCK_OBJC_EXCEPTIONS;
93 void TextField::setFont(const Font &font)
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]]];
102 void TextField::setColors(const Color& background, const Color& foreground)
104 NSTextField *textField = (NSTextField *)getView();
106 BEGIN_BLOCK_OBJC_EXCEPTIONS;
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>.
113 [textField setTextColor:nsColor(foreground)];
115 Color bg = background;
116 if (!bg.isValid() || bg.alpha() == 0)
118 [textField setBackgroundColor:nsColor(bg)];
120 END_BLOCK_OBJC_EXCEPTIONS;
123 void TextField::setText(const String& s)
125 NSTextField *textField = (NSTextField *)getView();
126 BEGIN_BLOCK_OBJC_EXCEPTIONS;
127 [textField setStringValue:s];
128 END_BLOCK_OBJC_EXCEPTIONS;
131 String TextField::text() const
133 BEGIN_BLOCK_OBJC_EXCEPTIONS;
134 return String([m_controller string]);
135 END_BLOCK_OBJC_EXCEPTIONS;
139 void TextField::setMaxLength(int len)
141 [m_controller setMaximumLength:len];
144 bool TextField::isReadOnly() const
146 NSTextField *textField = (NSTextField *)getView();
148 BEGIN_BLOCK_OBJC_EXCEPTIONS;
149 return ![textField isEditable];
150 END_BLOCK_OBJC_EXCEPTIONS;
155 void TextField::setReadOnly(bool flag)
157 NSTextField *textField = (NSTextField *)getView();
158 BEGIN_BLOCK_OBJC_EXCEPTIONS;
159 [textField setEditable:!flag];
160 END_BLOCK_OBJC_EXCEPTIONS;
163 int TextField::maxLength() const
165 return [m_controller maximumLength];
169 void TextField::selectAll()
172 // Do the makeFirstResponder ourselves explicitly (by calling setFocus)
173 // so WebHTMLView will know it's programmatic and not the user clicking.
176 BEGIN_BLOCK_OBJC_EXCEPTIONS;
177 NSTextField *textField = (NSTextField *)getView();
178 [textField selectText:nil];
179 END_BLOCK_OBJC_EXCEPTIONS;
183 int TextField::selectionStart() const
185 BEGIN_BLOCK_OBJC_EXCEPTIONS;
186 if ([m_controller hasSelection]) {
187 return [m_controller selectedRange].location;
189 END_BLOCK_OBJC_EXCEPTIONS;
193 String TextField::selectedText() const
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;
203 void TextField::setSelection(int start, int length)
205 BEGIN_BLOCK_OBJC_EXCEPTIONS;
206 NSRange tempRange = {start, length}; // 4213314
207 [m_controller setSelectedRange:tempRange];
208 END_BLOCK_OBJC_EXCEPTIONS;
211 bool TextField::hasSelectedText() const
213 return [m_controller hasSelection];
216 bool TextField::edited() const
218 return [m_controller edited];
221 void TextField::setEdited(bool flag)
223 [m_controller setEdited:flag];
226 static const NSSize textFieldMargins = { 8, 6 };
228 IntSize TextField::sizeForCharacterWidth(int numCharacters) const
230 // Figure out how big a text field needs to be for a given number of characters
231 // (using "0" as the nominal character).
233 NSTextField *textField = (NSTextField *)getView();
235 ASSERT(numCharacters > 0);
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;
242 BEGIN_BLOCK_OBJC_EXCEPTIONS;
244 RenderWidget *client = static_cast<RenderWidget *>(Widget::client());
245 FontPlatformData font([textField font]);
246 Font renderer(font, client->view()->printingMode());
248 NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
249 size.height += [layoutManager defaultLineHeightForFont:font.font];
250 [layoutManager release];
253 style.disableRoundingHacks();
255 const UniChar zero = '0';
256 TextRun run(&zero, 1);
258 size.width += ceilf(renderer.floatWidth(run, style) * numCharacters);
260 return IntSize(size);
261 END_BLOCK_OBJC_EXCEPTIONS;
262 return IntSize(0, 0);
265 int TextField::baselinePosition(int height) const
267 NSTextField *textField = (NSTextField *)getView();
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;
282 void TextField::setAlignment(HorizontalAlignment alignment)
284 BEGIN_BLOCK_OBJC_EXCEPTIONS;
286 NSTextField *textField = (NSTextField *)getView();
287 [textField setAlignment:TextAlignment(alignment)];
289 END_BLOCK_OBJC_EXCEPTIONS;
292 void TextField::setWritingDirection(TextDirection direction)
294 BEGIN_BLOCK_OBJC_EXCEPTIONS;
295 [m_controller setBaseWritingDirection:(direction == RTL ? NSWritingDirectionRightToLeft : NSWritingDirectionLeftToRight)];
296 END_BLOCK_OBJC_EXCEPTIONS;
299 Widget::FocusPolicy TextField::focusPolicy() const
301 FocusPolicy policy = Widget::focusPolicy();
302 return policy == TabFocus ? StrongFocus : policy;
305 bool TextField::checksDescendantsForFocus() const
310 NSTextAlignment TextAlignment(HorizontalAlignment a)
314 return NSLeftTextAlignment;
316 return NSRightTextAlignment;
318 return NSCenterTextAlignment;
320 LOG_ERROR("unsupported alignment");
321 return NSLeftTextAlignment;
324 void TextField::setLiveSearch(bool liveSearch)
326 NSSearchField *searchField = (NSSearchField *)getView();
327 [[searchField cell] setSendsWholeSearchString:!liveSearch];
330 void TextField::setAutoSaveName(const String& name)
334 autosave = "com.apple.WebKit.searchField:" + name;
336 NSSearchField *searchField = (NSSearchField *)getView();
337 [searchField setRecentsAutosaveName:autosave];
340 void TextField::setMaxResults(int maxResults)
342 NSSearchField *searchField = (NSSearchField *)getView();
343 id searchCell = [searchField cell];
344 if (maxResults == -1) {
345 [searchCell setSearchButtonCell:nil];
346 [searchCell setSearchMenuTemplate:nil];
349 NSMenu* cellMenu = [searchCell searchMenuTemplate];
350 NSButtonCell* buttonCell = [searchCell searchButtonCell];
352 [searchCell resetSearchButtonCell];
353 if (cellMenu && !maxResults)
354 [searchCell setSearchMenuTemplate:nil];
355 else if (!cellMenu && maxResults)
356 [searchCell setSearchMenuTemplate:[[WebCoreViewFactory sharedFactory] cellMenuForSearchField]];
359 [searchCell setMaximumRecents:maxResults];
362 void TextField::setPlaceholderString(const String& placeholder)
364 NSTextField *textField = (NSTextField *)getView();
365 [[textField cell] setPlaceholderString:placeholder];
368 void TextField::addSearchResult()
370 NSSearchField *searchField = (NSSearchField *)getView();
371 [[searchField cell] _addStringToRecentSearches:[searchField stringValue]];