/* * Copyright (C) 2007 Apple Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * */ #include "config.h" #include "RenderThemeSafari.h" #ifdef USE_SAFARI_THEME #include "CSSValueKeywords.h" #include "Document.h" #include "Element.h" #include "FrameView.h" #include "GraphicsContext.h" #include "HTMLInputElement.h" #include "RenderSlider.h" #include "RenderView.h" #include "RetainPtr.h" #include "cssstyleselector.h" #include using std::min; // FIXME: The platform-independent code in this class should be factored out and merged with RenderThemeMac. namespace WebCore { using namespace SafariTheme; enum { topMargin, rightMargin, bottomMargin, leftMargin }; enum { topPadding, rightPadding, bottomPadding, leftPadding }; RenderTheme* theme() { static RenderThemeSafari safariTheme; return &safariTheme; } static paintThemePartPtr paintThemePart; ThemeControlState RenderThemeSafari::determineState(RenderObject* o) const { ThemeControlState result = 0; if (isEnabled(o) && !isReadOnlyControl(o)) result |= SafariTheme::EnabledState; if (isPressed(o)) result |= SafariTheme::PressedState; if (isChecked(o)) result |= SafariTheme::CheckedState; if (isIndeterminate(o)) result |= SafariTheme::IndeterminateCheckedState; if (isFocused(o)) result |= SafariTheme::FocusedState; return result; } static NSControlSize controlSizeFromRect(const IntRect& rect, const IntSize sizes[]) { if (sizes[NSRegularControlSize].height() == rect.height()) return NSRegularControlSize; else if (sizes[NSMiniControlSize].height() == rect.height()) return NSMiniControlSize; return NSSmallControlSize; } RenderThemeSafari::RenderThemeSafari() : m_themeDLL(0) { m_themeDLL = ::LoadLibrary(SAFARITHEMEDLL); if (m_themeDLL) { paintThemePart = (paintThemePartPtr)GetProcAddress(m_themeDLL, "paintThemePart"); } } RenderThemeSafari::~RenderThemeSafari() { if (!m_themeDLL) return; // we don't need to close the themes here because uxtheme should do that for us // anyway (and we could crash if uxtheme has done cleanup already) ::FreeLibrary(m_themeDLL); } Color RenderThemeSafari::platformActiveSelectionBackgroundColor() const { return Color(181, 213, 255); } Color RenderThemeSafari::platformInactiveSelectionBackgroundColor() const { return Color(212, 212, 212); } Color RenderThemeSafari::activeListBoxSelectionBackgroundColor() const { // FIXME: This should probably just be a darker version of the platformActiveSelectionBackgroundColor return Color(56, 117, 215); } static float systemFontSizeForControlSize(NSControlSize controlSize) { static float sizes[] = { 13.0f, 11.0f, 9.0f }; return sizes[controlSize]; } void RenderThemeSafari::systemFont(int propId, FontDescription& fontDescription) const { static FontDescription systemFont; static FontDescription smallSystemFont; static FontDescription menuFont; static FontDescription labelFont; static FontDescription miniControlFont; static FontDescription smallControlFont; static FontDescription controlFont; FontDescription* cachedDesc; float fontSize = 0; switch (propId) { case CSS_VAL_SMALL_CAPTION: cachedDesc = &smallSystemFont; if (!smallSystemFont.isAbsoluteSize()) fontSize = systemFontSizeForControlSize(NSSmallControlSize); break; case CSS_VAL_MENU: cachedDesc = &menuFont; if (!menuFont.isAbsoluteSize()) fontSize = systemFontSizeForControlSize(NSRegularControlSize); break; case CSS_VAL_STATUS_BAR: cachedDesc = &labelFont; if (!labelFont.isAbsoluteSize()) fontSize = 10.0f; break; case CSS_VAL__WEBKIT_MINI_CONTROL: cachedDesc = &miniControlFont; if (!miniControlFont.isAbsoluteSize()) fontSize = systemFontSizeForControlSize(NSMiniControlSize); break; case CSS_VAL__WEBKIT_SMALL_CONTROL: cachedDesc = &smallControlFont; if (!smallControlFont.isAbsoluteSize()) fontSize = systemFontSizeForControlSize(NSSmallControlSize); break; case CSS_VAL__WEBKIT_CONTROL: cachedDesc = &controlFont; if (!controlFont.isAbsoluteSize()) fontSize = systemFontSizeForControlSize(NSRegularControlSize); break; default: cachedDesc = &systemFont; if (!systemFont.isAbsoluteSize()) fontSize = 13.0f; } if (fontSize) { cachedDesc->setIsAbsoluteSize(true); cachedDesc->setGenericFamily(FontDescription::NoFamily); cachedDesc->firstFamily().setFamily("Lucida Grande"); cachedDesc->setSpecifiedSize(fontSize); cachedDesc->setBold(false); cachedDesc->setItalic(false); } fontDescription = *cachedDesc; } bool RenderThemeSafari::isControlStyled(const RenderStyle* style, const BorderData& border, const BackgroundLayer& background, const Color& backgroundColor) const { if (style->appearance() == TextFieldAppearance || style->appearance() == TextAreaAppearance || style->appearance() == ListboxAppearance) return style->border() != border; return RenderTheme::isControlStyled(style, border, background, backgroundColor); } void RenderThemeSafari::adjustRepaintRect(const RenderObject* o, IntRect& r) { NSControlSize controlSize = controlSizeForFont(o->style()); switch (o->style()->appearance()) { case CheckboxAppearance: { // We inflate the rect as needed to account for padding included in the cell to accommodate the checkbox // shadow" and the check. We don't consider this part of the bounds of the control in WebKit. r = inflateRect(r, checkboxSizes()[controlSize], checkboxMargins(controlSize)); break; } case RadioAppearance: { // We inflate the rect as needed to account for padding included in the cell to accommodate the checkbox // shadow" and the check. We don't consider this part of the bounds of the control in WebKit. r = inflateRect(r, radioSizes()[controlSize], radioMargins(controlSize)); break; } case PushButtonAppearance: case ButtonAppearance: { // We inflate the rect as needed to account for padding included in the cell to accommodate the checkbox // shadow" and the check. We don't consider this part of the bounds of the control in WebKit. if (r.height() <= buttonSizes()[NSRegularControlSize].height()) r = inflateRect(r, buttonSizes()[controlSize], buttonMargins(controlSize)); break; } case MenulistAppearance: { r = inflateRect(r, popupButtonSizes()[controlSize], popupButtonMargins(controlSize)); break; } default: break; } } IntRect RenderThemeSafari::inflateRect(const IntRect& r, const IntSize& size, const int* margins) const { // Only do the inflation if the available width/height are too small. Otherwise try to // fit the glow/check space into the available box's width/height. int widthDelta = r.width() - (size.width() + margins[leftMargin] + margins[rightMargin]); int heightDelta = r.height() - (size.height() + margins[topMargin] + margins[bottomMargin]); IntRect result(r); if (widthDelta < 0) { result.setX(result.x() - margins[leftMargin]); result.setWidth(result.width() - widthDelta); } if (heightDelta < 0) { result.setY(result.y() - margins[topMargin]); result.setHeight(result.height() - heightDelta); } return result; } short RenderThemeSafari::baselinePosition(const RenderObject* o) const { if (o->style()->appearance() == CheckboxAppearance || o->style()->appearance() == RadioAppearance) return o->marginTop() + o->height() - 2; // The baseline is 2px up from the bottom of the checkbox/radio in AppKit. return RenderTheme::baselinePosition(o); } bool RenderThemeSafari::controlSupportsTints(const RenderObject* o) const { if (!isEnabled(o)) return false; // Checkboxes only have tint when checked. if (o->style()->appearance() == CheckboxAppearance) return isChecked(o); // For now assume other controls have tint if enabled. return true; } NSControlSize RenderThemeSafari::controlSizeForFont(RenderStyle* style) const { int fontSize = style->fontSize(); if (fontSize >= 16) return NSRegularControlSize; if (fontSize >= 11) return NSSmallControlSize; return NSMiniControlSize; } /* void RenderThemeSafari::setControlSize(NSCell* cell, const IntSize* sizes, const IntSize& minSize) { NSControlSize size; if (minSize.width() >= sizes[NSRegularControlSize].width() && minSize.height() >= sizes[NSRegularControlSize].height()) size = NSRegularControlSize; else if (minSize.width() >= sizes[NSSmallControlSize].width() && minSize.height() >= sizes[NSSmallControlSize].height()) size = NSSmallControlSize; else size = NSMiniControlSize; if (size != [cell controlSize]) // Only update if we have to, since AppKit does work even if the size is the same. [cell setControlSize:size]; } */ IntSize RenderThemeSafari::sizeForFont(RenderStyle* style, const IntSize* sizes) const { return sizes[controlSizeForFont(style)]; } IntSize RenderThemeSafari::sizeForSystemFont(RenderStyle* style, const IntSize* sizes) const { return sizes[controlSizeForSystemFont(style)]; } void RenderThemeSafari::setSizeFromFont(RenderStyle* style, const IntSize* sizes) const { // FIXME: Check is flawed, since it doesn't take min-width/max-width into account. IntSize size = sizeForFont(style, sizes); if (style->width().isIntrinsicOrAuto() && size.width() > 0) style->setWidth(Length(size.width(), Fixed)); if (style->height().isAuto() && size.height() > 0) style->setHeight(Length(size.height(), Fixed)); } void RenderThemeSafari::setFontFromControlSize(CSSStyleSelector* selector, RenderStyle* style, NSControlSize controlSize) const { FontDescription fontDescription; fontDescription.setIsAbsoluteSize(true); fontDescription.setGenericFamily(FontDescription::SerifFamily); float fontSize = systemFontSizeForControlSize(controlSize); fontDescription.firstFamily().setFamily("Lucida Grande"); fontDescription.setComputedSize(fontSize); fontDescription.setSpecifiedSize(fontSize); // Reset line height style->setLineHeight(RenderStyle::initialLineHeight()); if (style->setFontDescription(fontDescription)) style->font().update(); } NSControlSize RenderThemeSafari::controlSizeForSystemFont(RenderStyle* style) const { int fontSize = style->fontSize(); if (fontSize >= 13) return NSRegularControlSize; if (fontSize >= 11) return NSSmallControlSize; return NSMiniControlSize; } bool RenderThemeSafari::paintCheckbox(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) { NSControlSize controlSize = controlSizeForFont(o->style()); IntRect inflatedRect = inflateRect(r, checkboxSizes()[controlSize], checkboxMargins(controlSize)); paintThemePart(CheckboxPart, paintInfo.context->platformContext(), inflatedRect, controlSize, determineState(o)); return false; } const IntSize* RenderThemeSafari::checkboxSizes() const { static const IntSize sizes[3] = { IntSize(14, 14), IntSize(12, 12), IntSize(10, 10) }; return sizes; } const int* RenderThemeSafari::checkboxMargins(NSControlSize controlSize) const { static const int margins[3][4] = { { 2, 2, 2, 2 }, { 2, 2, 2, 1 }, { 1, 0, 0, 0 }, }; return margins[controlSize]; } void RenderThemeSafari::setCheckboxSize(RenderStyle* style) const { // If the width and height are both specified, then we have nothing to do. if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto()) return; // Use the font size to determine the intrinsic width of the control. setSizeFromFont(style, checkboxSizes()); } bool RenderThemeSafari::paintRadio(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) { NSControlSize controlSize = controlSizeForFont(o->style()); IntRect inflatedRect = inflateRect(r, radioSizes()[controlSize], radioMargins(controlSize)); paintThemePart(RadioButtonPart, paintInfo.context->platformContext(), inflatedRect, controlSize, determineState(o)); return false; } const IntSize* RenderThemeSafari::radioSizes() const { static const IntSize sizes[3] = { IntSize(14, 15), IntSize(12, 13), IntSize(10, 10) }; return sizes; } const int* RenderThemeSafari::radioMargins(NSControlSize controlSize) const { static const int margins[3][4] = { { 1, 2, 2, 2 }, { 0, 1, 2, 1 }, { 0, 0, 1, 0 }, }; return margins[controlSize]; } void RenderThemeSafari::setRadioSize(RenderStyle* style) const { // If the width and height are both specified, then we have nothing to do. if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto()) return; // Use the font size to determine the intrinsic width of the control. setSizeFromFont(style, radioSizes()); } void RenderThemeSafari::setButtonPaddingFromControlSize(RenderStyle* style, NSControlSize size) const { // Just use 8px. AppKit wants to use 11px for mini buttons, but that padding is just too large // for real-world Web sites (creating a huge necessary minimum width for buttons whose space is // by definition constrained, since we select mini only for small cramped environments. // This also guarantees the HTML4