diff options
Diffstat (limited to 'dw/style.cc')
-rw-r--r-- | dw/style.cc | 632 |
1 files changed, 632 insertions, 0 deletions
diff --git a/dw/style.cc b/dw/style.cc new file mode 100644 index 00000000..bdb04f25 --- /dev/null +++ b/dw/style.cc @@ -0,0 +1,632 @@ +/* + * Dillo Widget + * + * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + + +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <ctype.h> + +#include "core.hh" + +namespace dw { +namespace core { +namespace style { + +void StyleAttrs::initValues () +{ + x_link = -1; + x_img = -1; + x_tooltip = NULL; + textDecoration = TEXT_DECORATION_NONE; + textAlign = TEXT_ALIGN_LEFT; + textAlignChar = '.'; + listStyleType = LIST_STYLE_TYPE_DISC; + valign = VALIGN_MIDDLE; + backgroundColor = NULL; + width = LENGTH_AUTO; + height = LENGTH_AUTO; + + margin.setVal (0); + borderWidth.setVal (0); + padding.setVal (0); + setBorderColor (NULL); + setBorderStyle (BORDER_NONE); + hBorderSpacing = 0; + vBorderSpacing = 0; + + display = DISPLAY_INLINE; + whiteSpace = WHITE_SPACE_NORMAL; + cursor = CURSOR_DEFAULT; +} + +/** + * \brief Reset those style attributes to their standard values, which are + * not inherited, according to CSS. + */ +void StyleAttrs::resetValues () +{ + x_link = -1; + x_img = -1; + x_tooltip = NULL; + + textAlign = TEXT_ALIGN_LEFT; /* ??? */ + valign = VALIGN_MIDDLE; + textAlignChar = '.'; + backgroundColor = NULL; + width = LENGTH_AUTO; + height = LENGTH_AUTO; + + margin.setVal (0); + borderWidth.setVal (0); + padding.setVal (0); + setBorderColor (NULL); + setBorderStyle (BORDER_NONE); + hBorderSpacing = 0; + vBorderSpacing = 0; + + display = DISPLAY_INLINE; + whiteSpace = WHITE_SPACE_NORMAL; + cursor = CURSOR_DEFAULT; /** \todo Check CSS specification again. */ +} + +/** + * \brief This method returns whether something may change its size, when + * its style changes from this style to \em otherStyle. + * + * It is mainly for optimizing style changes where only colors etc change + * (where false would be returned), in some cases it may return true, although + * a size change does not actually happen (e.g. when in a certain + * context a particular attribute is ignored). + * + * \todo Should for CSS implemented properly. Currently, size changes are + * not needed, so always false is returned. See also + * dw::core::Widget::setStyle. + */ +bool StyleAttrs::sizeDiffs (StyleAttrs *otherStyle) +{ + return false; +} + +bool StyleAttrs::equals (object::Object *other) { + StyleAttrs *otherAttrs = (StyleAttrs *) other; + + return this == otherAttrs || + (font == otherAttrs->font && + textDecoration == otherAttrs->textDecoration && + color == otherAttrs->color && + backgroundColor == otherAttrs->backgroundColor && + textAlign == otherAttrs->textAlign && + valign == otherAttrs->valign && + textAlignChar == otherAttrs->textAlignChar && + hBorderSpacing == otherAttrs->hBorderSpacing && + vBorderSpacing == otherAttrs->vBorderSpacing && + width == otherAttrs->width && + height == otherAttrs->height && + margin.equals (&otherAttrs->margin) && + borderWidth.equals (&otherAttrs->borderWidth) && + padding.equals (&otherAttrs->padding) && + borderColor.top == otherAttrs->borderColor.top && + borderColor.right == otherAttrs->borderColor.right && + borderColor.bottom == otherAttrs->borderColor.bottom && + borderColor.left == otherAttrs->borderColor.left && + borderStyle.top == otherAttrs->borderStyle.top && + borderStyle.right == otherAttrs->borderStyle.right && + borderStyle.bottom == otherAttrs->borderStyle.bottom && + borderStyle.left == otherAttrs->borderStyle.left && + display == otherAttrs->display && + whiteSpace == otherAttrs->whiteSpace && + listStyleType == otherAttrs->listStyleType && + x_link == otherAttrs->x_link && + x_img == otherAttrs->x_img && + x_tooltip == otherAttrs->x_tooltip); +} + +int StyleAttrs::hashValue () { + return (intptr_t) font + + textDecoration + + (intptr_t) color + + (intptr_t) backgroundColor + + textAlign + + valign + + textAlignChar + + hBorderSpacing + + vBorderSpacing + + width + + height + + margin.hashValue () + + borderWidth.hashValue () + + padding.hashValue () + + (intptr_t) borderColor.top + + (intptr_t) borderColor.right + + (intptr_t) borderColor.bottom + + (intptr_t) borderColor.left + + borderStyle.top + + borderStyle.right + + borderStyle.bottom + + borderStyle.left + + display + + whiteSpace + + listStyleType + + x_link + + x_img + + (intptr_t) x_tooltip; +} + +int Style::totalRef = 0; +container::typed::HashTable <StyleAttrs, Style> * Style::styleTable = + new container::typed::HashTable <StyleAttrs, Style> (false, false, 1024); + +Style::Style (StyleAttrs *attrs) +{ + copyAttrs (attrs); + + refCount = 1; + + font->ref (); + if (color) + color->ref (); + if (backgroundColor) + backgroundColor->ref (); + if (borderColor.top) + borderColor.top->ref(); + if (borderColor.bottom) + borderColor.bottom->ref(); + if (borderColor.left) + borderColor.left->ref(); + if (borderColor.right) + borderColor.right->ref(); + if (x_tooltip) + x_tooltip->ref(); + + totalRef++; +} + +Style::~Style () +{ + font->unref (); + + if (color) + color->unref (); + if (backgroundColor) + backgroundColor->unref (); + if (borderColor.top) + borderColor.top->unref(); + if (borderColor.bottom) + borderColor.bottom->unref(); + if (borderColor.left) + borderColor.left->unref(); + if (borderColor.right) + borderColor.right->unref(); + if (x_tooltip) + x_tooltip->unref(); + + styleTable->remove (this); + totalRef--; +} + +void Style::copyAttrs (StyleAttrs *attrs) +{ + font = attrs->font; + textDecoration = attrs->textDecoration; + color = attrs->color; + backgroundColor = attrs->backgroundColor; + textAlign = attrs->textAlign; + valign = attrs->valign; + textAlignChar = attrs->textAlignChar; + hBorderSpacing = attrs->hBorderSpacing; + vBorderSpacing = attrs->vBorderSpacing; + width = attrs->width; + height = attrs->height; + margin = attrs->margin; + borderWidth = attrs->borderWidth; + padding = attrs->padding; + borderColor = attrs->borderColor; + borderStyle = attrs->borderStyle; + display = attrs->display; + whiteSpace = attrs->whiteSpace; + listStyleType = attrs->listStyleType; + cursor = attrs->cursor; + x_link = attrs->x_link; + x_img = attrs->x_img; + x_tooltip = attrs->x_tooltip; +} + +// ---------------------------------------------------------------------- + +bool FontAttrs::equals(object::Object *other) +{ + FontAttrs *otherAttrs = (FontAttrs*)other; + return + this == otherAttrs || + (size == otherAttrs->size && weight == otherAttrs->weight && + style == otherAttrs->style && strcmp (name, otherAttrs->name) == 0); +} + +int FontAttrs::hashValue() +{ + int h = object::String::hashValue (name); + h = (h << 5) - h + size; + h = (h << 5) - h + weight; + h = (h << 5) - h + style; + return h; +} + +Font::~Font () +{ + delete name; +} + +void Font::copyAttrs (FontAttrs *attrs) +{ + name = strdup (attrs->name); + size = attrs->size; + weight = attrs->weight; + style = attrs->style; +} + +Font *Font::create0 (Layout *layout, FontAttrs *attrs, + bool tryEverything) +{ + return layout->createFont (attrs, tryEverything); +} + +Font *Font::create (Layout *layout, FontAttrs *attrs) +{ + return create0 (layout, attrs, false); +} + +Font *Font::createFromList (Layout *layout, FontAttrs *attrs, + char *defaultFamily) +{ + Font *font = NULL; + FontAttrs attrs2; + char *comma, *list, *current; + + attrs2 = *attrs; + current = list = strdup (attrs->name); + + while (current && (font == NULL)) { + comma = strchr (current, ','); + if (comma) *comma = 0; + + attrs2.name = current; + font = create0 (layout, &attrs2, false); + if (font) + break; + + if (comma) { + current = comma + 1; + while (isspace (*current)) current++; + } else + current = NULL; + } + + delete list; + + if (font == NULL) { + attrs2.name = defaultFamily; + font = create0 (layout, &attrs2, true); + } + + if (font == NULL) + fprintf (stderr, "Could not find any font.\n"); + + return font; +} + +// ---------------------------------------------------------------------- + +bool ColorAttrs::equals(object::Object *other) +{ + ColorAttrs *oc = (ColorAttrs*)other; + return this == oc || (color == oc->color && type == oc->type); +} + +int ColorAttrs::hashValue() +{ + return color ^ type; +} + +Color::~Color () +{ +} + +int Color::shadeColor (int color, int d) +{ + int red = (color >> 16) & 255; + int green = (color >> 8) & 255; + int blue = color & 255; + + double oldLightness = ((double) misc::max (red, green, blue)) / 255; + double newLightness; + + if (oldLightness > 0.8) { + if (d > 0) + newLightness = oldLightness - 0.2; + else + newLightness = oldLightness - 0.4; + } else if (oldLightness < 0.2) { + if (d > 0) + newLightness = oldLightness + 0.4; + else + newLightness = oldLightness + 0.2; + } else + newLightness = oldLightness + d * 0.2; + + if (oldLightness) { + double f = (newLightness / oldLightness); + red = (int)(red * f); + green = (int)(green * f); + blue = (int)(blue * f); + } else { + red = green = blue = (int)(newLightness * 255); + } + + return (red << 16) | (green << 8) | blue; +} + +int Color::shadeColor (int color, Shading shading) +{ + switch (shading) { + case SHADING_NORMAL: + return color; + + case SHADING_LIGHT: + return shadeColor(color, +1); + + case SHADING_INVERSE: + return color ^ 0xffffff; + + case SHADING_DARK: + return shadeColor(color, -1); + + default: + // compiler happiness + misc::assertNotReached (); + return -1; + } +} + + +Color *Color::create (Layout *layout, int col, Type type) +{ + ColorAttrs attrs(col, type); + Color *color = NULL; + + switch (type) { + case TYPE_SIMPLE: + color = layout->createSimpleColor (col); + break; + case TYPE_SHADED: + color = layout->createShadedColor (col); + break; + } + + return color; +} + +// ---------------------------------------------------------------------- + +/** + * \brief Draw a part of a border. + */ +static void drawPolygon (View *view, Color *color, Color::Shading shading, + int x1, int y1, int x2, int y2, + int width, int w1, int w2) +{ + int points[4][2]; + + if (width != 0) { + if (width == 1) { + if (x1 == x2) + view->drawLine (color, shading, x1, y1, x2, y2 - 1); + else + view->drawLine (color, shading, x1, y1, x2 - 1, y2); + } else if (width == -1) { + if (x1 == x2) + view->drawLine (color, shading, x1 - 1, y1, x2 - 1, y2 - 1); + else + view->drawLine (color, shading, x1, y1 - 1, x2 - 1, y2 - 1); + } else { + points[0][0] = x1; + points[0][1] = y1; + points[1][0] = x2; + points[1][1] = y2; + + if (x1 == x2) { + points[2][0] = x1 + width; + points[2][1] = y2 + w2; + points[3][0] = x1 + width; + points[3][1] = y1 + w1; + } else { + points[2][0] = x2 + w2; + points[2][1] = y1 + width; + points[3][0] = x1 + w1; + points[3][1] = y1 + width; + } + + /* + printf ("drawPolygon: (%d, %d) .. (%d, %d) .. (%d, %d) .. (%d, %d)\n", + points[0][0], points[0][1], points[1][0], points[1][1], + points[2][0], points[2][1], points[3][0], points[3][1]); + */ + view->drawPolygon (color, shading, true, points, 4); + } + } +} + +/** + * \brief Draw the border of a region in window, according to style. + * + * Used by dw::core::Widget::drawBox and dw::core::Widget::drawWidgetBox. + */ +void drawBorder (View *view, Rectangle *area, + int x, int y, int width, int height, + Style *style, bool inverse) +{ + /** \todo a lot! */ + Color::Shading dark, light, normal; + Color::Shading top, right, bottom, left; + int xb1, yb1, xb2, yb2, xp1, yp1, xp2, yp2; + + if (style->borderStyle.top == BORDER_NONE) + return; + + xb1 = x + style->margin.left; + yb1 = y + style->margin.top; + xb2 = xb1 + width - style->margin.left - style->margin.right; + yb2 = yb1 + height - style->margin.top - style->margin.bottom; + + xp1 = xb1 + style->borderWidth.top; + yp1 = yb1 + style->borderWidth.left; + xp2 = xb2 + style->borderWidth.bottom; + yp2 = yb2 + style->borderWidth.right; + + light = inverse ? Color::SHADING_DARK : Color::SHADING_LIGHT; + dark = inverse ? Color::SHADING_LIGHT : Color::SHADING_DARK; + normal = inverse ? Color::SHADING_INVERSE : Color::SHADING_NORMAL; + + switch (style->borderStyle.top) { + case BORDER_INSET: + top = left = dark; + right = bottom = light; + break; + + case BORDER_OUTSET: + top = left = light; + right = bottom = dark; + break; + + default: + top = right = bottom = left = normal; + break; + } + + drawPolygon (view, style->borderColor.top, top, xb1, yb1, xb2, yb1, + style->borderWidth.top, style->borderWidth.left, + - style->borderWidth.right); + drawPolygon (view, style->borderColor.right, right, xb2, yb1, xb2, yb2, + - style->borderWidth.right, style->borderWidth.top, + - style->borderWidth.bottom); + drawPolygon (view, style->borderColor.bottom, bottom, xb1, yb2, xb2, yb2, + - style->borderWidth.bottom, style->borderWidth.left, + - style->borderWidth.right); + drawPolygon (view, style->borderColor.left, left, xb1, yb1, xb1, yb2, + style->borderWidth.left, style->borderWidth.top, + - style->borderWidth.bottom); +} + + +/** + * \brief Draw the background (content plus padding) of a region in window, + * according to style. + * + * Used by dw::core::Widget::drawBox and dw::core::Widget::drawWidgetBox. + */ +void drawBackground (View *view, Rectangle *area, + int x, int y, int width, int height, + Style *style, bool inverse) +{ + Rectangle bgArea, intersection; + + if (style->backgroundColor) { + bgArea.x = x + style->margin.left + style->borderWidth.left; + bgArea.y = y + style->margin.top + style->borderWidth.top; + bgArea.width = + width - style->margin.left - style->borderWidth.left - + style->margin.right - style->borderWidth.right; + bgArea.height = + height - style->margin.top - style->borderWidth.top - + style->margin.bottom - style->borderWidth.bottom; + + if (area->intersectsWith (&bgArea, &intersection)) + view->drawRectangle (style->backgroundColor, + inverse ? + Color::SHADING_INVERSE : Color::SHADING_NORMAL, + true, intersection.x, intersection.y, + intersection.width, intersection.height); + } +} + +// ---------------------------------------------------------------------- + +static const char + *roman_I0[] = { "","I","II","III","IV","V","VI","VII","VIII","IX" }, + *roman_I1[] = { "","X","XX","XXX","XL","L","LX","LXX","LXXX","XC" }, + *roman_I2[] = { "","C","CC","CCC","CD","D","DC","DCC","DCCC","CM" }, + *roman_I3[] = { "","M","MM","MMM","MMMM" }; + +void strtolower (char *s) +{ + for ( ; *s; s++) + *s = tolower (*s); +} + +/** + * \brief Convert a number into a string, in a given list style. + * + * Used for ordered lists. + */ +void numtostr (int num, char *buf, int buflen, ListStyleType listStyleType) +{ + int i3, i2, i1, i0; + bool low = false; + int start_ch = 'A'; + + switch(listStyleType){ + case LIST_STYLE_TYPE_LOWER_ALPHA: + start_ch = 'a'; + case LIST_STYLE_TYPE_UPPER_ALPHA: + i0 = num - 1; + i1 = i0/26 - 1; i2 = i1/26 - 1; + if (i2 > 25) /* more than 26+26^2+26^3=18278 elements ? */ + sprintf(buf, "****."); + else + sprintf(buf, "%c%c%c.", + i2<0 ? ' ' : start_ch + i2%26, + i1<0 ? ' ' : start_ch + i1%26, + i0<0 ? ' ' : start_ch + i0%26); + break; + case LIST_STYLE_TYPE_LOWER_ROMAN: + low = true; + case LIST_STYLE_TYPE_UPPER_ROMAN: + i0 = num; + i1 = i0/10; i2 = i1/10; i3 = i2/10; + i0 %= 10; i1 %= 10; i2 %= 10; + if (num < 0 || i3 > 4) /* more than 4999 elements ? */ + sprintf(buf, "****."); + else + snprintf(buf, buflen, "%s%s%s%s.", roman_I3[i3], roman_I2[i2], + roman_I1[i1], roman_I0[i0]); + if (low) + strtolower(buf); + break; + case LIST_STYLE_TYPE_DECIMAL: + default: + sprintf(buf, "%d.", num); + break; + } +} + +} // namespace style +} // namespace dw +} // namespace core |