Build fixes for ppc64 and x86_64, and fixes needed now that we use -Wshorten-64-to-32.
[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., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 #import "config.h"
23 #import "RenderThemeMac.h"
24
25 #import "CSSValueKeywords.h"
26 #import "Document.h"
27 #import "FoundationExtras.h"
28 #import "FrameView.h"
29 #import "GraphicsContext.h"
30 #import "Image.h"
31 #import "RenderView.h"
32 #import "WebCoreSystemInterface.h"
33 #import "cssstyleselector.h"
34 #import "RenderPopupMenuMac.h"
35
36 // The methods in this file are specific to the Mac OS X platform.
37
38 namespace WebCore {
39
40 enum {
41     topMargin,
42     rightMargin,
43     bottomMargin,
44     leftMargin
45 };
46
47 enum {
48     topPadding,
49     rightPadding,
50     bottomPadding,
51     leftPadding
52 };
53     
54 RenderTheme* theme()
55 {
56     static RenderThemeMac macTheme;
57     return &macTheme;
58 }
59
60 RenderThemeMac::RenderThemeMac()
61     : checkbox(nil)
62     , radio(nil)
63     , button(nil)
64     , resizeCornerImage(0)
65 {
66 }
67
68 Color RenderThemeMac::platformActiveSelectionBackgroundColor() const
69 {
70     NSColor* color = [[NSColor selectedTextBackgroundColor] colorUsingColorSpaceName:NSDeviceRGBColorSpace];
71     return Color((int)(255 * [color redComponent]), (int)(255 * [color greenComponent]), (int)(255 * [color blueComponent]));
72 }
73
74 Color RenderThemeMac::platformInactiveSelectionBackgroundColor() const
75 {
76     NSColor* color = [[NSColor secondarySelectedControlColor] colorUsingColorSpaceName:NSDeviceRGBColorSpace];
77     return Color((int)(255 * [color redComponent]), (int)(255 * [color greenComponent]), (int)(255 * [color blueComponent]));
78 }
79
80 void RenderThemeMac::systemFont(int propId, FontDescription& fontDescription) const
81 {
82     static FontDescription systemFont;
83     static FontDescription smallSystemFont;
84     static FontDescription menuFont;
85     static FontDescription labelFont;
86     static FontDescription miniControlFont;
87     static FontDescription smallControlFont;
88     static FontDescription controlFont;
89     
90     FontDescription* cachedDesc;
91     NSFont* font = nil;
92     switch (propId) {
93         case CSS_VAL_SMALL_CAPTION:
94             cachedDesc = &smallSystemFont;
95             if (!smallSystemFont.isAbsoluteSize())
96                 font = [NSFont systemFontOfSize:[NSFont smallSystemFontSize]];
97             break;
98         case CSS_VAL_MENU:
99             cachedDesc = &menuFont;
100             if (!menuFont.isAbsoluteSize())
101                 font = [NSFont menuFontOfSize:[NSFont systemFontSize]];
102             break;
103         case CSS_VAL_STATUS_BAR:
104             cachedDesc = &labelFont;
105             if (!labelFont.isAbsoluteSize())
106                 font = [NSFont labelFontOfSize:[NSFont labelFontSize]];
107             break;
108         case CSS_VAL__WEBKIT_MINI_CONTROL:
109             cachedDesc = &miniControlFont;
110             if (!miniControlFont.isAbsoluteSize())
111                 font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSMiniControlSize]];
112             break;
113         case CSS_VAL__WEBKIT_SMALL_CONTROL:
114             cachedDesc = &smallControlFont;
115             if (!smallControlFont.isAbsoluteSize())
116                 font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]];
117             break;
118         case CSS_VAL__WEBKIT_CONTROL:
119             cachedDesc = &controlFont;
120             if (!controlFont.isAbsoluteSize())
121                 font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSRegularControlSize]];
122             break;
123         default:
124             cachedDesc = &systemFont;
125             if (!systemFont.isAbsoluteSize())
126                 font = [NSFont systemFontOfSize:[NSFont systemFontSize]];
127     }
128     
129     if (font) {
130         cachedDesc->setIsAbsoluteSize(true);
131         cachedDesc->setGenericFamily(FontDescription::SerifFamily);
132         cachedDesc->firstFamily().setFamily([font familyName]);
133         cachedDesc->setComputedSize([font pointSize]);
134         cachedDesc->setSpecifiedSize([font pointSize]);
135         NSFontTraitMask traits = [[NSFontManager sharedFontManager] traitsOfFont:font];
136         cachedDesc->setBold(traits & NSBoldFontMask);
137         cachedDesc->setItalic(traits & NSItalicFontMask);
138     }
139     fontDescription = *cachedDesc;
140 }
141
142 bool RenderThemeMac::isControlStyled(const RenderStyle* style, const BorderData& border, 
143                                      const BackgroundLayer& background, const Color& backgroundColor) const
144 {
145     if (style->appearance() == TextFieldAppearance || style->appearance() == TextAreaAppearance)
146         return style->border() != border;
147     return RenderTheme::isControlStyled(style, border, background, backgroundColor);
148 }
149
150 void RenderThemeMac::paintResizeControl(GraphicsContext* c, const IntRect& r)
151 {
152     if (!resizeCornerImage)
153         resizeCornerImage = Image::loadPlatformResource("textAreaResizeCorner");
154
155     IntPoint imagePoint(r.right() - resizeCornerImage->width(), r.bottom() - resizeCornerImage->height());
156     c->drawImage(resizeCornerImage, imagePoint);
157 }
158
159 void RenderThemeMac::adjustRepaintRect(const RenderObject* o, IntRect& r)
160 {
161     switch (o->style()->appearance()) {
162         case CheckboxAppearance: {
163             // Since we query the prototype cell, we need to update its state to match.
164             setCheckboxCellState(o, r);
165         
166             // We inflate the rect as needed to account for padding included in the cell to accommodate the checkbox
167             // shadow" and the check.  We don't consider this part of the bounds of the control in WebKit.
168             r = inflateRect(r, checkboxSizes()[[checkbox controlSize]], checkboxMargins());
169             break;
170         }
171         case RadioAppearance: {
172             // Since we query the prototype cell, we need to update its state to match.
173             setRadioCellState(o, r);
174         
175             // We inflate the rect as needed to account for padding included in the cell to accommodate the checkbox
176             // shadow" and the check.  We don't consider this part of the bounds of the control in WebKit.
177             r = inflateRect(r, radioSizes()[[radio controlSize]], radioMargins());
178             break;
179         }
180         case PushButtonAppearance:
181         case ButtonAppearance: {
182             // Since we query the prototype cell, we need to update its state to match.
183             setButtonCellState(o, r);
184         
185             // We inflate the rect as needed to account for padding included in the cell to accommodate the checkbox
186             // shadow" and the check.  We don't consider this part of the bounds of the control in WebKit.
187             if ([button bezelStyle] == NSRoundedBezelStyle)
188                 r = inflateRect(r, buttonSizes()[[button controlSize]], buttonMargins());
189             break;
190         }
191         case MenulistAppearance: {
192             setPopupButtonCellState(o, r); 
193             r = inflateRect(r, popupButtonSizes()[[popupButton controlSize]], popupButtonMargins());
194             break;
195         }
196         default:
197             break;
198     }
199 }
200
201 IntRect RenderThemeMac::inflateRect(const IntRect& r, const IntSize& size, const int* margins) const
202 {
203     // Only do the inflation if the available width/height are too small.  Otherwise try to
204     // fit the glow/check space into the available box's width/height.
205     int widthDelta = r.width() - (size.width() + margins[leftMargin] + margins[rightMargin]);
206     int heightDelta = r.height() - (size.height() + margins[topMargin] + margins[bottomMargin]);
207     IntRect result(r);
208     if (widthDelta < 0) {
209         result.setX(result.x() - margins[leftMargin]);
210         result.setWidth(result.width() - widthDelta);
211     }
212     if (heightDelta < 0) {
213         result.setY(result.y() - margins[topMargin]);
214         result.setHeight(result.height() - heightDelta);
215     }
216     return result;
217 }
218
219 void RenderThemeMac::updateCheckedState(NSCell* cell, const RenderObject* o)
220 {
221     bool oldIndeterminate = [cell state] == NSMixedState;
222     bool indeterminate = isIndeterminate(o);
223     bool checked = isChecked(o);
224     
225     if (oldIndeterminate != indeterminate) {
226         [cell setState:indeterminate ? NSMixedState : (checked ? NSOnState : NSOffState)];
227         return;
228     }
229
230     bool oldChecked = [cell state] == NSOnState;
231     if (checked != oldChecked)
232         [cell setState:checked ? NSOnState : NSOffState];
233 }
234
235 void RenderThemeMac::updateEnabledState(NSCell* cell, const RenderObject* o)
236 {
237     bool oldEnabled = [cell isEnabled];
238     bool enabled = isEnabled(o);
239     if (enabled != oldEnabled)
240         [cell setEnabled:enabled];
241 }
242
243 void RenderThemeMac::updateFocusedState(NSCell* cell, const RenderObject* o)
244 {
245     // FIXME: Need to add a key window test here, or the element will look
246     // focused even when in the background.
247     bool oldFocused = [cell showsFirstResponder];
248     bool focused = (o->element() && o->document()->focusNode() == o->element()) && (o->style()->outlineStyleIsAuto());
249     if (focused != oldFocused)
250         [cell setShowsFirstResponder:focused];
251 }
252
253 void RenderThemeMac::updatePressedState(NSCell* cell, const RenderObject* o)
254 {
255     bool oldPressed = [cell isHighlighted];
256     bool pressed = (o->element() && o->element()->active());
257     if (pressed != oldPressed)
258         [cell setHighlighted:pressed];
259 }
260
261 short RenderThemeMac::baselinePosition(const RenderObject* o) const
262 {
263     if (o->style()->appearance() == CheckboxAppearance || o->style()->appearance() == RadioAppearance)
264         return o->marginTop() + o->height() - 2; // The baseline is 2px up from the bottom of the checkbox/radio in AppKit.
265     return RenderTheme::baselinePosition(o);
266 }
267
268 bool RenderThemeMac::controlSupportsTints(const RenderObject* o) const
269 {
270     if (!isEnabled(o))
271         return false;
272     
273     // Checkboxes only have tint when checked.
274     if (o->style()->appearance() == CheckboxAppearance)
275         return isChecked(o);
276     
277     // For now assume other controls have tint if enabled.
278     return true;
279 }
280
281 NSControlSize RenderThemeMac::controlSizeForFont(RenderStyle* style) const
282 {
283     int fontSize = style->fontSize();
284     if (fontSize >= 16)
285         return NSRegularControlSize;
286     if (fontSize >= 11)
287         return NSSmallControlSize;
288     return NSMiniControlSize;
289 }
290
291 void RenderThemeMac::setControlSize(NSCell* cell, const IntSize* sizes, const IntSize& minSize)
292 {
293     NSControlSize size;
294     if (minSize.width() >= sizes[NSRegularControlSize].width() &&
295         minSize.height() >= sizes[NSRegularControlSize].height())
296         size = NSRegularControlSize;
297     else if (minSize.width() >= sizes[NSSmallControlSize].width() &&
298              minSize.height() >= sizes[NSSmallControlSize].height())
299         size = NSSmallControlSize;
300     else
301         size = NSMiniControlSize;
302     if (size != [cell controlSize]) // Only update if we have to, since AppKit does work even if the size is the same.
303         [cell setControlSize:size];
304 }
305
306 IntSize RenderThemeMac::sizeForFont(RenderStyle* style, const IntSize* sizes) const
307 {
308     return sizes[controlSizeForFont(style)];
309 }
310
311 void RenderThemeMac::setSizeFromFont(RenderStyle* style, const IntSize* sizes) const
312 {
313     // FIXME: Check is flawed, since it doesn't take min-width/max-width into account.
314     IntSize size = sizeForFont(style, sizes);
315     if (style->width().isIntrinsicOrAuto() && size.width() > 0)
316         style->setWidth(Length(size.width(), Fixed));
317     if (style->height().isAuto() && size.height() > 0)
318         style->setHeight(Length(size.height(), Fixed));
319 }
320
321 void RenderThemeMac::setFontFromControlSize(CSSStyleSelector* selector, RenderStyle* style, NSControlSize controlSize) const
322 {
323     FontDescription fontDescription;
324     fontDescription.setIsAbsoluteSize(true);
325     fontDescription.setGenericFamily(FontDescription::SerifFamily);
326     
327     NSFont* font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:controlSize]];
328     fontDescription.firstFamily().setFamily([font familyName]);
329     fontDescription.setComputedSize([font pointSize]);
330     fontDescription.setSpecifiedSize([font pointSize]);
331
332     if (style->setFontDescription(fontDescription))
333         style->font().update();
334 }
335
336 void RenderThemeMac::addIntrinsicMargins(RenderStyle* style, NSControlSize size) const
337 {
338     // Cut out the intrinsic margins completely if we end up using mini controls.
339     if (size == NSMiniControlSize)
340         return;
341     
342     // Intrinsic margin value.
343     const int m = 2;
344     
345     // FIXME: Using width/height alone and not also dealing with min-width/max-width is flawed.
346     if (style->width().isIntrinsicOrAuto()) {
347         if (style->marginLeft().quirk())
348             style->setMarginLeft(Length(m, Fixed));
349         if (style->marginRight().quirk())
350             style->setMarginRight(Length(m, Fixed));
351     }
352
353     if (style->height().isAuto()) {
354         if (style->marginTop().quirk())
355             style->setMarginTop(Length(m, Fixed));
356         if (style->marginBottom().quirk())
357             style->setMarginBottom(Length(m, Fixed));
358     }
359 }
360
361 bool RenderThemeMac::paintCheckbox(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
362 {
363     // Determine the width and height needed for the control and prepare the cell for painting.
364     setCheckboxCellState(o, r);
365     
366     // We inflate the rect as needed to account for padding included in the cell to accommodate the checkbox
367     // shadow" and the check.  We don't consider this part of the bounds of the control in WebKit.
368     IntRect inflatedRect = inflateRect(r, checkboxSizes()[[checkbox controlSize]], checkboxMargins());
369     [checkbox drawWithFrame:NSRect(inflatedRect) inView:o->view()->frameView()->getDocumentView()];
370     [checkbox setControlView: nil];
371     
372     return false;
373 }
374
375 const IntSize* RenderThemeMac::checkboxSizes() const
376 {
377     static const IntSize sizes[3] = { IntSize(14, 14), IntSize(12, 12), IntSize(10, 10) };
378     return sizes;
379 }
380
381 const int* RenderThemeMac::checkboxMargins() const
382 {
383     static const int margins[3][4] = 
384     {
385         { 3, 4, 4, 2 },
386         { 4, 3, 3, 3 },
387         { 4, 3, 3, 3 },
388     };
389     return margins[[checkbox controlSize]];
390 }
391
392 void RenderThemeMac::setCheckboxCellState(const RenderObject* o, const IntRect& r)
393 {
394     if (!checkbox) {
395         checkbox = HardRetainWithNSRelease([[NSButtonCell alloc] init]);
396         [checkbox setButtonType:NSSwitchButton];
397         [checkbox setTitle:nil];
398         [checkbox setAllowsMixedState:YES];
399     }
400     
401     // Set the control size based off the rectangle we're painting into.
402     setControlSize(checkbox, checkboxSizes(), r.size());
403     
404     // Update the various states we respond to.
405     updateCheckedState(checkbox, o);
406     updateEnabledState(checkbox, o);
407     updatePressedState(checkbox, o);
408     updateFocusedState(checkbox, o);
409 }
410
411 void RenderThemeMac::setCheckboxSize(RenderStyle* style) const
412 {
413     // If the width and height are both specified, then we have nothing to do.
414     if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
415         return;
416     
417     // Use the font size to determine the intrinsic width of the control.
418     setSizeFromFont(style, checkboxSizes());
419 }
420
421 bool RenderThemeMac::paintRadio(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
422 {
423     // Determine the width and height needed for the control and prepare the cell for painting.
424     setRadioCellState(o, r);
425     
426     // We inflate the rect as needed to account for padding included in the cell to accommodate the checkbox
427     // shadow" and the check.  We don't consider this part of the bounds of the control in WebKit.
428     IntRect inflatedRect = inflateRect(r, radioSizes()[[radio controlSize]], radioMargins());
429     [radio drawWithFrame:NSRect(inflatedRect) inView:o->view()->frameView()->getDocumentView()];
430     [radio setControlView: nil];
431     
432     return false;
433 }
434
435 const IntSize* RenderThemeMac::radioSizes() const
436 {
437     static const IntSize sizes[3] = { IntSize(14, 15), IntSize(12, 13), IntSize(10, 10) };
438     return sizes;
439 }
440
441 const int* RenderThemeMac::radioMargins() const
442 {
443     static const int margins[3][4] = 
444     {
445         { 2, 2, 4, 2 },
446         { 3, 2, 3, 2 },
447         { 1, 0, 2, 0 },
448     };
449     return margins[[radio controlSize]];
450 }
451
452 void RenderThemeMac::setRadioCellState(const RenderObject* o, const IntRect& r)
453 {
454     if (!radio) {
455         radio = HardRetainWithNSRelease([[NSButtonCell alloc] init]);
456         [radio setButtonType:NSRadioButton];
457         [radio setTitle:nil];
458     }
459     
460     // Set the control size based off the rectangle we're painting into.
461     setControlSize(radio, radioSizes(), r.size());
462     
463     // Update the various states we respond to.
464     updateCheckedState(radio, o);
465     updateEnabledState(radio, o);
466     updatePressedState(radio, o);
467     updateFocusedState(radio, o);
468 }
469
470
471 void RenderThemeMac::setRadioSize(RenderStyle* style) const
472 {
473     // If the width and height are both specified, then we have nothing to do.
474     if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
475         return;
476     
477     // Use the font size to determine the intrinsic width of the control.
478     setSizeFromFont(style, radioSizes());
479 }
480
481 void RenderThemeMac::setButtonPaddingFromControlSize(RenderStyle* style, NSControlSize size) const
482 {
483     // Just use 8px.  AppKit wants to use 11px for mini buttons, but that padding is just too large
484     // for real-world Web sites (creating a huge necessary minimum width for buttons whose space is
485     // by definition constrained, since we select mini only for small cramped environments.
486     // This also guarantees the HTML4 <button> will match our rendering by default, since we're using a consistent
487     // padding.
488     const int padding = 8;
489     style->setPaddingLeft(Length(padding, Fixed));
490     style->setPaddingRight(Length(padding, Fixed));
491     style->setPaddingTop(Length(0, Fixed));
492     style->setPaddingBottom(Length(0, Fixed));
493 }
494
495 void RenderThemeMac::adjustButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
496 {
497     // There are three appearance constants for buttons.
498     // (1) Push-button is the constant for the default Aqua system button.  Push buttons will not scale vertically and will not allow
499     // custom fonts or colors.  <input>s use this constant.  This button will allow custom colors and font weights/variants but won't
500     // scale vertically.
501     // (2) square-button is the constant for the square button.  This button will allow custom fonts and colors and will scale vertically.
502     // (3) Button is the constant that means "pick the best button as appropriate."  <button>s use this constant.  This button will
503     // also scale vertically and allow custom fonts and colors.  It will attempt to use Aqua if possible and will make this determination
504     // solely on the rectangle of the control.
505     
506     // Determine our control size based off our font.
507     NSControlSize controlSize = controlSizeForFont(style);
508
509     // Add in intrinsic margins
510     addIntrinsicMargins(style, controlSize);
511
512     if (style->appearance() == PushButtonAppearance) {
513         // Ditch the border.
514         style->resetBorder();
515
516         // Height is locked to auto.
517         style->setHeight(Length(Auto));
518         
519         // White-space is locked to pre
520         style->setWhiteSpace(PRE);
521
522         // Set the button's vertical size.
523         setButtonSize(style);
524
525         // Add in the padding that we'd like to use.
526         setButtonPaddingFromControlSize(style, controlSize);
527
528         // 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
529         // a reasonable control size, but once that control size is determined, we throw that font away and use the appropriate
530         // system font for the control size instead.
531         setFontFromControlSize(selector, style, controlSize);
532     } else {
533         // Set a min-height so that we can't get smaller than the mini button.
534         style->setMinHeight(Length(15, Fixed));
535         
536         // Reset the top and bottom borders.
537         style->resetBorderTop();
538         style->resetBorderBottom();
539     }
540 }
541
542 const IntSize* RenderThemeMac::buttonSizes() const
543 {
544     static const IntSize sizes[3] = { IntSize(0, 21), IntSize(0, 18), IntSize(0, 15) };
545     return sizes;
546 }
547
548 const int* RenderThemeMac::buttonMargins() const
549 {
550     static const int margins[3][4] = 
551     {
552         { 4, 6, 7, 6 },
553         { 4, 5, 6, 5 },
554         { 0, 1, 1, 1 },
555     };
556     return margins[[button controlSize]];
557 }
558
559 void RenderThemeMac::setButtonSize(RenderStyle* style) const
560 {
561     // If the width and height are both specified, then we have nothing to do.
562     if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
563         return;
564     
565     // Use the font size to determine the intrinsic width of the control.
566     setSizeFromFont(style, buttonSizes());
567 }
568
569 void RenderThemeMac::setButtonCellState(const RenderObject* o, const IntRect& r)
570 {
571     if (!button) {
572         button = HardRetainWithNSRelease([[NSButtonCell alloc] init]);
573         [button setTitle:nil];
574         [button setButtonType:NSMomentaryPushInButton];
575     }
576
577     // Set the control size based off the rectangle we're painting into.
578     if (o->style()->appearance() == SquareButtonAppearance ||
579         r.height() > buttonSizes()[NSRegularControlSize].height()) {
580         // Use the square button
581         if ([button bezelStyle] != NSShadowlessSquareBezelStyle)
582             [button setBezelStyle:NSShadowlessSquareBezelStyle];
583     } else if ([button bezelStyle] != NSRoundedBezelStyle)
584         [button setBezelStyle:NSRoundedBezelStyle];
585             
586     setControlSize(button, buttonSizes(), r.size());
587     
588     // Update the various states we respond to.
589     updateCheckedState(button, o);
590     updateEnabledState(button, o);
591     updatePressedState(button, o);
592     updateFocusedState(button, o);
593 }
594
595 bool RenderThemeMac::paintButton(RenderObject* o, const RenderObject::PaintInfo&, const IntRect& r)
596 {    
597     // FIXME: Ignores the GraphicsContext in the PaintInfo and always draws into the current
598     // NSGraphicsContext instead.
599
600     // Determine the width and height needed for the control and prepare the cell for painting.
601     setButtonCellState(o, r);
602     
603     // We inflate the rect as needed to account for padding included in the cell to accommodate the button
604     // shadow.  We don't consider this part of the bounds of the control in WebKit.
605     IntSize size = buttonSizes()[[button controlSize]];
606     size.setWidth(r.width());
607     IntRect inflatedRect = r;
608     if ([button bezelStyle] == NSRoundedBezelStyle) {
609         // Center the button within the available space.
610         if (inflatedRect.height() > size.height()) {
611             inflatedRect.setY(inflatedRect.y() + (inflatedRect.height() - size.height()) / 2);
612             inflatedRect.setHeight(size.height());
613         }
614         
615         // Now inflate it to account for the shadow.
616         inflatedRect = inflateRect(inflatedRect, size, buttonMargins());
617     }
618
619     [button drawWithFrame:NSRect(inflatedRect) inView:o->view()->frameView()->getDocumentView()];
620     [button setControlView:nil];
621
622     return false;
623 }
624
625 bool RenderThemeMac::paintTextField(RenderObject* o, const RenderObject::PaintInfo&, const IntRect& r)
626 {
627     // FIXME: Ignores the GraphicsContext in the PaintInfo and always draws into the current
628     // NSGraphicsContext instead.
629     wkDrawBezeledTextFieldCell(r, isEnabled(o) && !isReadOnlyControl(o));
630     return false;
631 }
632
633 void RenderThemeMac::adjustTextFieldStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
634 {
635     // Add in intrinsic margins if the font size isn't too small
636     if (style->fontSize() >= 11)
637         addIntrinsicMargins(style, NSRegularControlSize);
638 }
639
640 bool RenderThemeMac::paintTextArea(RenderObject* o, const RenderObject::PaintInfo&, const IntRect& r)
641 {
642     // FIXME: Ignores the GraphicsContext in the PaintInfo and always draws into the current
643     // NSGraphicsContext instead.
644     wkDrawBezeledTextArea(r, isEnabled(o) && !isReadOnlyControl(o));
645     return false;
646 }
647
648 void RenderThemeMac::adjustTextAreaStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
649 {
650     // Add in intrinsic margins if the font size isn't too small
651     if (style->fontSize() >= 11)
652         addIntrinsicMargins(style, NSRegularControlSize);
653 }
654
655 const int* RenderThemeMac::popupButtonMargins() const
656 {
657     static const int margins[3][4] = 
658     {
659         { 0, 3, 1, 3 },
660         { 0, 3, 2, 3 },
661         { 0, 1, 0, 1 }
662     };
663     return margins[[popupButton controlSize]];
664 }
665
666 const IntSize* RenderThemeMac::popupButtonSizes() const
667 {
668     static const IntSize sizes[3] = { IntSize(0, 21), IntSize(0, 18), IntSize(0, 15) };
669     return sizes;
670 }
671
672 const int* RenderThemeMac::popupButtonPadding(NSControlSize size) const
673 {
674     static const int padding[3][4] = 
675     {
676         { 2, 26, 3, 8 },
677         { 2, 23, 3, 8 },
678         { 2, 22, 3, 10 }
679     };
680     return padding[size];
681 }
682
683 void RenderThemeMac::setPopupPaddingFromControlSize(RenderStyle* style, NSControlSize size) const
684 {
685     style->setPaddingLeft(Length(popupButtonPadding(size)[leftPadding], Fixed));
686     style->setPaddingRight(Length(popupButtonPadding(size)[rightPadding], Fixed));
687     style->setPaddingTop(Length(popupButtonPadding(size)[topPadding], Fixed));
688     style->setPaddingBottom(Length(popupButtonPadding(size)[bottomPadding], Fixed));
689 }
690
691 bool RenderThemeMac::paintMenuList(RenderObject* o, const RenderObject::PaintInfo&, const IntRect& r)
692 {
693     setPopupButtonCellState(o, r);
694     
695     IntRect inflatedRect = r;
696     IntSize size = popupButtonSizes()[[popupButton controlSize]];
697     size.setWidth(r.width());
698
699     // Now inflate it to account for the shadow.
700     inflatedRect = inflateRect(inflatedRect, size, popupButtonMargins());
701         
702     [popupButton drawWithFrame:inflatedRect inView:o->view()->frameView()->getDocumentView()];
703     [popupButton setControlView:nil];
704     return false;
705 }
706
707 const float baseFontSize = 11.0;
708 const float baseArrowHeight = 4.0;
709 const float baseArrowWidth = 5.0;
710 const float baseSpaceBetweenArrows = 2.0;
711 const int arrowPaddingLeft = 6;
712 const int arrowPaddingRight = 6;
713 const int paddingBeforeSeparator = 4;
714 const int baseBorderRadius = 5;
715 const int styledPopupPaddingLeft = 8;
716 const int styledPopupPaddingTop = 1;
717 const int styledPopupPaddingBottom = 2;
718
719 static void TopGradientInterpolate( void *info, const CGFloat *inData, CGFloat *outData )
720 {
721     static float dark[4] = { 1, 1, 1, 0.4 };
722     static float light[4] = { 1, 1, 1, 0.15 };
723     float a = inData[0];
724     int i = 0;
725     for( i = 0; i < 4; i++ )
726         outData[i] = ( 1.0 - a ) * dark[i] + a * light[i];
727 }
728
729 static void BottomGradientInterpolate( void *info, const CGFloat *inData, CGFloat *outData )
730 {
731     static float dark[4] = { 1, 1, 1, 0 };
732     static float light[4] = { 1, 1, 1, 0.3 };
733     float a = inData[0];
734     int i = 0;
735     for( i = 0; i < 4; i++ )
736         outData[i] = ( 1.0 - a ) * dark[i] + a * light[i];
737 }
738
739 static void MainGradientInterpolate( void *info, const CGFloat *inData, CGFloat *outData )
740 {
741     static float dark[4] = { 0, 0, 0, 0.15 };
742     static float light[4] = { 0, 0, 0, 0 };
743     float a = inData[0];
744     int i = 0;
745     for( i = 0; i < 4; i++ )
746         outData[i] = ( 1.0 - a ) * dark[i] + a * light[i];
747 }
748
749 void RenderThemeMac::paintMenuListButtonGradients(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
750 {
751     CGContextRef context = i.p->platformContext();
752         
753     i.p->save();
754
755     int radius = o->style()->borderTopLeftRadius().width();
756     
757     CGColorSpaceRef cspace = CGColorSpaceCreateDeviceRGB();    
758
759     FloatRect topGradient(r.x(), r.y(), r.width(), r.height() / 2.0);
760     struct CGFunctionCallbacks topCallbacks = { 0, TopGradientInterpolate, NULL };
761     CGFunctionRef topFunction = CGFunctionCreate( NULL, 1, NULL, 4, NULL, &topCallbacks );
762     CGShadingRef topShading = CGShadingCreateAxial( cspace, CGPointMake(topGradient.x(),  topGradient.y()), 
763                             CGPointMake(topGradient.x(), topGradient.bottom()), topFunction, false, false );
764
765     FloatRect bottomGradient(r.x() + radius, r.y() + r.height() / 2.0, r.width() - 2 * radius, r.height() / 2.0);
766     struct CGFunctionCallbacks bottomCallbacks = { 0, BottomGradientInterpolate, NULL };
767     CGFunctionRef bottomFunction = CGFunctionCreate( NULL, 1, NULL, 4, NULL, &bottomCallbacks );
768     CGShadingRef bottomShading = CGShadingCreateAxial( cspace, CGPointMake(bottomGradient.x(),  bottomGradient.y()), 
769                             CGPointMake(bottomGradient.x(), bottomGradient.bottom()), bottomFunction, false, false );
770
771     struct CGFunctionCallbacks mainCallbacks = { 0, MainGradientInterpolate, NULL };
772     CGFunctionRef mainFunction = CGFunctionCreate( NULL, 1, NULL, 4, NULL, &mainCallbacks );
773     CGShadingRef mainShading = CGShadingCreateAxial( cspace, CGPointMake(r.x(),  r.y()), 
774                             CGPointMake(r.x(), r.bottom()), mainFunction, false, false );
775
776     CGShadingRef leftShading = CGShadingCreateAxial( cspace, CGPointMake(r.x(),  r.y()), 
777                             CGPointMake(r.x() + radius, r.y()), mainFunction, false, false );                        
778
779     CGShadingRef rightShading = CGShadingCreateAxial( cspace, CGPointMake(r.right(),  r.y()), 
780                             CGPointMake(r.right() - radius, r.y()), mainFunction, false, false );                             
781     i.p->save();
782     CGContextClipToRect(context, r);
783     i.p->addRoundedRectClip(r, 
784         o->style()->borderTopLeftRadius(), o->style()->borderTopRightRadius(),
785         o->style()->borderBottomLeftRadius(), o->style()->borderBottomRightRadius());
786     CGContextDrawShading(context, mainShading);  
787     i.p->restore();      
788
789     i.p->save();
790     CGContextClipToRect(context, topGradient);
791     i.p->addRoundedRectClip(enclosingIntRect(topGradient), 
792         o->style()->borderTopLeftRadius(), o->style()->borderTopRightRadius(),
793         IntSize(), IntSize());
794     CGContextDrawShading(context, topShading);  
795     i.p->restore();      
796
797     i.p->save();
798     CGContextClipToRect(context, bottomGradient);
799     i.p->addRoundedRectClip(enclosingIntRect(bottomGradient), 
800         IntSize(), IntSize(),
801         o->style()->borderBottomLeftRadius(), o->style()->borderBottomRightRadius());
802     CGContextDrawShading(context, bottomShading); 
803     i.p->restore();
804     
805     i.p->save();
806     CGContextClipToRect(context, r);
807     i.p->addRoundedRectClip(r, 
808         o->style()->borderTopLeftRadius(), o->style()->borderTopRightRadius(),
809         o->style()->borderBottomLeftRadius(), o->style()->borderBottomRightRadius());
810     CGContextDrawShading(context, leftShading);
811     CGContextDrawShading(context, rightShading);
812     i.p->restore();
813     
814     i.p->restore();
815 }
816
817 bool RenderThemeMac::paintMenuListButton(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
818 {        
819     i.p->save();
820         
821     IntRect bounds = IntRect(r.x() + o->style()->borderLeftWidth(), 
822                              r.y() + o->style()->borderTopWidth(), 
823                              r.width() - o->style()->borderLeftWidth() - o->style()->borderRightWidth(),
824                              r.height() - o->style()->borderTopWidth() - o->style()->borderBottomWidth());
825     // Draw the gradients to give the styled popup menu a button appearance
826     paintMenuListButtonGradients(o, i, bounds);
827                 
828     float fontScale = o->style()->fontSize() / baseFontSize;
829     float centerY = bounds.y() + bounds.height() / 2.0;
830     float arrowHeight = baseArrowHeight * fontScale;
831     float arrowWidth = baseArrowWidth * fontScale;    
832     float leftEdge = bounds.right() - arrowPaddingRight - arrowWidth;
833     float spaceBetweenArrows = baseSpaceBetweenArrows * fontScale;
834     
835     i.p->setFillColor(o->style()->color());
836     i.p->setPen(Pen(o->style()->color()));
837     
838     FloatPoint arrow1[3];
839     arrow1[0] = FloatPoint(leftEdge, centerY - spaceBetweenArrows / 2.0);
840     arrow1[1] = FloatPoint(leftEdge + arrowWidth, centerY - spaceBetweenArrows / 2.0);
841     arrow1[2] = FloatPoint(leftEdge + arrowWidth / 2.0, centerY - spaceBetweenArrows / 2.0 - arrowHeight);
842
843     // Draw the top arrow
844     i.p->drawConvexPolygon(3, arrow1, true);
845
846     FloatPoint arrow2[3];
847     arrow2[0] = FloatPoint(leftEdge, centerY + spaceBetweenArrows / 2.0);
848     arrow2[1] = FloatPoint(leftEdge + arrowWidth, centerY + spaceBetweenArrows / 2.0);
849     arrow2[2] = FloatPoint(leftEdge + arrowWidth / 2.0, centerY + spaceBetweenArrows / 2.0 + arrowHeight);   
850     
851     // Draw the bottom arrow
852     i.p->drawConvexPolygon(3, arrow2, true); 
853     
854     Color leftSeparatorColor(0, 0, 0, 40);
855     Color rightSeparatorColor(255, 255, 255, 40);
856     int separatorSpace = 2;
857     int leftEdgeOfSeparator = int(leftEdge - arrowPaddingLeft); // FIXME: Round?
858     
859     // Draw the separator to the left of the arrows
860     i.p->setPen(Pen(leftSeparatorColor));
861     i.p->drawLine(IntPoint(leftEdgeOfSeparator, bounds.y()), IntPoint(leftEdgeOfSeparator, bounds.bottom()));
862
863     i.p->setPen(Pen(rightSeparatorColor));
864     i.p->drawLine(IntPoint(leftEdgeOfSeparator + separatorSpace, bounds.y()), IntPoint(leftEdgeOfSeparator + separatorSpace, bounds.bottom()));
865
866     i.p->restore();
867     return false;
868 }
869
870 void RenderThemeMac::adjustMenuListStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
871 {
872     NSControlSize controlSize = controlSizeForFont(style);
873
874     // Add in intrinsic margins
875     addIntrinsicMargins(style, controlSize);
876
877     style->resetBorder();
878     
879     // Height is locked to auto.
880     style->setHeight(Length(Auto));
881     
882     // White-space is locked to pre
883     style->setWhiteSpace(PRE);
884
885     // Set the foreground color to black when we have the aqua look
886     style->setColor(Color::black);
887
888     // Set the button's vertical size.
889     setButtonSize(style);
890
891     // Add in the padding that we'd like to use.
892     setPopupPaddingFromControlSize(style, controlSize);
893
894     // 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
895     // a reasonable control size, but once that control size is determined, we throw that font away and use the appropriate
896     // system font for the control size instead.
897     setFontFromControlSize(selector, style, controlSize);
898 }
899
900 void RenderThemeMac::adjustMenuListButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
901 {
902     // Add in intrinsic margins if the font size isn't too small
903     if (style->fontSize() >= 11)
904         addIntrinsicMargins(style, NSRegularControlSize);
905         
906     float fontScale = style->fontSize() / baseFontSize;
907     float arrowWidth = baseArrowWidth * fontScale;
908     
909     // We're overriding the padding to allow for the arrow control.  WinIE doesn't honor padding on selects, so
910     // this shouldn't cause problems on the web.  If IE7 changes that, we should reconsider this.
911     style->setPaddingLeft(Length(styledPopupPaddingLeft, Fixed));
912     style->setPaddingRight(Length(int(ceilf(arrowWidth + arrowPaddingLeft + arrowPaddingRight + paddingBeforeSeparator)), Fixed));
913     style->setPaddingTop(Length(styledPopupPaddingTop, Fixed));
914     style->setPaddingBottom(Length(styledPopupPaddingBottom, Fixed));
915     
916     if (style->hasBorderRadius())
917         style->setBorderRadius(IntSize(int(baseBorderRadius + fontScale - 1), int(baseBorderRadius + fontScale - 1))); // FIXME: Round up?
918     
919     const int minHeight = 15;
920     style->setMinHeight(Length(minHeight, Fixed));
921 }
922
923 void RenderThemeMac::setPopupButtonCellState(const RenderObject* o, const IntRect& r)
924 {
925     if (!popupButton) {
926         popupButton = HardRetainWithNSRelease([[NSPopUpButtonCell alloc] initTextCell:@"" pullsDown:NO]);
927         [popupButton setUsesItemFromMenu:NO];
928     }
929
930     // Set the control size based off the rectangle we're painting into.
931     setControlSize(popupButton, popupButtonSizes(), r.size());
932
933     // Update the various states we respond to.
934     updateCheckedState(popupButton, o);
935     updateEnabledState(popupButton, o);
936     updatePressedState(popupButton, o);
937     updateFocusedState(popupButton, o);
938 }
939
940 RenderPopupMenu* RenderThemeMac::createPopupMenu(RenderArena* arena, Document* doc, RenderMenuList* menuList)
941 {
942     return new (arena) RenderPopupMenuMac(doc, menuList);
943 }
944
945 int RenderThemeMac::minimumMenuListSize(RenderStyle* style) const
946 {
947     int fontSize = style->fontSize();
948     if (fontSize >= 13)
949         return 9;
950     if (fontSize >= 11)
951         return 5;
952     return 0;
953 }
954
955 }