webengine/osswebengine/WebCore/rendering/RenderThemeMac.mm
changeset 0 dd21522fd290
equal deleted inserted replaced
-1:000000000000 0:dd21522fd290
       
     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();
       
   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::paintTextArea(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
       
   672 {
       
   673     LocalCurrentGraphicsContext localContext(paintInfo.context);
       
   674     wkDrawBezeledTextArea(r, isEnabled(o) && !isReadOnlyControl(o));
       
   675     return false;
       
   676 }
       
   677 
       
   678 void RenderThemeMac::adjustTextAreaStyle(CSSStyleSelector*, RenderStyle*, Element*) const
       
   679 {
       
   680 }
       
   681 
       
   682 const int* RenderThemeMac::popupButtonMargins() const
       
   683 {
       
   684     static const int margins[3][4] =
       
   685     {
       
   686         { 0, 3, 1, 3 },
       
   687         { 0, 3, 2, 3 },
       
   688         { 0, 1, 0, 1 }
       
   689     };
       
   690     return margins[[popupButton() controlSize]];
       
   691 }
       
   692 
       
   693 const IntSize* RenderThemeMac::popupButtonSizes() const
       
   694 {
       
   695     static const IntSize sizes[3] = { IntSize(0, 21), IntSize(0, 18), IntSize(0, 15) };
       
   696     return sizes;
       
   697 }
       
   698 
       
   699 const int* RenderThemeMac::popupButtonPadding(NSControlSize size) const
       
   700 {
       
   701     static const int padding[3][4] =
       
   702     {
       
   703         { 2, 26, 3, 8 },
       
   704         { 2, 23, 3, 8 },
       
   705         { 2, 22, 3, 10 }
       
   706     };
       
   707     return padding[size];
       
   708 }
       
   709 
       
   710 bool RenderThemeMac::paintMenuList(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
       
   711 {
       
   712     setPopupButtonCellState(o, r);
       
   713 
       
   714     NSPopUpButtonCell* popupButton = this->popupButton();
       
   715 
       
   716     IntRect inflatedRect = r;
       
   717     IntSize size = popupButtonSizes()[[popupButton controlSize]];
       
   718     size.setWidth(r.width());
       
   719 
       
   720     // Now inflate it to account for the shadow.
       
   721     if (r.width() >= minimumMenuListSize(o->style()))
       
   722         inflatedRect = inflateRect(inflatedRect, size, popupButtonMargins());
       
   723 
       
   724 #ifndef BUILDING_ON_TIGER
       
   725     // On Leopard, the cell will draw outside of the given rect, so we have to clip to the rect
       
   726     paintInfo.context->save();
       
   727     paintInfo.context->clip(inflatedRect);
       
   728 #endif
       
   729 
       
   730     [popupButton drawWithFrame:inflatedRect inView:o->view()->frameView()->getDocumentView()];
       
   731     [popupButton setControlView:nil];
       
   732 
       
   733 #ifndef BUILDING_ON_TIGER
       
   734     paintInfo.context->restore();
       
   735 #endif
       
   736 
       
   737     return false;
       
   738 }
       
   739 
       
   740 const float baseFontSize = 11.0f;
       
   741 const float baseArrowHeight = 4.0f;
       
   742 const float baseArrowWidth = 5.0f;
       
   743 const float baseSpaceBetweenArrows = 2.0f;
       
   744 const int arrowPaddingLeft = 6;
       
   745 const int arrowPaddingRight = 6;
       
   746 const int paddingBeforeSeparator = 4;
       
   747 const int baseBorderRadius = 5;
       
   748 const int styledPopupPaddingLeft = 8;
       
   749 const int styledPopupPaddingTop = 1;
       
   750 const int styledPopupPaddingBottom = 2;
       
   751 
       
   752 static void TopGradientInterpolate(void* info, const CGFloat* inData, CGFloat* outData)
       
   753 {
       
   754     static float dark[4] = { 1.0f, 1.0f, 1.0f, 0.4f };
       
   755     static float light[4] = { 1.0f, 1.0f, 1.0f, 0.15f };
       
   756     float a = inData[0];
       
   757     int i = 0;
       
   758     for (i = 0; i < 4; i++)
       
   759         outData[i] = (1.0f - a) * dark[i] + a * light[i];
       
   760 }
       
   761 
       
   762 static void BottomGradientInterpolate(void* info, const CGFloat* inData, CGFloat* outData)
       
   763 {
       
   764     static float dark[4] = { 1.0f, 1.0f, 1.0f, 0.0f };
       
   765     static float light[4] = { 1.0f, 1.0f, 1.0f, 0.3f };
       
   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 MainGradientInterpolate(void* info, const CGFloat* inData, CGFloat* outData)
       
   773 {
       
   774     static float dark[4] = { 0.0f, 0.0f, 0.0f, 0.15f };
       
   775     static float light[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
       
   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 TrackGradientInterpolate(void* info, const CGFloat* inData, CGFloat* outData)
       
   783 {
       
   784     static float dark[4] = { 0.0f, 0.0f, 0.0f, 0.678f };
       
   785     static float light[4] = { 0.0f, 0.0f, 0.0f, 0.13f };
       
   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 void RenderThemeMac::paintMenuListButtonGradients(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
       
   793 {
       
   794     CGContextRef context = paintInfo.context->platformContext();
       
   795 
       
   796     paintInfo.context->save();
       
   797 
       
   798     int radius = o->style()->borderTopLeftRadius().width();
       
   799 
       
   800     RetainPtr<CGColorSpaceRef> cspace(AdoptCF, CGColorSpaceCreateDeviceRGB());
       
   801 
       
   802     FloatRect topGradient(r.x(), r.y(), r.width(), r.height() / 2.0f);
       
   803     struct CGFunctionCallbacks topCallbacks = { 0, TopGradientInterpolate, NULL };
       
   804     RetainPtr<CGFunctionRef> topFunction(AdoptCF, CGFunctionCreate(NULL, 1, NULL, 4, NULL, &topCallbacks));
       
   805     RetainPtr<CGShadingRef> topShading(AdoptCF, CGShadingCreateAxial(cspace.get(), CGPointMake(topGradient.x(), topGradient.y()), CGPointMake(topGradient.x(), topGradient.bottom()), topFunction.get(), false, false));
       
   806 
       
   807     FloatRect bottomGradient(r.x() + radius, r.y() + r.height() / 2.0f, r.width() - 2.0f * radius, r.height() / 2.0f);
       
   808     struct CGFunctionCallbacks bottomCallbacks = { 0, BottomGradientInterpolate, NULL };
       
   809     RetainPtr<CGFunctionRef> bottomFunction(AdoptCF, CGFunctionCreate(NULL, 1, NULL, 4, NULL, &bottomCallbacks));
       
   810     RetainPtr<CGShadingRef> bottomShading(AdoptCF, CGShadingCreateAxial(cspace.get(), CGPointMake(bottomGradient.x(),  bottomGradient.y()), CGPointMake(bottomGradient.x(), bottomGradient.bottom()), bottomFunction.get(), false, false));
       
   811 
       
   812     struct CGFunctionCallbacks mainCallbacks = { 0, MainGradientInterpolate, NULL };
       
   813     RetainPtr<CGFunctionRef> mainFunction(AdoptCF, CGFunctionCreate(NULL, 1, NULL, 4, NULL, &mainCallbacks));
       
   814     RetainPtr<CGShadingRef> mainShading(AdoptCF, CGShadingCreateAxial(cspace.get(), CGPointMake(r.x(),  r.y()), CGPointMake(r.x(), r.bottom()), mainFunction.get(), false, false));
       
   815 
       
   816     RetainPtr<CGShadingRef> leftShading(AdoptCF, CGShadingCreateAxial(cspace.get(), CGPointMake(r.x(),  r.y()), CGPointMake(r.x() + radius, r.y()), mainFunction.get(), false, false));
       
   817 
       
   818     RetainPtr<CGShadingRef> rightShading(AdoptCF, CGShadingCreateAxial(cspace.get(), CGPointMake(r.right(),  r.y()), CGPointMake(r.right() - radius, r.y()), mainFunction.get(), false, false));
       
   819     paintInfo.context->save();
       
   820     CGContextClipToRect(context, r);
       
   821     paintInfo.context->addRoundedRectClip(r,
       
   822         o->style()->borderTopLeftRadius(), o->style()->borderTopRightRadius(),
       
   823         o->style()->borderBottomLeftRadius(), o->style()->borderBottomRightRadius());
       
   824     CGContextDrawShading(context, mainShading.get());
       
   825     paintInfo.context->restore();
       
   826 
       
   827     paintInfo.context->save();
       
   828     CGContextClipToRect(context, topGradient);
       
   829     paintInfo.context->addRoundedRectClip(enclosingIntRect(topGradient),
       
   830         o->style()->borderTopLeftRadius(), o->style()->borderTopRightRadius(),
       
   831         IntSize(), IntSize());
       
   832     CGContextDrawShading(context, topShading.get());
       
   833     paintInfo.context->restore();
       
   834 
       
   835     paintInfo.context->save();
       
   836     CGContextClipToRect(context, bottomGradient);
       
   837     paintInfo.context->addRoundedRectClip(enclosingIntRect(bottomGradient),
       
   838         IntSize(), IntSize(),
       
   839         o->style()->borderBottomLeftRadius(), o->style()->borderBottomRightRadius());
       
   840     CGContextDrawShading(context, bottomShading.get());
       
   841     paintInfo.context->restore();
       
   842 
       
   843     paintInfo.context->save();
       
   844     CGContextClipToRect(context, r);
       
   845     paintInfo.context->addRoundedRectClip(r,
       
   846         o->style()->borderTopLeftRadius(), o->style()->borderTopRightRadius(),
       
   847         o->style()->borderBottomLeftRadius(), o->style()->borderBottomRightRadius());
       
   848     CGContextDrawShading(context, leftShading.get());
       
   849     CGContextDrawShading(context, rightShading.get());
       
   850     paintInfo.context->restore();
       
   851 
       
   852     paintInfo.context->restore();
       
   853 }
       
   854 
       
   855 bool RenderThemeMac::paintMenuListButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
       
   856 {
       
   857     paintInfo.context->save();
       
   858 
       
   859     IntRect bounds = IntRect(r.x() + o->style()->borderLeftWidth(),
       
   860                              r.y() + o->style()->borderTopWidth(),
       
   861                              r.width() - o->style()->borderLeftWidth() - o->style()->borderRightWidth(),
       
   862                              r.height() - o->style()->borderTopWidth() - o->style()->borderBottomWidth());
       
   863     // Draw the gradients to give the styled popup menu a button appearance
       
   864     paintMenuListButtonGradients(o, paintInfo, bounds);
       
   865 
       
   866     // 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
       
   867     float fontScale = min(o->style()->fontSize() / baseFontSize, bounds.height() / (baseArrowHeight * 2 + baseSpaceBetweenArrows));
       
   868     float centerY = bounds.y() + bounds.height() / 2.0f;
       
   869     float arrowHeight = baseArrowHeight * fontScale;
       
   870     float arrowWidth = baseArrowWidth * fontScale;
       
   871     float leftEdge = bounds.right() - arrowPaddingRight - arrowWidth;
       
   872     float spaceBetweenArrows = baseSpaceBetweenArrows * fontScale;
       
   873 
       
   874     if (bounds.width() < arrowWidth + arrowPaddingLeft)
       
   875         return false;
       
   876     
       
   877     paintInfo.context->setFillColor(o->style()->color());
       
   878     paintInfo.context->setStrokeStyle(NoStroke);
       
   879 
       
   880     FloatPoint arrow1[3];
       
   881     arrow1[0] = FloatPoint(leftEdge, centerY - spaceBetweenArrows / 2.0f);
       
   882     arrow1[1] = FloatPoint(leftEdge + arrowWidth, centerY - spaceBetweenArrows / 2.0f);
       
   883     arrow1[2] = FloatPoint(leftEdge + arrowWidth / 2.0f, centerY - spaceBetweenArrows / 2.0f - arrowHeight);
       
   884 
       
   885     // Draw the top arrow
       
   886     paintInfo.context->drawConvexPolygon(3, arrow1, true);
       
   887 
       
   888     FloatPoint arrow2[3];
       
   889     arrow2[0] = FloatPoint(leftEdge, centerY + spaceBetweenArrows / 2.0f);
       
   890     arrow2[1] = FloatPoint(leftEdge + arrowWidth, centerY + spaceBetweenArrows / 2.0f);
       
   891     arrow2[2] = FloatPoint(leftEdge + arrowWidth / 2.0f, centerY + spaceBetweenArrows / 2.0f + arrowHeight);
       
   892 
       
   893     // Draw the bottom arrow
       
   894     paintInfo.context->drawConvexPolygon(3, arrow2, true);
       
   895 
       
   896     Color leftSeparatorColor(0, 0, 0, 40);
       
   897     Color rightSeparatorColor(255, 255, 255, 40);
       
   898 
       
   899     // FIXME: Should the separator thickness and space be scaled up by fontScale?
       
   900     int separatorSpace = 2;
       
   901     int leftEdgeOfSeparator = static_cast<int>(leftEdge - arrowPaddingLeft); // FIXME: Round?
       
   902 
       
   903     // Draw the separator to the left of the arrows
       
   904     paintInfo.context->setStrokeThickness(1.0f);
       
   905     paintInfo.context->setStrokeStyle(SolidStroke);
       
   906     paintInfo.context->setStrokeColor(leftSeparatorColor);
       
   907     paintInfo.context->drawLine(IntPoint(leftEdgeOfSeparator, bounds.y()),
       
   908                                 IntPoint(leftEdgeOfSeparator, bounds.bottom()));
       
   909 
       
   910     paintInfo.context->setStrokeColor(rightSeparatorColor);
       
   911     paintInfo.context->drawLine(IntPoint(leftEdgeOfSeparator + separatorSpace, bounds.y()),
       
   912                                 IntPoint(leftEdgeOfSeparator + separatorSpace, bounds.bottom()));
       
   913 
       
   914     paintInfo.context->restore();
       
   915     return false;
       
   916 }
       
   917 
       
   918 void RenderThemeMac::adjustMenuListStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
       
   919 {
       
   920     NSControlSize controlSize = controlSizeForFont(style);
       
   921 
       
   922     style->resetBorder();
       
   923     style->resetPadding();
       
   924     
       
   925     // Height is locked to auto.
       
   926     style->setHeight(Length(Auto));
       
   927 
       
   928     // White-space is locked to pre
       
   929     style->setWhiteSpace(PRE);
       
   930 
       
   931     // Set the foreground color to black or gray when we have the aqua look.
       
   932     // Cast to RGB32 is to work around a compiler bug.
       
   933     style->setColor(e->isEnabled() ? static_cast<RGBA32>(Color::black) : Color::darkGray);
       
   934 
       
   935     // Set the button's vertical size.
       
   936     setButtonSize(style);
       
   937 
       
   938     // 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
       
   939     // a reasonable control size, but once that control size is determined, we throw that font away and use the appropriate
       
   940     // system font for the control size instead.
       
   941     setFontFromControlSize(selector, style, controlSize);
       
   942 
       
   943     style->setBoxShadow(0);
       
   944 }
       
   945 
       
   946 int RenderThemeMac::popupInternalPaddingLeft(RenderStyle* style) const
       
   947 {
       
   948     if (style->appearance() == MenulistAppearance)
       
   949         return popupButtonPadding(controlSizeForFont(style))[leftPadding];
       
   950     if (style->appearance() == MenulistButtonAppearance)
       
   951         return styledPopupPaddingLeft;
       
   952     return 0;
       
   953 }
       
   954 
       
   955 int RenderThemeMac::popupInternalPaddingRight(RenderStyle* style) const
       
   956 {
       
   957     if (style->appearance() == MenulistAppearance)
       
   958         return popupButtonPadding(controlSizeForFont(style))[rightPadding];
       
   959     if (style->appearance() == MenulistButtonAppearance) {
       
   960         float fontScale = style->fontSize() / baseFontSize;
       
   961         float arrowWidth = baseArrowWidth * fontScale;
       
   962         return static_cast<int>(ceilf(arrowWidth + arrowPaddingLeft + arrowPaddingRight + paddingBeforeSeparator));
       
   963     }
       
   964     return 0;
       
   965 }
       
   966 
       
   967 int RenderThemeMac::popupInternalPaddingTop(RenderStyle* style) const
       
   968 {
       
   969     if (style->appearance() == MenulistAppearance)
       
   970         return popupButtonPadding(controlSizeForFont(style))[topPadding];
       
   971     if (style->appearance() == MenulistButtonAppearance)
       
   972         return styledPopupPaddingTop;
       
   973     return 0;
       
   974 }
       
   975 
       
   976 int RenderThemeMac::popupInternalPaddingBottom(RenderStyle* style) const
       
   977 {
       
   978     if (style->appearance() == MenulistAppearance)
       
   979         return popupButtonPadding(controlSizeForFont(style))[bottomPadding];
       
   980     if (style->appearance() == MenulistButtonAppearance)
       
   981         return styledPopupPaddingBottom;
       
   982     return 0;
       
   983 }
       
   984 
       
   985 void RenderThemeMac::adjustMenuListButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
       
   986 {
       
   987     float fontScale = style->fontSize() / baseFontSize;
       
   988 
       
   989     style->resetPadding();
       
   990     style->setBorderRadius(IntSize(int(baseBorderRadius + fontScale - 1), int(baseBorderRadius + fontScale - 1))); // FIXME: Round up?
       
   991 
       
   992     const int minHeight = 15;
       
   993     style->setMinHeight(Length(minHeight, Fixed));
       
   994     
       
   995     style->setLineHeight(RenderStyle::initialLineHeight());
       
   996 }
       
   997 
       
   998 void RenderThemeMac::setPopupButtonCellState(const RenderObject* o, const IntRect& r)
       
   999 {
       
  1000     NSPopUpButtonCell* popupButton = this->popupButton();
       
  1001 
       
  1002     // Set the control size based off the rectangle we're painting into.
       
  1003     setControlSize(popupButton, popupButtonSizes(), r.size());
       
  1004 
       
  1005     // Update the various states we respond to.
       
  1006     updateCheckedState(popupButton, o);
       
  1007     updateEnabledState(popupButton, o);
       
  1008     updatePressedState(popupButton, o);
       
  1009     updateFocusedState(popupButton, o);
       
  1010 }
       
  1011 
       
  1012 const IntSize* RenderThemeMac::menuListSizes() const
       
  1013 {
       
  1014     static const IntSize sizes[3] = { IntSize(9, 0), IntSize(5, 0), IntSize(0, 0) };
       
  1015     return sizes;
       
  1016 }
       
  1017 
       
  1018 int RenderThemeMac::minimumMenuListSize(RenderStyle* style) const
       
  1019 {
       
  1020     return sizeForSystemFont(style, menuListSizes()).width();
       
  1021 }
       
  1022 
       
  1023 const int trackWidth = 5;
       
  1024 const int trackRadius = 2;
       
  1025 
       
  1026 void RenderThemeMac::adjustSliderTrackStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
       
  1027 {
       
  1028     style->setBoxShadow(0);
       
  1029 }
       
  1030 
       
  1031 bool RenderThemeMac::paintSliderTrack(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
       
  1032 {
       
  1033     IntRect bounds = r;
       
  1034 
       
  1035     if (o->style()->appearance() ==  SliderHorizontalAppearance) {
       
  1036         bounds.setHeight(trackWidth);
       
  1037         bounds.setY(r.y() + r.height() / 2 - trackWidth / 2);
       
  1038     } else if (o->style()->appearance() == SliderVerticalAppearance) {
       
  1039         bounds.setWidth(trackWidth);
       
  1040         bounds.setX(r.x() + r.width() / 2 - trackWidth / 2);
       
  1041     }
       
  1042 
       
  1043     LocalCurrentGraphicsContext localContext(paintInfo.context);
       
  1044     CGContextRef context = paintInfo.context->platformContext();
       
  1045     RetainPtr<CGColorSpaceRef> cspace(AdoptCF, CGColorSpaceCreateDeviceRGB());
       
  1046 
       
  1047     paintInfo.context->save();
       
  1048     CGContextClipToRect(context, bounds);
       
  1049 
       
  1050     struct CGFunctionCallbacks mainCallbacks = { 0, TrackGradientInterpolate, NULL };
       
  1051     RetainPtr<CGFunctionRef> mainFunction(AdoptCF, CGFunctionCreate(NULL, 1, NULL, 4, NULL, &mainCallbacks));
       
  1052     RetainPtr<CGShadingRef> mainShading;
       
  1053     if (o->style()->appearance() == SliderVerticalAppearance)
       
  1054         mainShading.adoptCF(CGShadingCreateAxial(cspace.get(), CGPointMake(bounds.x(),  bounds.bottom()), CGPointMake(bounds.right(), bounds.bottom()), mainFunction.get(), false, false));
       
  1055     else
       
  1056         mainShading.adoptCF(CGShadingCreateAxial(cspace.get(), CGPointMake(bounds.x(),  bounds.y()), CGPointMake(bounds.x(), bounds.bottom()), mainFunction.get(), false, false));
       
  1057 
       
  1058     IntSize radius(trackRadius, trackRadius);
       
  1059     paintInfo.context->addRoundedRectClip(bounds,
       
  1060         radius, radius,
       
  1061         radius, radius);
       
  1062     CGContextDrawShading(context, mainShading.get());
       
  1063     paintInfo.context->restore();
       
  1064     
       
  1065     return false;
       
  1066 }
       
  1067 
       
  1068 void RenderThemeMac::adjustSliderThumbStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
       
  1069 {
       
  1070     style->setBoxShadow(0);
       
  1071 }
       
  1072 
       
  1073 const float verticalSliderHeightPadding = 0.1f;
       
  1074 
       
  1075 bool RenderThemeMac::paintSliderThumb(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
       
  1076 {
       
  1077     ASSERT(o->parent()->isSlider());
       
  1078 
       
  1079     NSSliderCell* sliderThumbCell = o->style()->appearance() == SliderThumbVerticalAppearance
       
  1080         ? sliderThumbVertical()
       
  1081         : sliderThumbHorizontal();
       
  1082 
       
  1083     LocalCurrentGraphicsContext localContext(paintInfo.context);
       
  1084 
       
  1085     // Update the various states we respond to.
       
  1086     updateEnabledState(sliderThumbCell, o->parent());
       
  1087     updateFocusedState(sliderThumbCell, o->parent());
       
  1088 
       
  1089     // Update the pressed state using the NSCell tracking methods, since that's how NSSliderCell keeps track of it.
       
  1090     bool oldPressed;
       
  1091     if (o->style()->appearance() == SliderThumbVerticalAppearance)
       
  1092         oldPressed = m_isSliderThumbVerticalPressed;
       
  1093     else
       
  1094         oldPressed = m_isSliderThumbHorizontalPressed;
       
  1095 
       
  1096     bool pressed = static_cast<RenderSlider*>(o->parent())->inDragMode();
       
  1097 
       
  1098     if (o->style()->appearance() == SliderThumbVerticalAppearance)
       
  1099         m_isSliderThumbVerticalPressed = pressed;
       
  1100     else
       
  1101         m_isSliderThumbHorizontalPressed = pressed;
       
  1102 
       
  1103     if (pressed != oldPressed) {
       
  1104         if (pressed)
       
  1105             [sliderThumbCell startTrackingAt:NSPoint() inView:nil];
       
  1106         else
       
  1107             [sliderThumbCell stopTracking:NSPoint() at:NSPoint() inView:nil mouseIsUp:YES];
       
  1108     }
       
  1109 
       
  1110     FloatRect bounds = r;
       
  1111     // Make the height of the vertical slider slightly larger so NSSliderCell will draw a vertical slider.
       
  1112     if (o->style()->appearance() == SliderThumbVerticalAppearance)
       
  1113         bounds.setHeight(bounds.height() + verticalSliderHeightPadding);
       
  1114 
       
  1115     [sliderThumbCell drawWithFrame:NSRect(bounds) inView:o->view()->frameView()->getDocumentView()];
       
  1116     [sliderThumbCell setControlView:nil];
       
  1117 
       
  1118     return false;
       
  1119 }
       
  1120 
       
  1121 const int sliderThumbWidth = 15;
       
  1122 const int sliderThumbHeight = 15;
       
  1123 
       
  1124 void RenderThemeMac::adjustSliderThumbSize(RenderObject* o) const
       
  1125 {
       
  1126     if (o->style()->appearance() == SliderThumbHorizontalAppearance || o->style()->appearance() == SliderThumbVerticalAppearance) {
       
  1127         o->style()->setWidth(Length(sliderThumbWidth, Fixed));
       
  1128         o->style()->setHeight(Length(sliderThumbHeight, Fixed));
       
  1129     }
       
  1130 }
       
  1131 
       
  1132 bool RenderThemeMac::paintSearchField(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
       
  1133 {
       
  1134     NSSearchFieldCell* search = this->search();
       
  1135     LocalCurrentGraphicsContext localContext(paintInfo.context);
       
  1136 
       
  1137     setSearchCellState(o, r);
       
  1138 
       
  1139     // Set the search button to nil before drawing.  Then reset it so we can draw it later.
       
  1140     [search setSearchButtonCell:nil];
       
  1141 
       
  1142     [search drawWithFrame:NSRect(r) inView:o->view()->frameView()->getDocumentView()];
       
  1143 #ifdef BUILDING_ON_TIGER
       
  1144     if ([search showsFirstResponder])
       
  1145         wkDrawTextFieldCellFocusRing(search, NSRect(r));
       
  1146 #endif
       
  1147 
       
  1148     [search setControlView:nil];
       
  1149     [search resetSearchButtonCell];
       
  1150 
       
  1151     return false;
       
  1152 }
       
  1153 
       
  1154 void RenderThemeMac::setSearchCellState(RenderObject* o, const IntRect& r)
       
  1155 {
       
  1156     NSSearchFieldCell* search = this->search();
       
  1157 
       
  1158     [search setControlSize:controlSizeForFont(o->style())];
       
  1159 
       
  1160     // Update the various states we respond to.
       
  1161     updateEnabledState(search, o);
       
  1162     updateFocusedState(search, o);
       
  1163 }
       
  1164 
       
  1165 const IntSize* RenderThemeMac::searchFieldSizes() const
       
  1166 {
       
  1167     static const IntSize sizes[3] = { IntSize(0, 22), IntSize(0, 19), IntSize(0, 17) };
       
  1168     return sizes;
       
  1169 }
       
  1170 
       
  1171 void RenderThemeMac::setSearchFieldSize(RenderStyle* style) const
       
  1172 {
       
  1173     // If the width and height are both specified, then we have nothing to do.
       
  1174     if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
       
  1175         return;
       
  1176     
       
  1177     // Use the font size to determine the intrinsic width of the control.
       
  1178     setSizeFromFont(style, searchFieldSizes());
       
  1179 }
       
  1180 
       
  1181 void RenderThemeMac::adjustSearchFieldStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
       
  1182 {
       
  1183     // Override border.
       
  1184     style->resetBorder();
       
  1185     const short borderWidth = 2;
       
  1186     style->setBorderLeftWidth(borderWidth);
       
  1187     style->setBorderLeftStyle(INSET);
       
  1188     style->setBorderRightWidth(borderWidth);
       
  1189     style->setBorderRightStyle(INSET);
       
  1190     style->setBorderBottomWidth(borderWidth);
       
  1191     style->setBorderBottomStyle(INSET);
       
  1192     style->setBorderTopWidth(borderWidth);
       
  1193     style->setBorderTopStyle(INSET);    
       
  1194     
       
  1195     // Override height.
       
  1196     style->setHeight(Length(Auto));
       
  1197     setSearchFieldSize(style);
       
  1198     
       
  1199     // Override padding size to match AppKit text positioning.
       
  1200     const int padding = 1;
       
  1201     style->setPaddingLeft(Length(padding, Fixed));
       
  1202     style->setPaddingRight(Length(padding, Fixed));
       
  1203     style->setPaddingTop(Length(padding, Fixed));
       
  1204     style->setPaddingBottom(Length(padding, Fixed));
       
  1205     
       
  1206     NSControlSize controlSize = controlSizeForFont(style);
       
  1207     setFontFromControlSize(selector, style, controlSize);
       
  1208 
       
  1209     style->setBoxShadow(0);
       
  1210 }
       
  1211 
       
  1212 bool RenderThemeMac::paintSearchFieldCancelButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
       
  1213 {
       
  1214     Node* input = o->node()->shadowAncestorNode();
       
  1215     setSearchCellState(input->renderer(), r);
       
  1216 
       
  1217     NSSearchFieldCell* search = this->search();
       
  1218 
       
  1219     updatePressedState([search cancelButtonCell], o);
       
  1220 
       
  1221     NSRect bounds = [search cancelButtonRectForBounds:NSRect(input->renderer()->absoluteBoundingBoxRect())];
       
  1222     [[search cancelButtonCell] drawWithFrame:bounds inView:o->view()->frameView()->getDocumentView()];
       
  1223     [[search cancelButtonCell] setControlView:nil];
       
  1224 
       
  1225     return false;
       
  1226 }
       
  1227 
       
  1228 const IntSize* RenderThemeMac::cancelButtonSizes() const
       
  1229 {
       
  1230     static const IntSize sizes[3] = { IntSize(16, 13), IntSize(13, 11), IntSize(13, 9) };
       
  1231     return sizes;
       
  1232 }
       
  1233 
       
  1234 void RenderThemeMac::adjustSearchFieldCancelButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
       
  1235 {
       
  1236     IntSize size = sizeForSystemFont(style, cancelButtonSizes());
       
  1237     style->setWidth(Length(size.width(), Fixed));
       
  1238     style->setHeight(Length(size.height(), Fixed));
       
  1239     style->setBoxShadow(0);
       
  1240 }
       
  1241 
       
  1242 const IntSize* RenderThemeMac::resultsButtonSizes() const
       
  1243 {
       
  1244     static const IntSize sizes[3] = { IntSize(19, 13), IntSize(17, 11), IntSize(17, 9) };
       
  1245     return sizes;
       
  1246 }
       
  1247 
       
  1248 const int emptyResultsOffset = 9;
       
  1249 void RenderThemeMac::adjustSearchFieldDecorationStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
       
  1250 {
       
  1251     IntSize size = sizeForSystemFont(style, resultsButtonSizes());
       
  1252     style->setWidth(Length(size.width() - emptyResultsOffset, Fixed));
       
  1253     style->setHeight(Length(size.height(), Fixed));
       
  1254     style->setBoxShadow(0);
       
  1255 }
       
  1256 
       
  1257 bool RenderThemeMac::paintSearchFieldDecoration(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
       
  1258 {
       
  1259     return false;
       
  1260 }
       
  1261 
       
  1262 void RenderThemeMac::adjustSearchFieldResultsDecorationStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
       
  1263 {
       
  1264     IntSize size = sizeForSystemFont(style, resultsButtonSizes());
       
  1265     style->setWidth(Length(size.width(), Fixed));
       
  1266     style->setHeight(Length(size.height(), Fixed));
       
  1267     style->setBoxShadow(0);
       
  1268 }
       
  1269 
       
  1270 bool RenderThemeMac::paintSearchFieldResultsDecoration(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
       
  1271 {
       
  1272     Node* input = o->node()->shadowAncestorNode();
       
  1273     setSearchCellState(input->renderer(), r);
       
  1274 
       
  1275     NSSearchFieldCell* search = this->search();
       
  1276 
       
  1277     if ([search searchMenuTemplate] != nil)
       
  1278         [search setSearchMenuTemplate:nil];
       
  1279 
       
  1280     NSRect bounds = [search searchButtonRectForBounds:NSRect(input->renderer()->absoluteBoundingBoxRect())];
       
  1281     [[search searchButtonCell] drawWithFrame:bounds inView:o->view()->frameView()->getDocumentView()];
       
  1282     [[search searchButtonCell] setControlView:nil];
       
  1283     return false;
       
  1284 }
       
  1285 
       
  1286 const int resultsArrowWidth = 5;
       
  1287 void RenderThemeMac::adjustSearchFieldResultsButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
       
  1288 {
       
  1289     IntSize size = sizeForSystemFont(style, resultsButtonSizes());
       
  1290     style->setWidth(Length(size.width() + resultsArrowWidth, Fixed));
       
  1291     style->setHeight(Length(size.height(), Fixed));
       
  1292     style->setBoxShadow(0);
       
  1293 }
       
  1294 
       
  1295 bool RenderThemeMac::paintSearchFieldResultsButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
       
  1296 {
       
  1297     Node* input = o->node()->shadowAncestorNode();
       
  1298     setSearchCellState(input->renderer(), r);
       
  1299 
       
  1300     NSSearchFieldCell* search = this->search();
       
  1301 
       
  1302     if (![search searchMenuTemplate])
       
  1303         [search setSearchMenuTemplate:searchMenuTemplate()];
       
  1304 
       
  1305     NSRect bounds = [search searchButtonRectForBounds:NSRect(input->renderer()->absoluteBoundingBoxRect())];
       
  1306     [[search searchButtonCell] drawWithFrame:bounds inView:o->view()->frameView()->getDocumentView()];
       
  1307     [[search searchButtonCell] setControlView:nil];
       
  1308     return false;
       
  1309 }
       
  1310 
       
  1311 NSButtonCell* RenderThemeMac::checkbox() const
       
  1312 {
       
  1313     if (!m_checkbox) {
       
  1314         m_checkbox.adoptNS([[NSButtonCell alloc] init]);
       
  1315         [m_checkbox.get() setButtonType:NSSwitchButton];
       
  1316         [m_checkbox.get() setTitle:nil];
       
  1317         [m_checkbox.get() setAllowsMixedState:YES];
       
  1318         [m_checkbox.get() setFocusRingType:NSFocusRingTypeExterior];
       
  1319     }
       
  1320     
       
  1321     return m_checkbox.get();
       
  1322 }
       
  1323 
       
  1324 NSButtonCell* RenderThemeMac::radio() const
       
  1325 {
       
  1326     if (!m_radio) {
       
  1327         m_radio.adoptNS([[NSButtonCell alloc] init]);
       
  1328         [m_radio.get() setButtonType:NSRadioButton];
       
  1329         [m_radio.get() setTitle:nil];
       
  1330         [m_radio.get() setFocusRingType:NSFocusRingTypeExterior];
       
  1331     }
       
  1332     
       
  1333     return m_radio.get();
       
  1334 }
       
  1335 
       
  1336 NSButtonCell* RenderThemeMac::button() const
       
  1337 {
       
  1338     if (!m_button) {
       
  1339         m_button.adoptNS([[NSButtonCell alloc] init]);
       
  1340         [m_button.get() setTitle:nil];
       
  1341         [m_button.get() setButtonType:NSMomentaryPushInButton];
       
  1342     }
       
  1343     
       
  1344     return m_button.get();
       
  1345 }
       
  1346 
       
  1347 NSPopUpButtonCell* RenderThemeMac::popupButton() const
       
  1348 {
       
  1349     if (!m_popupButton) {
       
  1350         m_popupButton.adoptNS([[NSPopUpButtonCell alloc] initTextCell:@"" pullsDown:NO]);
       
  1351         [m_popupButton.get() setUsesItemFromMenu:NO];
       
  1352         [m_popupButton.get() setFocusRingType:NSFocusRingTypeExterior];
       
  1353     }
       
  1354     
       
  1355     return m_popupButton.get();
       
  1356 }
       
  1357 
       
  1358 NSSearchFieldCell* RenderThemeMac::search() const
       
  1359 {
       
  1360     if (!m_search) {
       
  1361         m_search.adoptNS([[NSSearchFieldCell alloc] initTextCell:@""]);
       
  1362         [m_search.get() setBezelStyle:NSTextFieldRoundedBezel];
       
  1363         [m_search.get() setBezeled:YES];
       
  1364         [m_search.get() setEditable:YES];
       
  1365         [m_search.get() setFocusRingType:NSFocusRingTypeExterior];
       
  1366     }
       
  1367 
       
  1368     return m_search.get();
       
  1369 }
       
  1370 
       
  1371 NSMenu* RenderThemeMac::searchMenuTemplate() const
       
  1372 {
       
  1373     if (!m_searchMenuTemplate)
       
  1374         m_searchMenuTemplate.adoptNS([[NSMenu alloc] initWithTitle:@""]);
       
  1375 
       
  1376     return m_searchMenuTemplate.get();
       
  1377 }
       
  1378 
       
  1379 NSSliderCell* RenderThemeMac::sliderThumbHorizontal() const
       
  1380 {
       
  1381     if (!m_sliderThumbHorizontal) {
       
  1382         m_sliderThumbHorizontal.adoptNS([[NSSliderCell alloc] init]);
       
  1383         [m_sliderThumbHorizontal.get() setTitle:nil];
       
  1384         [m_sliderThumbHorizontal.get() setSliderType:NSLinearSlider];
       
  1385         [m_sliderThumbHorizontal.get() setControlSize:NSSmallControlSize];
       
  1386         [m_sliderThumbHorizontal.get() setFocusRingType:NSFocusRingTypeExterior];
       
  1387     }
       
  1388     
       
  1389     return m_sliderThumbHorizontal.get();
       
  1390 }
       
  1391 
       
  1392 NSSliderCell* RenderThemeMac::sliderThumbVertical() const
       
  1393 {
       
  1394     if (!m_sliderThumbVertical) {
       
  1395         m_sliderThumbVertical.adoptNS([[NSSliderCell alloc] init]);
       
  1396         [m_sliderThumbVertical.get() setTitle:nil];
       
  1397         [m_sliderThumbVertical.get() setSliderType:NSLinearSlider];
       
  1398         [m_sliderThumbVertical.get() setControlSize:NSSmallControlSize];
       
  1399         [m_sliderThumbVertical.get() setFocusRingType:NSFocusRingTypeExterior];
       
  1400     }
       
  1401     
       
  1402     return m_sliderThumbVertical.get();
       
  1403 }
       
  1404 
       
  1405 Image* RenderThemeMac::resizeCornerImage() const
       
  1406 {
       
  1407     if (!m_resizeCornerImage)
       
  1408         m_resizeCornerImage = Image::loadPlatformResource("textAreaResizeCorner");
       
  1409     return m_resizeCornerImage;
       
  1410 }
       
  1411 
       
  1412 } // namespace WebCore