Home | History | Annotate | Download | only in rendering
      1 /*
      2  * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org)
      3  *           (C) 1999 Antti Koivisto (koivisto (at) kde.org)
      4  *           (C) 2000 Dirk Mueller (mueller (at) kde.org)
      5  * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc.
      6  *
      7  * This library is free software; you can redistribute it and/or
      8  * modify it under the terms of the GNU Library General Public
      9  * License as published by the Free Software Foundation; either
     10  * version 2 of the License, or (at your option) any later version.
     11  *
     12  * This library is distributed in the hope that it will be useful,
     13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     15  * Library General Public License for more details.
     16  *
     17  * You should have received a copy of the GNU Library General Public License
     18  * along with this library; see the file COPYING.LIB.  If not, write to
     19  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     20  * Boston, MA 02110-1301, USA.
     21  *
     22  */
     23 
     24 #include "config.h"
     25 #include "RenderFieldset.h"
     26 
     27 #include "HTMLNames.h"
     28 #include "GraphicsContext.h"
     29 
     30 #if ENABLE(WML)
     31 #include "WMLNames.h"
     32 #endif
     33 
     34 using std::min;
     35 using std::max;
     36 
     37 namespace WebCore {
     38 
     39 using namespace HTMLNames;
     40 
     41 RenderFieldset::RenderFieldset(Node* element)
     42     : RenderBlock(element)
     43 {
     44 }
     45 
     46 void RenderFieldset::calcPrefWidths()
     47 {
     48     RenderBlock::calcPrefWidths();
     49     if (RenderBox* legend = findLegend()) {
     50         int legendMinWidth = legend->minPrefWidth();
     51 
     52         Length legendMarginLeft = legend->style()->marginLeft();
     53         Length legendMarginRight = legend->style()->marginLeft();
     54 
     55         if (legendMarginLeft.isFixed())
     56             legendMinWidth += legendMarginLeft.value();
     57 
     58         if (legendMarginRight.isFixed())
     59             legendMinWidth += legendMarginRight.value();
     60 
     61         m_minPrefWidth = max(m_minPrefWidth, legendMinWidth + paddingLeft() + paddingRight() + borderLeft() + borderRight());
     62     }
     63 }
     64 
     65 RenderObject* RenderFieldset::layoutLegend(bool relayoutChildren)
     66 {
     67     RenderBox* legend = findLegend();
     68     if (legend) {
     69         if (relayoutChildren)
     70             legend->setNeedsLayout(true);
     71         legend->layoutIfNeeded();
     72 
     73         int xPos;
     74         if (style()->direction() == RTL) {
     75             switch (legend->style()->textAlign()) {
     76                 case LEFT:
     77                     xPos = borderLeft() + paddingLeft();
     78                     break;
     79                 case CENTER:
     80                     xPos = (width() - legend->width()) / 2;
     81                     break;
     82                 default:
     83                     xPos = width() - paddingRight() - borderRight() - legend->width() - legend->marginRight();
     84             }
     85         } else {
     86             switch (legend->style()->textAlign()) {
     87                 case RIGHT:
     88                     xPos = width() - paddingRight() - borderRight() - legend->width();
     89                     break;
     90                 case CENTER:
     91                     xPos = (width() - legend->width()) / 2;
     92                     break;
     93                 default:
     94                     xPos = borderLeft() + paddingLeft() + legend->marginLeft();
     95             }
     96         }
     97         int b = borderTop();
     98         int h = legend->height();
     99         legend->setLocation(xPos, max((b-h)/2, 0));
    100         setHeight(max(b, h) + paddingTop());
    101     }
    102     return legend;
    103 }
    104 
    105 RenderBox* RenderFieldset::findLegend() const
    106 {
    107     for (RenderObject* legend = firstChild(); legend; legend = legend->nextSibling()) {
    108         if (!legend->isFloatingOrPositioned() && legend->node() &&
    109             (legend->node()->hasTagName(legendTag)
    110 #if ENABLE(WML)
    111             || legend->node()->hasTagName(WMLNames::insertedLegendTag)
    112 #endif
    113             )
    114            )
    115             return toRenderBox(legend);
    116     }
    117     return 0;
    118 }
    119 
    120 void RenderFieldset::paintBoxDecorations(PaintInfo& paintInfo, int tx, int ty)
    121 {
    122     if (!shouldPaintWithinRoot(paintInfo))
    123         return;
    124 
    125     int w = width();
    126     int h = height();
    127     RenderBox* legend = findLegend();
    128     if (!legend)
    129         return RenderBlock::paintBoxDecorations(paintInfo, tx, ty);
    130 
    131     int yOff = (legend->y() > 0) ? 0 : (legend->height() - borderTop()) / 2;
    132     int legendBottom = ty + legend->y() + legend->height();
    133     h -= yOff;
    134     ty += yOff;
    135 
    136     paintBoxShadow(paintInfo.context, tx, ty, w, h, style(), Normal);
    137 
    138     paintFillLayers(paintInfo, style()->backgroundColor(), style()->backgroundLayers(), tx, ty, w, h);
    139     paintBoxShadow(paintInfo.context, tx, ty, w, h, style(), Inset);
    140 
    141     if (!style()->hasBorder())
    142         return;
    143 
    144     // Save time by not saving and restoring the GraphicsContext in the straight border case
    145     if (!style()->hasBorderRadius())
    146         return paintBorderMinusLegend(paintInfo.context, tx, ty, w, h, style(), legend->x(), legend->width(), legendBottom);
    147 
    148     // We have rounded borders, create a clipping region
    149     // around the legend and paint the border as normal
    150     GraphicsContext* graphicsContext = paintInfo.context;
    151     graphicsContext->save();
    152 
    153     int clipTop = ty;
    154     int clipHeight = max(static_cast<int>(style()->borderTopWidth()), legend->height());
    155 
    156     graphicsContext->clipOut(IntRect(tx + legend->x(), clipTop,
    157                                        legend->width(), clipHeight));
    158     paintBorder(paintInfo.context, tx, ty, w, h, style(), true, true);
    159 
    160     graphicsContext->restore();
    161 }
    162 
    163 void RenderFieldset::paintMask(PaintInfo& paintInfo, int tx, int ty)
    164 {
    165     if (style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask)
    166         return;
    167 
    168     int w = width();
    169     int h = height();
    170     RenderBox* legend = findLegend();
    171     if (!legend)
    172         return RenderBlock::paintMask(paintInfo, tx, ty);
    173 
    174     int yOff = (legend->y() > 0) ? 0 : (legend->height() - borderTop()) / 2;
    175     h -= yOff;
    176     ty += yOff;
    177 
    178     paintMaskImages(paintInfo, tx, ty, w, h);
    179 }
    180 
    181 void RenderFieldset::paintBorderMinusLegend(GraphicsContext* graphicsContext, int tx, int ty, int w, int h,
    182                                             const RenderStyle* style, int lx, int lw, int lb)
    183 {
    184     const Color& tc = style->borderTopColor();
    185     const Color& bc = style->borderBottomColor();
    186 
    187     EBorderStyle ts = style->borderTopStyle();
    188     EBorderStyle bs = style->borderBottomStyle();
    189     EBorderStyle ls = style->borderLeftStyle();
    190     EBorderStyle rs = style->borderRightStyle();
    191 
    192     bool render_t = ts > BHIDDEN;
    193     bool render_l = ls > BHIDDEN;
    194     bool render_r = rs > BHIDDEN;
    195     bool render_b = bs > BHIDDEN;
    196 
    197     int borderLeftWidth = style->borderLeftWidth();
    198     int borderRightWidth = style->borderRightWidth();
    199 
    200     if (render_t) {
    201         if (lx >= borderLeftWidth)
    202             drawLineForBoxSide(graphicsContext, tx, ty, tx + min(lx, w), ty + style->borderTopWidth(), BSTop, tc, style->color(), ts,
    203                        (render_l && (ls == DOTTED || ls == DASHED || ls == DOUBLE) ? borderLeftWidth : 0),
    204                        (lx >= w && render_r && (rs == DOTTED || rs == DASHED || rs == DOUBLE) ? borderRightWidth : 0));
    205         if (lx + lw <=  w - borderRightWidth)
    206             drawLineForBoxSide(graphicsContext, tx + max(0, lx + lw), ty, tx + w, ty + style->borderTopWidth(), BSTop, tc, style->color(), ts,
    207                        (lx + lw <= 0 && render_l && (ls == DOTTED || ls == DASHED || ls == DOUBLE) ? borderLeftWidth : 0),
    208                        (render_r && (rs == DOTTED || rs == DASHED || rs == DOUBLE) ? borderRightWidth : 0));
    209     }
    210 
    211     if (render_b)
    212         drawLineForBoxSide(graphicsContext, tx, ty + h - style->borderBottomWidth(), tx + w, ty + h, BSBottom, bc, style->color(), bs,
    213                    (render_l && (ls == DOTTED || ls == DASHED || ls == DOUBLE) ? style->borderLeftWidth() : 0),
    214                    (render_r && (rs == DOTTED || rs == DASHED || rs == DOUBLE) ? style->borderRightWidth() : 0));
    215 
    216     if (render_l) {
    217         const Color& lc = style->borderLeftColor();
    218         int startY = ty;
    219 
    220         bool ignore_top =
    221             (tc == lc) &&
    222             (ls >= OUTSET) &&
    223             (ts == DOTTED || ts == DASHED || ts == SOLID || ts == OUTSET);
    224 
    225         bool ignore_bottom =
    226             (bc == lc) &&
    227             (ls >= OUTSET) &&
    228             (bs == DOTTED || bs == DASHED || bs == SOLID || bs == INSET);
    229 
    230         if (lx < borderLeftWidth && lx + lw > 0) {
    231             // The legend intersects the border.
    232             ignore_top = true;
    233             startY = lb;
    234         }
    235 
    236         drawLineForBoxSide(graphicsContext, tx, startY, tx + borderLeftWidth, ty + h, BSLeft, lc, style->color(), ls,
    237                    ignore_top ? 0 : style->borderTopWidth(), ignore_bottom ? 0 : style->borderBottomWidth());
    238     }
    239 
    240     if (render_r) {
    241         const Color& rc = style->borderRightColor();
    242         int startY = ty;
    243 
    244         bool ignore_top =
    245             (tc == rc) &&
    246             (rs >= DOTTED || rs == INSET) &&
    247             (ts == DOTTED || ts == DASHED || ts == SOLID || ts == OUTSET);
    248 
    249         bool ignore_bottom =
    250             (bc == rc) &&
    251             (rs >= DOTTED || rs == INSET) &&
    252             (bs == DOTTED || bs == DASHED || bs == SOLID || bs == INSET);
    253 
    254         if (lx < w && lx + lw > w - borderRightWidth) {
    255             // The legend intersects the border.
    256             ignore_top = true;
    257             startY = lb;
    258         }
    259 
    260         drawLineForBoxSide(graphicsContext, tx + w - borderRightWidth, startY, tx + w, ty + h, BSRight, rc, style->color(), rs,
    261                    ignore_top ? 0 : style->borderTopWidth(), ignore_bottom ? 0 : style->borderBottomWidth());
    262     }
    263 }
    264 
    265 void RenderFieldset::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
    266 {
    267     RenderBlock::styleDidChange(diff, oldStyle);
    268 
    269     // WinIE renders fieldsets with display:inline like they're inline-blocks.  For us,
    270     // an inline-block is just a block element with replaced set to true and inline set
    271     // to true.  Ensure that if we ended up being inline that we set our replaced flag
    272     // so that we're treated like an inline-block.
    273     if (isInline())
    274         setReplaced(true);
    275 }
    276 
    277 } // namespace WebCore
    278