1/*
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 *           (C) 1999 Antti Koivisto (koivisto@kde.org)
4 *           (C) 2000 Dirk Mueller (mueller@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 "CSSPropertyNames.h"
28#include "GraphicsContext.h"
29#include "HTMLNames.h"
30#include "PaintInfo.h"
31
32#if ENABLE(WML)
33#include "WMLNames.h"
34#endif
35
36using std::min;
37using std::max;
38
39namespace WebCore {
40
41using namespace HTMLNames;
42
43RenderFieldset::RenderFieldset(Node* element)
44    : RenderBlock(element)
45{
46}
47
48void RenderFieldset::computePreferredLogicalWidths()
49{
50    RenderBlock::computePreferredLogicalWidths();
51    if (RenderBox* legend = findLegend()) {
52        int legendMinWidth = legend->minPreferredLogicalWidth();
53
54        Length legendMarginLeft = legend->style()->marginLeft();
55        Length legendMarginRight = legend->style()->marginLeft();
56
57        if (legendMarginLeft.isFixed())
58            legendMinWidth += legendMarginLeft.value();
59
60        if (legendMarginRight.isFixed())
61            legendMinWidth += legendMarginRight.value();
62
63        m_minPreferredLogicalWidth = max(m_minPreferredLogicalWidth, legendMinWidth + borderAndPaddingWidth());
64    }
65}
66
67RenderObject* RenderFieldset::layoutSpecialExcludedChild(bool relayoutChildren)
68{
69    RenderBox* legend = findLegend();
70    if (legend) {
71        if (relayoutChildren)
72            legend->setNeedsLayout(true);
73        legend->layoutIfNeeded();
74
75        int logicalLeft;
76        if (style()->isLeftToRightDirection()) {
77            switch (legend->style()->textAlign()) {
78            case CENTER:
79                logicalLeft = (logicalWidth() - logicalWidthForChild(legend)) / 2;
80                break;
81            case RIGHT:
82                logicalLeft = logicalWidth() - borderEnd() - paddingEnd() - logicalWidthForChild(legend);
83                break;
84            default:
85                logicalLeft = borderStart() + paddingStart() + marginStartForChild(legend);
86                break;
87            }
88        } else {
89            switch (legend->style()->textAlign()) {
90            case LEFT:
91                logicalLeft = borderStart() + paddingStart();
92                break;
93            case CENTER: {
94                // Make sure that the extra pixel goes to the end side in RTL (since it went to the end side
95                // in LTR).
96                int centeredWidth = logicalWidth() - logicalWidthForChild(legend);
97                logicalLeft = centeredWidth - centeredWidth / 2;
98                break;
99            }
100            default:
101                logicalLeft = logicalWidth() - borderStart() - paddingStart() - marginStartForChild(legend) - logicalWidthForChild(legend);
102                break;
103            }
104        }
105
106        setLogicalLeftForChild(legend, logicalLeft);
107
108        int b = borderBefore();
109        int h = logicalHeightForChild(legend);
110        setLogicalTopForChild(legend, max((b - h) / 2, 0));
111        setLogicalHeight(max(b, h) + paddingBefore());
112    }
113    return legend;
114}
115
116RenderBox* RenderFieldset::findLegend() const
117{
118    for (RenderObject* legend = firstChild(); legend; legend = legend->nextSibling()) {
119        if (!legend->isFloatingOrPositioned() && legend->node() &&
120            (legend->node()->hasTagName(legendTag)
121#if ENABLE(WML)
122            || legend->node()->hasTagName(WMLNames::insertedLegendTag)
123#endif
124            )
125           )
126            return toRenderBox(legend);
127    }
128    return 0;
129}
130
131void RenderFieldset::paintBoxDecorations(PaintInfo& paintInfo, int tx, int ty)
132{
133    if (!paintInfo.shouldPaintWithinRoot(this))
134        return;
135
136    int w = width();
137    int h = height();
138    RenderBox* legend = findLegend();
139    if (!legend)
140        return RenderBlock::paintBoxDecorations(paintInfo, tx, ty);
141
142    // FIXME: We need to work with "rl" and "bt" block flow directions.  In those
143    // cases the legend is embedded in the right and bottom borders respectively.
144    // https://bugs.webkit.org/show_bug.cgi?id=47236
145    if (style()->isHorizontalWritingMode()) {
146        int yOff = (legend->y() > 0) ? 0 : (legend->height() - borderTop()) / 2;
147        h -= yOff;
148        ty += yOff;
149    } else {
150        int xOff = (legend->x() > 0) ? 0 : (legend->width() - borderLeft()) / 2;
151        w -= xOff;
152        tx += xOff;
153    }
154
155    paintBoxShadow(paintInfo.context, tx, ty, w, h, style(), Normal);
156
157    paintFillLayers(paintInfo, style()->visitedDependentColor(CSSPropertyBackgroundColor), style()->backgroundLayers(), tx, ty, w, h);
158    paintBoxShadow(paintInfo.context, tx, ty, w, h, style(), Inset);
159
160    if (!style()->hasBorder())
161        return;
162
163    // Create a clipping region around the legend and paint the border as normal
164    GraphicsContext* graphicsContext = paintInfo.context;
165    graphicsContext->save();
166
167    // FIXME: We need to work with "rl" and "bt" block flow directions.  In those
168    // cases the legend is embedded in the right and bottom borders respectively.
169    // https://bugs.webkit.org/show_bug.cgi?id=47236
170    if (style()->isHorizontalWritingMode()) {
171        int clipTop = ty;
172        int clipHeight = max(static_cast<int>(style()->borderTopWidth()), legend->height());
173        graphicsContext->clipOut(IntRect(tx + legend->x(), clipTop, legend->width(), clipHeight));
174    } else {
175        int clipLeft = tx;
176        int clipWidth = max(static_cast<int>(style()->borderLeftWidth()), legend->width());
177        graphicsContext->clipOut(IntRect(clipLeft, ty + legend->y(), clipWidth, legend->height()));
178    }
179
180    paintBorder(paintInfo.context, tx, ty, w, h, style(), true, true);
181
182    graphicsContext->restore();
183}
184
185void RenderFieldset::paintMask(PaintInfo& paintInfo, int tx, int ty)
186{
187    if (style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask)
188        return;
189
190    int w = width();
191    int h = height();
192    RenderBox* legend = findLegend();
193    if (!legend)
194        return RenderBlock::paintMask(paintInfo, tx, ty);
195
196    // FIXME: We need to work with "rl" and "bt" block flow directions.  In those
197    // cases the legend is embedded in the right and bottom borders respectively.
198    // https://bugs.webkit.org/show_bug.cgi?id=47236
199    if (style()->isHorizontalWritingMode()) {
200        int yOff = (legend->y() > 0) ? 0 : (legend->height() - borderTop()) / 2;
201        h -= yOff;
202        ty += yOff;
203    } else {
204        int xOff = (legend->x() > 0) ? 0 : (legend->width() - borderLeft()) / 2;
205        w -= xOff;
206        tx += xOff;
207    }
208
209    paintMaskImages(paintInfo, tx, ty, w, h);
210}
211
212} // namespace WebCore
213