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