KHtml

render_table.cpp
1 /**
2  * This file is part of the DOM implementation for KDE.
3  *
4  * Copyright (C) 1997 Martin Jones ([email protected])
5  * (C) 1997 Torben Weis ([email protected])
6  * (C) 1998 Waldo Bastian ([email protected])
7  * (C) 1999-2003 Lars Knoll ([email protected])
8  * (C) 1999 Antti Koivisto ([email protected])
9  * (C) 2003 Apple Computer, Inc.
10  * (C) 2005,2011 Allan Sandfeld Jensen ([email protected])
11  * (C) 2008 Germain Garand ([email protected])
12  * (C) 2009 Carlos Licea ([email protected])
13  *
14  * This library is free software; you can redistribute it and/or
15  * modify it under the terms of the GNU Library General Public
16  * License as published by the Free Software Foundation; either
17  * version 2 of the License, or (at your option) any later version.
18  *
19  * This library is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22  * Library General Public License for more details.
23  *
24  * You should have received a copy of the GNU Library General Public License
25  * along with this library; see the file COPYING.LIB. If not, write to
26  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
27  * Boston, MA 02110-1301, USA.
28  */
29 
30 //#define TABLE_DEBUG
31 //#define TABLE_PRINT
32 //#define DEBUG_LAYOUT
33 //#define BOX_DEBUG
34 #include "rendering/render_table.h"
35 #include "rendering/render_replaced.h"
36 #include "rendering/render_canvas.h"
37 #include "rendering/table_layout.h"
38 #include "html/html_tableimpl.h"
39 #include "html/html_formimpl.h"
40 #include "rendering/render_line.h"
41 #include "xml/dom_docimpl.h"
42 
43 #include <QApplication>
44 #include <QStyle>
45 
46 #include "khtml_debug.h"
47 #include <assert.h>
48 
49 using namespace khtml;
50 using namespace DOM;
51 
52 RenderTable::RenderTable(DOM::NodeImpl *node)
53  : RenderBlock(node)
54 {
55 
56  tCaption = nullptr;
57  head = foot = firstBody = nullptr;
58  tableLayout = nullptr;
59  m_currentBorder = nullptr;
60 
61  has_col_elems = false;
62  hspacing = vspacing = 0;
63  padding = 0;
64  needSectionRecalc = false;
65  padding = 0;
66 
67  columnPos.resize(2);
68  columnPos.fill(0);
69  columns.resize(1);
70  columns.fill(ColumnStruct());
71 
72  columnPos[0] = 0;
73 }
74 
75 RenderTable::~RenderTable()
76 {
77  delete tableLayout;
78 }
79 
80 void RenderTable::setStyle(RenderStyle *_style)
81 {
82  ETableLayout oldTableLayout = style() ? style()->tableLayout() : TAUTO;
83  if (_style->display() == INLINE) {
84  _style->setDisplay(INLINE_TABLE);
85  }
86  if (_style->display() != INLINE_TABLE) {
87  _style->setDisplay(TABLE);
88  }
89  if (!_style->flowAroundFloats()) {
90  _style->setFlowAroundFloats(true);
91  }
92  RenderBlock::setStyle(_style);
93 
94  // init RenderObject attributes
95  setInline(style()->display() == INLINE_TABLE && !isPositioned());
96  setReplaced(style()->display() == INLINE_TABLE);
97 
98  // In the collapsed border model, there is no cell spacing.
99  hspacing = collapseBorders() ? 0 : style()->borderHorizontalSpacing();
100  vspacing = collapseBorders() ? 0 : style()->borderVerticalSpacing();
101  columnPos[0] = hspacing;
102 
103  if (!tableLayout || style()->tableLayout() != oldTableLayout) {
104  delete tableLayout;
105 
106  // According to the CSS2 spec, you only use fixed table layout if an
107  // explicit width is specified on the table. Auto width implies auto table layout.
108  if (style()->tableLayout() == TFIXED && !style()->width().isAuto()) {
109  tableLayout = new FixedTableLayout(this);
110 #ifdef DEBUG_LAYOUT
111  qCDebug(KHTML_LOG) << "using fixed table layout";
112 #endif
113  } else {
114  tableLayout = new AutoTableLayout(this);
115  }
116  }
117 }
118 
119 short RenderTable::lineHeight(bool b) const
120 {
121  // Inline tables are replaced elements. Otherwise, just pass off to
122  // the base class.
123  if (isReplaced()) {
124  return height() + marginTop() + marginBottom();
125  }
126  return RenderBlock::lineHeight(b);
127 }
128 
129 short RenderTable::baselinePosition(bool b) const
130 {
131  // CSS2.1 - 10.8.1 The baseline of an 'inline-table' is the baseline of the first row of the table.
132  if (isReplaced() && !needsLayout()) {
133  // compatibility with Gecko: only apply to generic containers, not to HTML Table.
134  if (element() && element()->id() == ID_TABLE) {
135  return height() + marginTop() + marginBottom();
136  }
137 
138  if (firstBodySection() && firstBodySection()->grid.size()) {
139  return firstBodySection()->grid[0].baseLine + firstBodySection()->yPos() + marginTop();
140  }
141  return 0;
142  }
143  return RenderBox::baselinePosition(b);
144 }
145 
146 static inline void resetSectionPointerIfNotBefore(RenderTableSection *&ptr, RenderObject *before)
147 {
148  if (!before || !ptr) {
149  return;
150  }
151  RenderObject *o = before->previousSibling();
152  while (o && o != ptr) {
153  o = o->previousSibling();
154  }
155  if (!o) {
156  ptr = nullptr;
157  }
158 }
159 
160 void RenderTable::addChild(RenderObject *child, RenderObject *beforeChild)
161 {
162 #ifdef DEBUG_LAYOUT
163  qCDebug(KHTML_LOG) << renderName() << "(Table)::addChild( " << child->renderName() << ", " <<
164  (beforeChild ? beforeChild->renderName() : "0") << " )";
165 #endif
166  bool wrapInAnonymousSection = false;
167 
168  switch (child->style()->display()) {
169  case TABLE_CAPTION:
170  if (child->isRenderBlock()) {
171  // First caption wins.
172  if (beforeChild && tCaption) {
173  RenderObject *o = beforeChild->previousSibling();
174  while (o && o != tCaption) {
175  o = o->previousSibling();
176  }
177  if (!o) {
178  tCaption = nullptr;
179  }
180  }
181  if (!tCaption) {
182  tCaption = static_cast<RenderBlock *>(child);
183  }
184  }
185  break;
186  case TABLE_COLUMN:
187  case TABLE_COLUMN_GROUP:
188  has_col_elems = true;
189  break;
190  case TABLE_HEADER_GROUP:
191  if (child->isTableSection()) {
192  resetSectionPointerIfNotBefore(head, beforeChild);
193  if (!head) {
194  head = static_cast<RenderTableSection *>(child);
195  } else {
196  resetSectionPointerIfNotBefore(firstBody, beforeChild);
197  if (!firstBody) {
198  firstBody = static_cast<RenderTableSection *>(child);
199  }
200  }
201  }
202  break;
203  case TABLE_FOOTER_GROUP:
204  if (child->isTableSection()) {
205  resetSectionPointerIfNotBefore(foot, beforeChild);
206  if (!foot) {
207  foot = static_cast<RenderTableSection *>(child);
208  break;
209  }
210  }
211  // fall through
212  case TABLE_ROW_GROUP:
213  if (child->isTableSection()) {
214  resetSectionPointerIfNotBefore(firstBody, beforeChild);
215  if (!firstBody) {
216  firstBody = static_cast<RenderTableSection *>(child);
217  }
218  }
219  break;
220  case TABLE_CELL:
221  case TABLE_ROW:
222  wrapInAnonymousSection = true;
223  break;
224  case BLOCK:
225 // case BOX:
226  case COMPACT:
227  case INLINE:
228  case INLINE_BLOCK:
229 // case INLINE_BOX:
230  case INLINE_TABLE:
231  case LIST_ITEM:
232  case NONE:
233  case RUN_IN:
234  case TABLE:
235  // The special TABLE > FORM quirk allows the form to sit directly under the table
236  if (child->element() && child->element()->isHTMLElement() && child->element()->id() == ID_FORM) {
237  wrapInAnonymousSection = !static_cast<HTMLFormElementImpl *>(child->element())->isMalformed();
238  } else {
239  wrapInAnonymousSection = true;
240  }
241  break;
242  }
243 
244  if (!wrapInAnonymousSection) {
245  RenderContainer::addChild(child, beforeChild);
246  return;
247  }
248 
249  if (!beforeChild && lastChild() && lastChild()->isTableSection() && lastChild()->isAnonymous()) {
250  lastChild()->addChild(child);
251  return;
252  }
253 
254  RenderObject *lastBox = beforeChild;
255  while (lastBox && lastBox->parent()->isAnonymous() &&
256  !lastBox->isTableSection() && lastBox->style()->display() != TABLE_CAPTION) {
257  lastBox = lastBox->parent();
258  }
259  if (lastBox && lastBox->isAnonymous()) {
260  lastBox->addChild(child, beforeChild);
261  return;
262  }
263 
264  if (beforeChild && !beforeChild->isTableSection()) {
265  beforeChild = nullptr;
266  }
267  RenderTableSection *section = new(renderArena()) RenderTableSection(document() /* anonymous */);
268  RenderStyle *newStyle = new RenderStyle();
269  newStyle->inheritFrom(style());
270  newStyle->setDisplay(TABLE_ROW_GROUP);
271  section->setStyle(newStyle);
272  addChild(section, beforeChild);
273  section->addChild(child);
274 }
275 
276 void RenderTable::calcWidth()
277 {
278  if (isPositioned()) {
279  calcAbsoluteHorizontal();
280  }
281 
282  RenderBlock *cb = containingBlock();
283  int availableWidth = cb->lineWidth(m_y);
284 
285  LengthType widthType = style()->width().type();
286  if (widthType > Relative && style()->width().isPositive()) {
287  // Percent or fixed table
288  // Percent is calculated from contentWidth, not available width
289  m_width = calcBoxWidth(style()->width().minWidth(cb->contentWidth()));
290  } else {
291  // Subtract out any fixed margins from our available width for auto width tables.
292  int marginTotal = 0;
293  if (!style()->marginLeft().isAuto()) {
294  marginTotal += style()->marginLeft().width(availableWidth);
295  }
296  if (!style()->marginRight().isAuto()) {
297  marginTotal += style()->marginRight().width(availableWidth);
298  }
299 
300  // Subtract out our margins to get the available content width.
301  int availContentWidth = qMax(0, availableWidth - marginTotal);
302 
303  // Ensure we aren't bigger than our max width or smaller than our min width.
304  m_width = qMin(availContentWidth, m_maxWidth);
305  }
306 
307  m_width = qMax(m_width, m_minWidth);
308 
309  // Finally, with our true width determined, compute our margins for real.
310  m_marginRight = 0;
311  m_marginLeft = 0;
312 
313  calcHorizontalMargins(style()->marginLeft(), style()->marginRight(), availableWidth);
314 }
315 
316 QList< QRectF > RenderTable::getClientRects()
317 {
318  RenderFlow *cap = caption();
319  if (cap) {
320  // tables with caption report y&height inclusive caption, but we need them
321  // exclusive and a extra rect for the caption
322  // NOTE: first table, then caption
324 
325  int x = 0;
326  int y = 0;
327  absolutePosition(x, y);
328 
329  QRectF tableRect(x, y + cap->height(), width(), height() - cap->height());
330  list.append(clientRectToViewport(tableRect));
331 
332  QRectF captionRect(x, y, cap->width(), cap->height());
333  list.append(clientRectToViewport(captionRect));
334 
335  return list;
336  } else {
337  return RenderObject::getClientRects();
338  }
339 }
340 
341 void RenderTable::layout()
342 {
343  KHTMLAssert(needsLayout());
344  KHTMLAssert(minMaxKnown());
345  KHTMLAssert(!needSectionRecalc);
346 
347  if (markedForRepaint()) {
348  repaintDuringLayout();
349  setMarkedForRepaint(false);
350  }
351 
352  if (posChildNeedsLayout() && !normalChildNeedsLayout() && !selfNeedsLayout()) {
353  // All we have to is lay out our positioned objects.
354  layoutPositionedObjects(true);
355  setNeedsLayout(false);
356  return;
357  }
358 
359  m_height = 0;
360  initMaxMarginValues();
361 
362  int oldWidth = m_width;
363  calcWidth();
364  m_overflowWidth = m_width;
365 
366  if (tCaption && (oldWidth != m_width || tCaption->style()->height().isPercent())) {
367  tCaption->setChildNeedsLayout(true);
368  }
369 
370  // the optimization below doesn't work since the internal table
371  // layout could have changed. we need to add a flag to the table
372  // layout that tells us if something has changed in the min max
373  // calculations to do it correctly.
374 // if ( oldWidth != m_width || columns.size() + 1 != columnPos.size() )
375  tableLayout->layout();
376 
377 #ifdef DEBUG_LAYOUT
378  qCDebug(KHTML_LOG) << renderName() << "(Table)::layout1() width=" << width() << ", marginLeft=" << marginLeft() << " marginRight=" << marginRight();
379 #endif
380 
381  setCellWidths();
382 
383  // layout child objects
384  int calculatedHeight = 0;
385 
386  RenderObject *child = firstChild();
387  while (child) {
388  // FIXME: What about a form that has a display value that makes it a table section?
389  if (child->needsLayout() && !(child->element() && child->element()->id() == ID_FORM)) {
390  child->layout();
391  }
392  if (child->isTableSection()) {
393  static_cast<RenderTableSection *>(child)->calcRowHeight();
394  calculatedHeight += static_cast<RenderTableSection *>(child)->layoutRows(0);
395  }
396  child = child->nextSibling();
397  }
398 
399  // ### collapse caption margin
400  if (tCaption && tCaption->style()->captionSide() != CAPBOTTOM) {
401  tCaption->setPos(tCaption->marginLeft(), tCaption->marginTop() + m_height);
402  m_height += tCaption->height() + tCaption->marginTop() + tCaption->marginBottom();
403  }
404 
405  int bpTop = borderTop() + (collapseBorders() ? 0 : paddingTop());
406  int bpBottom = borderBottom() + (collapseBorders() ? 0 : paddingBottom());
407 
408  m_height += bpTop;
409 
410  int oldHeight = m_height;
411  if (isPositioned()) {
412  m_height += calculatedHeight + bpBottom;
413  }
414  calcHeight();
415  int newHeight = m_height;
416  m_height = oldHeight;
417 
418  Length h = style()->height();
419  int th = -(bpTop + bpBottom); // Tables size as though CSS height includes border/padding.
420  if (isPositioned()) {
421  th += newHeight;
422  } else if (h.isFixed()) {
423  th += h.value();
424  } else if (h.isPercent()) {
425  th += calcPercentageHeight(h);
426  }
427 
428  // layout rows
429  if (th > calculatedHeight) {
430  // we have to redistribute that height to get the constraint correctly
431  // just force the first body to the height needed
432  // ### FIXME This should take height constraints on all table sections into account and distribute
433  // accordingly. For now this should be good enough
434  if (firstBody) {
435  firstBody->calcRowHeight();
436  firstBody->layoutRows(th - calculatedHeight);
437  } else if (!style()->htmlHacks()) {
438  // Completely empty tables (with no sections or anything) should at least honor specified height
439  // in strict mode.
440  m_height += th;
441  }
442  }
443 
444  int bl = borderLeft();
445  if (!collapseBorders()) {
446  bl += paddingLeft();
447  }
448 
449  // position the table sections
450  RenderTableSection *section = head ? head : (firstBody ? firstBody : foot);
451  while (section) {
452  section->setPos(bl, m_height);
453 
454  m_height += section->height();
455  m_overflowLeft = qMin(m_overflowLeft, section->effectiveXPos());
456  m_overflowWidth = qMax(m_overflowWidth, section->effectiveXPos() + section->effectiveWidth());
457  section = sectionBelow(section);
458  }
459 
460  m_height += bpBottom;
461 
462  if (tCaption && tCaption->style()->captionSide() == CAPBOTTOM) {
463  tCaption->setPos(tCaption->marginLeft(), tCaption->marginTop() + m_height);
464  m_height += tCaption->height() + tCaption->marginTop() + tCaption->marginBottom();
465  }
466 
467  if (canvas()->pagedMode()) {
468  RenderObject *child = firstChild();
469  // relayout taking real position into account
470  while (child) {
471  if (!(child->element() && child->element()->id() == ID_FORM)) {
472  child->setNeedsLayout(true);
473  child->layout();
474  if (child->containsPageBreak()) {
475  setContainsPageBreak(true);
476  }
477  if (child->needsPageClear()) {
478  setNeedsPageClear(true);
479  }
480  }
481  child = child->nextSibling();
482  }
483  }
484 
485  //qCDebug(KHTML_LOG) << "table height: " << m_height;
486 
487  // table can be containing block of positioned elements.
488  // ### only pass true if width or height changed.
489  layoutPositionedObjects(true);
490 
491  m_overflowHeight = m_height;
492 
493  setNeedsLayout(false);
494 }
495 
496 void RenderTable::setCellWidths()
497 {
498 #ifdef DEBUG_LAYOUT
499  qCDebug(KHTML_LOG) << renderName() << "(Table, this=0x" << this << ")::setCellWidths()";
500 #endif
501 
502  RenderObject *child = firstChild();
503  while (child) {
504  if (child->isTableSection()) {
505  static_cast<RenderTableSection *>(child)->setCellWidths();
506  }
507  child = child->nextSibling();
508  }
509 }
510 
511 void RenderTable::paint(PaintInfo &pI, int _tx, int _ty)
512 {
513  if (needsLayout()) {
514  return;
515  }
516 
517  _tx += xPos();
518  _ty += yPos();
519 
520 #ifdef TABLE_PRINT
521  qCDebug(KHTML_LOG) << "RenderTable::paint() w/h = (" << width() << "/" << height() << ")";
522 #endif
523  if (!isRelPositioned() && !isPositioned()) {
524  int os = 2 * maximalOutlineSize(pI.phase);
525  if ((_ty > pI.r.y() + pI.r.height() + os) || (_ty + height() < pI.r.y() - os)) {
526  return;
527  }
528  if ((_tx > pI.r.x() + pI.r.width() + os) || (_tx + width() < pI.r.x() - os)) {
529  return;
530  }
531  }
532 
533 #ifdef TABLE_PRINT
534  qCDebug(KHTML_LOG) << "RenderTable::paint(2) " << _tx << "/" << _ty << " (" << _y << "/" << _h << ")";
535 #endif
536 
537  if (pI.phase == PaintActionOutline) {
538  paintOutline(pI.p, _tx, _ty, width(), height(), style());
539  }
540 
541  if ((pI.phase == PaintActionElementBackground || pI.phase == PaintActionChildBackground)
542  && shouldPaintBackgroundOrBorder() && style()->visibility() == VISIBLE) {
543  paintBoxDecorations(pI, _tx, _ty);
544  }
545 
546  if (pI.phase == PaintActionElementBackground) {
547  return;
548  }
549 
550  PaintAction oldphase = pI.phase;
551  if (pI.phase == PaintActionChildBackgrounds) {
552  pI.phase = PaintActionChildBackground;
553  }
554 
555  for (RenderObject *child = firstChild(); child; child = child->nextSibling())
556  if (child->isTableSection() || child == tCaption) {
557  child->paint(pI, _tx, _ty);
558  }
559 
560  if (collapseBorders() &&
561  (pI.phase == PaintActionElementBackground || pI.phase == PaintActionChildBackground)
562  && style()->visibility() == VISIBLE) {
563  // Collect all the unique border styles that we want to paint in a sorted list. Once we
564  // have all the styles sorted, we then do individual passes, painting each style of border
565  // from lowest precedence to highest precedence.
566  pI.phase = PaintActionCollapsedTableBorders;
567  QList<CollapsedBorderValue> borderStyles;
568  collectBorders(borderStyles);
569 #if 0
570  QString m;
571  for (uint i = 0; i < borderStyles.count(); i++) {
572  m += QString("%1 ").arg((*borderStyles.at(i)).width());
573  }
574  // qCDebug(KHTML_LOG) << m;
575 #endif
576  QList<CollapsedBorderValue>::Iterator it = borderStyles.begin();
578  for (; it != end; ++it) {
579  m_currentBorder = &*it;
580  for (RenderObject *child = firstChild(); child; child = child->nextSibling()) {
581  if (child->isTableSection()) {
582  child->paint(pI, _tx, _ty);
583  }
584  }
585  }
586  m_currentBorder = nullptr;
587  }
588 
589  pI.phase = oldphase;
590 #ifdef BOX_DEBUG
591  outlineBox(pI.p, _tx, _ty, "blue");
592 #endif
593 }
594 
595 void RenderTable::paintBoxDecorations(PaintInfo &pI, int _tx, int _ty)
596 {
597  int w = width();
598  int h = height();
599 
600  // Account for the caption.
601  if (tCaption) {
602  int captionHeight = (tCaption->height() + tCaption->marginBottom() + tCaption->marginTop());
603  h -= captionHeight;
604  if (tCaption->style()->captionSide() != CAPBOTTOM) {
605  _ty += captionHeight;
606  }
607  }
608  QRect cr;
609  cr.setX(qMax(_tx, pI.r.x()));
610  cr.setY(qMax(_ty, pI.r.y()));
611  int mw, mh;
612  if (_ty < pI.r.y()) {
613  mh = qMax(0, h - (pI.r.y() - _ty));
614  } else {
615  mh = qMin(pI.r.height(), h);
616  }
617  if (_tx < pI.r.x()) {
618  mw = qMax(0, w - (pI.r.x() - _tx));
619  } else {
620  mw = qMin(pI.r.width(), w);
621  }
622  cr.setWidth(mw);
623  cr.setHeight(mh);
624 
625  paintOneBackground(pI.p, style()->backgroundColor(), style()->backgroundLayers(), cr, _tx, _ty, w, h);
626 
627  if (style()->hasBorder() && !collapseBorders()) {
628  paintBorder(pI.p, _tx, _ty, w, h, style());
629  }
630 }
631 
632 void RenderTable::calcMinMaxWidth()
633 {
634  KHTMLAssert(!minMaxKnown());
635 
636  if (needSectionRecalc) {
637  recalcSections();
638  }
639 
640 #ifdef DEBUG_LAYOUT
641  qCDebug(KHTML_LOG) << renderName() << "(Table " << this << ")::calcMinMaxWidth()";
642 #endif
643 
644  tableLayout->calcMinMaxWidth();
645 
646  if (tCaption) {
647  tCaption->calcWidth();
648  if (tCaption->marginLeft() + tCaption->marginRight() + tCaption->minWidth() > m_minWidth) {
649  m_minWidth = tCaption->marginLeft() + tCaption->marginRight() + tCaption->minWidth();
650  }
651  }
652 
653  setMinMaxKnown();
654 #ifdef DEBUG_LAYOUT
655  qCDebug(KHTML_LOG) << renderName() << " END: (Table " << this << ")::calcMinMaxWidth() min = " << m_minWidth << " max = " << m_maxWidth;
656 #endif
657 }
658 
659 void RenderTable::close()
660 {
661 // qCDebug(KHTML_LOG) << "RenderTable::close()";
662  setNeedsLayoutAndMinMaxRecalc();
663 }
664 
665 void RenderTable::splitColumn(int pos, int firstSpan)
666 {
667  // we need to add a new columnStruct
668  int oldSize = columns.size();
669  columns.resize(oldSize + 1);
670  int oldSpan = columns[pos].span;
671 // qDebug("splitColumn( %d,%d ), oldSize=%d, oldSpan=%d", pos, firstSpan, oldSize, oldSpan );
672  KHTMLAssert(oldSpan > firstSpan);
673  columns[pos].span = firstSpan;
674  memmove(columns.data() + pos + 1, columns.data() + pos, (oldSize - pos)*sizeof(ColumnStruct));
675  columns[pos + 1].span = oldSpan - firstSpan;
676 
677  // change width of all rows.
678  RenderObject *child = firstChild();
679  while (child) {
680  if (child->isTableSection()) {
681  RenderTableSection *section = static_cast<RenderTableSection *>(child);
682  int size = section->grid.size();
683  int row = 0;
684  if (section->cCol > pos) {
685  section->cCol++;
686  }
687  while (row < size) {
688  section->grid[row].row->resize(oldSize + 1);
689  RenderTableSection::Row &r = *section->grid[row].row;
690  memmove(r.data() + pos + 1, r.data() + pos, (oldSize - pos)*sizeof(RenderTableCell *));
691 // qDebug("moving from %d to %d, num=%d", pos, pos+1, (oldSize-pos-1) );
692  r[pos + 1] = r[pos] ? (RenderTableCell *) - 1 : nullptr;
693  row++;
694  }
695  }
696  child = child->nextSibling();
697  }
698  columnPos.resize(numEffCols() + 1);
699  setNeedsLayoutAndMinMaxRecalc();
700 }
701 
702 void RenderTable::appendColumn(int span)
703 {
704  // easy case.
705  int pos = columns.size();
706 // qDebug("appendColumn( %d ), size=%d", span, pos );
707  int newSize = pos + 1;
708  columns.resize(newSize);
709  columns[pos].span = span;
710  //qDebug("appending column at %d, span %d", pos, span );
711 
712  // change width of all rows.
713  RenderObject *child = firstChild();
714  while (child) {
715  if (child->isTableSection()) {
716  RenderTableSection *section = static_cast<RenderTableSection *>(child);
717  int size = section->grid.size();
718  int row = 0;
719  while (row < size) {
720  section->grid[row].row->resize(newSize);
721  section->cellAt(row, pos) = nullptr;
722  row++;
723  }
724 
725  }
726  child = child->nextSibling();
727  }
728  columnPos.resize(numEffCols() + 1);
729  setNeedsLayoutAndMinMaxRecalc();
730 }
731 
732 RenderTableCol *RenderTable::colElement(int col, bool *startEdge, bool *endEdge) const
733 {
734  if (!has_col_elems) {
735  return nullptr;
736  }
737  RenderObject *child = firstChild();
738  int cCol = 0;
739  while (child) {
740  if (child->isTableCol()) {
741  RenderTableCol *colElem = static_cast<RenderTableCol *>(child);
742  int span = colElem->span();
743  if (!colElem->firstChild()) {
744  int startCol = cCol;
745  int endCol = cCol + span - 1;
746  cCol += span;
747  if (cCol > col) {
748  if (startEdge) {
749  *startEdge = startCol == col;
750  }
751  if (endEdge) {
752  *endEdge = endCol == col;
753  }
754  return colElem;
755  }
756  }
757 
758  RenderObject *next = child->firstChild();
759  if (!next) {
760  next = child->nextSibling();
761  }
762  if (!next && child->parent()->isTableCol()) {
763  next = child->parent()->nextSibling();
764  }
765  child = next;
766  } else if (child == tCaption) {
767  child = child->nextSibling();
768  } else {
769  break;
770  }
771  }
772  return nullptr;
773 }
774 
775 void RenderTable::recalcSections()
776 {
777  tCaption = nullptr;
778  head = foot = firstBody = nullptr;
779  has_col_elems = false;
780 
781  RenderObject *child = firstChild();
782  // We need to get valid pointers to caption, head, foot and firstbody again
783  while (child) {
784  switch (child->style()->display()) {
785  case TABLE_CAPTION:
786  if (!tCaption && child->isRenderBlock()) {
787  tCaption = static_cast<RenderBlock *>(child);
788  tCaption->setNeedsLayout(true);
789  }
790  break;
791  case TABLE_COLUMN:
792  case TABLE_COLUMN_GROUP:
793  has_col_elems = true;
794  break;
795  case TABLE_HEADER_GROUP:
796  if (child->isTableSection()) {
797  RenderTableSection *section = static_cast<RenderTableSection *>(child);
798  if (!head) {
799  head = section;
800  } else if (!firstBody) {
801  firstBody = section;
802  }
803  if (section->needCellRecalc) {
804  section->recalcCells();
805  }
806  }
807  break;
808  case TABLE_FOOTER_GROUP:
809  if (child->isTableSection()) {
810  RenderTableSection *section = static_cast<RenderTableSection *>(child);
811  if (!foot) {
812  foot = section;
813  } else if (!firstBody) {
814  firstBody = section;
815  }
816  if (section->needCellRecalc) {
817  section->recalcCells();
818  }
819  }
820  break;
821  case TABLE_ROW_GROUP:
822  if (child->isTableSection()) {
823  RenderTableSection *section = static_cast<RenderTableSection *>(child);
824  if (!firstBody) {
825  firstBody = section;
826  }
827  if (section->needCellRecalc) {
828  section->recalcCells();
829  }
830  }
831  break;
832  default:
833  break;
834  }
835  child = child->nextSibling();
836  }
837 
838  int maxCols = 0;
839  for (RenderObject *child = firstChild(); child; child = child->nextSibling()) {
840  if (child->isTableSection()) {
841  RenderTableSection *section = static_cast<RenderTableSection *>(child);
842  int sectionCols = section->numColumns();
843  if (sectionCols > maxCols) {
844  maxCols = sectionCols;
845  }
846  }
847  }
848 
849  columns.resize(maxCols);
850  columnPos.resize(maxCols + 1);
851 
852  needSectionRecalc = false;
853  setNeedsLayout(true);
854 }
855 
856 RenderObject *RenderTable::removeChildNode(RenderObject *child)
857 {
858  setNeedSectionRecalc();
859  return RenderContainer::removeChildNode(child);
860 }
861 
862 int RenderTable::borderLeft() const
863 {
864  if (collapseBorders()) {
865  // FIXME: For strict mode, returning 0 is correct, since the table border half spills into the margin,
866  // but I'm working to get this changed. For now, follow the spec.
867  return 0;
868  }
869  return RenderBlock::borderLeft();
870 }
871 
872 int RenderTable::borderRight() const
873 {
874  if (collapseBorders()) {
875  // FIXME: For strict mode, returning 0 is correct, since the table border half spills into the margin,
876  // but I'm working to get this changed. For now, follow the spec.
877  return 0;
878  }
879  return RenderBlock::borderRight();
880 }
881 
882 int RenderTable::borderTop() const
883 {
884  if (collapseBorders()) {
885  // FIXME: For strict mode, returning 0 is correct, since the table border half spills into the margin,
886  // but I'm working to get this changed. For now, follow the spec.
887  return 0;
888  }
889  return RenderBlock::borderTop();
890 }
891 
892 int RenderTable::borderBottom() const
893 {
894  if (collapseBorders()) {
895  // FIXME: For strict mode, returning 0 is correct, since the table border half spills into the margin,
896  // but I'm working to get this changed. For now, follow the spec.
897  return 0;
898  }
899  return RenderBlock::borderBottom();
900 }
901 
902 RenderTableSection *RenderTable::sectionAbove(const RenderTableSection *section, bool skipEmptySections)
903 {
904  if (needSectionRecalc) {
905  recalcSections();
906  }
907 
908  if (section == head) {
909  return nullptr;
910  }
911  RenderObject *prevSection = section == foot ? lastChild() : section->previousSibling();
912  while (prevSection) {
913  if (prevSection->isTableSection() && prevSection != head && prevSection != foot && (!skipEmptySections || static_cast<RenderTableSection *>(prevSection)->numRows())) {
914  break;
915  }
916  prevSection = prevSection->previousSibling();
917  }
918  if (!prevSection && head && (!skipEmptySections || head->numRows())) {
919  prevSection = head;
920  }
921  return static_cast<RenderTableSection *>(prevSection);
922 }
923 
924 RenderTableSection *RenderTable::sectionBelow(const RenderTableSection *section, bool skipEmptySections)
925 {
926  if (needSectionRecalc) {
927  recalcSections();
928  }
929 
930  if (section == foot) {
931  return nullptr;
932  }
933  RenderObject *nextSection = section == head ? firstChild() : section->nextSibling();
934  while (nextSection) {
935  if (nextSection->isTableSection() && nextSection != head && nextSection != foot && (!skipEmptySections || static_cast<RenderTableSection *>(nextSection)->numRows())) {
936  break;
937  }
938  nextSection = nextSection->nextSibling();
939  }
940  if (!nextSection && foot && (!skipEmptySections || foot->numRows())) {
941  nextSection = foot;
942  }
943  return static_cast<RenderTableSection *>(nextSection);
944 }
945 
946 RenderTableCell *RenderTable::cellAbove(const RenderTableCell *cell)
947 {
948  if (needSectionRecalc) {
949  recalcSections();
950  }
951 
952  // Find the section and row to look in
953  int r = cell->row();
954  RenderTableSection *section = nullptr;
955  int rAbove = 0;
956  if (r > 0) {
957  // cell is not in the first row, so use the above row in its own section
958  section = cell->section();
959  rAbove = r - 1;
960  } else {
961  section = sectionAbove(cell->section(), true);
962  if (section) {
963  rAbove = section->numRows() - 1;
964  }
965  }
966 
967  // Look up the cell in the section's grid, which requires effective col index
968  if (section) {
969  int effCol = colToEffCol(cell->col());
970  RenderTableCell *aboveCell;
971  // If we hit a span back up to a real cell.
972  do {
973  aboveCell = section->cellAt(rAbove, effCol);
974  effCol--;
975  } while (aboveCell == (RenderTableCell *) - 1 && effCol >= 0);
976  return (aboveCell == (RenderTableCell *) - 1) ? nullptr : aboveCell;
977  } else {
978  return nullptr;
979  }
980 }
981 
982 RenderTableCell *RenderTable::cellBelow(const RenderTableCell *cell)
983 {
984  if (needSectionRecalc) {
985  recalcSections();
986  }
987 
988  // Find the section and row to look in
989  int r = cell->row() + cell->rowSpan() - 1;
990  RenderTableSection *section = nullptr;
991  int rBelow = 0;
992  if (r < cell->section()->numRows() - 1) {
993  // The cell is not in the last row, so use the next row in the section.
994  section = cell->section();
995  rBelow = r + 1;
996  } else {
997  section = sectionBelow(cell->section(), true);
998  if (section) {
999  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(&section()->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(&section()->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 
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 
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
TableSectionIterator & operator--()
Moves to the previous section in visual order.
QString caption()
This class provides an iterator to iterate through the sections of a render table in their visual ord...
Definition: render_table.h:686
This file is part of the HTML rendering engine for KDE.
const T & at(int i) const const
QTextStream & right(QTextStream &stream)
KIOFILEWIDGETS_EXPORT void add(const QString &fileClass, const QString &directory)
MESSAGECORE_EXPORT KMime::Content * next(KMime::Content *node, bool allowChildren=true)
QTextStream & left(QTextStream &stream)
TableSectionIterator()
Uninitialized iterator.
Definition: render_table.h:707
T * data()
void drawRect(const QRectF &rectangle)
int count(const T &value) const const
void append(const T &value)
void setPen(const QColor &color)
void setWidth(int width)
MESSAGECORE_EXPORT KMime::Content * firstChild(const KMime::Content *node)
QRegion intersected(const QRegion &r) const const
void setBrush(const QBrush &brush)
This class implements the basic string we use in the DOM.
Definition: dom_string.h:44
TableSectionIterator & operator++()
Moves to the next section in visual order.
QList::iterator end()
bool contains(const T &value) const const
all geometry managing stuff is only in the block elements.
Definition: render_flow.h:44
This library provides a full-featured HTML parser and widget.
const QList< QKeySequence > & end()
void setX(int x)
void setY(int y)
void insert(int i, const T &value)
void setHeight(int height)
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
Base Class for all rendering tree objects.
bool isEmpty() const const
int size() const const
MESSAGECORE_EXPORT KMime::Content * nextSibling(const KMime::Content *node)
QList::iterator begin()
bool isValid() const const
KIOFILEWIDGETS_EXPORT QStringList list(const QString &fileClass)
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Sat Oct 16 2021 22:48:01 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.