File indexing completed on 2024-04-28 11:38:57
0001 /** 0002 * This file is part of the DOM implementation for KDE. 0003 * 0004 * Copyright (C) 1997 Martin Jones (mjones@kde.org) 0005 * (C) 1997 Torben Weis (weis@kde.org) 0006 * (C) 1998 Waldo Bastian (bastian@kde.org) 0007 * (C) 1999-2003 Lars Knoll (knoll@kde.org) 0008 * (C) 1999 Antti Koivisto (koivisto@kde.org) 0009 * (C) 2003 Apple Computer, Inc. 0010 * (C) 2005,2011 Allan Sandfeld Jensen (kde@carewolf.com) 0011 * (C) 2008 Germain Garand (germain@ebooksfrance.org) 0012 * (C) 2009 Carlos Licea (carlos.licea@kdemail.net) 0013 * 0014 * This library is free software; you can redistribute it and/or 0015 * modify it under the terms of the GNU Library General Public 0016 * License as published by the Free Software Foundation; either 0017 * version 2 of the License, or (at your option) any later version. 0018 * 0019 * This library is distributed in the hope that it will be useful, 0020 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0021 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0022 * Library General Public License for more details. 0023 * 0024 * You should have received a copy of the GNU Library General Public License 0025 * along with this library; see the file COPYING.LIB. If not, write to 0026 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0027 * Boston, MA 02110-1301, USA. 0028 */ 0029 0030 //#define TABLE_DEBUG 0031 //#define TABLE_PRINT 0032 //#define DEBUG_LAYOUT 0033 //#define BOX_DEBUG 0034 #include "rendering/render_table.h" 0035 #include "rendering/render_replaced.h" 0036 #include "rendering/render_canvas.h" 0037 #include "rendering/table_layout.h" 0038 #include "html/html_tableimpl.h" 0039 #include "html/html_formimpl.h" 0040 #include "rendering/render_line.h" 0041 #include "xml/dom_docimpl.h" 0042 0043 #include <QApplication> 0044 #include <QStyle> 0045 0046 #include "khtml_debug.h" 0047 #include <assert.h> 0048 0049 using namespace khtml; 0050 using namespace DOM; 0051 0052 RenderTable::RenderTable(DOM::NodeImpl *node) 0053 : RenderBlock(node) 0054 { 0055 0056 tCaption = nullptr; 0057 head = foot = firstBody = nullptr; 0058 tableLayout = nullptr; 0059 m_currentBorder = nullptr; 0060 0061 has_col_elems = false; 0062 hspacing = vspacing = 0; 0063 padding = 0; 0064 needSectionRecalc = false; 0065 padding = 0; 0066 0067 columnPos.resize(2); 0068 columnPos.fill(0); 0069 columns.resize(1); 0070 columns.fill(ColumnStruct()); 0071 0072 columnPos[0] = 0; 0073 } 0074 0075 RenderTable::~RenderTable() 0076 { 0077 delete tableLayout; 0078 } 0079 0080 void RenderTable::setStyle(RenderStyle *_style) 0081 { 0082 ETableLayout oldTableLayout = style() ? style()->tableLayout() : TAUTO; 0083 if (_style->display() == INLINE) { 0084 _style->setDisplay(INLINE_TABLE); 0085 } 0086 if (_style->display() != INLINE_TABLE) { 0087 _style->setDisplay(TABLE); 0088 } 0089 if (!_style->flowAroundFloats()) { 0090 _style->setFlowAroundFloats(true); 0091 } 0092 RenderBlock::setStyle(_style); 0093 0094 // init RenderObject attributes 0095 setInline(style()->display() == INLINE_TABLE && !isPositioned()); 0096 setReplaced(style()->display() == INLINE_TABLE); 0097 0098 // In the collapsed border model, there is no cell spacing. 0099 hspacing = collapseBorders() ? 0 : style()->borderHorizontalSpacing(); 0100 vspacing = collapseBorders() ? 0 : style()->borderVerticalSpacing(); 0101 columnPos[0] = hspacing; 0102 0103 if (!tableLayout || style()->tableLayout() != oldTableLayout) { 0104 delete tableLayout; 0105 0106 // According to the CSS2 spec, you only use fixed table layout if an 0107 // explicit width is specified on the table. Auto width implies auto table layout. 0108 if (style()->tableLayout() == TFIXED && !style()->width().isAuto()) { 0109 tableLayout = new FixedTableLayout(this); 0110 #ifdef DEBUG_LAYOUT 0111 qCDebug(KHTML_LOG) << "using fixed table layout"; 0112 #endif 0113 } else { 0114 tableLayout = new AutoTableLayout(this); 0115 } 0116 } 0117 } 0118 0119 short RenderTable::lineHeight(bool b) const 0120 { 0121 // Inline tables are replaced elements. Otherwise, just pass off to 0122 // the base class. 0123 if (isReplaced()) { 0124 return height() + marginTop() + marginBottom(); 0125 } 0126 return RenderBlock::lineHeight(b); 0127 } 0128 0129 short RenderTable::baselinePosition(bool b) const 0130 { 0131 // CSS2.1 - 10.8.1 The baseline of an 'inline-table' is the baseline of the first row of the table. 0132 if (isReplaced() && !needsLayout()) { 0133 // compatibility with Gecko: only apply to generic containers, not to HTML Table. 0134 if (element() && element()->id() == ID_TABLE) { 0135 return height() + marginTop() + marginBottom(); 0136 } 0137 0138 if (firstBodySection() && firstBodySection()->grid.size()) { 0139 return firstBodySection()->grid[0].baseLine + firstBodySection()->yPos() + marginTop(); 0140 } 0141 return 0; 0142 } 0143 return RenderBox::baselinePosition(b); 0144 } 0145 0146 static inline void resetSectionPointerIfNotBefore(RenderTableSection *&ptr, RenderObject *before) 0147 { 0148 if (!before || !ptr) { 0149 return; 0150 } 0151 RenderObject *o = before->previousSibling(); 0152 while (o && o != ptr) { 0153 o = o->previousSibling(); 0154 } 0155 if (!o) { 0156 ptr = nullptr; 0157 } 0158 } 0159 0160 void RenderTable::addChild(RenderObject *child, RenderObject *beforeChild) 0161 { 0162 #ifdef DEBUG_LAYOUT 0163 qCDebug(KHTML_LOG) << renderName() << "(Table)::addChild( " << child->renderName() << ", " << 0164 (beforeChild ? beforeChild->renderName() : "0") << " )"; 0165 #endif 0166 bool wrapInAnonymousSection = false; 0167 0168 switch (child->style()->display()) { 0169 case TABLE_CAPTION: 0170 if (child->isRenderBlock()) { 0171 // First caption wins. 0172 if (beforeChild && tCaption) { 0173 RenderObject *o = beforeChild->previousSibling(); 0174 while (o && o != tCaption) { 0175 o = o->previousSibling(); 0176 } 0177 if (!o) { 0178 tCaption = nullptr; 0179 } 0180 } 0181 if (!tCaption) { 0182 tCaption = static_cast<RenderBlock *>(child); 0183 } 0184 } 0185 break; 0186 case TABLE_COLUMN: 0187 case TABLE_COLUMN_GROUP: 0188 has_col_elems = true; 0189 break; 0190 case TABLE_HEADER_GROUP: 0191 if (child->isTableSection()) { 0192 resetSectionPointerIfNotBefore(head, beforeChild); 0193 if (!head) { 0194 head = static_cast<RenderTableSection *>(child); 0195 } else { 0196 resetSectionPointerIfNotBefore(firstBody, beforeChild); 0197 if (!firstBody) { 0198 firstBody = static_cast<RenderTableSection *>(child); 0199 } 0200 } 0201 } 0202 break; 0203 case TABLE_FOOTER_GROUP: 0204 if (child->isTableSection()) { 0205 resetSectionPointerIfNotBefore(foot, beforeChild); 0206 if (!foot) { 0207 foot = static_cast<RenderTableSection *>(child); 0208 break; 0209 } 0210 } 0211 // fall through 0212 case TABLE_ROW_GROUP: 0213 if (child->isTableSection()) { 0214 resetSectionPointerIfNotBefore(firstBody, beforeChild); 0215 if (!firstBody) { 0216 firstBody = static_cast<RenderTableSection *>(child); 0217 } 0218 } 0219 break; 0220 case TABLE_CELL: 0221 case TABLE_ROW: 0222 wrapInAnonymousSection = true; 0223 break; 0224 case BLOCK: 0225 // case BOX: 0226 case COMPACT: 0227 case INLINE: 0228 case INLINE_BLOCK: 0229 // case INLINE_BOX: 0230 case INLINE_TABLE: 0231 case LIST_ITEM: 0232 case NONE: 0233 case RUN_IN: 0234 case TABLE: 0235 // The special TABLE > FORM quirk allows the form to sit directly under the table 0236 if (child->element() && child->element()->isHTMLElement() && child->element()->id() == ID_FORM) { 0237 wrapInAnonymousSection = !static_cast<HTMLFormElementImpl *>(child->element())->isMalformed(); 0238 } else { 0239 wrapInAnonymousSection = true; 0240 } 0241 break; 0242 } 0243 0244 if (!wrapInAnonymousSection) { 0245 RenderContainer::addChild(child, beforeChild); 0246 return; 0247 } 0248 0249 if (!beforeChild && lastChild() && lastChild()->isTableSection() && lastChild()->isAnonymous()) { 0250 lastChild()->addChild(child); 0251 return; 0252 } 0253 0254 RenderObject *lastBox = beforeChild; 0255 while (lastBox && lastBox->parent()->isAnonymous() && 0256 !lastBox->isTableSection() && lastBox->style()->display() != TABLE_CAPTION) { 0257 lastBox = lastBox->parent(); 0258 } 0259 if (lastBox && lastBox->isAnonymous()) { 0260 lastBox->addChild(child, beforeChild); 0261 return; 0262 } 0263 0264 if (beforeChild && !beforeChild->isTableSection()) { 0265 beforeChild = nullptr; 0266 } 0267 RenderTableSection *section = new(renderArena()) RenderTableSection(document() /* anonymous */); 0268 RenderStyle *newStyle = new RenderStyle(); 0269 newStyle->inheritFrom(style()); 0270 newStyle->setDisplay(TABLE_ROW_GROUP); 0271 section->setStyle(newStyle); 0272 addChild(section, beforeChild); 0273 section->addChild(child); 0274 } 0275 0276 void RenderTable::calcWidth() 0277 { 0278 if (isPositioned()) { 0279 calcAbsoluteHorizontal(); 0280 } 0281 0282 RenderBlock *cb = containingBlock(); 0283 int availableWidth = cb->lineWidth(m_y); 0284 0285 LengthType widthType = style()->width().type(); 0286 if (widthType > Relative && style()->width().isPositive()) { 0287 // Percent or fixed table 0288 // Percent is calculated from contentWidth, not available width 0289 m_width = calcBoxWidth(style()->width().minWidth(cb->contentWidth())); 0290 } else { 0291 // Subtract out any fixed margins from our available width for auto width tables. 0292 int marginTotal = 0; 0293 if (!style()->marginLeft().isAuto()) { 0294 marginTotal += style()->marginLeft().width(availableWidth); 0295 } 0296 if (!style()->marginRight().isAuto()) { 0297 marginTotal += style()->marginRight().width(availableWidth); 0298 } 0299 0300 // Subtract out our margins to get the available content width. 0301 int availContentWidth = qMax(0, availableWidth - marginTotal); 0302 0303 // Ensure we aren't bigger than our max width or smaller than our min width. 0304 m_width = qMin(availContentWidth, m_maxWidth); 0305 } 0306 0307 m_width = qMax(m_width, m_minWidth); 0308 0309 // Finally, with our true width determined, compute our margins for real. 0310 m_marginRight = 0; 0311 m_marginLeft = 0; 0312 0313 calcHorizontalMargins(style()->marginLeft(), style()->marginRight(), availableWidth); 0314 } 0315 0316 QList< QRectF > RenderTable::getClientRects() 0317 { 0318 RenderFlow *cap = caption(); 0319 if (cap) { 0320 // tables with caption report y&height inclusive caption, but we need them 0321 // exclusive and a extra rect for the caption 0322 // NOTE: first table, then caption 0323 QList<QRectF> list; 0324 0325 int x = 0; 0326 int y = 0; 0327 absolutePosition(x, y); 0328 0329 QRectF tableRect(x, y + cap->height(), width(), height() - cap->height()); 0330 list.append(clientRectToViewport(tableRect)); 0331 0332 QRectF captionRect(x, y, cap->width(), cap->height()); 0333 list.append(clientRectToViewport(captionRect)); 0334 0335 return list; 0336 } else { 0337 return RenderObject::getClientRects(); 0338 } 0339 } 0340 0341 void RenderTable::layout() 0342 { 0343 KHTMLAssert(needsLayout()); 0344 KHTMLAssert(minMaxKnown()); 0345 KHTMLAssert(!needSectionRecalc); 0346 0347 if (markedForRepaint()) { 0348 repaintDuringLayout(); 0349 setMarkedForRepaint(false); 0350 } 0351 0352 if (posChildNeedsLayout() && !normalChildNeedsLayout() && !selfNeedsLayout()) { 0353 // All we have to is lay out our positioned objects. 0354 layoutPositionedObjects(true); 0355 setNeedsLayout(false); 0356 return; 0357 } 0358 0359 m_height = 0; 0360 initMaxMarginValues(); 0361 0362 int oldWidth = m_width; 0363 calcWidth(); 0364 m_overflowWidth = m_width; 0365 0366 if (tCaption && (oldWidth != m_width || tCaption->style()->height().isPercent())) { 0367 tCaption->setChildNeedsLayout(true); 0368 } 0369 0370 // the optimization below doesn't work since the internal table 0371 // layout could have changed. we need to add a flag to the table 0372 // layout that tells us if something has changed in the min max 0373 // calculations to do it correctly. 0374 // if ( oldWidth != m_width || columns.size() + 1 != columnPos.size() ) 0375 tableLayout->layout(); 0376 0377 #ifdef DEBUG_LAYOUT 0378 qCDebug(KHTML_LOG) << renderName() << "(Table)::layout1() width=" << width() << ", marginLeft=" << marginLeft() << " marginRight=" << marginRight(); 0379 #endif 0380 0381 setCellWidths(); 0382 0383 // layout child objects 0384 int calculatedHeight = 0; 0385 0386 RenderObject *child = firstChild(); 0387 while (child) { 0388 // FIXME: What about a form that has a display value that makes it a table section? 0389 if (child->needsLayout() && !(child->element() && child->element()->id() == ID_FORM)) { 0390 child->layout(); 0391 } 0392 if (child->isTableSection()) { 0393 static_cast<RenderTableSection *>(child)->calcRowHeight(); 0394 calculatedHeight += static_cast<RenderTableSection *>(child)->layoutRows(0); 0395 } 0396 child = child->nextSibling(); 0397 } 0398 0399 // ### collapse caption margin 0400 if (tCaption && tCaption->style()->captionSide() != CAPBOTTOM) { 0401 tCaption->setPos(tCaption->marginLeft(), tCaption->marginTop() + m_height); 0402 m_height += tCaption->height() + tCaption->marginTop() + tCaption->marginBottom(); 0403 } 0404 0405 int bpTop = borderTop() + (collapseBorders() ? 0 : paddingTop()); 0406 int bpBottom = borderBottom() + (collapseBorders() ? 0 : paddingBottom()); 0407 0408 m_height += bpTop; 0409 0410 int oldHeight = m_height; 0411 if (isPositioned()) { 0412 m_height += calculatedHeight + bpBottom; 0413 } 0414 calcHeight(); 0415 int newHeight = m_height; 0416 m_height = oldHeight; 0417 0418 Length h = style()->height(); 0419 int th = -(bpTop + bpBottom); // Tables size as though CSS height includes border/padding. 0420 if (isPositioned()) { 0421 th += newHeight; 0422 } else if (h.isFixed()) { 0423 th += h.value(); 0424 } else if (h.isPercent()) { 0425 th += calcPercentageHeight(h); 0426 } 0427 0428 // layout rows 0429 if (th > calculatedHeight) { 0430 // we have to redistribute that height to get the constraint correctly 0431 // just force the first body to the height needed 0432 // ### FIXME This should take height constraints on all table sections into account and distribute 0433 // accordingly. For now this should be good enough 0434 if (firstBody) { 0435 firstBody->calcRowHeight(); 0436 firstBody->layoutRows(th - calculatedHeight); 0437 } else if (!style()->htmlHacks()) { 0438 // Completely empty tables (with no sections or anything) should at least honor specified height 0439 // in strict mode. 0440 m_height += th; 0441 } 0442 } 0443 0444 int bl = borderLeft(); 0445 if (!collapseBorders()) { 0446 bl += paddingLeft(); 0447 } 0448 0449 // position the table sections 0450 RenderTableSection *section = head ? head : (firstBody ? firstBody : foot); 0451 while (section) { 0452 section->setPos(bl, m_height); 0453 0454 m_height += section->height(); 0455 m_overflowLeft = qMin(m_overflowLeft, section->effectiveXPos()); 0456 m_overflowWidth = qMax(m_overflowWidth, section->effectiveXPos() + section->effectiveWidth()); 0457 section = sectionBelow(section); 0458 } 0459 0460 m_height += bpBottom; 0461 0462 if (tCaption && tCaption->style()->captionSide() == CAPBOTTOM) { 0463 tCaption->setPos(tCaption->marginLeft(), tCaption->marginTop() + m_height); 0464 m_height += tCaption->height() + tCaption->marginTop() + tCaption->marginBottom(); 0465 } 0466 0467 if (canvas()->pagedMode()) { 0468 RenderObject *child = firstChild(); 0469 // relayout taking real position into account 0470 while (child) { 0471 if (!(child->element() && child->element()->id() == ID_FORM)) { 0472 child->setNeedsLayout(true); 0473 child->layout(); 0474 if (child->containsPageBreak()) { 0475 setContainsPageBreak(true); 0476 } 0477 if (child->needsPageClear()) { 0478 setNeedsPageClear(true); 0479 } 0480 } 0481 child = child->nextSibling(); 0482 } 0483 } 0484 0485 //qCDebug(KHTML_LOG) << "table height: " << m_height; 0486 0487 // table can be containing block of positioned elements. 0488 // ### only pass true if width or height changed. 0489 layoutPositionedObjects(true); 0490 0491 m_overflowHeight = m_height; 0492 0493 setNeedsLayout(false); 0494 } 0495 0496 void RenderTable::setCellWidths() 0497 { 0498 #ifdef DEBUG_LAYOUT 0499 qCDebug(KHTML_LOG) << renderName() << "(Table, this=0x" << this << ")::setCellWidths()"; 0500 #endif 0501 0502 RenderObject *child = firstChild(); 0503 while (child) { 0504 if (child->isTableSection()) { 0505 static_cast<RenderTableSection *>(child)->setCellWidths(); 0506 } 0507 child = child->nextSibling(); 0508 } 0509 } 0510 0511 void RenderTable::paint(PaintInfo &pI, int _tx, int _ty) 0512 { 0513 if (needsLayout()) { 0514 return; 0515 } 0516 0517 _tx += xPos(); 0518 _ty += yPos(); 0519 0520 #ifdef TABLE_PRINT 0521 qCDebug(KHTML_LOG) << "RenderTable::paint() w/h = (" << width() << "/" << height() << ")"; 0522 #endif 0523 if (!isRelPositioned() && !isPositioned()) { 0524 int os = 2 * maximalOutlineSize(pI.phase); 0525 if ((_ty > pI.r.y() + pI.r.height() + os) || (_ty + height() < pI.r.y() - os)) { 0526 return; 0527 } 0528 if ((_tx > pI.r.x() + pI.r.width() + os) || (_tx + width() < pI.r.x() - os)) { 0529 return; 0530 } 0531 } 0532 0533 #ifdef TABLE_PRINT 0534 qCDebug(KHTML_LOG) << "RenderTable::paint(2) " << _tx << "/" << _ty << " (" << _y << "/" << _h << ")"; 0535 #endif 0536 0537 if (pI.phase == PaintActionOutline) { 0538 paintOutline(pI.p, _tx, _ty, width(), height(), style()); 0539 } 0540 0541 if ((pI.phase == PaintActionElementBackground || pI.phase == PaintActionChildBackground) 0542 && shouldPaintBackgroundOrBorder() && style()->visibility() == VISIBLE) { 0543 paintBoxDecorations(pI, _tx, _ty); 0544 } 0545 0546 if (pI.phase == PaintActionElementBackground) { 0547 return; 0548 } 0549 0550 PaintAction oldphase = pI.phase; 0551 if (pI.phase == PaintActionChildBackgrounds) { 0552 pI.phase = PaintActionChildBackground; 0553 } 0554 0555 for (RenderObject *child = firstChild(); child; child = child->nextSibling()) 0556 if (child->isTableSection() || child == tCaption) { 0557 child->paint(pI, _tx, _ty); 0558 } 0559 0560 if (collapseBorders() && 0561 (pI.phase == PaintActionElementBackground || pI.phase == PaintActionChildBackground) 0562 && style()->visibility() == VISIBLE) { 0563 // Collect all the unique border styles that we want to paint in a sorted list. Once we 0564 // have all the styles sorted, we then do individual passes, painting each style of border 0565 // from lowest precedence to highest precedence. 0566 pI.phase = PaintActionCollapsedTableBorders; 0567 QList<CollapsedBorderValue> borderStyles; 0568 collectBorders(borderStyles); 0569 #if 0 0570 QString m; 0571 for (uint i = 0; i < borderStyles.count(); i++) { 0572 m += QString("%1 ").arg((*borderStyles.at(i)).width()); 0573 } 0574 // qCDebug(KHTML_LOG) << m; 0575 #endif 0576 QList<CollapsedBorderValue>::Iterator it = borderStyles.begin(); 0577 QList<CollapsedBorderValue>::Iterator end = borderStyles.end(); 0578 for (; it != end; ++it) { 0579 m_currentBorder = &*it; 0580 for (RenderObject *child = firstChild(); child; child = child->nextSibling()) { 0581 if (child->isTableSection()) { 0582 child->paint(pI, _tx, _ty); 0583 } 0584 } 0585 } 0586 m_currentBorder = nullptr; 0587 } 0588 0589 pI.phase = oldphase; 0590 #ifdef BOX_DEBUG 0591 outlineBox(pI.p, _tx, _ty, "blue"); 0592 #endif 0593 } 0594 0595 void RenderTable::paintBoxDecorations(PaintInfo &pI, int _tx, int _ty) 0596 { 0597 int w = width(); 0598 int h = height(); 0599 0600 // Account for the caption. 0601 if (tCaption) { 0602 int captionHeight = (tCaption->height() + tCaption->marginBottom() + tCaption->marginTop()); 0603 h -= captionHeight; 0604 if (tCaption->style()->captionSide() != CAPBOTTOM) { 0605 _ty += captionHeight; 0606 } 0607 } 0608 QRect cr; 0609 cr.setX(qMax(_tx, pI.r.x())); 0610 cr.setY(qMax(_ty, pI.r.y())); 0611 int mw, mh; 0612 if (_ty < pI.r.y()) { 0613 mh = qMax(0, h - (pI.r.y() - _ty)); 0614 } else { 0615 mh = qMin(pI.r.height(), h); 0616 } 0617 if (_tx < pI.r.x()) { 0618 mw = qMax(0, w - (pI.r.x() - _tx)); 0619 } else { 0620 mw = qMin(pI.r.width(), w); 0621 } 0622 cr.setWidth(mw); 0623 cr.setHeight(mh); 0624 0625 paintOneBackground(pI.p, style()->backgroundColor(), style()->backgroundLayers(), cr, _tx, _ty, w, h); 0626 0627 if (style()->hasBorder() && !collapseBorders()) { 0628 paintBorder(pI.p, _tx, _ty, w, h, style()); 0629 } 0630 } 0631 0632 void RenderTable::calcMinMaxWidth() 0633 { 0634 KHTMLAssert(!minMaxKnown()); 0635 0636 if (needSectionRecalc) { 0637 recalcSections(); 0638 } 0639 0640 #ifdef DEBUG_LAYOUT 0641 qCDebug(KHTML_LOG) << renderName() << "(Table " << this << ")::calcMinMaxWidth()"; 0642 #endif 0643 0644 tableLayout->calcMinMaxWidth(); 0645 0646 if (tCaption) { 0647 tCaption->calcWidth(); 0648 if (tCaption->marginLeft() + tCaption->marginRight() + tCaption->minWidth() > m_minWidth) { 0649 m_minWidth = tCaption->marginLeft() + tCaption->marginRight() + tCaption->minWidth(); 0650 } 0651 } 0652 0653 setMinMaxKnown(); 0654 #ifdef DEBUG_LAYOUT 0655 qCDebug(KHTML_LOG) << renderName() << " END: (Table " << this << ")::calcMinMaxWidth() min = " << m_minWidth << " max = " << m_maxWidth; 0656 #endif 0657 } 0658 0659 void RenderTable::close() 0660 { 0661 // qCDebug(KHTML_LOG) << "RenderTable::close()"; 0662 setNeedsLayoutAndMinMaxRecalc(); 0663 } 0664 0665 void RenderTable::splitColumn(int pos, int firstSpan) 0666 { 0667 // we need to add a new columnStruct 0668 int oldSize = columns.size(); 0669 columns.resize(oldSize + 1); 0670 int oldSpan = columns[pos].span; 0671 // qDebug("splitColumn( %d,%d ), oldSize=%d, oldSpan=%d", pos, firstSpan, oldSize, oldSpan ); 0672 KHTMLAssert(oldSpan > firstSpan); 0673 columns[pos].span = firstSpan; 0674 memmove(columns.data() + pos + 1, columns.data() + pos, (oldSize - pos)*sizeof(ColumnStruct)); 0675 columns[pos + 1].span = oldSpan - firstSpan; 0676 0677 // change width of all rows. 0678 RenderObject *child = firstChild(); 0679 while (child) { 0680 if (child->isTableSection()) { 0681 RenderTableSection *section = static_cast<RenderTableSection *>(child); 0682 int size = section->grid.size(); 0683 int row = 0; 0684 if (section->cCol > pos) { 0685 section->cCol++; 0686 } 0687 while (row < size) { 0688 section->grid[row].row->resize(oldSize + 1); 0689 RenderTableSection::Row &r = *section->grid[row].row; 0690 memmove(r.data() + pos + 1, r.data() + pos, (oldSize - pos)*sizeof(RenderTableCell *)); 0691 // qDebug("moving from %d to %d, num=%d", pos, pos+1, (oldSize-pos-1) ); 0692 r[pos + 1] = r[pos] ? (RenderTableCell *) - 1 : nullptr; 0693 row++; 0694 } 0695 } 0696 child = child->nextSibling(); 0697 } 0698 columnPos.resize(numEffCols() + 1); 0699 setNeedsLayoutAndMinMaxRecalc(); 0700 } 0701 0702 void RenderTable::appendColumn(int span) 0703 { 0704 // easy case. 0705 int pos = columns.size(); 0706 // qDebug("appendColumn( %d ), size=%d", span, pos ); 0707 int newSize = pos + 1; 0708 columns.resize(newSize); 0709 columns[pos].span = span; 0710 //qDebug("appending column at %d, span %d", pos, span ); 0711 0712 // change width of all rows. 0713 RenderObject *child = firstChild(); 0714 while (child) { 0715 if (child->isTableSection()) { 0716 RenderTableSection *section = static_cast<RenderTableSection *>(child); 0717 int size = section->grid.size(); 0718 int row = 0; 0719 while (row < size) { 0720 section->grid[row].row->resize(newSize); 0721 section->cellAt(row, pos) = nullptr; 0722 row++; 0723 } 0724 0725 } 0726 child = child->nextSibling(); 0727 } 0728 columnPos.resize(numEffCols() + 1); 0729 setNeedsLayoutAndMinMaxRecalc(); 0730 } 0731 0732 RenderTableCol *RenderTable::colElement(int col, bool *startEdge, bool *endEdge) const 0733 { 0734 if (!has_col_elems) { 0735 return nullptr; 0736 } 0737 RenderObject *child = firstChild(); 0738 int cCol = 0; 0739 while (child) { 0740 if (child->isTableCol()) { 0741 RenderTableCol *colElem = static_cast<RenderTableCol *>(child); 0742 int span = colElem->span(); 0743 if (!colElem->firstChild()) { 0744 int startCol = cCol; 0745 int endCol = cCol + span - 1; 0746 cCol += span; 0747 if (cCol > col) { 0748 if (startEdge) { 0749 *startEdge = startCol == col; 0750 } 0751 if (endEdge) { 0752 *endEdge = endCol == col; 0753 } 0754 return colElem; 0755 } 0756 } 0757 0758 RenderObject *next = child->firstChild(); 0759 if (!next) { 0760 next = child->nextSibling(); 0761 } 0762 if (!next && child->parent()->isTableCol()) { 0763 next = child->parent()->nextSibling(); 0764 } 0765 child = next; 0766 } else if (child == tCaption) { 0767 child = child->nextSibling(); 0768 } else { 0769 break; 0770 } 0771 } 0772 return nullptr; 0773 } 0774 0775 void RenderTable::recalcSections() 0776 { 0777 tCaption = nullptr; 0778 head = foot = firstBody = nullptr; 0779 has_col_elems = false; 0780 0781 RenderObject *child = firstChild(); 0782 // We need to get valid pointers to caption, head, foot and firstbody again 0783 while (child) { 0784 switch (child->style()->display()) { 0785 case TABLE_CAPTION: 0786 if (!tCaption && child->isRenderBlock()) { 0787 tCaption = static_cast<RenderBlock *>(child); 0788 tCaption->setNeedsLayout(true); 0789 } 0790 break; 0791 case TABLE_COLUMN: 0792 case TABLE_COLUMN_GROUP: 0793 has_col_elems = true; 0794 break; 0795 case TABLE_HEADER_GROUP: 0796 if (child->isTableSection()) { 0797 RenderTableSection *section = static_cast<RenderTableSection *>(child); 0798 if (!head) { 0799 head = section; 0800 } else if (!firstBody) { 0801 firstBody = section; 0802 } 0803 if (section->needCellRecalc) { 0804 section->recalcCells(); 0805 } 0806 } 0807 break; 0808 case TABLE_FOOTER_GROUP: 0809 if (child->isTableSection()) { 0810 RenderTableSection *section = static_cast<RenderTableSection *>(child); 0811 if (!foot) { 0812 foot = section; 0813 } else if (!firstBody) { 0814 firstBody = section; 0815 } 0816 if (section->needCellRecalc) { 0817 section->recalcCells(); 0818 } 0819 } 0820 break; 0821 case TABLE_ROW_GROUP: 0822 if (child->isTableSection()) { 0823 RenderTableSection *section = static_cast<RenderTableSection *>(child); 0824 if (!firstBody) { 0825 firstBody = section; 0826 } 0827 if (section->needCellRecalc) { 0828 section->recalcCells(); 0829 } 0830 } 0831 break; 0832 default: 0833 break; 0834 } 0835 child = child->nextSibling(); 0836 } 0837 0838 int maxCols = 0; 0839 for (RenderObject *child = firstChild(); child; child = child->nextSibling()) { 0840 if (child->isTableSection()) { 0841 RenderTableSection *section = static_cast<RenderTableSection *>(child); 0842 int sectionCols = section->numColumns(); 0843 if (sectionCols > maxCols) { 0844 maxCols = sectionCols; 0845 } 0846 } 0847 } 0848 0849 columns.resize(maxCols); 0850 columnPos.resize(maxCols + 1); 0851 0852 needSectionRecalc = false; 0853 setNeedsLayout(true); 0854 } 0855 0856 RenderObject *RenderTable::removeChildNode(RenderObject *child) 0857 { 0858 setNeedSectionRecalc(); 0859 return RenderContainer::removeChildNode(child); 0860 } 0861 0862 int RenderTable::borderLeft() const 0863 { 0864 if (collapseBorders()) { 0865 // FIXME: For strict mode, returning 0 is correct, since the table border half spills into the margin, 0866 // but I'm working to get this changed. For now, follow the spec. 0867 return 0; 0868 } 0869 return RenderBlock::borderLeft(); 0870 } 0871 0872 int RenderTable::borderRight() const 0873 { 0874 if (collapseBorders()) { 0875 // FIXME: For strict mode, returning 0 is correct, since the table border half spills into the margin, 0876 // but I'm working to get this changed. For now, follow the spec. 0877 return 0; 0878 } 0879 return RenderBlock::borderRight(); 0880 } 0881 0882 int RenderTable::borderTop() const 0883 { 0884 if (collapseBorders()) { 0885 // FIXME: For strict mode, returning 0 is correct, since the table border half spills into the margin, 0886 // but I'm working to get this changed. For now, follow the spec. 0887 return 0; 0888 } 0889 return RenderBlock::borderTop(); 0890 } 0891 0892 int RenderTable::borderBottom() const 0893 { 0894 if (collapseBorders()) { 0895 // FIXME: For strict mode, returning 0 is correct, since the table border half spills into the margin, 0896 // but I'm working to get this changed. For now, follow the spec. 0897 return 0; 0898 } 0899 return RenderBlock::borderBottom(); 0900 } 0901 0902 RenderTableSection *RenderTable::sectionAbove(const RenderTableSection *section, bool skipEmptySections) 0903 { 0904 if (needSectionRecalc) { 0905 recalcSections(); 0906 } 0907 0908 if (section == head) { 0909 return nullptr; 0910 } 0911 RenderObject *prevSection = section == foot ? lastChild() : section->previousSibling(); 0912 while (prevSection) { 0913 if (prevSection->isTableSection() && prevSection != head && prevSection != foot && (!skipEmptySections || static_cast<RenderTableSection *>(prevSection)->numRows())) { 0914 break; 0915 } 0916 prevSection = prevSection->previousSibling(); 0917 } 0918 if (!prevSection && head && (!skipEmptySections || head->numRows())) { 0919 prevSection = head; 0920 } 0921 return static_cast<RenderTableSection *>(prevSection); 0922 } 0923 0924 RenderTableSection *RenderTable::sectionBelow(const RenderTableSection *section, bool skipEmptySections) 0925 { 0926 if (needSectionRecalc) { 0927 recalcSections(); 0928 } 0929 0930 if (section == foot) { 0931 return nullptr; 0932 } 0933 RenderObject *nextSection = section == head ? firstChild() : section->nextSibling(); 0934 while (nextSection) { 0935 if (nextSection->isTableSection() && nextSection != head && nextSection != foot && (!skipEmptySections || static_cast<RenderTableSection *>(nextSection)->numRows())) { 0936 break; 0937 } 0938 nextSection = nextSection->nextSibling(); 0939 } 0940 if (!nextSection && foot && (!skipEmptySections || foot->numRows())) { 0941 nextSection = foot; 0942 } 0943 return static_cast<RenderTableSection *>(nextSection); 0944 } 0945 0946 RenderTableCell *RenderTable::cellAbove(const RenderTableCell *cell) 0947 { 0948 if (needSectionRecalc) { 0949 recalcSections(); 0950 } 0951 0952 // Find the section and row to look in 0953 int r = cell->row(); 0954 RenderTableSection *section = nullptr; 0955 int rAbove = 0; 0956 if (r > 0) { 0957 // cell is not in the first row, so use the above row in its own section 0958 section = cell->section(); 0959 rAbove = r - 1; 0960 } else { 0961 section = sectionAbove(cell->section(), true); 0962 if (section) { 0963 rAbove = section->numRows() - 1; 0964 } 0965 } 0966 0967 // Look up the cell in the section's grid, which requires effective col index 0968 if (section) { 0969 int effCol = colToEffCol(cell->col()); 0970 RenderTableCell *aboveCell; 0971 // If we hit a span back up to a real cell. 0972 do { 0973 aboveCell = section->cellAt(rAbove, effCol); 0974 effCol--; 0975 } while (aboveCell == (RenderTableCell *) - 1 && effCol >= 0); 0976 return (aboveCell == (RenderTableCell *) - 1) ? nullptr : aboveCell; 0977 } else { 0978 return nullptr; 0979 } 0980 } 0981 0982 RenderTableCell *RenderTable::cellBelow(const RenderTableCell *cell) 0983 { 0984 if (needSectionRecalc) { 0985 recalcSections(); 0986 } 0987 0988 // Find the section and row to look in 0989 int r = cell->row() + cell->rowSpan() - 1; 0990 RenderTableSection *section = nullptr; 0991 int rBelow = 0; 0992 if (r < cell->section()->numRows() - 1) { 0993 // The cell is not in the last row, so use the next row in the section. 0994 section = cell->section(); 0995 rBelow = r + 1; 0996 } else { 0997 section = sectionBelow(cell->section(), true); 0998 if (section) { 0999 rBelow = 0; 1000 } 1001 } 1002 1003 // Look up the cell in the section's grid, which requires effective col index 1004 if (section) { 1005 int effCol = colToEffCol(cell->col()); 1006 RenderTableCell *belowCell; 1007 // If we hit a colspan back up to a real cell. 1008 do { 1009 belowCell = section->cellAt(rBelow, effCol); 1010 effCol--; 1011 } while (belowCell == (RenderTableCell *) - 1 && effCol >= 0); 1012 return (belowCell == (RenderTableCell *) - 1) ? nullptr : belowCell; 1013 } else { 1014 return nullptr; 1015 } 1016 } 1017 1018 RenderTableCell *RenderTable::cellBefore(const RenderTableCell *cell) 1019 { 1020 if (needSectionRecalc) { 1021 recalcSections(); 1022 } 1023 1024 RenderTableSection *section = cell->section(); 1025 int effCol = colToEffCol(cell->col()); 1026 if (effCol == 0) { 1027 return nullptr; 1028 } 1029 1030 // If we hit a colspan back up to a real cell. 1031 RenderTableCell *prevCell; 1032 do { 1033 prevCell = section->cellAt(cell->row(), effCol - 1); 1034 effCol--; 1035 } while (prevCell == (RenderTableCell *) - 1 && effCol >= 0); 1036 return (prevCell == (RenderTableCell *) - 1) ? nullptr : prevCell; 1037 } 1038 1039 RenderTableCell *RenderTable::cellAfter(const RenderTableCell *cell) 1040 { 1041 if (needSectionRecalc) { 1042 recalcSections(); 1043 } 1044 1045 int effCol = colToEffCol(cell->col() + cell->colSpan()); 1046 if (effCol >= numEffCols()) { 1047 return nullptr; 1048 } 1049 RenderTableCell *result = cell->section()->cellAt(cell->row(), effCol); 1050 return (result == (RenderTableCell *) - 1) ? nullptr : result; 1051 } 1052 1053 #ifdef ENABLE_DUMP 1054 void RenderTable::dump(QTextStream &stream, const QString &ind) const 1055 { 1056 RenderBlock::dump(stream, ind); 1057 1058 if (tCaption) { 1059 stream << " tCaption"; 1060 } 1061 if (head) { 1062 stream << " head"; 1063 } 1064 if (foot) { 1065 stream << " foot"; 1066 } 1067 1068 stream << " [cspans:"; 1069 for (int i = 0; i < columns.size(); i++) { 1070 stream << " " << columns[i].span; 1071 } 1072 stream << "]"; 1073 } 1074 1075 #endif 1076 1077 FindSelectionResult RenderTable::checkSelectionPoint(int _x, int _y, int _tx, int _ty, DOM::NodeImpl *&node, int &offset, SelPointState &state) 1078 { 1079 int off = offset; 1080 DOM::NodeImpl *nod = node; 1081 1082 FindSelectionResult pos; 1083 TableSectionIterator it(this); 1084 for (; *it; ++it) { 1085 pos = (*it)->checkSelectionPoint(_x, _y, _tx + m_x, _ty + m_y, nod, off, state); 1086 switch (pos) { 1087 case SelectionPointBeforeInLine: 1088 case SelectionPointInside: 1089 //qCDebug(KHTML_LOG) << "RenderTable::checkSelectionPoint " << this << " returning SelectionPointInside offset=" << offset; 1090 node = nod; 1091 offset = off; 1092 return SelectionPointInside; 1093 case SelectionPointBefore: 1094 //x,y is before this element -> stop here 1095 if (state.m_lastNode) { 1096 node = state.m_lastNode; 1097 offset = state.m_lastOffset; 1098 //qCDebug(KHTML_LOG) << "RenderTable::checkSelectionPoint " << this << " before this child " 1099 // << node << "-> returning SelectionPointInside, offset=" << offset; 1100 return SelectionPointInside; 1101 } else { 1102 node = nod; 1103 offset = off; 1104 //qCDebug(KHTML_LOG) << "RenderTable::checkSelectionPoint " << this << " before us -> returning SelectionPointBefore " << node << "/" << offset; 1105 return SelectionPointBefore; 1106 } 1107 break; 1108 case SelectionPointAfter: 1109 if (state.m_afterInLine) { 1110 break; 1111 } 1112 // fall through 1113 case SelectionPointAfterInLine: 1114 if (pos == SelectionPointAfterInLine) { 1115 state.m_afterInLine = true; 1116 } 1117 //qCDebug(KHTML_LOG) << "RenderTable::checkSelectionPoint: selection after: " << nod << " offset: " << off << " afterInLine: " << state.m_afterInLine; 1118 state.m_lastNode = nod; 1119 state.m_lastOffset = off; 1120 // No "return" here, obviously. We must keep looking into the children. 1121 break; 1122 } 1123 } 1124 // If we are after the last child, return lastNode/lastOffset 1125 // But lastNode can be 0L if there is no child, for instance. 1126 if (state.m_lastNode) { 1127 node = state.m_lastNode; 1128 offset = state.m_lastOffset; 1129 } 1130 // Fallback 1131 return SelectionPointAfter; 1132 } 1133 1134 // -------------------------------------------------------------------------- 1135 1136 RenderTableSection::RenderTableSection(DOM::NodeImpl *node) 1137 : RenderBox(node) 1138 { 1139 // init RenderObject attributes 1140 setInline(false); // our object is not Inline 1141 cCol = 0; 1142 cRow = -1; 1143 needCellRecalc = false; 1144 } 1145 1146 RenderTableSection::~RenderTableSection() 1147 { 1148 clearGrid(); 1149 } 1150 1151 void RenderTableSection::detach() 1152 { 1153 // recalc cell info because RenderTable has unguarded pointers 1154 // stored that point to this RenderTableSection. 1155 if (table()) { 1156 table()->setNeedSectionRecalc(); 1157 } 1158 1159 RenderBox::detach(); 1160 } 1161 1162 void RenderTableSection::setStyle(RenderStyle *_style) 1163 { 1164 // we don't allow changing this one 1165 if (style()) { 1166 _style->setDisplay(style()->display()); 1167 } else if (_style->display() != TABLE_FOOTER_GROUP && _style->display() != TABLE_HEADER_GROUP) { 1168 _style->setDisplay(TABLE_ROW_GROUP); 1169 } 1170 1171 RenderBox::setStyle(_style); 1172 } 1173 1174 void RenderTableSection::addChild(RenderObject *child, RenderObject *beforeChild) 1175 { 1176 #ifdef DEBUG_LAYOUT 1177 qCDebug(KHTML_LOG) << renderName() << "(TableSection)::addChild( " << child->renderName() << ", beforeChild=" << 1178 (beforeChild ? beforeChild->renderName() : "0") << " )"; 1179 #endif 1180 if (!child->isTableRow()) { 1181 // TBODY > FORM quirk (???) 1182 if (child->element() && child->element()->isHTMLElement() && child->element()->id() == ID_FORM && 1183 static_cast<HTMLFormElementImpl *>(child->element())->isMalformed()) { 1184 RenderContainer::addChild(child, beforeChild); 1185 return; 1186 } 1187 1188 RenderObject *last = beforeChild; 1189 if (!last) { 1190 last = lastChild(); 1191 } 1192 if (last && last->isAnonymous()) { 1193 last->addChild(child); 1194 return; 1195 } 1196 1197 // If beforeChild is inside an anonymous cell/row, insert into the cell or into 1198 // the anonymous row containing it, if there is one. 1199 RenderObject *lastBox = last; 1200 while (lastBox && lastBox->parent()->isAnonymous() && !lastBox->isTableRow()) { 1201 lastBox = lastBox->parent(); 1202 } 1203 if (lastBox && lastBox->isAnonymous()) { 1204 lastBox->addChild(child, beforeChild); 1205 return; 1206 } 1207 1208 RenderObject *row = new(renderArena()) RenderTableRow(document() /* anonymous table */); 1209 RenderStyle *newStyle = new RenderStyle(); 1210 newStyle->inheritFrom(style()); 1211 newStyle->setDisplay(TABLE_ROW); 1212 row->setStyle(newStyle); 1213 addChild(row, beforeChild); 1214 row->addChild(child); 1215 return; 1216 } 1217 1218 if (beforeChild) { 1219 setNeedCellRecalc(); 1220 } 1221 1222 cRow++; 1223 cCol = 0; 1224 1225 ensureRows(cRow + 1); 1226 KHTMLAssert(child->isTableRow()); 1227 grid[cRow].rowRenderer = static_cast<RenderTableRow *>(child); 1228 1229 if (!beforeChild) { 1230 grid[cRow].height = child->style()->height(); 1231 if (grid[cRow].height.isRelative()) { 1232 grid[cRow].height = Length(); 1233 } 1234 } 1235 1236 RenderContainer::addChild(child, beforeChild); 1237 } 1238 1239 void RenderTableSection::ensureRows(int numRows) 1240 { 1241 int nRows = grid.size(); 1242 int nCols = table()->numEffCols(); 1243 if (numRows > nRows) { 1244 grid.resize(numRows); 1245 for (int r = nRows; r < numRows; r++) { 1246 grid[r].row = new Row(nCols); 1247 grid[r].row->fill(nullptr); 1248 grid[r].rowRenderer = nullptr; 1249 grid[r].baseLine = 0; 1250 grid[r].height = Length(); 1251 } 1252 } 1253 1254 } 1255 1256 void RenderTableSection::addCell(RenderTableCell *cell, RenderTableRow *row) 1257 { 1258 int rSpan = cell->rowSpan(); 1259 int cSpan = cell->colSpan(); 1260 const QVector<RenderTable::ColumnStruct> &columns = table()->columns; 1261 int nCols = columns.size(); 1262 1263 // ### mozilla still seems to do the old HTML way, even for strict DTD 1264 // (see the annotation on table cell layouting in the CSS specs and the testcase below: 1265 // <TABLE border> 1266 // <TR><TD>1 <TD rowspan="2">2 <TD>3 <TD>4 1267 // <TR><TD colspan="2">5 1268 // </TABLE> 1269 while (cCol < nCols && cellAt(cRow, cCol)) { 1270 ++cCol; 1271 } 1272 1273 // qDebug("adding cell at %d/%d span=(%d/%d)", cRow, cCol, rSpan, cSpan ); 1274 1275 if (rSpan == 1) { 1276 // we ignore height settings on rowspan cells 1277 Length height = cell->style()->height(); 1278 if (height.isPositive() || (height.isRelative() && (height.isPositive() || height.isZero()))) { 1279 Length cRowHeight = grid[cRow].height; 1280 switch (height.type()) { 1281 case Percent: 1282 if (!cRowHeight.isPercent() || 1283 (cRowHeight.isPercent() && cRowHeight.rawValue() < height.rawValue())) { 1284 grid[cRow].height = height; 1285 } 1286 break; 1287 case Fixed: 1288 if (cRowHeight.type() < Percent || 1289 (cRowHeight.isFixed() && cRowHeight.value() < height.value())) { 1290 grid[cRow].height = height; 1291 } 1292 break; 1293 case Relative: 1294 #if 0 1295 // we treat this as variable. This is correct according to HTML4, as it only specifies length for the height. 1296 if (cRowHeight.type() == Auto || 1297 (cRowHeight.type() == Relative && cRowHeight.value() < height.value())) { 1298 grid[cRow].height = height; 1299 } 1300 break; 1301 #endif 1302 default: 1303 break; 1304 } 1305 } 1306 } 1307 1308 //What:Postion the cell 1309 //How: we take care of special case when colspan or rowspan equals 0 1310 //setting an initial "guess" of a colspan and rowspan, look at 1311 //https://www.w3.org/TR/html401/struct/tables.html for details on cells with span = 0 1312 //after that position the cell normally, we do it to tell the cell where it is 1313 //and tell other cells where they can't be located (marking the cells as -1), 1314 //later taking the span into account (and in other function) the cell is 1315 //then painted (that's why we need to set the colspan and rowspan properly 1316 //when any of them is zero, so that the cell is properly painted) 1317 1318 // make sure we have enough rows 1319 ensureRows(cRow + (rSpan ? rSpan : 1)); 1320 1321 grid[cRow].rowRenderer = row; 1322 1323 int col = cCol; 1324 // tell the cell where it is 1325 RenderTableCell *set = cell; 1326 // qCDebug(KHTML_LOG)<<"row"<<cRow<<"col"<<cCol; 1327 // qCDebug(KHTML_LOG)<<"cSpan"<<cSpan<<"rSpan"<<rSpan; 1328 1329 //check whether we need to update any of the cells with span = 0 1330 QList< int > columnsToAvoid; 1331 if (!cellsWithColSpanZero.isEmpty()) { 1332 //Update any column which its last span update was in a previous column 1333 int lowestCol = cellsWithColSpanZero.lowerBound(0).key(); 1334 if (lowestCol < cCol) { 1335 //add the columns for this cell 1336 if (cCol >= nCols) { 1337 table()->appendColumn(cSpan); 1338 nCols = columns.size(); 1339 } 1340 //check and update all the cells that are updated to a previous column 1341 while (lowestCol < nCols) { 1342 while (RenderTableCell *cell = cellsWithColSpanZero.take(lowestCol)) { 1343 const int cellRow = cell->row(); 1344 //const int cellRowSpan = cell->rowSpan(); 1345 int finalSpan = cell->colSpan(); 1346 for (int i = lowestCol; i < nCols; ++i) { 1347 if (!cellAt(cellRow, i)) { 1348 cellAt(cellRow, i) = (RenderTableCell *) - 1; 1349 } 1350 ++finalSpan; 1351 } 1352 cell->setColSpan(finalSpan); 1353 cellsWithColSpanZero.insertMulti(nCols, cell); 1354 } 1355 lowestCol = cellsWithColSpanZero.lowerBound(0).key(); 1356 } 1357 } 1358 } 1359 1360 if (!cellsWithRowSpanZero.isEmpty()) { 1361 if (cellsWithRowSpanZero.contains(cRow)) { 1362 //No need to check if we have enough columns, we already found the first cell 1363 //when rowspan="0", and as such, we've already inserted it 1364 while (RenderTableCell *cell = cellsWithRowSpanZero.take(cRow)) { 1365 const int cellCol = cell->col(); 1366 const int finalCol = cellCol + cell->colSpan() - 1; 1367 RenderTableCell *set = cell; 1368 for (int i = cellCol; i <= finalCol; ++i) { 1369 if (!cellAt(cRow, i)) { 1370 cellAt(cRow, i) = set; 1371 } 1372 set = (RenderTableCell *) - 1; 1373 columnsToAvoid << i; 1374 } 1375 cell->setRowSpan(cell->rowSpan() + 1); 1376 //mark it to be inserted in next row 1377 cellsWithRowSpanZero.insertMulti(cRow + 1, cell); 1378 } 1379 } 1380 } 1381 1382 //Save the column if the its span is 0 1383 if (!cSpan) { 1384 //Check if we only span in the current colgroup 1385 if (RenderTableCol *colgroup = table()->colElement(cCol)) { 1386 //Calculate the correct span and then handle the cell normally 1387 1388 int firstColumnOfColgroup = cCol; 1389 while (--firstColumnOfColgroup >= 0 && colgroup == table()->colElement(firstColumnOfColgroup)); 1390 ++firstColumnOfColgroup; 1391 1392 int alreadyUsedSpan = 0; 1393 RenderTableCell *colgroupCell = cellAt(cRow, firstColumnOfColgroup + alreadyUsedSpan); 1394 while (firstColumnOfColgroup + alreadyUsedSpan < cCol) { 1395 alreadyUsedSpan += colgroupCell->colSpan(); 1396 colgroupCell = cellAt(cRow, firstColumnOfColgroup + alreadyUsedSpan); 1397 } 1398 1399 const int finalSpan = colgroup->span() - alreadyUsedSpan; 1400 cell->setColSpan(finalSpan); 1401 1402 //We know exactly the cSpan so we can handle the cell as a normal cell 1403 //unless, of course, the rowspan is also 0 1404 cSpan = finalSpan; 1405 } 1406 //or if we span in the whole table and mark it as inserted till 1407 //this column and updated whenever another column is inserted 1408 else { 1409 //We define the proper span and let the other code handle it 1410 const int finalSpan = nCols - cCol; 1411 cSpan = finalSpan; 1412 1413 cell->setColSpan(finalSpan); 1414 1415 cellsWithColSpanZero.insertMulti(cCol + finalSpan - 1, cell); 1416 } 1417 } 1418 1419 if (!rSpan) { 1420 //For now we span 1 1421 rSpan = 1; 1422 cell->setRowSpan(1); 1423 1424 //mark it to be inserted in next row 1425 cellsWithRowSpanZero.insertMulti(cRow + 1, cell); 1426 } 1427 1428 while (cSpan) { 1429 int currentSpan; 1430 if (cCol >= nCols) { 1431 table()->appendColumn(cSpan); 1432 currentSpan = cSpan; 1433 } else { 1434 if (cSpan < columns[cCol].span) { 1435 table()->splitColumn(cCol, cSpan); 1436 } 1437 currentSpan = columns[cCol].span; 1438 } 1439 1440 while (columnsToAvoid.contains(cCol)) { 1441 ++cCol; 1442 } 1443 1444 int r = 0; 1445 while (r < rSpan) { 1446 if (!cellAt(cRow + r, cCol)) { 1447 // qDebug(" adding cell at %d, %d", cRow + r, cCol ); 1448 cellAt(cRow + r, cCol) = set; 1449 } 1450 ++r; 1451 } 1452 ++cCol; 1453 cSpan -= currentSpan; 1454 set = (RenderTableCell *) - 1; 1455 } 1456 1457 if (cell) { 1458 cell->setRow(cRow); 1459 cell->setCol(table()->effColToCol(col)); 1460 } 1461 } 1462 1463 void RenderTableSection::setCellWidths() 1464 { 1465 #ifdef DEBUG_LAYOUT 1466 qCDebug(KHTML_LOG) << renderName() << "(Table, this=0x" << this << ")::setCellWidths()"; 1467 #endif 1468 const QVector<int> &columnPos = table()->columnPos; 1469 1470 int rows = grid.size(); 1471 for (int i = 0; i < rows; i++) { 1472 Row &row = *grid[i].row; 1473 int cols = row.size(); 1474 for (int j = 0; j < cols; j++) { 1475 RenderTableCell *cell = row[j]; 1476 // qDebug("cell[%d,%d] = %p", i, j, cell ); 1477 if (!cell || cell == (RenderTableCell *) - 1) { 1478 continue; 1479 } 1480 int endCol = j; 1481 int cspan = cell->colSpan(); 1482 while (cspan && endCol < cols) { 1483 cspan -= table()->columns[endCol].span; 1484 endCol++; 1485 } 1486 int w = columnPos[endCol] - columnPos[j] - table()->borderHSpacing(); 1487 #ifdef DEBUG_LAYOUT 1488 qCDebug(KHTML_LOG) << "setting width of cell " << cell << " " << cell->row() << "/" << cell->col() << " to " << w << " colspan=" << cell->colSpan() << " start=" << j << " end=" << endCol; 1489 #endif 1490 int oldWidth = cell->width(); 1491 if (w != oldWidth) { 1492 cell->setNeedsLayout(true); 1493 cell->setWidth(w); 1494 } 1495 } 1496 } 1497 } 1498 1499 void RenderTableSection::calcRowHeight() 1500 { 1501 int indx; 1502 RenderTableCell *cell; 1503 1504 int totalRows = grid.size(); 1505 int vspacing = table()->borderVSpacing(); 1506 1507 rowPos.resize(totalRows + 1); 1508 rowPos[0] = vspacing + borderTop(); 1509 1510 for (int r = 0; r < totalRows; r++) { 1511 rowPos[r + 1] = 0; 1512 1513 int baseline = 0; 1514 int bdesc = 0; 1515 grid[r].baseLine = 0; 1516 // qDebug("height of row %d is %d/%d", r, grid[r].height.value, grid[r].height.type ); 1517 int ch = grid[r].height.minWidth(0); 1518 int pos = rowPos[r] + ch + (grid[r].rowRenderer ? vspacing : 0); 1519 1520 if (pos > rowPos[r + 1]) { 1521 rowPos[r + 1] = pos; 1522 } 1523 1524 Row *row = grid[r].row; 1525 int totalCols = row->size(); 1526 int totalRows = grid.size(); 1527 bool pagedMode = canvas()->pagedMode(); 1528 1529 grid[r].needFlex = false; 1530 1531 for (int c = 0; c < totalCols; c++) { 1532 cell = cellAt(r, c); 1533 if (!cell || cell == (RenderTableCell *) - 1) { 1534 continue; 1535 } 1536 if (r < totalRows - 1 && cellAt(r + 1, c) == cell) { 1537 continue; 1538 } 1539 1540 if ((indx = r - cell->rowSpan() + 1) < 0) { 1541 indx = 0; 1542 } 1543 1544 if (cell->cellPercentageHeight() != -1) { 1545 cell->setCellPercentageHeight(-1); 1546 cell->setChildNeedsLayout(true, false); 1547 if (cell->hasFlexedAnonymous()) { 1548 for (RenderObject *o = cell->firstChild(); o; o = o->nextSibling()) 1549 if (o->isAnonymousBlock()) { 1550 o->setChildNeedsLayout(true, false); 1551 } 1552 } 1553 if (pagedMode) { 1554 cell->setNeedsLayout(true); 1555 } 1556 cell->layoutIfNeeded(); 1557 } 1558 1559 ch = cell->style()->height().width(0); 1560 if (cell->height() > ch) { 1561 ch = cell->height(); 1562 } 1563 1564 if (!cell->style()->height().isAuto()) { 1565 grid[r].needFlex = true; 1566 } 1567 1568 pos = rowPos[indx] + ch + (grid[r].rowRenderer ? vspacing : 0); 1569 1570 if (pos > rowPos[r + 1]) { 1571 rowPos[r + 1] = pos; 1572 } 1573 1574 // find out the baseline 1575 EVerticalAlign va = cell->style()->verticalAlign(); 1576 if (va == BASELINE || va == TEXT_BOTTOM || va == TEXT_TOP 1577 || va == SUPER || va == SUB) { 1578 int b = cell->baselinePosition(); 1579 if (b > cell->borderTop() + cell->paddingTop()) { 1580 if (b > baseline) { 1581 baseline = b; 1582 } 1583 1584 int td = rowPos[ indx ] + ch - b; 1585 if (td > bdesc) { 1586 bdesc = td; 1587 } 1588 } 1589 } 1590 } 1591 1592 //do we have baseline aligned elements? 1593 if (baseline) { 1594 // increase rowheight if baseline requires 1595 int bRowPos = baseline + bdesc + (grid[r].rowRenderer ? vspacing : 0); 1596 if (rowPos[r + 1] < bRowPos) { 1597 rowPos[r + 1] = bRowPos; 1598 } 1599 1600 grid[r].baseLine = baseline; 1601 } 1602 1603 if (rowPos[r + 1] < rowPos[r]) { 1604 rowPos[r + 1] = rowPos[r]; 1605 } 1606 // qDebug("rowpos(%d)=%d", r, rowPos[r] ); 1607 } 1608 } 1609 1610 int RenderTableSection::layoutRows(int toAdd) 1611 { 1612 int rHeight; 1613 int rindx; 1614 int totalRows = grid.size(); 1615 int hspacing = table()->borderHSpacing(); 1616 int vspacing = table()->borderVSpacing(); 1617 1618 // Set the width of our section now. The rows will also be this width. 1619 m_width = table()->contentWidth(); 1620 1621 if (markedForRepaint()) { 1622 repaintDuringLayout(); 1623 setMarkedForRepaint(false); 1624 } 1625 1626 if (toAdd && totalRows && (rowPos[totalRows] || !nextSibling())) { 1627 1628 int totalHeight = rowPos[totalRows] + toAdd; 1629 // qDebug("layoutRows: totalHeight = %d", totalHeight ); 1630 1631 int dh = toAdd; 1632 int totalPercent = 0; 1633 int numAuto = 0; 1634 for (int r = 0; r < totalRows; r++) { 1635 if (grid[r].height.isAuto() && !emptyRow(r)) { 1636 numAuto++; 1637 } else if (grid[r].height.isPercent()) { 1638 totalPercent += grid[r].height.rawValue(); 1639 } 1640 } 1641 if (totalPercent) { 1642 // qDebug("distributing %d over percent rows totalPercent=%d", dh, totalPercent ); 1643 // try to satisfy percent 1644 int add = 0; 1645 if (totalPercent > 100 * PERCENT_SCALE_FACTOR) { 1646 totalPercent = 100 * PERCENT_SCALE_FACTOR; 1647 } 1648 int rh = rowPos[1] - rowPos[0]; 1649 for (int r = 0; r < totalRows; r++) { 1650 if (totalPercent > 0 && grid[r].height.isPercent()) { 1651 int toAdd = qMin(dh, (totalHeight * grid[r].height.rawValue() / (100 * PERCENT_SCALE_FACTOR)) - rh); 1652 // If toAdd is negative, then we don't want to shrink the row (this bug 1653 // affected Outlook Web Access). 1654 toAdd = qMax(0, toAdd); 1655 add += toAdd; 1656 dh -= toAdd; 1657 totalPercent -= grid[r].height.rawValue(); 1658 // qDebug( "adding %d to row %d", toAdd, r ); 1659 } 1660 if (r < totalRows - 1) { 1661 rh = rowPos[r + 2] - rowPos[r + 1]; 1662 } 1663 rowPos[r + 1] += add; 1664 } 1665 } 1666 if (numAuto) { 1667 // distribute over non-empty variable rows 1668 // qDebug("distributing %d over variable rows numAuto=%d", dh, numAuto ); 1669 int add = 0; 1670 int toAdd = dh / numAuto; 1671 for (int r = 0; r < totalRows; r++) { 1672 if (grid[r].height.isAuto() && !emptyRow(r)) { 1673 add += toAdd; 1674 } 1675 rowPos[r + 1] += add; 1676 } 1677 dh -= add; 1678 } 1679 if (dh > 0 && rowPos[totalRows]) { 1680 // if some left overs, distribute weighted. 1681 int tot = rowPos[totalRows]; 1682 int add = 0; 1683 int prev = rowPos[0]; 1684 for (int r = 0; r < totalRows; r++) { 1685 //weight with the original height 1686 add += dh * (rowPos[r + 1] - prev) / tot; 1687 prev = rowPos[r + 1]; 1688 rowPos[r + 1] += add; 1689 } 1690 dh -= add; 1691 } 1692 if (dh > totalRows) { 1693 // distribute to tables with all empty rows 1694 int add = 0; 1695 int toAdd = dh / totalRows; 1696 for (int r = 0; r < totalRows; r++) { 1697 add += toAdd; 1698 rowPos[r + 1] += add; 1699 } 1700 dh -= add; 1701 } 1702 // Finally distribute round-off values 1703 if (dh > 0) { 1704 // There is not enough for every row 1705 int add = 0; 1706 for (int r = 0; r < totalRows; r++) { 1707 if (add < dh) { 1708 add++; 1709 } 1710 rowPos[r + 1] += add; 1711 } 1712 dh -= add; 1713 } 1714 assert(dh == 0); 1715 } 1716 1717 int leftOffset = borderLeft() + hspacing; 1718 1719 int nEffCols = table()->numEffCols(); 1720 for (int r = 0; r < totalRows; r++) { 1721 Row *row = grid[r].row; 1722 int totalCols = row->size(); 1723 1724 // Set the row's x/y position and width/height. 1725 if (grid[r].rowRenderer) { 1726 grid[r].rowRenderer->setPos(0, rowPos[r]); 1727 grid[r].rowRenderer->setWidth(m_width); 1728 grid[r].rowRenderer->setHeight(rowPos[r + 1] - rowPos[r] - vspacing); 1729 } 1730 1731 for (int c = 0; c < nEffCols; c++) { 1732 RenderTableCell *cell = cellAt(r, c); 1733 if (!cell || cell == (RenderTableCell *) - 1) { 1734 continue; 1735 } 1736 if (r < totalRows - 1 && cell == cellAt(r + 1, c)) { 1737 continue; 1738 } 1739 1740 if ((rindx = r - cell->rowSpan() + 1) < 0) { 1741 rindx = 0; 1742 } 1743 1744 rHeight = rowPos[r + 1] - rowPos[rindx] - vspacing; 1745 1746 // Force percent height children to lay themselves out again. 1747 // This will cause, e.g., textareas to grow to 1748 // fill the area. FIXME: <div>s and blocks still don't 1749 // work right. We'll need to have an efficient way of 1750 // invalidating all percent height objects in a render subtree. 1751 // For now, we just handle immediate children. -dwh 1752 1753 bool flexAllChildren = grid[r].needFlex || (!table()->style()->height().isAuto() && rHeight != cell->height()); 1754 cell->setHasFlexedAnonymous(false); 1755 if (flexAllChildren && flexCellChildren(cell)) { 1756 cell->setCellPercentageHeight(qMax(0, 1757 rHeight - cell->borderTop() - cell->paddingTop() - 1758 cell->borderBottom() - cell->paddingBottom())); 1759 cell->layoutIfNeeded(); 1760 1761 // If the baseline moved, we may have to update the data for our row. Find out the new baseline. 1762 EVerticalAlign va = cell->style()->verticalAlign(); 1763 if (va == BASELINE || va == TEXT_BOTTOM || va == TEXT_TOP || va == SUPER || va == SUB) { 1764 int b = cell->baselinePosition(); 1765 if (b > cell->borderTop() + cell->paddingTop()) { 1766 grid[r].baseLine = qMax(grid[r].baseLine, b); 1767 } 1768 } 1769 } 1770 { 1771 #ifdef DEBUG_LAYOUT 1772 qCDebug(KHTML_LOG) << "setting position " << r << "/" << c << ": " 1773 << table()->columnPos[c] /*+ padding */ << "/" << rowPos[rindx] << " height=" << rHeight; 1774 #endif 1775 1776 EVerticalAlign va = cell->style()->verticalAlign(); 1777 int te = 0; 1778 switch (va) { 1779 case SUB: 1780 case SUPER: 1781 case TEXT_TOP: 1782 case TEXT_BOTTOM: 1783 case BASELINE: 1784 te = getBaseline(r) - cell->baselinePosition(); 1785 break; 1786 case TOP: 1787 te = 0; 1788 break; 1789 case MIDDLE: 1790 te = (rHeight - cell->height()) / 2; 1791 break; 1792 case BOTTOM: 1793 te = rHeight - cell->height(); 1794 break; 1795 default: 1796 break; 1797 } 1798 te = qMax(0, te); 1799 #ifdef DEBUG_LAYOUT 1800 qCDebug(KHTML_LOG) << "CELL " << cell << " te=" << te << ", be=" << rHeight - cell->height() - te << ", rHeight=" << rHeight << ", valign=" << va; 1801 #endif 1802 cell->setCellTopExtra(te); 1803 cell->setCellBottomExtra(rHeight - cell->height() - te); 1804 } 1805 if (style()->direction() == RTL) { 1806 cell->setPos( 1807 table()->columnPos[(int)totalCols] - 1808 table()->columnPos[table()->colToEffCol(cell->col() + cell->colSpan())] + 1809 leftOffset, 1810 0); 1811 } else { 1812 cell->setPos(table()->columnPos[c] + leftOffset, 0); 1813 } 1814 } 1815 } 1816 1817 m_height = rowPos[totalRows]; 1818 return m_height; 1819 } 1820 1821 bool RenderTableSection::flexCellChildren(RenderObject *p) const 1822 { 1823 if (!p) { 1824 return false; 1825 } 1826 RenderObject *o = p->firstChild(); 1827 bool didFlex = false; 1828 while (o) { 1829 if (!o->isText() && o->style()->height().isPercent()) { 1830 o->setNeedsLayout(true, false); 1831 p->setChildNeedsLayout(true, false); 1832 didFlex = true; 1833 } else if (o->isAnonymousBlock() && flexCellChildren(o)) { 1834 p->setChildNeedsLayout(true, false); 1835 if (p->isTableCell()) { 1836 static_cast<RenderTableCell *>(p)->setHasFlexedAnonymous(); 1837 } 1838 didFlex = true; 1839 } 1840 o = o->nextSibling(); 1841 } 1842 return didFlex; 1843 } 1844 1845 inline static RenderTableRow *firstTableRow(RenderObject *row) 1846 { 1847 while (row && !row->isTableRow()) { 1848 row = row->nextSibling(); 1849 } 1850 return static_cast<RenderTableRow *>(row); 1851 } 1852 1853 inline static RenderTableRow *nextTableRow(RenderObject *row) 1854 { 1855 row = row ? row->nextSibling() : row; 1856 while (row && !row->isTableRow()) { 1857 row = row->nextSibling(); 1858 } 1859 return static_cast<RenderTableRow *>(row); 1860 } 1861 1862 int RenderTableSection::lowestPosition(bool includeOverflowInterior, bool includeSelf) const 1863 { 1864 int bottom = RenderBox::lowestPosition(includeOverflowInterior, includeSelf); 1865 if (!includeOverflowInterior && hasOverflowClip()) { 1866 return bottom; 1867 } 1868 1869 for (RenderObject *row = firstChild(); row; row = row->nextSibling()) { 1870 for (RenderObject *cell = row->firstChild(); cell; cell = cell->nextSibling()) 1871 if (cell->isTableCell()) { 1872 int bp = row->yPos() + cell->lowestPosition(false); 1873 bottom = qMax(bottom, bp); 1874 } 1875 } 1876 1877 return bottom; 1878 } 1879 1880 int RenderTableSection::rightmostPosition(bool includeOverflowInterior, bool includeSelf) const 1881 { 1882 int right = RenderBox::rightmostPosition(includeOverflowInterior, includeSelf); 1883 if (!includeOverflowInterior && hasOverflowClip()) { 1884 return right; 1885 } 1886 1887 for (RenderObject *row = firstChild(); row; row = row->nextSibling()) { 1888 for (RenderObject *cell = row->firstChild(); cell; cell = cell->nextSibling()) 1889 if (cell->isTableCell()) { 1890 int rp = cell->xPos() + cell->rightmostPosition(false); 1891 right = qMax(right, rp); 1892 } 1893 } 1894 1895 return right; 1896 } 1897 1898 int RenderTableSection::leftmostPosition(bool includeOverflowInterior, bool includeSelf) const 1899 { 1900 int left = RenderBox::leftmostPosition(includeOverflowInterior, includeSelf); 1901 if (!includeOverflowInterior && hasOverflowClip()) { 1902 return left; 1903 } 1904 1905 for (RenderObject *row = firstChild(); row; row = row->nextSibling()) { 1906 for (RenderObject *cell = row->firstChild(); cell; cell = cell->nextSibling()) 1907 if (cell->isTableCell()) { 1908 int lp = cell->xPos() + cell->leftmostPosition(false); 1909 left = qMin(left, lp); 1910 } 1911 } 1912 1913 return left; 1914 } 1915 1916 int RenderTableSection::highestPosition(bool includeOverflowInterior, bool includeSelf) const 1917 { 1918 int top = RenderBox::highestPosition(includeOverflowInterior, includeSelf); 1919 if (!includeOverflowInterior && hasOverflowClip()) { 1920 return top; 1921 } 1922 1923 for (RenderObject *row = firstChild(); row; row = row->nextSibling()) { 1924 for (RenderObject *cell = row->firstChild(); cell; cell = cell->nextSibling()) 1925 if (cell->isTableCell()) { 1926 int hp = row->yPos() + cell->highestPosition(false); 1927 top = qMin(top, hp); 1928 } 1929 } 1930 1931 return top; 1932 } 1933 1934 // Search from first_row to last_row for the row containing y 1935 static unsigned int findRow(unsigned int first_row, unsigned int last_row, 1936 const QVector<int> &rowPos, int y) 1937 { 1938 unsigned int under = first_row; 1939 unsigned int over = last_row; 1940 int offset = (over - under) / 2; 1941 while (over - under > 1) { 1942 if (rowPos[under + offset] <= y) { 1943 under = under + offset; 1944 } else { 1945 over = under + offset; 1946 } 1947 offset = (over - under) / 2; 1948 } 1949 1950 assert(under == first_row || rowPos[under] <= y); 1951 assert(over == last_row || rowPos[over] > y); 1952 1953 return under; 1954 } 1955 1956 static void findRowCover(unsigned int &startrow, unsigned int &endrow, 1957 const QVector<int> &rowPos, 1958 int min_y, int max_y) 1959 { 1960 assert(max_y >= min_y); 1961 unsigned int totalRows = endrow; 1962 1963 unsigned int index = 0; 1964 // Initial binary search boost: 1965 if (totalRows >= 8) { 1966 int offset = (endrow - startrow) / 2; 1967 while (endrow - startrow > 1) { 1968 index = startrow + offset; 1969 if (rowPos[index] <= min_y) 1970 // index is below both min_y and max_y 1971 { 1972 startrow = index; 1973 } else if (rowPos[index] > max_y) 1974 // index is above both min_y and max_y 1975 { 1976 endrow = index; 1977 } else 1978 // index is within the selection 1979 { 1980 break; 1981 } 1982 offset = (endrow - startrow) / 2; 1983 } 1984 } 1985 1986 // Binary search for startrow 1987 startrow = findRow(startrow, endrow, rowPos, min_y); 1988 // Binary search for endrow 1989 endrow = findRow(startrow, endrow, rowPos, max_y) + 1; 1990 if (endrow > totalRows) { 1991 endrow = totalRows; 1992 } 1993 } 1994 1995 void RenderTableSection::paint(PaintInfo &pI, int tx, int ty) 1996 { 1997 unsigned int totalRows = grid.size(); 1998 unsigned int totalCols = table()->columns.size(); 1999 2000 tx += m_x; 2001 ty += m_y; 2002 2003 if (pI.phase == PaintActionOutline) { 2004 paintOutline(pI.p, tx, ty, width(), height(), style()); 2005 } 2006 2007 CollapsedBorderValue *cbs = table()->currentBorderStyle(); 2008 int cbsw2 = cbs ? cbs->width() / 2 : 0; 2009 int cbsw21 = cbs ? (cbs->width() + 1) / 2 : 0; 2010 2011 int x = pI.r.x(), y = pI.r.y(), w = pI.r.width(), h = pI.r.height(); 2012 // check which rows and cols are visible and only paint these 2013 int os = 2 * maximalOutlineSize(pI.phase); 2014 unsigned int startrow = 0; 2015 unsigned int endrow = totalRows; 2016 findRowCover(startrow, endrow, rowPos, y - os - ty - qMax(cbsw21, os), y + h + os - ty + qMax(cbsw2, os)); 2017 2018 // A binary search is probably not worthwhile for columns 2019 unsigned int startcol = 0; 2020 unsigned int endcol = totalCols; 2021 if (style()->direction() == LTR) { 2022 for (; startcol < totalCols; startcol++) { 2023 if (tx + table()->columnPos[startcol + 1] + qMax(cbsw21, os) > x - os) { 2024 break; 2025 } 2026 } 2027 for (; endcol > 0; endcol--) { 2028 if (tx + table()->columnPos[endcol - 1] - qMax(cbsw2, os) < x + w + os) { 2029 break; 2030 } 2031 } 2032 } 2033 if (startcol < endcol) { 2034 // draw the cells 2035 for (unsigned int r = startrow; r < endrow; r++) { 2036 // paint the row 2037 if (grid[r].rowRenderer) { 2038 int height = rowPos[r + 1] - rowPos[r] - table()->borderVSpacing(); 2039 grid[r].rowRenderer->paintRow(pI, tx, ty + rowPos[r], width(), height); 2040 } 2041 2042 unsigned int c = startcol; 2043 Row *row = grid[r].row; 2044 Row *nextrow = (r < endrow - 1) ? grid[r + 1].row : nullptr; 2045 // since a cell can be -1 (indicating a colspan) we might have to search backwards to include it 2046 while (c && (*row)[c] == (RenderTableCell *) - 1) { 2047 c--; 2048 } 2049 for (; c < endcol; c++) { 2050 RenderTableCell *cell = (*row)[c]; 2051 if (!cell || cell == (RenderTableCell *) - 1 || (nextrow && (*nextrow)[c] == cell)) { 2052 continue; 2053 } 2054 RenderObject *rowr = cell->parent(); 2055 int rtx = tx + rowr->xPos(); 2056 int rty = ty + rowr->yPos(); 2057 #ifdef TABLE_PRINT 2058 qCDebug(KHTML_LOG) << "painting cell " << r << "/" << c; 2059 #endif 2060 if (pI.phase == PaintActionElementBackground || pI.phase == PaintActionChildBackground) { 2061 // We need to handle painting a stack of backgrounds. This stack (from bottom to top) consists of 2062 // the column group, column, row group, row, and then the cell. 2063 RenderObject *col = table()->colElement(c); 2064 RenderObject *colGroup = nullptr; 2065 if (col) { 2066 RenderStyle *style = col->parent()->style(); 2067 if (style->display() == TABLE_COLUMN_GROUP) { 2068 colGroup = col->parent(); 2069 } 2070 } 2071 2072 // ### 2073 // Column groups and columns first. 2074 // FIXME: Columns and column groups do not currently support opacity, and they are being painted "too late" in 2075 // the stack, since we have already opened a transparency layer (potentially) for the table row group. 2076 // Note that we deliberately ignore whether or not the cell has a layer, since these backgrounds paint "behind" the 2077 // cell. 2078 cell->paintBackgroundsBehindCell(pI, rtx, rty, colGroup); 2079 cell->paintBackgroundsBehindCell(pI, rtx, rty, col); 2080 2081 // Paint the row group next. 2082 cell->paintBackgroundsBehindCell(pI, rtx, rty, this); 2083 2084 // Paint the row next, but only if it doesn't have a layer. If a row has a layer, it will be responsible for 2085 // painting the row background for the cell. 2086 if (!rowr->layer()) { 2087 cell->paintBackgroundsBehindCell(pI, rtx, rty, rowr); 2088 } 2089 } 2090 2091 if ((!cell->layer() && !cell->parent()->layer()) || pI.phase == PaintActionCollapsedTableBorders) { 2092 cell->paint(pI, rtx, rty); 2093 } 2094 } 2095 } 2096 } 2097 } 2098 2099 int RenderTableSection::numColumns() const 2100 { 2101 int result = 0; 2102 2103 for (int r = 0; r < numRows(); ++r) { 2104 for (int c = result; c < table()->numEffCols(); ++c) { 2105 if (cellAt(r, c)) { 2106 result = c; 2107 } 2108 } 2109 } 2110 2111 return result + 1; 2112 } 2113 2114 void RenderTableSection::recalcCells() 2115 { 2116 cCol = 0; 2117 cRow = -1; 2118 clearGrid(); 2119 grid.resize(0); 2120 cellsWithColSpanZero.clear(); 2121 cellsWithRowSpanZero.clear(); 2122 2123 for (RenderObject *row = firstChild(); row; row = row->nextSibling()) { 2124 if (row->isTableRow()) { 2125 cRow++; 2126 cCol = 0; 2127 ensureRows(cRow + 1); 2128 grid[cRow].rowRenderer = static_cast<RenderTableRow *>(row); 2129 2130 for (RenderObject *cell = row->firstChild(); cell; cell = cell->nextSibling()) 2131 if (cell->isTableCell()) { 2132 addCell(static_cast<RenderTableCell *>(cell), static_cast<RenderTableRow *>(row)); 2133 } 2134 2135 } 2136 } 2137 needCellRecalc = false; 2138 setNeedsLayout(true); 2139 } 2140 2141 void RenderTableSection::clearGrid() 2142 { 2143 int rows = grid.size(); 2144 while (rows--) { 2145 delete grid[rows].row; 2146 } 2147 } 2148 2149 bool RenderTableSection::emptyRow(int rowNum) 2150 { 2151 Row &r = *grid[rowNum].row; 2152 const int s = r.size(); 2153 RenderTableCell *cell; 2154 for (int i = 0; i < s; i++) { 2155 cell = r[i]; 2156 if (!cell || cell == (RenderTableCell *) - 1) { 2157 continue; 2158 } 2159 return false; 2160 } 2161 return true; 2162 } 2163 2164 RenderObject *RenderTableSection::removeChildNode(RenderObject *child) 2165 { 2166 setNeedCellRecalc(); 2167 return RenderContainer::removeChildNode(child); 2168 } 2169 2170 bool RenderTableSection::canClear(RenderObject * /*child*/, PageBreakLevel level) 2171 { 2172 // We cannot clear rows yet. 2173 return parent()->canClear(this, level); 2174 } 2175 2176 void RenderTableSection::addSpaceAt(int pos, int dy) 2177 { 2178 const int totalRows = numRows(); 2179 for (int r = 0; r < totalRows; r++) { 2180 if (rowPos[r] > pos) { 2181 rowPos[r] += dy; 2182 if (grid[r].rowRenderer) { 2183 grid[r].rowRenderer->setPos(0, rowPos[r]); 2184 } 2185 } 2186 } 2187 if (rowPos[totalRows] > pos) { 2188 rowPos[totalRows] += dy; 2189 } 2190 m_height = rowPos[totalRows]; 2191 2192 setContainsPageBreak(true); 2193 } 2194 2195 #ifdef ENABLE_DUMP 2196 void RenderTableSection::dump(QTextStream &stream, const QString &ind) const 2197 { 2198 RenderContainer::dump(stream, ind); 2199 2200 stream << " grid=(" << grid.size() << "," << table()->numEffCols() << ")"; 2201 for (int r = 0; r < grid.size(); r++) { 2202 for (int c = 0; c < table()->numEffCols(); c++) { 2203 if (cellAt(r, c) && cellAt(r, c) != (RenderTableCell *) - 1) 2204 stream << " (" << cellAt(r, c)->row() << "," << cellAt(r, c)->col() << "," 2205 << cellAt(r, c)->rowSpan() << "," << cellAt(r, c)->colSpan() << ") "; 2206 else { 2207 stream << " null cell"; 2208 } 2209 } 2210 } 2211 } 2212 #endif 2213 2214 static RenderTableCell *seekCell(RenderTableSection *section, int row, int col) 2215 { 2216 if (row < 0 || col < 0 || row >= section->numRows()) { 2217 return nullptr; 2218 } 2219 // since a cell can be -1 (indicating a colspan) we might have to search backwards to include it 2220 while (col && section->cellAt(row, col) == (RenderTableCell *) - 1) { 2221 col--; 2222 } 2223 2224 return section->cellAt(row, col); 2225 } 2226 2227 /** Looks for the first element suitable for text selection, beginning from 2228 * the last. 2229 * @param base search is restricted within this node. This node must have 2230 * a renderer. 2231 * @return the element or @p base if no suitable element found. 2232 */ 2233 static NodeImpl *findLastSelectableNode(NodeImpl *base) 2234 { 2235 NodeImpl *last = base; 2236 // Look for last text/cdata node that has a renderer, 2237 // or last childless replaced element 2238 while (last && !(last->renderer() 2239 && ((last->nodeType() == Node::TEXT_NODE || last->nodeType() == Node::CDATA_SECTION_NODE) 2240 || (last->renderer()->isReplaced() && !last->renderer()->lastChild())))) { 2241 NodeImpl *next = last->lastChild(); 2242 if (!next) { 2243 next = last->previousSibling(); 2244 } 2245 while (last && last != base && !next) { 2246 last = last->parentNode(); 2247 if (last && last != base) { 2248 next = last->previousSibling(); 2249 } 2250 } 2251 last = next; 2252 } 2253 2254 return last ? last : base; 2255 } 2256 2257 FindSelectionResult RenderTableSection::checkSelectionPoint(int _x, int _y, int _tx, int _ty, DOM::NodeImpl *&node, int &offset, SelPointState &state) 2258 { 2259 Q_UNUSED(node); 2260 Q_UNUSED(offset); 2261 // Table sections need extra treatment for selections. The rows are scanned 2262 // from top to bottom, and within each row, only the cell that matches 2263 // the given position best is descended into. 2264 2265 unsigned int totalRows = grid.size(); 2266 unsigned int totalCols = table()->columns.size(); 2267 2268 // absolutePosition(_tx, _ty, false); 2269 2270 _tx += m_x; 2271 _ty += m_y; 2272 2273 // bool save_last = false; // true to save last investigated cell 2274 2275 if (needsLayout() || _y < _ty) { 2276 return SelectionPointBefore; 2277 } 2278 // else if (_y >= _ty + height()) save_last = true; 2279 2280 // Find the row containing the pointer 2281 int row_idx = findRow(0, totalRows, rowPos, _y - _ty); 2282 2283 int col_idx; 2284 if (style()->direction() == LTR) { 2285 for (col_idx = (int)totalCols - 1; col_idx >= 0; col_idx--) { 2286 if (_tx + table()->columnPos[col_idx] < _x) { 2287 break; 2288 } 2289 } 2290 if (col_idx < 0) { 2291 col_idx = 0; 2292 } 2293 } else { 2294 for (col_idx = 0; col_idx < (int)totalCols; col_idx++) { 2295 if (_tx + table()->columnPos[col_idx] > _x) { 2296 break; 2297 } 2298 } 2299 if (col_idx >= (int)totalCols) { 2300 col_idx = (int)totalCols - 1; 2301 } 2302 } 2303 2304 FindSelectionResult pos = SelectionPointBefore; 2305 2306 RenderTableCell *cell = seekCell(this, row_idx, col_idx); 2307 // ### dunno why cell can be 0, maybe due to weird spans? (LS) 2308 if (cell) { 2309 SelPointState localState; 2310 // pos = cell->checkSelectionPoint(_x, _y, _tx, _ty, node, offset, localState); 2311 } 2312 2313 if (pos != SelectionPointBefore) { 2314 return pos; 2315 } 2316 2317 // store last column of last line 2318 row_idx--; 2319 col_idx = totalCols - 1; 2320 cell = seekCell(this, row_idx, col_idx); 2321 2322 // end of section? take previous section 2323 RenderTableSection *sec = this; 2324 if (!cell) { 2325 sec = *--TableSectionIterator(sec); 2326 if (!sec) { 2327 return pos; 2328 } 2329 2330 cell = seekCell(sec, sec->grid.size() - 1, col_idx); 2331 if (!cell) { 2332 return pos; 2333 } 2334 } 2335 2336 // take last child of previous cell, and store this one as last node 2337 NodeImpl *element = cell->element(); 2338 if (!element) { 2339 return SelectionPointBefore; 2340 } 2341 2342 element = findLastSelectableNode(element); 2343 2344 state.m_lastNode = element; 2345 state.m_lastOffset = element->maxOffset(); 2346 return SelectionPointBefore; 2347 } 2348 2349 // Hit Testing 2350 bool RenderTableSection::nodeAtPoint(NodeInfo &info, int x, int y, int tx, int ty, HitTestAction action, bool inside) 2351 { 2352 // Table sections cannot ever be hit tested. Effectively they do not exist. 2353 // Just forward to our children always. 2354 tx += m_x; 2355 ty += m_y; 2356 2357 for (RenderObject *child = lastChild(); child; child = child->previousSibling()) { 2358 // FIXME: We have to skip over inline flows, since they can show up inside table rows 2359 // at the moment (a demoted inline <form> for example). If we ever implement a 2360 // table-specific hit-test method (which we should do for performance reasons anyway), 2361 // then we can remove this check. 2362 if (!child->layer() && !child->isInlineFlow() && child->nodeAtPoint(info, x, y, tx, ty, action, inside)) { 2363 return true; 2364 } 2365 } 2366 2367 return false; 2368 } 2369 2370 // ------------------------------------------------------------------------- 2371 2372 RenderTableRow::RenderTableRow(DOM::NodeImpl *node) 2373 : RenderBox(node) 2374 { 2375 // init RenderObject attributes 2376 setInline(false); // our object is not Inline 2377 } 2378 2379 RenderObject *RenderTableRow::removeChildNode(RenderObject *child) 2380 { 2381 RenderTableSection *s = section(); 2382 if (s) { 2383 s->setNeedCellRecalc(); 2384 } 2385 2386 return RenderContainer::removeChildNode(child); 2387 } 2388 2389 void RenderTableRow::detach() 2390 { 2391 RenderTableSection *s = section(); 2392 if (s) { 2393 s->setNeedCellRecalc(); 2394 } 2395 2396 RenderContainer::detach(); 2397 } 2398 2399 void RenderTableRow::setStyle(RenderStyle *newStyle) 2400 { 2401 if (section() && style() && style()->height() != newStyle->height()) { 2402 section()->setNeedCellRecalc(); 2403 } 2404 2405 newStyle->setDisplay(TABLE_ROW); 2406 RenderContainer::setStyle(newStyle); 2407 } 2408 2409 void RenderTableRow::addChild(RenderObject *child, RenderObject *beforeChild) 2410 { 2411 #ifdef DEBUG_LAYOUT 2412 qCDebug(KHTML_LOG) << renderName() << "(TableRow)::addChild( " << child->renderName() << " )" << ", " << 2413 (beforeChild ? beforeChild->renderName() : "0") << " )"; 2414 #endif 2415 2416 if (!child->isTableCell()) { 2417 // TR > FORM quirk (???) 2418 if (child->element() && child->element()->isHTMLElement() && child->element()->id() == ID_FORM && 2419 static_cast<HTMLFormElementImpl *>(child->element())->isMalformed()) { 2420 RenderContainer::addChild(child, beforeChild); 2421 return; 2422 } 2423 2424 RenderObject *last = beforeChild; 2425 if (!last) { 2426 last = lastChild(); 2427 } 2428 if (last && last->isAnonymous() && last->isTableCell()) { 2429 last->addChild(child); 2430 return; 2431 } 2432 2433 // If beforeChild is inside an anonymous cell, insert into the cell. 2434 if (last && !last->isTableCell() && last->parent() && last->parent()->isAnonymous()) { 2435 last->parent()->addChild(child, beforeChild); 2436 return; 2437 } 2438 2439 RenderTableCell *cell = new(renderArena()) RenderTableCell(document() /* anonymous object */); 2440 RenderStyle *newStyle = new RenderStyle(); 2441 newStyle->inheritFrom(style()); 2442 newStyle->setDisplay(TABLE_CELL); 2443 cell->setStyle(newStyle); 2444 addChild(cell, beforeChild); 2445 cell->addChild(child); 2446 return; 2447 } 2448 2449 RenderTableCell *cell = static_cast<RenderTableCell *>(child); 2450 2451 section()->addCell(cell, this); 2452 2453 RenderContainer::addChild(cell, beforeChild); 2454 2455 if (beforeChild || nextSibling()) { 2456 section()->setNeedCellRecalc(); 2457 } 2458 } 2459 2460 void RenderTableRow::layout() 2461 { 2462 KHTMLAssert(needsLayout()); 2463 KHTMLAssert(minMaxKnown()); 2464 2465 RenderObject *child = firstChild(); 2466 const bool pagedMode = canvas()->pagedMode(); 2467 while (child) { 2468 if (child->isTableCell()) { 2469 RenderTableCell *cell = static_cast<RenderTableCell *>(child); 2470 if (pagedMode) { 2471 cell->setNeedsLayout(true); 2472 int oldHeight = child->height(); 2473 cell->layout(); 2474 if (oldHeight > 0 && child->containsPageBreak() && child->height() != oldHeight) { 2475 // child has grown to accommodate a page-page, grow the same amount ourselves, 2476 // and shift following rows down without relayouting the entire table 2477 int adjust = child->height() - oldHeight; 2478 setHeight(height() + adjust); 2479 section()->addSpaceAt(yPos() + 1, adjust); 2480 } 2481 } else if (child->needsLayout()) { 2482 if (markedForRepaint()) { 2483 cell->setMarkedForRepaint(true); 2484 } 2485 cell->calcVerticalMargins(); 2486 cell->layout(); 2487 cell->setCellTopExtra(0); 2488 cell->setCellBottomExtra(0); 2489 if (child->containsPageBreak()) { 2490 setContainsPageBreak(true); 2491 } 2492 } 2493 } 2494 child = child->nextSibling(); 2495 } 2496 setMarkedForRepaint(false); 2497 setNeedsLayout(false); 2498 } 2499 2500 int RenderTableRow::offsetLeft() const 2501 { 2502 RenderObject *child = firstChild(); 2503 while (child && !child->isTableCell()) { 2504 child = child->nextSibling(); 2505 } 2506 2507 if (!child) { 2508 return 0; 2509 } 2510 2511 return child->offsetLeft(); 2512 } 2513 2514 int RenderTableRow::offsetTop() const 2515 { 2516 RenderObject *child = firstChild(); 2517 while (child && !child->isTableCell()) { 2518 child = child->nextSibling(); 2519 } 2520 2521 if (!child) { 2522 return 0; 2523 } 2524 2525 return child->offsetTop() - 2526 static_cast<RenderTableCell *>(child)->cellTopExtra(); 2527 } 2528 2529 int RenderTableRow::offsetHeight() const 2530 { 2531 RenderObject *child = firstChild(); 2532 while (child && !child->isTableCell()) { 2533 child = child->nextSibling(); 2534 } 2535 2536 if (!child) { 2537 return 0; 2538 } 2539 2540 return child->offsetHeight(); 2541 } 2542 2543 short RenderTableRow::offsetWidth() const 2544 { 2545 RenderObject *fc = firstChild(); 2546 RenderObject *lc = lastChild(); 2547 while (fc && !fc->isTableCell()) { 2548 fc = fc->nextSibling(); 2549 } 2550 while (lc && !lc->isTableCell()) { 2551 lc = lc->previousSibling(); 2552 } 2553 if (!lc || !fc) { 2554 return 0; 2555 } 2556 2557 return lc->xPos() + lc->width() - fc->xPos(); 2558 } 2559 2560 void RenderTableRow::paintRow(PaintInfo &pI, int tx, int ty, int w, int h) 2561 { 2562 if (pI.phase == PaintActionOutline) { 2563 paintOutline(pI.p, tx, ty, w, h, style()); 2564 } 2565 } 2566 2567 void RenderTableRow::paint(PaintInfo &i, int tx, int ty) 2568 { 2569 KHTMLAssert(layer()); 2570 if (!layer()) { 2571 return; 2572 } 2573 2574 tx += m_x; 2575 ty += m_y; 2576 2577 for (RenderObject *child = firstChild(); child; child = child->nextSibling()) { 2578 if (child->isTableCell()) { 2579 // Paint the row background behind the cell. 2580 if (i.phase == PaintActionElementBackground || i.phase == PaintActionChildBackground) { 2581 RenderTableCell *cell = static_cast<RenderTableCell *>(child); 2582 cell->paintBackgroundsBehindCell(i, tx, ty, this); 2583 } 2584 if (!child->layer()) { 2585 child->paint(i, tx, ty); 2586 } 2587 } 2588 } 2589 } 2590 2591 // Hit Testing 2592 bool RenderTableRow::nodeAtPoint(NodeInfo &info, int x, int y, int tx, int ty, HitTestAction action, bool inside) 2593 { 2594 // Table rows cannot ever be hit tested. Effectively they do not exist. 2595 // Just forward to our children always. 2596 tx += m_x; 2597 ty += m_y; 2598 2599 for (RenderObject *child = lastChild(); child; child = child->previousSibling()) { 2600 // FIXME: We have to skip over inline flows, since they can show up inside table rows 2601 // at the moment (a demoted inline <form> for example). If we ever implement a 2602 // table-specific hit-test method (which we should do for performance reasons anyway), 2603 // then we can remove this check. 2604 if (!child->layer() && !child->isInlineFlow() && child->nodeAtPoint(info, x, y, tx, ty, action, inside)) { 2605 return true; 2606 } 2607 } 2608 2609 return false; 2610 } 2611 2612 // ------------------------------------------------------------------------- 2613 2614 RenderTableCell::RenderTableCell(DOM::NodeImpl *_node) 2615 : RenderBlock(_node) 2616 { 2617 _col = -1; 2618 _row = -1; 2619 cSpan = 1; 2620 rSpan = 1; 2621 updateFromElement(); 2622 setShouldPaintBackgroundOrBorder(true); 2623 _topExtra = 0; 2624 _bottomExtra = 0; 2625 m_percentageHeight = -1; 2626 m_hasFlexedAnonymous = false; 2627 m_widthChanged = false; 2628 } 2629 2630 void RenderTableCell::detach() 2631 { 2632 if (parent() && section()) { 2633 section()->setNeedCellRecalc(); 2634 } 2635 2636 RenderBlock::detach(); 2637 } 2638 2639 void RenderTableCell::updateFromElement() 2640 { 2641 DOM::NodeImpl *node = element(); 2642 if (node && (node->id() == ID_TD || node->id() == ID_TH)) { 2643 DOM::HTMLTableCellElementImpl *tc = static_cast<DOM::HTMLTableCellElementImpl *>(node); 2644 int oldCSpan = cSpan; 2645 int oldRSpan = rSpan; 2646 2647 cSpan = tc->colSpan(); 2648 rSpan = tc->rowSpan(); 2649 if ((oldRSpan != rSpan || oldCSpan != cSpan) && style() && parent()) { 2650 setNeedsLayoutAndMinMaxRecalc(); 2651 if (section()) { 2652 section()->setNeedCellRecalc(); 2653 } 2654 } 2655 } else { 2656 cSpan = rSpan = 1; 2657 } 2658 } 2659 2660 Length RenderTableCell::styleOrColWidth() 2661 { 2662 Length w = style()->width(); 2663 if (colSpan() > 1 || !w.isAuto()) { 2664 return w; 2665 } 2666 RenderTableCol *col = table()->colElement(_col); 2667 if (col) { 2668 w = col->style()->width(); 2669 2670 // Column widths specified on <col> apply to the border box of the cell. 2671 // Percentages don't need to be handled since they're always treated this way (even when specified on the cells). 2672 if (w.isFixed() && w.isPositive()) { 2673 w = Length(qMax(0, w.value() - borderLeft() - borderRight() - paddingLeft() - paddingRight()), Fixed); 2674 } 2675 } 2676 return w; 2677 } 2678 2679 void RenderTableCell::calcMinMaxWidth() 2680 { 2681 KHTMLAssert(!minMaxKnown()); 2682 #ifdef DEBUG_LAYOUT 2683 qCDebug(KHTML_LOG) << renderName() << "(TableCell)::calcMinMaxWidth() known=" << minMaxKnown(); 2684 #endif 2685 2686 if (section()->needCellRecalc) { 2687 section()->recalcCells(); 2688 } 2689 2690 RenderBlock::calcMinMaxWidth(); 2691 if (element() && style()->whiteSpace() == NORMAL) { 2692 // See if nowrap was set. 2693 Length w = styleOrColWidth(); 2694 DOMString nowrap = static_cast<ElementImpl *>(element())->getAttribute(ATTR_NOWRAP); 2695 if (!nowrap.isNull() && w.isFixed() && 2696 m_minWidth < w.value()) 2697 // Nowrap is set, but we didn't actually use it because of the 2698 // fixed width set on the cell. Even so, it is a WinIE/Moz trait 2699 // to make the minwidth of the cell into the fixed width. They do this 2700 // even in strict mode, so do not make this a quirk. Affected the top 2701 // of hiptop.com. 2702 { 2703 m_minWidth = w.value(); 2704 } 2705 } 2706 2707 setMinMaxKnown(); 2708 } 2709 2710 void RenderTableCell::calcWidth() 2711 { 2712 } 2713 2714 void RenderTableCell::setWidth(int width) 2715 { 2716 if (width != m_width) { 2717 m_width = width; 2718 m_widthChanged = true; 2719 } 2720 } 2721 2722 void RenderTableCell::layout() 2723 { 2724 layoutBlock(m_widthChanged); 2725 m_widthChanged = false; 2726 } 2727 2728 void RenderTableCell::close() 2729 { 2730 RenderBlock::close(); 2731 2732 #ifdef DEBUG_LAYOUT 2733 qCDebug(KHTML_LOG) << renderName() << "(RenderTableCell)::close() total height =" << m_height; 2734 #endif 2735 } 2736 2737 bool RenderTableCell::requiresLayer() const 2738 { 2739 // table-cell display is never positioned (css 2.1-9.7) 2740 return style()->opacity() < 1.0f || hasOverflowClip() || isRelPositioned(); 2741 } 2742 2743 void RenderTableCell::repaintRectangle(int x, int y, int w, int h, Priority p, bool f) 2744 { 2745 RenderBlock::repaintRectangle(x, y, w, h + _topExtra + _bottomExtra, p, f); 2746 } 2747 2748 int RenderTableCell::pageTopAfter(int y) const 2749 { 2750 return parent()->pageTopAfter(y + m_y + _topExtra) - (m_y + _topExtra); 2751 } 2752 2753 short RenderTableCell::baselinePosition(bool) const 2754 { 2755 RenderObject *o = firstChild(); 2756 int offset = paddingTop() + borderTop(); 2757 if (!o) { 2758 return offset + contentHeight(); 2759 } 2760 while (o->firstChild()) { 2761 if (!o->isInline()) { 2762 offset += o->paddingTop() + o->borderTop(); 2763 } 2764 o = o->firstChild(); 2765 } 2766 2767 if (!o->isInline()) { 2768 return paddingTop() + borderTop() + contentHeight(); 2769 } 2770 2771 offset += o->baselinePosition(true); 2772 return offset; 2773 } 2774 2775 void RenderTableCell::setStyle(RenderStyle *newStyle) 2776 { 2777 if (parent() && section() && style() && style()->height() != newStyle->height()) { 2778 section()->setNeedCellRecalc(); 2779 } 2780 2781 newStyle->setDisplay(TABLE_CELL); 2782 RenderBlock::setStyle(newStyle); 2783 setShouldPaintBackgroundOrBorder(true); 2784 2785 if (newStyle->whiteSpace() == KHTML_NOWRAP) { 2786 // Figure out if we are really nowrapping or if we should just 2787 // use normal instead. If the width of the cell is fixed, then 2788 // we don't actually use NOWRAP. 2789 if (newStyle->width().isFixed()) { 2790 newStyle->setWhiteSpace(NORMAL); 2791 } else { 2792 newStyle->setWhiteSpace(NOWRAP); 2793 } 2794 } 2795 } 2796 2797 bool RenderTableCell::nodeAtPoint(NodeInfo &info, int _x, int _y, int _tx, int _ty, HitTestAction hitTestAction, bool inside) 2798 { 2799 int tx = _tx + m_x; 2800 int ty = _ty + m_y; 2801 2802 // also include the top and bottom extra space 2803 inside |= hitTestAction != HitTestChildrenOnly && style()->visibility() != HIDDEN 2804 && (_y >= ty) && (_y < ty + height() + _topExtra + _bottomExtra) 2805 && (_x >= tx) && (_x < tx + width()); 2806 2807 return RenderBlock::nodeAtPoint(info, _x, _y, _tx, _ty, hitTestAction, inside); 2808 } 2809 2810 // The following rules apply for resolving conflicts and figuring out which border 2811 // to use. 2812 // (1) Borders with the 'border-style' of 'hidden' take precedence over all other conflicting 2813 // borders. Any border with this value suppresses all borders at this location. 2814 // (2) Borders with a style of 'none' have the lowest priority. Only if the border properties of all 2815 // the elements meeting at this edge are 'none' will the border be omitted (but note that 'none' is 2816 // the default value for the border style.) 2817 // (3) If none of the styles are 'hidden' and at least one of them is not 'none', then narrow borders 2818 // are discarded in favor of wider ones. If several have the same 'border-width' then styles are preferred 2819 // in this order: 'double', 'solid', 'dashed', 'dotted', 'ridge', 'outset', 'groove', and the lowest: 'inset'. 2820 // (4) If border styles differ only in color, then a style set on a cell wins over one on a row, 2821 // which wins over a row group, column, column group and, lastly, table. It is undefined which color 2822 // is used when two elements of the same type disagree. 2823 static CollapsedBorderValue compareBorders(const CollapsedBorderValue &border1, 2824 const CollapsedBorderValue &border2) 2825 { 2826 // Sanity check the values passed in. If either is null, return the other. 2827 if (!border2.exists()) { 2828 return border1; 2829 } 2830 if (!border1.exists()) { 2831 return border2; 2832 } 2833 2834 // Rule #1 above. 2835 if (border1.style() == BHIDDEN || border2.style() == BHIDDEN) { 2836 return CollapsedBorderValue(); // No border should exist at this location. 2837 } 2838 2839 // Rule #2 above. A style of 'none' has lowest priority and always loses to any other border. 2840 if (border2.style() == BNONE) { 2841 return border1; 2842 } 2843 if (border1.style() == BNONE) { 2844 return border2; 2845 } 2846 2847 // The first part of rule #3 above. Wider borders win. 2848 if (border1.width() != border2.width()) { 2849 return border1.width() > border2.width() ? border1 : border2; 2850 } 2851 2852 // The borders have equal width. Sort by border style. 2853 if (border1.style() != border2.style()) { 2854 return border1.style() > border2.style() ? border1 : border2; 2855 } 2856 2857 // The border have the same width and style. Rely on precedence (cell over row over row group, etc.) 2858 return border1.precedence >= border2.precedence ? border1 : border2; 2859 } 2860 2861 CollapsedBorderValue RenderTableCell::collapsedLeftBorder(bool rtl) const 2862 { 2863 RenderTable *tableElt = table(); 2864 bool leftmostColumn; 2865 if (!rtl) { 2866 leftmostColumn = col() == 0; 2867 } else { 2868 int effCol = tableElt->colToEffCol(col() + colSpan() - 1); 2869 leftmostColumn = effCol == tableElt->numEffCols() - 1; 2870 } 2871 2872 // For border left, we need to check, in order of precedence: 2873 // (1) Our left border. 2874 CollapsedBorderValue result(&style()->borderLeft(), BCELL); 2875 2876 // (2) The right border of the cell to the left. 2877 RenderTableCell *prevCell = rtl ? tableElt->cellAfter(this) : tableElt->cellBefore(this); 2878 if (prevCell) { 2879 result = compareBorders(result, CollapsedBorderValue(&prevCell->style()->borderRight(), BCELL)); 2880 if (!result.exists()) { 2881 return result; 2882 } 2883 } else if (leftmostColumn) { 2884 // (3) Our row's left border. 2885 result = compareBorders(result, CollapsedBorderValue(&parent()->style()->borderLeft(), BROW)); 2886 if (!result.exists()) { 2887 return result; 2888 } 2889 2890 // (4) Our row group's left border. 2891 result = compareBorders(result, CollapsedBorderValue(§ion()->style()->borderLeft(), BROWGROUP)); 2892 if (!result.exists()) { 2893 return result; 2894 } 2895 } 2896 2897 // (5) Our column and column group's left borders. 2898 bool startColEdge; 2899 bool endColEdge; 2900 RenderTableCol *colElt = table()->colElement(col() + (rtl ? colSpan() - 1 : 0), &startColEdge, &endColEdge); 2901 if (colElt && (!rtl ? startColEdge : endColEdge)) { 2902 result = compareBorders(result, CollapsedBorderValue(&colElt->style()->borderLeft(), BCOL)); 2903 if (!result.exists()) { 2904 return result; 2905 } 2906 if (colElt->parent()->isTableCol() && (!rtl ? !colElt->previousSibling() : !colElt->nextSibling())) { 2907 result = compareBorders(result, CollapsedBorderValue(&colElt->parent()->style()->borderLeft(), BCOLGROUP)); 2908 if (!result.exists()) { 2909 return result; 2910 } 2911 } 2912 } 2913 2914 // (6) The previous column's right border. 2915 if (!leftmostColumn) { 2916 colElt = table()->colElement(col() + (rtl ? colSpan() : -1), &startColEdge, &endColEdge); 2917 if (colElt && (!rtl ? endColEdge : startColEdge)) { 2918 result = compareBorders(result, CollapsedBorderValue(&colElt->style()->borderRight(), BCOL)); 2919 if (!result.exists()) { 2920 return result; 2921 } 2922 } 2923 } else { 2924 // (7) The table's left border. 2925 result = compareBorders(result, CollapsedBorderValue(&table()->style()->borderLeft(), BTABLE)); 2926 if (!result.exists()) { 2927 return result; 2928 } 2929 } 2930 2931 return result; 2932 } 2933 2934 CollapsedBorderValue RenderTableCell::collapsedRightBorder(bool rtl) const 2935 { 2936 RenderTable *tableElt = table(); 2937 bool rightmostColumn; 2938 if (rtl) { 2939 rightmostColumn = col() == 0; 2940 } else { 2941 int effCol = tableElt->colToEffCol(col() + colSpan() - 1); 2942 rightmostColumn = effCol == tableElt->numEffCols() - 1; 2943 } 2944 2945 // For border right, we need to check, in order of precedence: 2946 // (1) Our right border. 2947 CollapsedBorderValue result = CollapsedBorderValue(&style()->borderRight(), BCELL); 2948 2949 // (2) The left border of the cell to the right. 2950 if (!rightmostColumn) { 2951 RenderTableCell *nextCell = rtl ? tableElt->cellBefore(this) : tableElt->cellAfter(this); 2952 if (nextCell && nextCell->style()) { 2953 result = compareBorders(result, CollapsedBorderValue(&nextCell->style()->borderLeft(), BCELL)); 2954 if (!result.exists()) { 2955 return result; 2956 } 2957 } 2958 } else { 2959 // (3) Our row's right border. 2960 result = compareBorders(result, CollapsedBorderValue(&parent()->style()->borderRight(), BROW)); 2961 if (!result.exists()) { 2962 return result; 2963 } 2964 2965 // (4) Our row group's right border. 2966 result = compareBorders(result, CollapsedBorderValue(§ion()->style()->borderRight(), BROWGROUP)); 2967 if (!result.exists()) { 2968 return result; 2969 } 2970 } 2971 2972 // (5) Our column and column group's right borders. 2973 bool startColEdge; 2974 bool endColEdge; 2975 RenderTableCol *colElt = tableElt->colElement(col() + (rtl ? 0 : colSpan() - 1), &startColEdge, &endColEdge); 2976 if (colElt && (!rtl ? endColEdge : startColEdge)) { 2977 result = compareBorders(result, CollapsedBorderValue(&colElt->style()->borderRight(), BCOL)); 2978 if (!result.exists()) { 2979 return result; 2980 } 2981 if (colElt->parent()->isTableCol() && (!rtl ? !colElt->nextSibling() : !colElt->previousSibling())) { 2982 result = compareBorders(result, CollapsedBorderValue(&colElt->parent()->style()->borderRight(), BCOLGROUP)); 2983 if (!result.exists()) { 2984 return result; 2985 } 2986 } 2987 } 2988 2989 // (6) The next column's left border. 2990 if (!rightmostColumn) { 2991 colElt = tableElt->colElement(col() + (rtl ? -1 : colSpan()), &startColEdge, &endColEdge); 2992 if (colElt && (!rtl ? startColEdge : endColEdge)) { 2993 result = compareBorders(result, CollapsedBorderValue(&colElt->style()->borderLeft(), BCOL)); 2994 if (!result.exists()) { 2995 return result; 2996 } 2997 } 2998 } else { 2999 // (7) The table's right border. 3000 result = compareBorders(result, CollapsedBorderValue(&tableElt->style()->borderRight(), BTABLE)); 3001 if (!result.exists()) { 3002 return result; 3003 } 3004 } 3005 3006 return result; 3007 } 3008 3009 CollapsedBorderValue RenderTableCell::collapsedTopBorder() const 3010 { 3011 // For border top, we need to check, in order of precedence: 3012 // (1) Our top border. 3013 CollapsedBorderValue result = CollapsedBorderValue(&style()->borderTop(), BCELL); 3014 3015 RenderTableCell *prevCell = table()->cellAbove(this); 3016 if (prevCell) { 3017 // (2) A previous cell's bottom border. 3018 result = compareBorders(result, CollapsedBorderValue(&prevCell->style()->borderBottom(), BCELL)); 3019 if (!result.exists()) { 3020 return result; 3021 } 3022 } 3023 3024 // (3) Our row's top border. 3025 result = compareBorders(result, CollapsedBorderValue(&parent()->style()->borderTop(), BROW)); 3026 if (!result.exists()) { 3027 return result; 3028 } 3029 3030 // (4) The previous row's bottom border. 3031 if (prevCell) { 3032 RenderObject *prevRow = nullptr; 3033 if (prevCell->section() == section()) { 3034 prevRow = parent()->previousSibling(); 3035 } else { 3036 prevRow = prevCell->section()->lastChild(); 3037 } 3038 3039 if (prevRow) { 3040 result = compareBorders(result, CollapsedBorderValue(&prevRow->style()->borderBottom(), BROW)); 3041 if (!result.exists()) { 3042 return result; 3043 } 3044 } 3045 } 3046 3047 // Now check row groups. 3048 RenderTableSection *currSection = section(); 3049 if (row() == 0) { 3050 // (5) Our row group's top border. 3051 result = compareBorders(result, CollapsedBorderValue(&currSection->style()->borderTop(), BROWGROUP)); 3052 if (!result.exists()) { 3053 return result; 3054 } 3055 3056 // (6) Previous row group's bottom border. 3057 currSection = table()->sectionAbove(currSection); 3058 if (currSection) { 3059 result = compareBorders(result, CollapsedBorderValue(&currSection->style()->borderBottom(), BROWGROUP)); 3060 if (!result.exists()) { 3061 return result; 3062 } 3063 } 3064 } 3065 3066 if (!currSection) { 3067 // (8) Our column and column group's top borders. 3068 RenderTableCol *colElt = table()->colElement(col()); 3069 if (colElt) { 3070 result = compareBorders(result, CollapsedBorderValue(&colElt->style()->borderTop(), BCOL)); 3071 if (!result.exists()) { 3072 return result; 3073 } 3074 if (colElt->parent()->isTableCol()) { 3075 result = compareBorders(result, CollapsedBorderValue(&colElt->parent()->style()->borderTop(), BCOLGROUP)); 3076 if (!result.exists()) { 3077 return result; 3078 } 3079 } 3080 } 3081 3082 // (9) The table's top border. 3083 result = compareBorders(result, CollapsedBorderValue(&table()->style()->borderTop(), BTABLE)); 3084 if (!result.exists()) { 3085 return result; 3086 } 3087 } 3088 3089 return result; 3090 } 3091 3092 CollapsedBorderValue RenderTableCell::collapsedBottomBorder() const 3093 { 3094 // For border top, we need to check, in order of precedence: 3095 // (1) Our bottom border. 3096 CollapsedBorderValue result = CollapsedBorderValue(&style()->borderBottom(), BCELL); 3097 3098 RenderTableCell *nextCell = table()->cellBelow(this); 3099 if (nextCell) { 3100 // (2) A following cell's top border. 3101 result = compareBorders(result, CollapsedBorderValue(&nextCell->style()->borderTop(), BCELL)); 3102 if (!result.exists()) { 3103 return result; 3104 } 3105 } 3106 3107 // (3) Our row's bottom border. (FIXME: Deal with rowspan!) 3108 result = compareBorders(result, CollapsedBorderValue(&parent()->style()->borderBottom(), BROW)); 3109 if (!result.exists()) { 3110 return result; 3111 } 3112 3113 // (4) The next row's top border. 3114 if (nextCell) { 3115 result = compareBorders(result, CollapsedBorderValue(&nextCell->parent()->style()->borderTop(), BROW)); 3116 if (!result.exists()) { 3117 return result; 3118 } 3119 } 3120 3121 // Now check row groups. 3122 RenderTableSection *currSection = section(); 3123 if (row() + rowSpan() >= static_cast<RenderTableSection *>(currSection)->numRows()) { 3124 // (5) Our row group's bottom border. 3125 result = compareBorders(result, CollapsedBorderValue(&currSection->style()->borderBottom(), BROWGROUP)); 3126 if (!result.exists()) { 3127 return result; 3128 } 3129 3130 // (6) Following row group's top border. 3131 currSection = table()->sectionBelow(currSection); 3132 if (currSection) { 3133 result = compareBorders(result, CollapsedBorderValue(&currSection->style()->borderTop(), BROWGROUP)); 3134 if (!result.exists()) { 3135 return result; 3136 } 3137 } 3138 } 3139 3140 if (!currSection) { 3141 // (8) Our column and column group's bottom borders. 3142 RenderTableCol *colElt = table()->colElement(col()); 3143 if (colElt) { 3144 result = compareBorders(result, CollapsedBorderValue(&colElt->style()->borderBottom(), BCOL)); 3145 if (!result.exists()) { 3146 return result; 3147 } 3148 if (colElt->parent()->isTableCol()) { 3149 result = compareBorders(result, CollapsedBorderValue(&colElt->parent()->style()->borderBottom(), BCOLGROUP)); 3150 if (!result.exists()) { 3151 return result; 3152 } 3153 } 3154 } 3155 3156 // (9) The table's bottom border. 3157 result = compareBorders(result, CollapsedBorderValue(&table()->style()->borderBottom(), BTABLE)); 3158 if (!result.exists()) { 3159 return result; 3160 } 3161 } 3162 3163 return result; 3164 } 3165 3166 int RenderTableCell::borderLeft() const 3167 { 3168 if (table()->collapseBorders()) { 3169 CollapsedBorderValue border = collapsedLeftBorder(table()->style()->direction() == RTL); 3170 if (border.exists()) { 3171 return (border.width() + 1) / 2; // Give the extra pixel to top and left. 3172 } 3173 return 0; 3174 } 3175 return RenderBlock::borderLeft(); 3176 } 3177 3178 int RenderTableCell::borderRight() const 3179 { 3180 if (table()->collapseBorders()) { 3181 CollapsedBorderValue border = collapsedRightBorder(table()->style()->direction() == RTL); 3182 if (border.exists()) { 3183 return border.width() / 2; 3184 } 3185 return 0; 3186 } 3187 return RenderBlock::borderRight(); 3188 } 3189 3190 int RenderTableCell::borderTop() const 3191 { 3192 if (table()->collapseBorders()) { 3193 CollapsedBorderValue border = collapsedTopBorder(); 3194 if (border.exists()) { 3195 return (border.width() + 1) / 2; // Give the extra pixel to top and left. 3196 } 3197 return 0; 3198 } 3199 return RenderBlock::borderTop(); 3200 } 3201 3202 int RenderTableCell::borderBottom() const 3203 { 3204 if (table()->collapseBorders()) { 3205 CollapsedBorderValue border = collapsedBottomBorder(); 3206 if (border.exists()) { 3207 return border.width() / 2; 3208 } 3209 return 0; 3210 } 3211 return RenderBlock::borderBottom(); 3212 } 3213 3214 #ifdef BOX_DEBUG 3215 #include <qpainter.h> 3216 3217 static void outlineBox(QPainter *p, int _tx, int _ty, int w, int h) 3218 { 3219 p->setPen(QPen(QColor("yellow"), 3, Qt::DotLine)); 3220 p->setBrush(Qt::NoBrush); 3221 p->drawRect(_tx, _ty, w, h); 3222 } 3223 #endif 3224 3225 void RenderTableCell::paint(PaintInfo &pI, int _tx, int _ty) 3226 { 3227 3228 #ifdef TABLE_PRINT 3229 qCDebug(KHTML_LOG) << renderName() << "(RenderTableCell)::paint() w/h = (" << width() << "/" << height() << ")" << " _y/_h=" << pI.r.y() << "/" << pI.r.height(); 3230 #endif 3231 3232 if (needsLayout()) { 3233 return; 3234 } 3235 3236 _tx += m_x; 3237 _ty += m_y/* + _topExtra*/; 3238 3239 RenderTable *tbl = table(); 3240 3241 // check if we need to do anything at all... 3242 int os = qMax(tbl->currentBorderStyle() ? (tbl->currentBorderStyle()->border->width + 1) / 2 : 0, 2 * maximalOutlineSize(pI.phase)); 3243 if ((_ty >= pI.r.y() + pI.r.height() + os) 3244 || (_ty + _topExtra + m_height + _bottomExtra <= pI.r.y() - os)) { 3245 return; 3246 } 3247 3248 if (pI.phase == PaintActionOutline) { 3249 paintOutline(pI.p, _tx, _ty, width(), height() + borderTopExtra() + borderBottomExtra(), style()); 3250 } 3251 3252 if (pI.phase == PaintActionCollapsedTableBorders && style()->visibility() == VISIBLE) { 3253 int w = width(); 3254 int h = height() + borderTopExtra() + borderBottomExtra(); 3255 paintCollapsedBorder(pI.p, _tx, _ty, w, h); 3256 } else { 3257 RenderBlock::paintObject(pI, _tx, _ty + _topExtra, false); 3258 } 3259 3260 #ifdef BOX_DEBUG 3261 ::outlineBox(pI.p, _tx, _ty - _topExtra, width(), height() + borderTopExtra() + borderBottomExtra()); 3262 #endif 3263 } 3264 3265 static EBorderStyle collapsedBorderStyle(EBorderStyle style) 3266 { 3267 if (style == OUTSET) { 3268 style = GROOVE; 3269 } else if (style == INSET) { 3270 style = RIDGE; 3271 } 3272 return style; 3273 } 3274 3275 struct CollapsedBorder { 3276 CollapsedBorder() {} 3277 3278 CollapsedBorderValue border; 3279 RenderObject::BorderSide side; 3280 bool shouldPaint; 3281 int x1; 3282 int y1; 3283 int x2; 3284 int y2; 3285 EBorderStyle style; 3286 }; 3287 3288 class CollapsedBorders 3289 { 3290 public: 3291 CollapsedBorders() : count(0) {} 3292 3293 void addBorder(const CollapsedBorderValue &b, RenderObject::BorderSide s, bool paint, 3294 int _x1, int _y1, int _x2, int _y2, 3295 EBorderStyle _style) 3296 { 3297 if (b.exists() && paint) { 3298 borders[count].border = b; 3299 borders[count].side = s; 3300 borders[count].shouldPaint = paint; 3301 borders[count].x1 = _x1; 3302 borders[count].x2 = _x2; 3303 borders[count].y1 = _y1; 3304 borders[count].y2 = _y2; 3305 borders[count].style = _style; 3306 count++; 3307 } 3308 } 3309 3310 CollapsedBorder *nextBorder() 3311 { 3312 for (int i = 0; i < count; i++) { 3313 if (borders[i].border.exists() && borders[i].shouldPaint) { 3314 borders[i].shouldPaint = false; 3315 return &borders[i]; 3316 } 3317 } 3318 3319 return nullptr; 3320 } 3321 3322 CollapsedBorder borders[4]; 3323 int count; 3324 }; 3325 3326 static void addBorderStyle(QList<CollapsedBorderValue> &borderStyles, CollapsedBorderValue borderValue) 3327 { 3328 if (!borderValue.exists() || borderStyles.contains(borderValue)) { 3329 return; 3330 } 3331 3332 QList<CollapsedBorderValue>::Iterator it = borderStyles.begin(); 3333 QList<CollapsedBorderValue>::Iterator end = borderStyles.end(); 3334 for (; it != end; ++it) { 3335 CollapsedBorderValue result = compareBorders(*it, borderValue); 3336 if (result == *it) { 3337 borderStyles.insert(it, borderValue); 3338 return; 3339 } 3340 } 3341 3342 borderStyles.append(borderValue); 3343 } 3344 3345 void RenderTableCell::collectBorders(QList<CollapsedBorderValue> &borderStyles) 3346 { 3347 bool rtl = table()->style()->direction() == RTL; 3348 addBorderStyle(borderStyles, collapsedLeftBorder(rtl)); 3349 addBorderStyle(borderStyles, collapsedRightBorder(rtl)); 3350 addBorderStyle(borderStyles, collapsedTopBorder()); 3351 addBorderStyle(borderStyles, collapsedBottomBorder()); 3352 } 3353 3354 void RenderTableCell::paintCollapsedBorder(QPainter *p, int _tx, int _ty, int w, int h) 3355 { 3356 if (!table()->currentBorderStyle()) { 3357 return; 3358 } 3359 3360 bool rtl = table()->style()->direction() == RTL; 3361 CollapsedBorderValue leftVal = collapsedLeftBorder(rtl); 3362 CollapsedBorderValue rightVal = collapsedRightBorder(rtl); 3363 CollapsedBorderValue topVal = collapsedTopBorder(); 3364 CollapsedBorderValue bottomVal = collapsedBottomBorder(); 3365 3366 // Adjust our x/y/width/height so that we paint the collapsed borders at the correct location. 3367 int topWidth = topVal.width(); 3368 int bottomWidth = bottomVal.width(); 3369 int leftWidth = leftVal.width(); 3370 int rightWidth = rightVal.width(); 3371 3372 _tx -= leftWidth / 2; 3373 _ty -= topWidth / 2; 3374 w += leftWidth / 2 + (rightWidth + 1) / 2; 3375 h += topWidth / 2 + (bottomWidth + 1) / 2; 3376 3377 bool tt = topVal.isTransparent(); 3378 bool bt = bottomVal.isTransparent(); 3379 bool rt = rightVal.isTransparent(); 3380 bool lt = leftVal.isTransparent(); 3381 3382 EBorderStyle ts = collapsedBorderStyle(topVal.style()); 3383 EBorderStyle bs = collapsedBorderStyle(bottomVal.style()); 3384 EBorderStyle ls = collapsedBorderStyle(leftVal.style()); 3385 EBorderStyle rs = collapsedBorderStyle(rightVal.style()); 3386 3387 bool render_t = ts > BHIDDEN && !tt && (topVal.precedence != BCELL || *topVal.border == style()->borderTop()); 3388 bool render_l = ls > BHIDDEN && !lt && (leftVal.precedence != BCELL || *leftVal.border == style()->borderLeft()); 3389 bool render_r = rs > BHIDDEN && !rt && (rightVal.precedence != BCELL || *rightVal.border == style()->borderRight()); 3390 bool render_b = bs > BHIDDEN && !bt && (bottomVal.precedence != BCELL || *bottomVal.border == style()->borderBottom()); 3391 3392 // We never paint diagonals at the joins. We simply let the border with the highest 3393 // precedence paint on top of borders with lower precedence. 3394 CollapsedBorders borders; 3395 borders.addBorder(topVal, BSTop, render_t, _tx, _ty, _tx + w, _ty + topWidth, ts); 3396 borders.addBorder(bottomVal, BSBottom, render_b, _tx, _ty + h - bottomWidth, _tx + w, _ty + h, bs); 3397 borders.addBorder(leftVal, BSLeft, render_l, _tx, _ty, _tx + leftWidth, _ty + h, ls); 3398 borders.addBorder(rightVal, BSRight, render_r, _tx + w - rightWidth, _ty, _tx + w, _ty + h, rs); 3399 3400 for (CollapsedBorder *border = borders.nextBorder(); border; border = borders.nextBorder()) { 3401 if (border->border == *table()->currentBorderStyle()) 3402 drawBorder(p, border->x1, border->y1, border->x2, border->y2, border->side, 3403 border->border.color(), style()->color(), border->style, 0, 0); 3404 } 3405 } 3406 3407 void RenderTableCell::paintBackgroundsBehindCell(PaintInfo &pI, int _tx, int _ty, RenderObject *bgObj) 3408 { 3409 if (!bgObj || style()->visibility() != VISIBLE) { 3410 return; 3411 } 3412 3413 RenderTable *tableElt = table(); 3414 3415 int w = bgObj->width(); 3416 int h = bgObj->height() + bgObj->borderTopExtra() + bgObj->borderBottomExtra(); 3417 3418 int cellx = _tx; 3419 int celly = _ty; 3420 int cellw = w; 3421 int cellh = h; 3422 if (bgObj != this) { 3423 cellx += m_x; 3424 celly += m_y; 3425 cellw = width(); 3426 cellh = height() + borderTopExtra() + borderBottomExtra(); 3427 } 3428 3429 QRect cr; 3430 cr.setX(qMax(cellx, pI.r.x())); 3431 cr.setY(qMax(celly, pI.r.y())); 3432 cr.setWidth(cellx < pI.r.x() ? qMax(0, cellw - (pI.r.x() - cellx)) : qMin(pI.r.width(), cellw)); 3433 cr.setHeight(celly < pI.r.y() ? qMax(0, cellh - (pI.r.y() - celly)) : qMin(pI.r.height(), cellh)); 3434 3435 QColor c = bgObj->style()->backgroundColor(); 3436 const BackgroundLayer *bgLayer = bgObj->style()->backgroundLayers(); 3437 3438 if (bgLayer->hasImage() || c.isValid()) { 3439 // We have to clip here because the background would paint 3440 // on top of the borders otherwise. This only matters for cells and rows. 3441 bool hasLayer = bgObj->layer() && (bgObj == this || bgObj == parent()); 3442 if (hasLayer && tableElt->collapseBorders()) { 3443 pI.p->save(); 3444 QRect clipRect(cellx + borderLeft(), celly + borderTop(), cellw - borderLeft() - borderRight(), cellh - borderTop() - borderBottom()); 3445 clipRect = pI.p->combinedMatrix().mapRect(clipRect); 3446 QRegion creg(clipRect); 3447 QRegion old = pI.p->clipRegion(); 3448 if (!old.isEmpty()) { 3449 creg = old.intersected(creg); 3450 } 3451 pI.p->setClipRegion(creg); 3452 } 3453 KHTMLAssert(bgObj->isBox()); 3454 static_cast<RenderBox *>(bgObj)->paintAllBackgrounds(pI.p, c, bgLayer, cr, _tx, _ty, w, h); 3455 if (hasLayer && tableElt->collapseBorders()) { 3456 pI.p->restore(); 3457 } 3458 } 3459 } 3460 3461 void RenderTableCell::paintBoxDecorations(PaintInfo &pI, int _tx, int _ty) 3462 { 3463 RenderTable *tableElt = table(); 3464 bool drawBorders = true; 3465 // Moz paints bgcolor/bgimage on <td>s in quirks mode even if 3466 // empty-cells are on. Fixes regression on #43426, attachment #354 3467 if (!tableElt->collapseBorders() && style()->emptyCells() == HIDE && !firstChild()) { 3468 drawBorders = false; 3469 } 3470 if (!style()->htmlHacks() && !drawBorders) { 3471 return; 3472 } 3473 3474 _ty -= borderTopExtra(); 3475 3476 // Paint our cell background. 3477 paintBackgroundsBehindCell(pI, _tx, _ty, this); 3478 3479 int w = width(); 3480 int h = height() + borderTopExtra() + borderBottomExtra(); 3481 3482 if (drawBorders && style()->hasBorder() && !tableElt->collapseBorders()) { 3483 paintBorder(pI.p, _tx, _ty, w, h, style()); 3484 } 3485 } 3486 3487 #ifdef ENABLE_DUMP 3488 void RenderTableCell::dump(QTextStream &stream, const QString &ind) const 3489 { 3490 RenderFlow::dump(stream, ind); 3491 stream << " row=" << _row; 3492 stream << " col=" << _col; 3493 stream << " rSpan=" << rSpan; 3494 stream << " cSpan=" << cSpan; 3495 // *stream << " nWrap=" << nWrap; 3496 } 3497 #endif 3498 3499 // ------------------------------------------------------------------------- 3500 3501 RenderTableCol::RenderTableCol(DOM::NodeImpl *node) 3502 : RenderBox(node), m_span(1) 3503 { 3504 // init RenderObject attributes 3505 setInline(true); // our object is not Inline 3506 updateFromElement(); 3507 } 3508 3509 void RenderTableCol::updateFromElement() 3510 { 3511 DOM::NodeImpl *node = element(); 3512 if (node && (node->id() == ID_COL || node->id() == ID_COLGROUP)) { 3513 DOM::HTMLTableColElementImpl *tc = static_cast<DOM::HTMLTableColElementImpl *>(node); 3514 m_span = tc->span(); 3515 } else { 3516 m_span = !(style() && style()->display() == TABLE_COLUMN_GROUP); 3517 } 3518 } 3519 3520 #ifdef ENABLE_DUMP 3521 void RenderTableCol::dump(QTextStream &stream, const QString &ind) const 3522 { 3523 RenderContainer::dump(stream, ind); 3524 stream << " _span=" << m_span; 3525 } 3526 #endif 3527 3528 // ------------------------------------------------------------------------- 3529 3530 TableSectionIterator::TableSectionIterator(RenderTable *table, bool fromEnd) 3531 { 3532 if (fromEnd) { 3533 sec = table->foot; 3534 if (sec) { 3535 return; 3536 } 3537 3538 sec = static_cast<RenderTableSection *>(table->lastChild()); 3539 while (sec && (!sec->isTableSection() 3540 || sec == table->head || sec == table->foot)) { 3541 sec = static_cast<RenderTableSection *>(sec->previousSibling()); 3542 } 3543 if (sec) { 3544 return; 3545 } 3546 3547 sec = table->head; 3548 } else { 3549 sec = table->head; 3550 if (sec) { 3551 return; 3552 } 3553 3554 sec = static_cast<RenderTableSection *>(table->firstChild()); 3555 while (sec && (!sec->isTableSection() 3556 || sec == table->head || sec == table->foot)) { 3557 sec = static_cast<RenderTableSection *>(sec->nextSibling()); 3558 } 3559 if (sec) { 3560 return; 3561 } 3562 3563 sec = table->foot; 3564 }/*end if*/ 3565 3566 } 3567 3568 TableSectionIterator &TableSectionIterator::operator ++() 3569 { 3570 RenderTable *table = sec->table(); 3571 if (sec == table->head) { 3572 3573 sec = static_cast<RenderTableSection *>(table->firstChild()); 3574 while (sec && (!sec->isTableSection() 3575 || sec == table->head || sec == table->foot)) { 3576 sec = static_cast<RenderTableSection *>(sec->nextSibling()); 3577 } 3578 if (sec) { 3579 return *this; 3580 } 3581 3582 } else if (sec == table->foot) { 3583 sec = nullptr; 3584 return *this; 3585 3586 } else { 3587 3588 do { 3589 sec = static_cast<RenderTableSection *>(sec->nextSibling()); 3590 } while (sec && (!sec->isTableSection() || sec == table->head || sec == table->foot)); 3591 if (sec) { 3592 return *this; 3593 } 3594 3595 }/*end if*/ 3596 3597 sec = table->foot; 3598 return *this; 3599 } 3600 3601 TableSectionIterator &TableSectionIterator::operator --() 3602 { 3603 RenderTable *table = sec->table(); 3604 if (sec == table->foot) { 3605 3606 sec = static_cast<RenderTableSection *>(table->lastChild()); 3607 while (sec && (!sec->isTableSection() 3608 || sec == table->head || sec == table->foot)) { 3609 sec = static_cast<RenderTableSection *>(sec->previousSibling()); 3610 } 3611 if (sec) { 3612 return *this; 3613 } 3614 3615 } else if (sec == table->head) { 3616 sec = nullptr; 3617 return *this; 3618 3619 } else { 3620 3621 do { 3622 sec = static_cast<RenderTableSection *>(sec->previousSibling()); 3623 } while (sec && (!sec->isTableSection() || sec == table->head || sec == table->foot)); 3624 if (sec) { 3625 return *this; 3626 } 3627 3628 }/*end if*/ 3629 3630 sec = table->foot; 3631 return *this; 3632 } 3633 3634 #undef TABLE_DEBUG 3635 #undef DEBUG_LAYOUT 3636 #undef BOX_DEBUG