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