16ab241552188074bc8f12ff713a57c842ca6b97
[WebKit-https.git] / WebCore / rendering / RenderThemeMac.mm
1 /*
2  * This file is part of the theme implementation for form controls in WebCore.
3  *
4  * Copyright (C) 2005, 2006 Apple Computer, Inc.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public License
17  * along with this library; see the file COPYING.LIB.  If not, write to
18  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21
22 #import "config.h"
23 #import "RenderThemeMac.h"
24
25 #import "CSSStyleSelector.h"
26 #import "CSSValueKeywords.h"
27 #import "Document.h"
28 #import "Element.h"
29 #import "FoundationExtras.h"
30 #import "FrameView.h"
31 #import "GraphicsContext.h"
32 #import "HTMLInputElement.h"
33 #import "Image.h"
34 #import "LocalCurrentGraphicsContext.h"
35 #import "RenderSlider.h"
36 #import "RenderView.h"
37 #import "WebCoreSystemInterface.h"
38 #import <Cocoa/Cocoa.h>
39 #import <wtf/RetainPtr.h>
40 #import <math.h>
41
42 using std::min;
43
44 // The methods in this file are specific to the Mac OS X platform.
45
46 // FIXME: The platform-independent code in this class should be factored out and merged with RenderThemeSafari. 
47
48 @interface WebCoreRenderThemeNotificationObserver : NSObject
49 {
50     WebCore::RenderTheme *_theme;
51 }
52
53 - (id)initWithTheme:(WebCore::RenderTheme *)theme;
54 - (void)systemColorsDidChange:(NSNotification *)notification;
55
56 @end
57
58 @implementation WebCoreRenderThemeNotificationObserver
59
60 - (id)initWithTheme:(WebCore::RenderTheme *)theme
61 {
62     [super init];
63     _theme = theme;
64     
65     return self;
66 }
67
68 - (void)systemColorsDidChange:(NSNotification *)notification
69 {
70     ASSERT([[notification name] isEqualToString:NSSystemColorsDidChangeNotification]);
71     _theme->platformColorsDidChange();
72 }
73
74 @end
75
76 namespace WebCore {
77
78 enum {
79     topMargin,
80     rightMargin,
81     bottomMargin,
82     leftMargin
83 };
84
85 enum {
86     topPadding,
87     rightPadding,
88     bottomPadding,
89     leftPadding
90 };
91
92 RenderTheme* theme()
93 {
94     static RenderThemeMac* macTheme = new RenderThemeMac;
95     return macTheme;
96 }
97
98 RenderThemeMac::RenderThemeMac()
99     : m_resizeCornerImage(0)
100     , m_isSliderThumbHorizontalPressed(false)
101     , m_isSliderThumbVerticalPressed(false)
102     , m_notificationObserver(AdoptNS, [[WebCoreRenderThemeNotificationObserver alloc] initWithTheme:this])
103 {
104     [[NSNotificationCenter defaultCenter] addObserver:m_notificationObserver.get()
105                                                         selector:@selector(systemColorsDidChange:)
106                                                             name:NSSystemColorsDidChangeNotification
107                                                           object:nil];
108 }
109
110 RenderThemeMac::~RenderThemeMac()
111 {
112     [[NSNotificationCenter defaultCenter] removeObserver:m_notificationObserver.get()];
113     delete m_resizeCornerImage;
114 }
115
116 Color RenderThemeMac::platformActiveSelectionBackgroundColor() const
117 {
118     NSColor* color = [[NSColor selectedTextBackgroundColor] colorUsingColorSpaceName:NSDeviceRGBColorSpace];
119     return Color(static_cast<int>(255.0 * [color redComponent]), static_cast<int>(255.0 * [color greenComponent]), static_cast<int>(255.0 * [color blueComponent]));
120 }
121
122 Color RenderThemeMac::platformInactiveSelectionBackgroundColor() const
123 {
124     NSColor* color = [[NSColor secondarySelectedControlColor] colorUsingColorSpaceName:NSDeviceRGBColorSpace];
125     return Color(static_cast<int>(255.0 * [color redComponent]), static_cast<int>(255.0 * [color greenComponent]), static_cast<int>(255.0 * [color blueComponent]));
126 }
127
128 Color RenderThemeMac::activeListBoxSelectionBackgroundColor() const
129 {
130     NSColor* color = [[NSColor alternateSelectedControlColor] colorUsingColorSpaceName:NSDeviceRGBColorSpace];
131     return Color(static_cast<int>(255.0 * [color redComponent]), static_cast<int>(255.0 * [color greenComponent]), static_cast<int>(255.0 * [color blueComponent]));
132 }
133
134 void RenderThemeMac::systemFont(int propId, FontDescription& fontDescription) const
135 {
136     static FontDescription systemFont;
137     static FontDescription smallSystemFont;
138     static FontDescription menuFont;
139     static FontDescription labelFont;
140     static FontDescription miniControlFont;
141     static FontDescription smallControlFont;
142     static FontDescription controlFont;
143
144     FontDescription* cachedDesc;
145     NSFont* font = nil;
146     switch (propId) {
147         case CSS_VAL_SMALL_CAPTION:
148             cachedDesc = &smallSystemFont;
149             if (!smallSystemFont.isAbsoluteSize())
150                 font = [NSFont systemFontOfSize:[NSFont smallSystemFontSize]];
151             break;
152         case CSS_VAL_MENU:
153             cachedDesc = &menuFont;
154             if (!menuFont.isAbsoluteSize())
155                 font = [NSFont menuFontOfSize:[NSFont systemFontSize]];
156             break;
157         case CSS_VAL_STATUS_BAR:
158             cachedDesc = &labelFont;
159             if (!labelFont.isAbsoluteSize())
160                 font = [NSFont labelFontOfSize:[NSFont labelFontSize]];
161             break;
162         case CSS_VAL__WEBKIT_MINI_CONTROL:
163             cachedDesc = &miniControlFont;
164             if (!miniControlFont.isAbsoluteSize())
165                 font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSMiniControlSize]];
166             break;
167         case CSS_VAL__WEBKIT_SMALL_CONTROL:
168             cachedDesc = &smallControlFont;
169             if (!smallControlFont.isAbsoluteSize())
170                 font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]];
171             break;
172         case CSS_VAL__WEBKIT_CONTROL:
173             cachedDesc = &controlFont;
174             if (!controlFont.isAbsoluteSize())
175                 font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSRegularControlSize]];
176             break;
177         default:
178             cachedDesc = &systemFont;
179             if (!systemFont.isAbsoluteSize())
180                 font = [NSFont systemFontOfSize:[NSFont systemFontSize]];
181     }
182
183     if (font) {
184         cachedDesc->setIsAbsoluteSize(true);
185         cachedDesc->setGenericFamily(FontDescription::NoFamily);
186         cachedDesc->firstFamily().setFamily([font familyName]);
187         cachedDesc->setSpecifiedSize([font pointSize]);
188         NSFontTraitMask traits = [[NSFontManager sharedFontManager] traitsOfFont:font];
189         cachedDesc->setBold(traits & NSBoldFontMask);
190         cachedDesc->setItalic(traits & NSItalicFontMask);
191     }
192     fontDescription = *cachedDesc;
193 }
194
195 bool RenderThemeMac::isControlStyled(const RenderStyle* style, const BorderData& border,
196                                      const BackgroundLayer& background, const Color& backgroundColor) const
197 {
198     if (style->appearance() == TextFieldAppearance || style->appearance() == TextAreaAppearance || style->appearance() == ListboxAppearance)
199         return style->border() != border;
200     return RenderTheme::isControlStyled(style, border, background, backgroundColor);
201 }
202
203 void RenderThemeMac::paintResizeControl(GraphicsContext* c, const IntRect& r)
204 {
205     Image* resizeCornerImage = this->resizeCornerImage();
206     IntPoint imagePoint(r.right() - resizeCornerImage->width(), r.bottom() - resizeCornerImage->height());
207     c->drawImage(resizeCornerImage, imagePoint);
208 }
209
210 void RenderThemeMac::adjustRepaintRect(const RenderObject* o, IntRect& r)
211 {
212     switch (o->style()->appearance()) {
213         case CheckboxAppearance: {
214             // Since we query the prototype cell, we need to update its state to match.
215             setCheckboxCellState(o, r);
216
217             // We inflate the rect as needed to account for padding included in the cell to accommodate the checkbox
218             // shadow" and the check.  We don't consider this part of the bounds of the control in WebKit.
219             r = inflateRect(r, checkboxSizes()[[checkbox() controlSize]], checkboxMargins());
220             break;
221         }
222         case RadioAppearance: {
223             // Since we query the prototype cell, we need to update its state to match.
224             setRadioCellState(o, r);
225
226             // We inflate the rect as needed to account for padding included in the cell to accommodate the checkbox
227             // shadow" and the check.  We don't consider this part of the bounds of the control in WebKit.
228             r = inflateRect(r, radioSizes()[[radio() controlSize]], radioMargins());
229             break;
230         }
231         case PushButtonAppearance:
232         case ButtonAppearance: {
233             // Since we query the prototype cell, we need to update its state to match.
234             setButtonCellState(o, r);
235
236             // We inflate the rect as needed to account for padding included in the cell to accommodate the checkbox
237             // shadow" and the check.  We don't consider this part of the bounds of the control in WebKit.
238             if ([button() bezelStyle] == NSRoundedBezelStyle)
239                 r = inflateRect(r, buttonSizes()[[button() controlSize]], buttonMargins());
240             break;
241         }
242         case MenulistAppearance: {
243             setPopupButtonCellState(o, r);
244             r = inflateRect(r, popupButtonSizes()[[popupButton() controlSize]], popupButtonMargins());
245             break;
246         }
247         default:
248             break;
249     }
250 }
251
252 IntRect RenderThemeMac::inflateRect(const IntRect& r, const IntSize& size, const int* margins) const
253 {
254     // Only do the inflation if the available width/height are too small.  Otherwise try to
255     // fit the glow/check space into the available box's width/height.
256     int widthDelta = r.width() - (size.width() + margins[leftMargin] + margins[rightMargin]);
257     int heightDelta = r.height() - (size.height() + margins[topMargin] + margins[bottomMargin]);
258     IntRect result(r);
259     if (widthDelta < 0) {
260         result.setX(result.x() - margins[leftMargin]);
261         result.setWidth(result.width() - widthDelta);
262     }
263     if (heightDelta < 0) {
264         result.setY(result.y() - margins[topMargin]);
265         result.setHeight(result.height() - heightDelta);
266     }
267     return result;
268 }
269
270 void RenderThemeMac::updateCheckedState(NSCell* cell, const RenderObject* o)
271 {
272     bool oldIndeterminate = [cell state] == NSMixedState;
273     bool indeterminate = isIndeterminate(o);
274     bool checked = isChecked(o);
275
276     if (oldIndeterminate != indeterminate) {
277         [cell setState:indeterminate ? NSMixedState : (checked ? NSOnState : NSOffState)];
278         return;
279     }
280
281     bool oldChecked = [cell state] == NSOnState;
282     if (checked != oldChecked)
283         [cell setState:checked ? NSOnState : NSOffState];
284 }
285
286 void RenderThemeMac::updateEnabledState(NSCell* cell, const RenderObject* o)
287 {
288     bool oldEnabled = [cell isEnabled];
289     bool enabled = isEnabled(o);
290     if (enabled != oldEnabled)
291         [cell setEnabled:enabled];
292 }
293
294 void RenderThemeMac::updateFocusedState(NSCell* cell, const RenderObject* o)
295 {
296     bool oldFocused = [cell showsFirstResponder];
297     bool focused = isFocused(o) && o->style()->outlineStyleIsAuto();
298     if (focused != oldFocused)
299         [cell setShowsFirstResponder:focused];
300 }
301
302 void RenderThemeMac::updatePressedState(NSCell* cell, const RenderObject* o)
303 {
304     bool oldPressed = [cell isHighlighted];
305     bool pressed = (o->element() && o->element()->active());
306     if (pressed != oldPressed)
307         [cell setHighlighted:pressed];
308 }
309
310 short RenderThemeMac::baselinePosition(const RenderObject* o) const
311 {
312     if (o->style()->appearance() == CheckboxAppearance || o->style()->appearance() == RadioAppearance)
313         return o->marginTop() + o->height() - 2; // The baseline is 2px up from the bottom of the checkbox/radio in AppKit.
314     return RenderTheme::baselinePosition(o);
315 }
316
317 bool RenderThemeMac::controlSupportsTints(const RenderObject* o) const
318 {
319     // An alternate way to implement this would be to get the appropriate cell object
320     // and call the private _needRedrawOnWindowChangedKeyState method. An advantage of
321     // that would be that we would match AppKit behavior more closely, but a disadvantage
322     // would be that we would rely on an AppKit SPI method.
323
324     if (!isEnabled(o))
325         return false;
326
327     // Checkboxes only have tint when checked.
328     if (o->style()->appearance() == CheckboxAppearance)
329         return isChecked(o);
330
331     // For now assume other controls have tint if enabled.
332     return true;
333 }
334
335 NSControlSize RenderThemeMac::controlSizeForFont(RenderStyle* style) const
336 {
337     int fontSize = style->fontSize();
338     if (fontSize >= 16)
339         return NSRegularControlSize;
340     if (fontSize >= 11)
341         return NSSmallControlSize;
342     return NSMiniControlSize;
343 }
344
345 void RenderThemeMac::setControlSize(NSCell* cell, const IntSize* sizes, const IntSize& minSize)
346 {
347     NSControlSize size;
348     if (minSize.width() >= sizes[NSRegularControlSize].width() &&
349         minSize.height() >= sizes[NSRegularControlSize].height())
350         size = NSRegularControlSize;
351     else if (minSize.width() >= sizes[NSSmallControlSize].width() &&
352              minSize.height() >= sizes[NSSmallControlSize].height())
353         size = NSSmallControlSize;
354     else
355         size = NSMiniControlSize;
356     if (size != [cell controlSize]) // Only update if we have to, since AppKit does work even if the size is the same.
357         [cell setControlSize:size];
358 }
359
360 IntSize RenderThemeMac::sizeForFont(RenderStyle* style, const IntSize* sizes) const
361 {
362     return sizes[controlSizeForFont(style)];
363 }
364
365 IntSize RenderThemeMac::sizeForSystemFont(RenderStyle* style, const IntSize* sizes) const
366 {
367     return sizes[controlSizeForSystemFont(style)];
368 }
369
370 void RenderThemeMac::setSizeFromFont(RenderStyle* style, const IntSize* sizes) const
371 {
372     // FIXME: Check is flawed, since it doesn't take min-width/max-width into account.
373     IntSize size = sizeForFont(style, sizes);
374     if (style->width().isIntrinsicOrAuto() && size.width() > 0)
375         style->setWidth(Length(size.width(), Fixed));
376     if (style->height().isAuto() && size.height() > 0)
377         style->setHeight(Length(size.height(), Fixed));
378 }
379
380 void RenderThemeMac::setFontFromControlSize(CSSStyleSelector* selector, RenderStyle* style, NSControlSize controlSize) const
381 {
382     FontDescription fontDescription;
383     fontDescription.setIsAbsoluteSize(true);
384     fontDescription.setGenericFamily(FontDescription::SerifFamily);
385
386     NSFont* font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:controlSize]];
387     fontDescription.firstFamily().setFamily([font familyName]);
388     fontDescription.setComputedSize([font pointSize]);
389     fontDescription.setSpecifiedSize([font pointSize]);
390
391     // Reset line height
392     style->setLineHeight(RenderStyle::initialLineHeight());
393
394     if (style->setFontDescription(fontDescription))
395         style->font().update(0);
396 }
397
398 NSControlSize RenderThemeMac::controlSizeForSystemFont(RenderStyle* style) const
399 {
400     int fontSize = style->fontSize();
401     if (fontSize >= [NSFont systemFontSizeForControlSize:NSRegularControlSize])
402         return NSRegularControlSize;
403     if (fontSize >= [NSFont systemFontSizeForControlSize:NSSmallControlSize])
404         return NSSmallControlSize;
405     return NSMiniControlSize;
406 }
407
408 bool RenderThemeMac::paintCheckbox(RenderObject* o, const RenderObject::PaintInfo&, const IntRect& r)
409 {
410     // Determine the width and height needed for the control and prepare the cell for painting.
411     setCheckboxCellState(o, r);
412
413     // We inflate the rect as needed to account for padding included in the cell to accommodate the checkbox
414     // shadow" and the check.  We don't consider this part of the bounds of the control in WebKit.
415     NSButtonCell* checkbox = this->checkbox();
416     IntRect inflatedRect = inflateRect(r, checkboxSizes()[[checkbox controlSize]], checkboxMargins());
417     [checkbox drawWithFrame:NSRect(inflatedRect) inView:o->view()->frameView()->getDocumentView()];
418     [checkbox setControlView:nil];
419
420     return false;
421 }
422
423 const IntSize* RenderThemeMac::checkboxSizes() const
424 {
425     static const IntSize sizes[3] = { IntSize(14, 14), IntSize(12, 12), IntSize(10, 10) };
426     return sizes;
427 }
428
429 const int* RenderThemeMac::checkboxMargins() const
430 {
431     static const int margins[3][4] =
432     {
433         { 3, 4, 4, 2 },
434         { 4, 3, 3, 3 },
435         { 4, 3, 3, 3 },
436     };
437     return margins[[checkbox() controlSize]];
438 }
439
440 void RenderThemeMac::setCheckboxCellState(const RenderObject* o, const IntRect& r)
441 {
442     NSButtonCell* checkbox = this->checkbox();
443
444     // Set the control size based off the rectangle we're painting into.
445     setControlSize(checkbox, checkboxSizes(), r.size());
446
447     // Update the various states we respond to.
448     updateCheckedState(checkbox, o);
449     updateEnabledState(checkbox, o);
450     updatePressedState(checkbox, o);
451     updateFocusedState(checkbox, o);
452 }
453
454 void RenderThemeMac::setCheckboxSize(RenderStyle* style) const
455 {
456     // If the width and height are both specified, then we have nothing to do.
457     if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
458         return;
459
460     // Use the font size to determine the intrinsic width of the control.
461     setSizeFromFont(style, checkboxSizes());
462 }
463
464 bool RenderThemeMac::paintRadio(RenderObject* o, const RenderObject::PaintInfo&, const IntRect& r)
465 {
466     // Determine the width and height needed for the control and prepare the cell for painting.
467     setRadioCellState(o, r);
468
469     // We inflate the rect as needed to account for padding included in the cell to accommodate the checkbox
470     // shadow" and the check.  We don't consider this part of the bounds of the control in WebKit.
471     NSButtonCell* radio = this->radio();
472     IntRect inflatedRect = inflateRect(r, radioSizes()[[radio controlSize]], radioMargins());
473     [radio drawWithFrame:NSRect(inflatedRect) inView:o->view()->frameView()->getDocumentView()];
474     [radio setControlView:nil];
475
476     return false;
477 }
478
479 const IntSize* RenderThemeMac::radioSizes() const
480 {
481     static const IntSize sizes[3] = { IntSize(14, 15), IntSize(12, 13), IntSize(10, 10) };
482     return sizes;
483 }
484
485 const int* RenderThemeMac::radioMargins() const
486 {
487     static const int margins[3][4] =
488     {
489         { 2, 2, 4, 2 },
490         { 3, 2, 3, 2 },
491         { 1, 0, 2, 0 },
492     };
493     return margins[[radio() controlSize]];
494 }
495
496 void RenderThemeMac::setRadioCellState(const RenderObject* o, const IntRect& r)
497 {
498     NSButtonCell* radio = this->radio();
499
500     // Set the control size based off the rectangle we're painting into.
501     setControlSize(radio, radioSizes(), r.size());
502
503     // Update the various states we respond to.
504     updateCheckedState(radio, o);
505     updateEnabledState(radio, o);
506     updatePressedState(radio, o);
507     updateFocusedState(radio, o);
508 }
509
510
511 void RenderThemeMac::setRadioSize(RenderStyle* style) const
512 {
513     // If the width and height are both specified, then we have nothing to do.
514     if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
515         return;
516
517     // Use the font size to determine the intrinsic width of the control.
518     setSizeFromFont(style, radioSizes());
519 }
520
521 void RenderThemeMac::setButtonPaddingFromControlSize(RenderStyle* style, NSControlSize size) const
522 {
523     // Just use 8px.  AppKit wants to use 11px for mini buttons, but that padding is just too large
524     // for real-world Web sites (creating a huge necessary minimum width for buttons whose space is
525     // by definition constrained, since we select mini only for small cramped environments.
526     // This also guarantees the HTML4 <button> will match our rendering by default, since we're using a consistent
527     // padding.
528     const int padding = 8;
529     style->setPaddingLeft(Length(padding, Fixed));
530     style->setPaddingRight(Length(padding, Fixed));
531     style->setPaddingTop(Length(0, Fixed));
532     style->setPaddingBottom(Length(0, Fixed));
533 }
534
535 void RenderThemeMac::adjustButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
536 {
537     // There are three appearance constants for buttons.
538     // (1) Push-button is the constant for the default Aqua system button.  Push buttons will not scale vertically and will not allow
539     // custom fonts or colors.  <input>s use this constant.  This button will allow custom colors and font weights/variants but won't
540     // scale vertically.
541     // (2) square-button is the constant for the square button.  This button will allow custom fonts and colors and will scale vertically.
542     // (3) Button is the constant that means "pick the best button as appropriate."  <button>s use this constant.  This button will
543     // also scale vertically and allow custom fonts and colors.  It will attempt to use Aqua if possible and will make this determination
544     // solely on the rectangle of the control.
545
546     // Determine our control size based off our font.
547     NSControlSize controlSize = controlSizeForFont(style);
548
549     if (style->appearance() == PushButtonAppearance) {
550         // Ditch the border.
551         style->resetBorder();
552
553         // Height is locked to auto.
554         style->setHeight(Length(Auto));
555
556         // White-space is locked to pre
557         style->setWhiteSpace(PRE);
558
559         // Set the button's vertical size.
560         setButtonSize(style);
561
562         // Add in the padding that we'd like to use.
563         setButtonPaddingFromControlSize(style, controlSize);
564
565         // Our font is locked to the appropriate system font size for the control.  To clarify, we first use the CSS-specified font to figure out
566         // a reasonable control size, but once that control size is determined, we throw that font away and use the appropriate
567         // system font for the control size instead.
568         setFontFromControlSize(selector, style, controlSize);
569     } else {
570         // Set a min-height so that we can't get smaller than the mini button.
571         style->setMinHeight(Length(15, Fixed));
572
573         // Reset the top and bottom borders.
574         style->resetBorderTop();
575         style->resetBorderBottom();
576     }
577
578     style->setBoxShadow(0);
579 }
580
581 const IntSize* RenderThemeMac::buttonSizes() const
582 {
583     static const IntSize sizes[3] = { IntSize(0, 21), IntSize(0, 18), IntSize(0, 15) };
584     return sizes;
585 }
586
587 const int* RenderThemeMac::buttonMargins() const
588 {
589     static const int margins[3][4] =
590     {
591         { 4, 6, 7, 6 },
592         { 4, 5, 6, 5 },
593         { 0, 1, 1, 1 },
594     };
595     return margins[[button() controlSize]];
596 }
597
598 void RenderThemeMac::setButtonSize(RenderStyle* style) const
599 {
600     // If the width and height are both specified, then we have nothing to do.
601     if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
602         return;
603
604     // Use the font size to determine the intrinsic width of the control.
605     setSizeFromFont(style, buttonSizes());
606 }
607
608 void RenderThemeMac::setButtonCellState(const RenderObject* o, const IntRect& r)
609 {
610     NSButtonCell* button = this->button();
611
612     // Set the control size based off the rectangle we're painting into.
613     if (o->style()->appearance() == SquareButtonAppearance ||
614         r.height() > buttonSizes()[NSRegularControlSize].height()) {
615         // Use the square button
616         if ([button bezelStyle] != NSShadowlessSquareBezelStyle)
617             [button setBezelStyle:NSShadowlessSquareBezelStyle];
618     } else if ([button bezelStyle] != NSRoundedBezelStyle)
619         [button setBezelStyle:NSRoundedBezelStyle];
620
621     setControlSize(button, buttonSizes(), r.size());
622
623     // Update the various states we respond to.
624     updateCheckedState(button, o);
625     updateEnabledState(button, o);
626     updatePressedState(button, o);
627     updateFocusedState(button, o);
628 }
629
630 bool RenderThemeMac::paintButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
631 {
632     NSButtonCell* button = this->button();
633     LocalCurrentGraphicsContext localContext(paintInfo.context);
634
635     // Determine the width and height needed for the control and prepare the cell for painting.
636     setButtonCellState(o, r);
637
638     // We inflate the rect as needed to account for padding included in the cell to accommodate the button
639     // shadow.  We don't consider this part of the bounds of the control in WebKit.
640     IntSize size = buttonSizes()[[button controlSize]];
641     size.setWidth(r.width());
642     IntRect inflatedRect = r;
643     if ([button bezelStyle] == NSRoundedBezelStyle) {
644         // Center the button within the available space.
645         if (inflatedRect.height() > size.height()) {
646             inflatedRect.setY(inflatedRect.y() + (inflatedRect.height() - size.height()) / 2);
647             inflatedRect.setHeight(size.height());
648         }
649
650         // Now inflate it to account for the shadow.
651         inflatedRect = inflateRect(inflatedRect, size, buttonMargins());
652     }
653
654     [button drawWithFrame:NSRect(inflatedRect) inView:o->view()->frameView()->getDocumentView()];
655     [button setControlView:nil];
656
657     return false;
658 }
659
660 bool RenderThemeMac::paintTextField(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
661 {
662     LocalCurrentGraphicsContext localContext(paintInfo.context);
663     wkDrawBezeledTextFieldCell(r, isEnabled(o) && !isReadOnlyControl(o));
664     return false;
665 }
666
667 void RenderThemeMac::adjustTextFieldStyle(CSSStyleSelector*, RenderStyle*, Element*) const
668 {
669 }
670
671 bool RenderThemeMac::paintCapsLockIndicator(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
672 {
673     if (paintInfo.context->paintingDisabled())
674         return true;
675
676     wkDrawCapsLockIndicator(paintInfo.context->platformContext(), r);
677     
678     return false;
679 }
680
681 bool RenderThemeMac::paintTextArea(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
682 {
683     LocalCurrentGraphicsContext localContext(paintInfo.context);
684     wkDrawBezeledTextArea(r, isEnabled(o) && !isReadOnlyControl(o));
685     return false;
686 }
687
688 void RenderThemeMac::adjustTextAreaStyle(CSSStyleSelector*, RenderStyle*, Element*) const
689 {
690 }
691
692 const int* RenderThemeMac::popupButtonMargins() const
693 {
694     static const int margins[3][4] =
695     {
696         { 0, 3, 1, 3 },
697         { 0, 3, 2, 3 },
698         { 0, 1, 0, 1 }
699     };
700     return margins[[popupButton() controlSize]];
701 }
702
703 const IntSize* RenderThemeMac::popupButtonSizes() const
704 {
705     static const IntSize sizes[3] = { IntSize(0, 21), IntSize(0, 18), IntSize(0, 15) };
706     return sizes;
707 }
708
709 const int* RenderThemeMac::popupButtonPadding(NSControlSize size) const
710 {
711     static const int padding[3][4] =
712     {
713         { 2, 26, 3, 8 },
714         { 2, 23, 3, 8 },
715         { 2, 22, 3, 10 }
716     };
717     return padding[size];
718 }
719
720 bool RenderThemeMac::paintMenuList(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
721 {
722     setPopupButtonCellState(o, r);
723
724     NSPopUpButtonCell* popupButton = this->popupButton();
725
726     IntRect inflatedRect = r;
727     IntSize size = popupButtonSizes()[[popupButton controlSize]];
728     size.setWidth(r.width());
729
730     // Now inflate it to account for the shadow.
731     if (r.width() >= minimumMenuListSize(o->style()))
732         inflatedRect = inflateRect(inflatedRect, size, popupButtonMargins());
733
734 #ifndef BUILDING_ON_TIGER
735     // On Leopard, the cell will draw outside of the given rect, so we have to clip to the rect
736     paintInfo.context->save();
737     paintInfo.context->clip(inflatedRect);
738 #endif
739
740     [popupButton drawWithFrame:inflatedRect inView:o->view()->frameView()->getDocumentView()];
741     [popupButton setControlView:nil];
742
743 #ifndef BUILDING_ON_TIGER
744     paintInfo.context->restore();
745 #endif
746
747     return false;
748 }
749
750 const float baseFontSize = 11.0f;
751 const float baseArrowHeight = 4.0f;
752 const float baseArrowWidth = 5.0f;
753 const float baseSpaceBetweenArrows = 2.0f;
754 const int arrowPaddingLeft = 6;
755 const int arrowPaddingRight = 6;
756 const int paddingBeforeSeparator = 4;
757 const int baseBorderRadius = 5;
758 const int styledPopupPaddingLeft = 8;
759 const int styledPopupPaddingTop = 1;
760 const int styledPopupPaddingBottom = 2;
761
762 static void TopGradientInterpolate(void* info, const CGFloat* inData, CGFloat* outData)
763 {
764     static float dark[4] = { 1.0f, 1.0f, 1.0f, 0.4f };
765     static float light[4] = { 1.0f, 1.0f, 1.0f, 0.15f };
766     float a = inData[0];
767     int i = 0;
768     for (i = 0; i < 4; i++)
769         outData[i] = (1.0f - a) * dark[i] + a * light[i];
770 }
771
772 static void BottomGradientInterpolate(void* info, const CGFloat* inData, CGFloat* outData)
773 {
774     static float dark[4] = { 1.0f, 1.0f, 1.0f, 0.0f };
775     static float light[4] = { 1.0f, 1.0f, 1.0f, 0.3f };
776     float a = inData[0];
777     int i = 0;
778     for (i = 0; i < 4; i++)
779         outData[i] = (1.0f - a) * dark[i] + a * light[i];
780 }
781
782 static void MainGradientInterpolate(void* info, const CGFloat* inData, CGFloat* outData)
783 {
784     static float dark[4] = { 0.0f, 0.0f, 0.0f, 0.15f };
785     static float light[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
786     float a = inData[0];
787     int i = 0;
788     for (i = 0; i < 4; i++)
789         outData[i] = (1.0f - a) * dark[i] + a * light[i];
790 }
791
792 static void TrackGradientInterpolate(void* info, const CGFloat* inData, CGFloat* outData)
793 {
794     static float dark[4] = { 0.0f, 0.0f, 0.0f, 0.678f };
795     static float light[4] = { 0.0f, 0.0f, 0.0f, 0.13f };
796     float a = inData[0];
797     int i = 0;
798     for (i = 0; i < 4; i++)
799         outData[i] = (1.0f - a) * dark[i] + a * light[i];
800 }
801
802 void RenderThemeMac::paintMenuListButtonGradients(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
803 {
804     CGContextRef context = paintInfo.context->platformContext();
805
806     paintInfo.context->save();
807
808     int radius = o->style()->borderTopLeftRadius().width();
809
810     RetainPtr<CGColorSpaceRef> cspace(AdoptCF, CGColorSpaceCreateDeviceRGB());
811
812     FloatRect topGradient(r.x(), r.y(), r.width(), r.height() / 2.0f);
813     struct CGFunctionCallbacks topCallbacks = { 0, TopGradientInterpolate, NULL };
814     RetainPtr<CGFunctionRef> topFunction(AdoptCF, CGFunctionCreate(NULL, 1, NULL, 4, NULL, &topCallbacks));
815     RetainPtr<CGShadingRef> topShading(AdoptCF, CGShadingCreateAxial(cspace.get(), CGPointMake(topGradient.x(), topGradient.y()), CGPointMake(topGradient.x(), topGradient.bottom()), topFunction.get(), false, false));
816
817     FloatRect bottomGradient(r.x() + radius, r.y() + r.height() / 2.0f, r.width() - 2.0f * radius, r.height() / 2.0f);
818     struct CGFunctionCallbacks bottomCallbacks = { 0, BottomGradientInterpolate, NULL };
819     RetainPtr<CGFunctionRef> bottomFunction(AdoptCF, CGFunctionCreate(NULL, 1, NULL, 4, NULL, &bottomCallbacks));
820     RetainPtr<CGShadingRef> bottomShading(AdoptCF, CGShadingCreateAxial(cspace.get(), CGPointMake(bottomGradient.x(),  bottomGradient.y()), CGPointMake(bottomGradient.x(), bottomGradient.bottom()), bottomFunction.get(), false, false));
821
822     struct CGFunctionCallbacks mainCallbacks = { 0, MainGradientInterpolate, NULL };
823     RetainPtr<CGFunctionRef> mainFunction(AdoptCF, CGFunctionCreate(NULL, 1, NULL, 4, NULL, &mainCallbacks));
824     RetainPtr<CGShadingRef> mainShading(AdoptCF, CGShadingCreateAxial(cspace.get(), CGPointMake(r.x(),  r.y()), CGPointMake(r.x(), r.bottom()), mainFunction.get(), false, false));
825
826     RetainPtr<CGShadingRef> leftShading(AdoptCF, CGShadingCreateAxial(cspace.get(), CGPointMake(r.x(),  r.y()), CGPointMake(r.x() + radius, r.y()), mainFunction.get(), false, false));
827
828     RetainPtr<CGShadingRef> rightShading(AdoptCF, CGShadingCreateAxial(cspace.get(), CGPointMake(r.right(),  r.y()), CGPointMake(r.right() - radius, r.y()), mainFunction.get(), false, false));
829     paintInfo.context->save();
830     CGContextClipToRect(context, r);
831     paintInfo.context->addRoundedRectClip(r,
832         o->style()->borderTopLeftRadius(), o->style()->borderTopRightRadius(),
833         o->style()->borderBottomLeftRadius(), o->style()->borderBottomRightRadius());
834     CGContextDrawShading(context, mainShading.get());
835     paintInfo.context->restore();
836
837     paintInfo.context->save();
838     CGContextClipToRect(context, topGradient);
839     paintInfo.context->addRoundedRectClip(enclosingIntRect(topGradient),
840         o->style()->borderTopLeftRadius(), o->style()->borderTopRightRadius(),
841         IntSize(), IntSize());
842     CGContextDrawShading(context, topShading.get());
843     paintInfo.context->restore();
844
845     paintInfo.context->save();
846     CGContextClipToRect(context, bottomGradient);
847     paintInfo.context->addRoundedRectClip(enclosingIntRect(bottomGradient),
848         IntSize(), IntSize(),
849         o->style()->borderBottomLeftRadius(), o->style()->borderBottomRightRadius());
850     CGContextDrawShading(context, bottomShading.get());
851     paintInfo.context->restore();
852
853     paintInfo.context->save();
854     CGContextClipToRect(context, r);
855     paintInfo.context->addRoundedRectClip(r,
856         o->style()->borderTopLeftRadius(), o->style()->borderTopRightRadius(),
857         o->style()->borderBottomLeftRadius(), o->style()->borderBottomRightRadius());
858     CGContextDrawShading(context, leftShading.get());
859     CGContextDrawShading(context, rightShading.get());
860     paintInfo.context->restore();
861
862     paintInfo.context->restore();
863 }
864
865 bool RenderThemeMac::paintMenuListButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
866 {
867     paintInfo.context->save();
868
869     IntRect bounds = IntRect(r.x() + o->style()->borderLeftWidth(),
870                              r.y() + o->style()->borderTopWidth(),
871                              r.width() - o->style()->borderLeftWidth() - o->style()->borderRightWidth(),
872                              r.height() - o->style()->borderTopWidth() - o->style()->borderBottomWidth());
873     // Draw the gradients to give the styled popup menu a button appearance
874     paintMenuListButtonGradients(o, paintInfo, bounds);
875
876     // Since we actually know the size of the control here, we restrict the font scale to make sure the arrows will fit vertically in the bounds
877     float fontScale = min(o->style()->fontSize() / baseFontSize, bounds.height() / (baseArrowHeight * 2 + baseSpaceBetweenArrows));
878     float centerY = bounds.y() + bounds.height() / 2.0f;
879     float arrowHeight = baseArrowHeight * fontScale;
880     float arrowWidth = baseArrowWidth * fontScale;
881     float leftEdge = bounds.right() - arrowPaddingRight - arrowWidth;
882     float spaceBetweenArrows = baseSpaceBetweenArrows * fontScale;
883
884     if (bounds.width() < arrowWidth + arrowPaddingLeft)
885         return false;
886     
887     paintInfo.context->setFillColor(o->style()->color());
888     paintInfo.context->setStrokeStyle(NoStroke);
889
890     FloatPoint arrow1[3];
891     arrow1[0] = FloatPoint(leftEdge, centerY - spaceBetweenArrows / 2.0f);
892     arrow1[1] = FloatPoint(leftEdge + arrowWidth, centerY - spaceBetweenArrows / 2.0f);
893     arrow1[2] = FloatPoint(leftEdge + arrowWidth / 2.0f, centerY - spaceBetweenArrows / 2.0f - arrowHeight);
894
895     // Draw the top arrow
896     paintInfo.context->drawConvexPolygon(3, arrow1, true);
897
898     FloatPoint arrow2[3];
899     arrow2[0] = FloatPoint(leftEdge, centerY + spaceBetweenArrows / 2.0f);
900     arrow2[1] = FloatPoint(leftEdge + arrowWidth, centerY + spaceBetweenArrows / 2.0f);
901     arrow2[2] = FloatPoint(leftEdge + arrowWidth / 2.0f, centerY + spaceBetweenArrows / 2.0f + arrowHeight);
902
903     // Draw the bottom arrow
904     paintInfo.context->drawConvexPolygon(3, arrow2, true);
905
906     Color leftSeparatorColor(0, 0, 0, 40);
907     Color rightSeparatorColor(255, 255, 255, 40);
908
909     // FIXME: Should the separator thickness and space be scaled up by fontScale?
910     int separatorSpace = 2;
911     int leftEdgeOfSeparator = static_cast<int>(leftEdge - arrowPaddingLeft); // FIXME: Round?
912
913     // Draw the separator to the left of the arrows
914     paintInfo.context->setStrokeThickness(1.0f);
915     paintInfo.context->setStrokeStyle(SolidStroke);
916     paintInfo.context->setStrokeColor(leftSeparatorColor);
917     paintInfo.context->drawLine(IntPoint(leftEdgeOfSeparator, bounds.y()),
918                                 IntPoint(leftEdgeOfSeparator, bounds.bottom()));
919
920     paintInfo.context->setStrokeColor(rightSeparatorColor);
921     paintInfo.context->drawLine(IntPoint(leftEdgeOfSeparator + separatorSpace, bounds.y()),
922                                 IntPoint(leftEdgeOfSeparator + separatorSpace, bounds.bottom()));
923
924     paintInfo.context->restore();
925     return false;
926 }
927
928 void RenderThemeMac::adjustMenuListStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
929 {
930     NSControlSize controlSize = controlSizeForFont(style);
931
932     style->resetBorder();
933     style->resetPadding();
934     
935     // Height is locked to auto.
936     style->setHeight(Length(Auto));
937
938     // White-space is locked to pre
939     style->setWhiteSpace(PRE);
940
941     // Set the foreground color to black or gray when we have the aqua look.
942     // Cast to RGB32 is to work around a compiler bug.
943     style->setColor(e->isEnabled() ? static_cast<RGBA32>(Color::black) : Color::darkGray);
944
945     // Set the button's vertical size.
946     setButtonSize(style);
947
948     // Our font is locked to the appropriate system font size for the control.  To clarify, we first use the CSS-specified font to figure out
949     // a reasonable control size, but once that control size is determined, we throw that font away and use the appropriate
950     // system font for the control size instead.
951     setFontFromControlSize(selector, style, controlSize);
952
953     style->setBoxShadow(0);
954 }
955
956 int RenderThemeMac::popupInternalPaddingLeft(RenderStyle* style) const
957 {
958     if (style->appearance() == MenulistAppearance)
959         return popupButtonPadding(controlSizeForFont(style))[leftPadding];
960     if (style->appearance() == MenulistButtonAppearance)
961         return styledPopupPaddingLeft;
962     return 0;
963 }
964
965 int RenderThemeMac::popupInternalPaddingRight(RenderStyle* style) const
966 {
967     if (style->appearance() == MenulistAppearance)
968         return popupButtonPadding(controlSizeForFont(style))[rightPadding];
969     if (style->appearance() == MenulistButtonAppearance) {
970         float fontScale = style->fontSize() / baseFontSize;
971         float arrowWidth = baseArrowWidth * fontScale;
972         return static_cast<int>(ceilf(arrowWidth + arrowPaddingLeft + arrowPaddingRight + paddingBeforeSeparator));
973     }
974     return 0;
975 }
976
977 int RenderThemeMac::popupInternalPaddingTop(RenderStyle* style) const
978 {
979     if (style->appearance() == MenulistAppearance)
980         return popupButtonPadding(controlSizeForFont(style))[topPadding];
981     if (style->appearance() == MenulistButtonAppearance)
982         return styledPopupPaddingTop;
983     return 0;
984 }
985
986 int RenderThemeMac::popupInternalPaddingBottom(RenderStyle* style) const
987 {
988     if (style->appearance() == MenulistAppearance)
989         return popupButtonPadding(controlSizeForFont(style))[bottomPadding];
990     if (style->appearance() == MenulistButtonAppearance)
991         return styledPopupPaddingBottom;
992     return 0;
993 }
994
995 void RenderThemeMac::adjustMenuListButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
996 {
997     float fontScale = style->fontSize() / baseFontSize;
998
999     style->resetPadding();
1000     style->setBorderRadius(IntSize(int(baseBorderRadius + fontScale - 1), int(baseBorderRadius + fontScale - 1))); // FIXME: Round up?
1001
1002     const int minHeight = 15;
1003     style->setMinHeight(Length(minHeight, Fixed));
1004     
1005     style->setLineHeight(RenderStyle::initialLineHeight());
1006 }
1007
1008 void RenderThemeMac::setPopupButtonCellState(const RenderObject* o, const IntRect& r)
1009 {
1010     NSPopUpButtonCell* popupButton = this->popupButton();
1011
1012     // Set the control size based off the rectangle we're painting into.
1013     setControlSize(popupButton, popupButtonSizes(), r.size());
1014
1015     // Update the various states we respond to.
1016     updateCheckedState(popupButton, o);
1017     updateEnabledState(popupButton, o);
1018     updatePressedState(popupButton, o);
1019     updateFocusedState(popupButton, o);
1020 }
1021
1022 const IntSize* RenderThemeMac::menuListSizes() const
1023 {
1024     static const IntSize sizes[3] = { IntSize(9, 0), IntSize(5, 0), IntSize(0, 0) };
1025     return sizes;
1026 }
1027
1028 int RenderThemeMac::minimumMenuListSize(RenderStyle* style) const
1029 {
1030     return sizeForSystemFont(style, menuListSizes()).width();
1031 }
1032
1033 const int trackWidth = 5;
1034 const int trackRadius = 2;
1035
1036 void RenderThemeMac::adjustSliderTrackStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
1037 {
1038     style->setBoxShadow(0);
1039 }
1040
1041 bool RenderThemeMac::paintSliderTrack(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
1042 {
1043     IntRect bounds = r;
1044
1045     if (o->style()->appearance() ==  SliderHorizontalAppearance) {
1046         bounds.setHeight(trackWidth);
1047         bounds.setY(r.y() + r.height() / 2 - trackWidth / 2);
1048     } else if (o->style()->appearance() == SliderVerticalAppearance) {
1049         bounds.setWidth(trackWidth);
1050         bounds.setX(r.x() + r.width() / 2 - trackWidth / 2);
1051     }
1052
1053     LocalCurrentGraphicsContext localContext(paintInfo.context);
1054     CGContextRef context = paintInfo.context->platformContext();
1055     RetainPtr<CGColorSpaceRef> cspace(AdoptCF, CGColorSpaceCreateDeviceRGB());
1056
1057     paintInfo.context->save();
1058     CGContextClipToRect(context, bounds);
1059
1060     struct CGFunctionCallbacks mainCallbacks = { 0, TrackGradientInterpolate, NULL };
1061     RetainPtr<CGFunctionRef> mainFunction(AdoptCF, CGFunctionCreate(NULL, 1, NULL, 4, NULL, &mainCallbacks));
1062     RetainPtr<CGShadingRef> mainShading;
1063     if (o->style()->appearance() == SliderVerticalAppearance)
1064         mainShading.adoptCF(CGShadingCreateAxial(cspace.get(), CGPointMake(bounds.x(),  bounds.bottom()), CGPointMake(bounds.right(), bounds.bottom()), mainFunction.get(), false, false));
1065     else
1066         mainShading.adoptCF(CGShadingCreateAxial(cspace.get(), CGPointMake(bounds.x(),  bounds.y()), CGPointMake(bounds.x(), bounds.bottom()), mainFunction.get(), false, false));
1067
1068     IntSize radius(trackRadius, trackRadius);
1069     paintInfo.context->addRoundedRectClip(bounds,
1070         radius, radius,
1071         radius, radius);
1072     CGContextDrawShading(context, mainShading.get());
1073     paintInfo.context->restore();
1074     
1075     return false;
1076 }
1077
1078 void RenderThemeMac::adjustSliderThumbStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
1079 {
1080     style->setBoxShadow(0);
1081 }
1082
1083 const float verticalSliderHeightPadding = 0.1f;
1084
1085 bool RenderThemeMac::paintSliderThumb(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
1086 {
1087     ASSERT(o->parent()->isSlider());
1088
1089     NSSliderCell* sliderThumbCell = o->style()->appearance() == SliderThumbVerticalAppearance
1090         ? sliderThumbVertical()
1091         : sliderThumbHorizontal();
1092
1093     LocalCurrentGraphicsContext localContext(paintInfo.context);
1094
1095     // Update the various states we respond to.
1096     updateEnabledState(sliderThumbCell, o->parent());
1097     updateFocusedState(sliderThumbCell, o->parent());
1098
1099     // Update the pressed state using the NSCell tracking methods, since that's how NSSliderCell keeps track of it.
1100     bool oldPressed;
1101     if (o->style()->appearance() == SliderThumbVerticalAppearance)
1102         oldPressed = m_isSliderThumbVerticalPressed;
1103     else
1104         oldPressed = m_isSliderThumbHorizontalPressed;
1105
1106     bool pressed = static_cast<RenderSlider*>(o->parent())->inDragMode();
1107
1108     if (o->style()->appearance() == SliderThumbVerticalAppearance)
1109         m_isSliderThumbVerticalPressed = pressed;
1110     else
1111         m_isSliderThumbHorizontalPressed = pressed;
1112
1113     if (pressed != oldPressed) {
1114         if (pressed)
1115             [sliderThumbCell startTrackingAt:NSPoint() inView:nil];
1116         else
1117             [sliderThumbCell stopTracking:NSPoint() at:NSPoint() inView:nil mouseIsUp:YES];
1118     }
1119
1120     FloatRect bounds = r;
1121     // Make the height of the vertical slider slightly larger so NSSliderCell will draw a vertical slider.
1122     if (o->style()->appearance() == SliderThumbVerticalAppearance)
1123         bounds.setHeight(bounds.height() + verticalSliderHeightPadding);
1124
1125     [sliderThumbCell drawWithFrame:NSRect(bounds) inView:o->view()->frameView()->getDocumentView()];
1126     [sliderThumbCell setControlView:nil];
1127
1128     return false;
1129 }
1130
1131 const int sliderThumbWidth = 15;
1132 const int sliderThumbHeight = 15;
1133
1134 void RenderThemeMac::adjustSliderThumbSize(RenderObject* o) const
1135 {
1136     if (o->style()->appearance() == SliderThumbHorizontalAppearance || o->style()->appearance() == SliderThumbVerticalAppearance) {
1137         o->style()->setWidth(Length(sliderThumbWidth, Fixed));
1138         o->style()->setHeight(Length(sliderThumbHeight, Fixed));
1139     }
1140 }
1141
1142 bool RenderThemeMac::paintSearchField(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
1143 {
1144     NSSearchFieldCell* search = this->search();
1145     LocalCurrentGraphicsContext localContext(paintInfo.context);
1146
1147     setSearchCellState(o, r);
1148
1149     // Set the search button to nil before drawing.  Then reset it so we can draw it later.
1150     [search setSearchButtonCell:nil];
1151
1152     [search drawWithFrame:NSRect(r) inView:o->view()->frameView()->getDocumentView()];
1153 #ifdef BUILDING_ON_TIGER
1154     if ([search showsFirstResponder])
1155         wkDrawTextFieldCellFocusRing(search, NSRect(r));
1156 #endif
1157
1158     [search setControlView:nil];
1159     [search resetSearchButtonCell];
1160
1161     return false;
1162 }
1163
1164 void RenderThemeMac::setSearchCellState(RenderObject* o, const IntRect& r)
1165 {
1166     NSSearchFieldCell* search = this->search();
1167
1168     [search setControlSize:controlSizeForFont(o->style())];
1169
1170     // Update the various states we respond to.
1171     updateEnabledState(search, o);
1172     updateFocusedState(search, o);
1173 }
1174
1175 const IntSize* RenderThemeMac::searchFieldSizes() const
1176 {
1177     static const IntSize sizes[3] = { IntSize(0, 22), IntSize(0, 19), IntSize(0, 17) };
1178     return sizes;
1179 }
1180
1181 void RenderThemeMac::setSearchFieldSize(RenderStyle* style) const
1182 {
1183     // If the width and height are both specified, then we have nothing to do.
1184     if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
1185         return;
1186     
1187     // Use the font size to determine the intrinsic width of the control.
1188     setSizeFromFont(style, searchFieldSizes());
1189 }
1190
1191 void RenderThemeMac::adjustSearchFieldStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
1192 {
1193     // Override border.
1194     style->resetBorder();
1195     const short borderWidth = 2;
1196     style->setBorderLeftWidth(borderWidth);
1197     style->setBorderLeftStyle(INSET);
1198     style->setBorderRightWidth(borderWidth);
1199     style->setBorderRightStyle(INSET);
1200     style->setBorderBottomWidth(borderWidth);
1201     style->setBorderBottomStyle(INSET);
1202     style->setBorderTopWidth(borderWidth);
1203     style->setBorderTopStyle(INSET);    
1204     
1205     // Override height.
1206     style->setHeight(Length(Auto));
1207     setSearchFieldSize(style);
1208     
1209     // Override padding size to match AppKit text positioning.
1210     const int padding = 1;
1211     style->setPaddingLeft(Length(padding, Fixed));
1212     style->setPaddingRight(Length(padding, Fixed));
1213     style->setPaddingTop(Length(padding, Fixed));
1214     style->setPaddingBottom(Length(padding, Fixed));
1215     
1216     NSControlSize controlSize = controlSizeForFont(style);
1217     setFontFromControlSize(selector, style, controlSize);
1218
1219     style->setBoxShadow(0);
1220 }
1221
1222 bool RenderThemeMac::paintSearchFieldCancelButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
1223 {
1224     Node* input = o->node()->shadowAncestorNode();
1225     setSearchCellState(input->renderer(), r);
1226
1227     NSSearchFieldCell* search = this->search();
1228
1229     updatePressedState([search cancelButtonCell], o);
1230
1231     NSRect bounds = [search cancelButtonRectForBounds:NSRect(input->renderer()->absoluteBoundingBoxRect())];
1232     [[search cancelButtonCell] drawWithFrame:bounds inView:o->view()->frameView()->getDocumentView()];
1233     [[search cancelButtonCell] setControlView:nil];
1234
1235     return false;
1236 }
1237
1238 const IntSize* RenderThemeMac::cancelButtonSizes() const
1239 {
1240     static const IntSize sizes[3] = { IntSize(16, 13), IntSize(13, 11), IntSize(13, 9) };
1241     return sizes;
1242 }
1243
1244 void RenderThemeMac::adjustSearchFieldCancelButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
1245 {
1246     IntSize size = sizeForSystemFont(style, cancelButtonSizes());
1247     style->setWidth(Length(size.width(), Fixed));
1248     style->setHeight(Length(size.height(), Fixed));
1249     style->setBoxShadow(0);
1250 }
1251
1252 const IntSize* RenderThemeMac::resultsButtonSizes() const
1253 {
1254     static const IntSize sizes[3] = { IntSize(19, 13), IntSize(17, 11), IntSize(17, 9) };
1255     return sizes;
1256 }
1257
1258 const int emptyResultsOffset = 9;
1259 void RenderThemeMac::adjustSearchFieldDecorationStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
1260 {
1261     IntSize size = sizeForSystemFont(style, resultsButtonSizes());
1262     style->setWidth(Length(size.width() - emptyResultsOffset, Fixed));
1263     style->setHeight(Length(size.height(), Fixed));
1264     style->setBoxShadow(0);
1265 }
1266
1267 bool RenderThemeMac::paintSearchFieldDecoration(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
1268 {
1269     return false;
1270 }
1271
1272 void RenderThemeMac::adjustSearchFieldResultsDecorationStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
1273 {
1274     IntSize size = sizeForSystemFont(style, resultsButtonSizes());
1275     style->setWidth(Length(size.width(), Fixed));
1276     style->setHeight(Length(size.height(), Fixed));
1277     style->setBoxShadow(0);
1278 }
1279
1280 bool RenderThemeMac::paintSearchFieldResultsDecoration(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
1281 {
1282     Node* input = o->node()->shadowAncestorNode();
1283     setSearchCellState(input->renderer(), r);
1284
1285     NSSearchFieldCell* search = this->search();
1286
1287     if ([search searchMenuTemplate] != nil)
1288         [search setSearchMenuTemplate:nil];
1289
1290     NSRect bounds = [search searchButtonRectForBounds:NSRect(input->renderer()->absoluteBoundingBoxRect())];
1291     [[search searchButtonCell] drawWithFrame:bounds inView:o->view()->frameView()->getDocumentView()];
1292     [[search searchButtonCell] setControlView:nil];
1293     return false;
1294 }
1295
1296 const int resultsArrowWidth = 5;
1297 void RenderThemeMac::adjustSearchFieldResultsButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
1298 {
1299     IntSize size = sizeForSystemFont(style, resultsButtonSizes());
1300     style->setWidth(Length(size.width() + resultsArrowWidth, Fixed));
1301     style->setHeight(Length(size.height(), Fixed));
1302     style->setBoxShadow(0);
1303 }
1304
1305 bool RenderThemeMac::paintSearchFieldResultsButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
1306 {
1307     Node* input = o->node()->shadowAncestorNode();
1308     setSearchCellState(input->renderer(), r);
1309
1310     NSSearchFieldCell* search = this->search();
1311
1312     if (![search searchMenuTemplate])
1313         [search setSearchMenuTemplate:searchMenuTemplate()];
1314
1315     NSRect bounds = [search searchButtonRectForBounds:NSRect(input->renderer()->absoluteBoundingBoxRect())];
1316     [[search searchButtonCell] drawWithFrame:bounds inView:o->view()->frameView()->getDocumentView()];
1317     [[search searchButtonCell] setControlView:nil];
1318     return false;
1319 }
1320
1321 NSButtonCell* RenderThemeMac::checkbox() const
1322 {
1323     if (!m_checkbox) {
1324         m_checkbox.adoptNS([[NSButtonCell alloc] init]);
1325         [m_checkbox.get() setButtonType:NSSwitchButton];
1326         [m_checkbox.get() setTitle:nil];
1327         [m_checkbox.get() setAllowsMixedState:YES];
1328         [m_checkbox.get() setFocusRingType:NSFocusRingTypeExterior];
1329     }
1330     
1331     return m_checkbox.get();
1332 }
1333
1334 NSButtonCell* RenderThemeMac::radio() const
1335 {
1336     if (!m_radio) {
1337         m_radio.adoptNS([[NSButtonCell alloc] init]);
1338         [m_radio.get() setButtonType:NSRadioButton];
1339         [m_radio.get() setTitle:nil];
1340         [m_radio.get() setFocusRingType:NSFocusRingTypeExterior];
1341     }
1342     
1343     return m_radio.get();
1344 }
1345
1346 NSButtonCell* RenderThemeMac::button() const
1347 {
1348     if (!m_button) {
1349         m_button.adoptNS([[NSButtonCell alloc] init]);
1350         [m_button.get() setTitle:nil];
1351         [m_button.get() setButtonType:NSMomentaryPushInButton];
1352     }
1353     
1354     return m_button.get();
1355 }
1356
1357 NSPopUpButtonCell* RenderThemeMac::popupButton() const
1358 {
1359     if (!m_popupButton) {
1360         m_popupButton.adoptNS([[NSPopUpButtonCell alloc] initTextCell:@"" pullsDown:NO]);
1361         [m_popupButton.get() setUsesItemFromMenu:NO];
1362         [m_popupButton.get() setFocusRingType:NSFocusRingTypeExterior];
1363     }
1364     
1365     return m_popupButton.get();
1366 }
1367
1368 NSSearchFieldCell* RenderThemeMac::search() const
1369 {
1370     if (!m_search) {
1371         m_search.adoptNS([[NSSearchFieldCell alloc] initTextCell:@""]);
1372         [m_search.get() setBezelStyle:NSTextFieldRoundedBezel];
1373         [m_search.get() setBezeled:YES];
1374         [m_search.get() setEditable:YES];
1375         [m_search.get() setFocusRingType:NSFocusRingTypeExterior];
1376     }
1377
1378     return m_search.get();
1379 }
1380
1381 NSMenu* RenderThemeMac::searchMenuTemplate() const
1382 {
1383     if (!m_searchMenuTemplate)
1384         m_searchMenuTemplate.adoptNS([[NSMenu alloc] initWithTitle:@""]);
1385
1386     return m_searchMenuTemplate.get();
1387 }
1388
1389 NSSliderCell* RenderThemeMac::sliderThumbHorizontal() const
1390 {
1391     if (!m_sliderThumbHorizontal) {
1392         m_sliderThumbHorizontal.adoptNS([[NSSliderCell alloc] init]);
1393         [m_sliderThumbHorizontal.get() setTitle:nil];
1394         [m_sliderThumbHorizontal.get() setSliderType:NSLinearSlider];
1395         [m_sliderThumbHorizontal.get() setControlSize:NSSmallControlSize];
1396         [m_sliderThumbHorizontal.get() setFocusRingType:NSFocusRingTypeExterior];
1397     }
1398     
1399     return m_sliderThumbHorizontal.get();
1400 }
1401
1402 NSSliderCell* RenderThemeMac::sliderThumbVertical() const
1403 {
1404     if (!m_sliderThumbVertical) {
1405         m_sliderThumbVertical.adoptNS([[NSSliderCell alloc] init]);
1406         [m_sliderThumbVertical.get() setTitle:nil];
1407         [m_sliderThumbVertical.get() setSliderType:NSLinearSlider];
1408         [m_sliderThumbVertical.get() setControlSize:NSSmallControlSize];
1409         [m_sliderThumbVertical.get() setFocusRingType:NSFocusRingTypeExterior];
1410     }
1411     
1412     return m_sliderThumbVertical.get();
1413 }
1414
1415 Image* RenderThemeMac::resizeCornerImage() const
1416 {
1417     if (!m_resizeCornerImage)
1418         m_resizeCornerImage = Image::loadPlatformResource("textAreaResizeCorner");
1419     return m_resizeCornerImage;
1420 }
1421
1422 } // namespace WebCore