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->element() &&
111 legend->element()->hasTagName(legendTag)
112 #if ENABLE(WML)
113 || legend->element()->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 int my = max(ty, paintInfo.rect.y());
135 int end = min(paintInfo.rect.bottom(), ty + h);
136 int mh = end - my;
137
138 paintBoxShadow(paintInfo.context, tx, ty, w, h, style());
139
140 paintFillLayers(paintInfo, style()->backgroundColor(), style()->backgroundLayers(), my, mh, tx, ty, w, h);
141
142 if (!style()->hasBorder())
143 return;
144
145 // Save time by not saving and restoring the GraphicsContext in the straight border case
146 if (!style()->hasBorderRadius())
147 return paintBorderMinusLegend(paintInfo.context, tx, ty, w, h, style(), legend->x(), legend->width(), legendBottom);
148
149 // We have rounded borders, create a clipping region
150 // around the legend and paint the border as normal
151 GraphicsContext* graphicsContext = paintInfo.context;
152 graphicsContext->save();
153
154 int clipTop = ty;
155 int clipHeight = max(static_cast<int>(style()->borderTopWidth()), legend->height());
156
157 graphicsContext->clipOut(IntRect(tx + legend->x(), clipTop,
158 legend->width(), clipHeight));
159 paintBorder(paintInfo.context, tx, ty, w, h, style(), true, true);
160
161 graphicsContext->restore();
162 }
163
paintMask(PaintInfo & paintInfo,int tx,int ty)164 void RenderFieldset::paintMask(PaintInfo& paintInfo, int tx, int ty)
165 {
166 if (style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask)
167 return;
168
169 int w = width();
170 int h = height();
171 RenderBox* legend = findLegend();
172 if (!legend)
173 return RenderBlock::paintMask(paintInfo, tx, ty);
174
175 int yOff = (legend->y() > 0) ? 0 : (legend->height() - borderTop()) / 2;
176 h -= yOff;
177 ty += yOff;
178
179 int my = max(ty, paintInfo.rect.y());
180 int end = min(paintInfo.rect.bottom(), ty + h);
181 int mh = end - my;
182
183 paintMaskImages(paintInfo, my, mh, tx, ty, w, h);
184 }
185
paintBorderMinusLegend(GraphicsContext * graphicsContext,int tx,int ty,int w,int h,const RenderStyle * style,int lx,int lw,int lb)186 void RenderFieldset::paintBorderMinusLegend(GraphicsContext* graphicsContext, int tx, int ty, int w, int h,
187 const RenderStyle* style, int lx, int lw, int lb)
188 {
189 const Color& tc = style->borderTopColor();
190 const Color& bc = style->borderBottomColor();
191
192 EBorderStyle ts = style->borderTopStyle();
193 EBorderStyle bs = style->borderBottomStyle();
194 EBorderStyle ls = style->borderLeftStyle();
195 EBorderStyle rs = style->borderRightStyle();
196
197 bool render_t = ts > BHIDDEN;
198 bool render_l = ls > BHIDDEN;
199 bool render_r = rs > BHIDDEN;
200 bool render_b = bs > BHIDDEN;
201
202 int borderLeftWidth = style->borderLeftWidth();
203 int borderRightWidth = style->borderRightWidth();
204
205 if (render_t) {
206 if (lx >= borderLeftWidth)
207 drawBorder(graphicsContext, tx, ty, tx + min(lx, w), ty + style->borderTopWidth(), BSTop, tc, style->color(), ts,
208 (render_l && (ls == DOTTED || ls == DASHED || ls == DOUBLE) ? borderLeftWidth : 0),
209 (lx >= w && render_r && (rs == DOTTED || rs == DASHED || rs == DOUBLE) ? borderRightWidth : 0));
210 if (lx + lw <= w - borderRightWidth)
211 drawBorder(graphicsContext, tx + max(0, lx + lw), ty, tx + w, ty + style->borderTopWidth(), BSTop, tc, style->color(), ts,
212 (lx + lw <= 0 && render_l && (ls == DOTTED || ls == DASHED || ls == DOUBLE) ? borderLeftWidth : 0),
213 (render_r && (rs == DOTTED || rs == DASHED || rs == DOUBLE) ? borderRightWidth : 0));
214 }
215
216 if (render_b)
217 drawBorder(graphicsContext, tx, ty + h - style->borderBottomWidth(), tx + w, ty + h, BSBottom, bc, style->color(), bs,
218 (render_l && (ls == DOTTED || ls == DASHED || ls == DOUBLE) ? style->borderLeftWidth() : 0),
219 (render_r && (rs == DOTTED || rs == DASHED || rs == DOUBLE) ? style->borderRightWidth() : 0));
220
221 if (render_l) {
222 const Color& lc = style->borderLeftColor();
223 int startY = ty;
224
225 bool ignore_top =
226 (tc == lc) &&
227 (ls >= OUTSET) &&
228 (ts == DOTTED || ts == DASHED || ts == SOLID || ts == OUTSET);
229
230 bool ignore_bottom =
231 (bc == lc) &&
232 (ls >= OUTSET) &&
233 (bs == DOTTED || bs == DASHED || bs == SOLID || bs == INSET);
234
235 if (lx < borderLeftWidth && lx + lw > 0) {
236 // The legend intersects the border.
237 ignore_top = true;
238 startY = lb;
239 }
240
241 drawBorder(graphicsContext, tx, startY, tx + borderLeftWidth, ty + h, BSLeft, lc, style->color(), ls,
242 ignore_top ? 0 : style->borderTopWidth(), ignore_bottom ? 0 : style->borderBottomWidth());
243 }
244
245 if (render_r) {
246 const Color& rc = style->borderRightColor();
247 int startY = ty;
248
249 bool ignore_top =
250 (tc == rc) &&
251 (rs >= DOTTED || rs == INSET) &&
252 (ts == DOTTED || ts == DASHED || ts == SOLID || ts == OUTSET);
253
254 bool ignore_bottom =
255 (bc == rc) &&
256 (rs >= DOTTED || rs == INSET) &&
257 (bs == DOTTED || bs == DASHED || bs == SOLID || bs == INSET);
258
259 if (lx < w && lx + lw > w - borderRightWidth) {
260 // The legend intersects the border.
261 ignore_top = true;
262 startY = lb;
263 }
264
265 drawBorder(graphicsContext, tx + w - borderRightWidth, startY, tx + w, ty + h, BSRight, rc, style->color(), rs,
266 ignore_top ? 0 : style->borderTopWidth(), ignore_bottom ? 0 : style->borderBottomWidth());
267 }
268 }
269
styleDidChange(RenderStyle::Diff diff,const RenderStyle * oldStyle)270 void RenderFieldset::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldStyle)
271 {
272 RenderBlock::styleDidChange(diff, oldStyle);
273
274 // WinIE renders fieldsets with display:inline like they're inline-blocks. For us,
275 // an inline-block is just a block element with replaced set to true and inline set
276 // to true. Ensure that if we ended up being inline that we set our replaced flag
277 // so that we're treated like an inline-block.
278 if (isInline())
279 setReplaced(true);
280 }
281
282 } // namespace WebCore
283