Mercurial > dillo_port1.3
view dw/style.cc @ 1098:614b1d02e6c3
Refactor: isolate calls to utf8 functions into a single source file.
author | corvid <corvid@lavabit.com> |
---|---|
date | Fri, 15 May 2009 22:08:10 -0400 |
parents | d7dbd3dcfa38 |
children | 9845cc109124 |
line wrap: on
line source
/* * 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, see <http://www.gnu.org/licenses/>. */ #include <stdio.h> #include <string.h> #include <unistd.h> #include <ctype.h> #include "core.hh" #include "../lout/msg.h" 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_img = -1; x_tooltip = NULL; 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; } /** * \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 () { free ((char*)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) MSG_WARN("Could not find any font.\n"); return font; } // ---------------------------------------------------------------------- bool ColorAttrs::equals(object::Object *other) { ColorAttrs *oc = (ColorAttrs*)other; return this == oc || (color == oc->color); } int ColorAttrs::hashValue() { return color; } 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) { ColorAttrs attrs(col); return layout->createColor (col); } // ---------------------------------------------------------------------- /** * \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; } _MSG("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; 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; } if (style->borderStyle.top != BORDER_NONE && style->borderColor.top) drawPolygon (view, style->borderColor.top, top, xb1, yb1, xb2, yb1, style->borderWidth.top, style->borderWidth.left, - style->borderWidth.right); if (style->borderStyle.right != BORDER_NONE && style->borderColor.right) drawPolygon (view, style->borderColor.right, right, xb2, yb1, xb2, yb2, - style->borderWidth.right, style->borderWidth.top, - style->borderWidth.bottom); if (style->borderStyle.bottom != BORDER_NONE && style->borderColor.bottom) drawPolygon (view, style->borderColor.bottom, bottom, xb1, yb2, xb2, yb2, - style->borderWidth.bottom, style->borderWidth.left, - style->borderWidth.right); if (style->borderStyle.left != BORDER_NONE && style->borderColor.left) 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 *const roman_I0[] = { "","I","II","III","IV","V","VI","VII","VIII","IX" }, *const roman_I1[] = { "","X","XX","XXX","XL","L","LX","LXX","LXXX","XC" }, *const roman_I2[] = { "","C","CC","CCC","CD","D","DC","DCC","DCCC","CM" }, *const 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