Border collapsing implementation for tables
David Hyatt
hyatt at apple.com
Sat Oct 18 01:01:24 CEST 2003
Index: khtml/css/cssparser.cpp
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/khtml/css/cssparser.cpp,v
retrieving revision 1.42
diff -u -p -r1.42 khtml/css/cssparser.cpp
--- khtml/css/cssparser.cpp 2003/10/01 23:27:11 1.42
+++ khtml/css/cssparser.cpp 2003/10/16 09:35:15
@@ -565,7 +565,7 @@ bool CSSParser::parseValue( int propId,
case CSS_PROP_BORDER_RIGHT_STYLE: // Defined as: none | hidden | dotted | dashed |
case CSS_PROP_BORDER_BOTTOM_STYLE: // solid | double | groove | ridge | inset | outset
case CSS_PROP_BORDER_LEFT_STYLE: ////
- if (id >= CSS_VAL_NONE && id <= CSS_VAL_RIDGE)
+ if (id >= CSS_VAL_NONE && id <= CSS_VAL_DOUBLE)
valid_primitive = true;
break;
Index: khtml/css/cssstyleselector.cpp
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/khtml/css/cssstyleselector.cpp,v
retrieving revision 1.97
diff -u -p -r1.97 khtml/css/cssstyleselector.cpp
--- khtml/css/cssstyleselector.cpp 2003/10/14 19:47:25 1.97
+++ khtml/css/cssstyleselector.cpp 2003/10/16 08:58:55
@@ -1762,7 +1762,21 @@ void CSSStyleSelector::applyRule( int id
}
case CSS_PROP_EMPTY_CELLS:
+ {
+ if (value->cssValueType() == CSSValue::CSS_INHERIT)
+ {
+ if(!parentNode) return;
+ style->setEmptyCells(parentStyle->emptyCells());
+ break;
+ }
+ if (!primitiveValue) break;
+ int id = primitiveValue->getIdent();
+ if (id == CSS_VAL_SHOW)
+ style->setEmptyCells(SHOW);
+ else if (id == CSS_VAL_HIDE)
+ style->setEmptyCells(HIDE);
break;
+ }
case CSS_PROP_FLOAT:
{
if(value->cssValueType() == CSSValue::CSS_INHERIT)
Index: khtml/css/cssvalues.in
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/khtml/css/cssvalues.in,v
retrieving revision 1.10
diff -u -p -r1.10 khtml/css/cssvalues.in
--- khtml/css/cssvalues.in 2003/10/01 23:27:11 1.10
+++ khtml/css/cssvalues.in 2003/10/16 09:35:22
@@ -17,14 +17,14 @@ inherit
# CSS_PROP_BORDER_LEFT_STYLE
none
hidden
-dotted
-dashed
-double
-solid
-outset
inset
groove
ridge
+outset
+dotted
+dashed
+solid
+double
#
# CSS_PROP_FONT:
#
Index: khtml/rendering/bidi.cpp
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/khtml/rendering/bidi.cpp,v
retrieving revision 1.76
diff -u -p -r1.76 khtml/rendering/bidi.cpp
--- khtml/rendering/bidi.cpp 2003/10/07 15:49:30 1.76
+++ khtml/rendering/bidi.cpp 2003/10/16 08:58:58
@@ -1282,8 +1282,8 @@ void RenderBlock::layoutInlineChildren(b
#if BIDI_DEBUG > 1 || defined( DEBUG_LINEBREAKS )
kdDebug(6041) << " ------- bidi start " << this << " -------" << endl;
#endif
- int toAdd = style()->borderBottomWidth();
- m_height = style()->borderTopWidth();
+ int toAdd = borderBottom();
+ m_height = borderTop();
m_height += paddingTop();
toAdd += paddingBottom();
Index: khtml/rendering/render_form.cpp
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/khtml/rendering/render_form.cpp,v
retrieving revision 1.65
diff -u -p -r1.65 khtml/rendering/render_form.cpp
--- khtml/rendering/render_form.cpp 2003/10/10 22:03:13 1.65
+++ khtml/rendering/render_form.cpp 2003/10/16 09:35:31
@@ -680,15 +680,15 @@ void RenderFieldset::paintBorderMinusLeg
if(render_t) {
drawBorder(p, _tx, _ty, _tx + lx, _ty + style->borderTopWidth(), BSTop, tc, style->color(), ts,
- (render_l && ls<=DOUBLE?style->borderLeftWidth():0), 0);
+ (render_l && (ls == DOTTED || ls == DASHED || ls == DOUBLE)?style->borderLeftWidth():0), 0);
drawBorder(p, _tx+lx+lw, _ty, _tx + w, _ty + style->borderTopWidth(), BSTop, tc, style->color(), ts,
- 0, (render_r && rs<=DOUBLE?style->borderRightWidth():0));
+ 0, (render_r && (rs == DOTTED || rs == DASHED || rs == DOUBLE)?style->borderRightWidth():0));
}
if(render_b)
drawBorder(p, _tx, _ty + h - style->borderBottomWidth(), _tx + w, _ty + h, BSBottom, bc, style->color(), bs,
- (render_l && ls<=DOUBLE?style->borderLeftWidth():0),
- (render_r && rs<=DOUBLE?style->borderRightWidth():0));
+ (render_l && (ls == DOTTED || ls == DASHED || ls == DOUBLE)?style->borderLeftWidth():0),
+ (render_r && (rs == DOTTED || rs == DASHED || rs == DOUBLE)?style->borderRightWidth():0));
if(render_l)
{
@@ -696,12 +696,12 @@ void RenderFieldset::paintBorderMinusLeg
bool ignore_top =
(tc == lc) &&
- (ls <= OUTSET) &&
+ (ls >= OUTSET) &&
(ts == DOTTED || ts == DASHED || ts == SOLID || ts == OUTSET);
bool ignore_bottom =
(bc == lc) &&
- (ls <= OUTSET) &&
+ (ls >= OUTSET) &&
(bs == DOTTED || bs == DASHED || bs == SOLID || bs == INSET);
drawBorder(p, _tx, _ty, _tx + style->borderLeftWidth(), _ty + h, BSLeft, lc, style->color(), ls,
@@ -715,12 +715,12 @@ void RenderFieldset::paintBorderMinusLeg
bool ignore_top =
(tc == rc) &&
- (rs <= SOLID || rs == INSET) &&
+ (rs >= DOTTED || rs == INSET) &&
(ts == DOTTED || ts == DASHED || ts == SOLID || ts == OUTSET);
bool ignore_bottom =
(bc == rc) &&
- (rs <= SOLID || rs == INSET) &&
+ (rs >= DOTTED || rs == INSET) &&
(bs == DOTTED || bs == DASHED || bs == SOLID || bs == INSET);
drawBorder(p, _tx + w - style->borderRightWidth(), _ty, _tx + w, _ty + h, BSRight, rc, style->color(), rs,
Index: khtml/rendering/render_object.cpp
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/khtml/rendering/render_object.cpp,v
retrieving revision 1.104
diff -u -p -r1.104 khtml/rendering/render_object.cpp
--- khtml/rendering/render_object.cpp 2003/10/10 22:03:14 1.104
+++ khtml/rendering/render_object.cpp 2003/10/16 09:35:33
@@ -811,12 +811,12 @@ void RenderObject::paintBorder(QPainter
if(render_t) {
bool ignore_left =
(tc == lc) && (tt == lt) &&
- (ts <= OUTSET) &&
+ (ts >= OUTSET) &&
(ls == DOTTED || ls == DASHED || ls == SOLID || ls == OUTSET);
bool ignore_right =
(tc == rc) && (tt == rt) &&
- (ts <= OUTSET) &&
+ (ts >= OUTSET) &&
(rs == DOTTED || rs == DASHED || rs == SOLID || rs == INSET);
drawBorder(p, _tx, _ty, _tx + w, _ty + style->borderTopWidth(), BSTop, tc, style->color(), ts,
@@ -827,12 +827,12 @@ void RenderObject::paintBorder(QPainter
if(render_b) {
bool ignore_left =
(bc == lc) && (bt == lt) &&
- (bs <= OUTSET) &&
+ (bs >= OUTSET) &&
(ls == DOTTED || ls == DASHED || ls == SOLID || ls == OUTSET);
bool ignore_right =
(bc == rc) && (bt == rt) &&
- (bs <= OUTSET) &&
+ (bs >= OUTSET) &&
(rs == DOTTED || rs == DASHED || rs == SOLID || rs == INSET);
drawBorder(p, _tx, _ty + h - style->borderBottomWidth(), _tx + w, _ty + h, BSBottom, bc, style->color(), bs,
@@ -844,12 +844,12 @@ void RenderObject::paintBorder(QPainter
{
bool ignore_top =
(tc == lc) && (tt == lt) &&
- (ls <= OUTSET) &&
+ (ls >= OUTSET) &&
(ts == DOTTED || ts == DASHED || ts == SOLID || ts == OUTSET);
bool ignore_bottom =
(bc == lc) && (bt == lt) &&
- (ls <= OUTSET) &&
+ (ls >= OUTSET) &&
(bs == DOTTED || bs == DASHED || bs == SOLID || bs == INSET);
drawBorder(p, _tx, _ty, _tx + style->borderLeftWidth(), _ty + h, BSLeft, lc, style->color(), ls,
@@ -861,12 +861,12 @@ void RenderObject::paintBorder(QPainter
{
bool ignore_top =
(tc == rc) && (tt == rt) &&
- (rs <= SOLID || rs == INSET) &&
+ (rs >= DOTTED || rs == INSET) &&
(ts == DOTTED || ts == DASHED || ts == SOLID || ts == OUTSET);
bool ignore_bottom =
(bc == rc) && (bt == rt) &&
- (rs <= SOLID || rs == INSET) &&
+ (rs >= DOTTED || rs == INSET) &&
(bs == DOTTED || bs == DASHED || bs == SOLID || bs == INSET);
drawBorder(p, _tx + w - style->borderRightWidth(), _ty, _tx + w, _ty + h, BSRight, rc, style->color(), rs,
@@ -1791,6 +1791,12 @@ void RenderObject::updateWidgetPositions
}
#endif
+void RenderObject::collectBorders(QPtrList<CollapsedBorderValue>& borderStyles)
+{
+ for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling())
+ curr->collectBorders(borderStyles);
+}
+
QChar RenderObject::backslashAsCurrencySymbol() const
{
#if !APPLE_CHANGES
Index: khtml/rendering/render_object.h
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/khtml/rendering/render_object.h,v
retrieving revision 1.85
diff -u -p -r1.85 khtml/rendering/render_object.h
--- khtml/rendering/render_object.h 2003/10/10 22:03:14 1.85
+++ khtml/rendering/render_object.h 2003/10/16 08:59:06
@@ -71,7 +71,8 @@ typedef enum {
PaintActionChildBackgrounds,
PaintActionFloat,
PaintActionForeground,
- PaintActionSelection
+ PaintActionSelection,
+ PaintActionCollapsedTableBorders
} PaintAction;
typedef enum {
@@ -100,6 +101,7 @@ namespace khtml {
class RenderLayer;
class InlineBox;
class InlineFlowBox;
+ class CollapsedBorderValue;
/**
* Base Class for all rendering tree objects.
@@ -577,6 +579,9 @@ public:
int adjbw1, int adjbw2, bool invalidisInvert = false);
virtual void setTable(RenderTable*) {};
+
+ // Used by collapsed border tables.
+ virtual void collectBorders(QPtrList<CollapsedBorderValue>& borderStyles);
// Repaint the entire object. Called when, e.g., the color of a border changes, or when a border
// style changes.
Index: khtml/rendering/render_style.h
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/khtml/rendering/render_style.h,v
retrieving revision 1.31
diff -u -p -r1.31 khtml/rendering/render_style.h
--- khtml/rendering/render_style.h 2003/10/10 23:22:10 1.31
+++ khtml/rendering/render_style.h 2003/10/16 08:59:09
@@ -184,10 +184,10 @@ enum EFloat {
//------------------------------------------------
// Border attributes. Not inherited.
-
+// These have been defined in the order of their precedence for border-collapsing. Do
+// not change this order!
enum EBorderStyle {
- BNONE, BHIDDEN, DOTTED, DASHED, DOUBLE, SOLID,
- OUTSET, INSET, GROOVE, RIDGE
+ BNONE, BHIDDEN, INSET, GROOVE, RIDGE, OUTSET, DOTTED, DASHED, SOLID, DOUBLE
};
@@ -222,6 +222,30 @@ public:
};
+enum EBorderPrecedence { BOFF, BTABLE, BCOLGROUP, BCOL, BROWGROUP, BROW, BCELL };
+
+struct CollapsedBorderValue
+{
+ CollapsedBorderValue() :border(0), precedence(BOFF) {}
+ CollapsedBorderValue(const BorderValue* b, EBorderPrecedence p) :border(b), precedence(p) {}
+
+ int width() const { return border ? border->width : 0; }
+ EBorderStyle style() const { return border ? border->style : BHIDDEN; }
+ bool exists() const { return border; }
+ QColor color() const { return border ? border->color : QColor(); }
+ bool isTransparent() const { return border ? border->transparent : true; }
+
+ bool operator==(const CollapsedBorderValue& o) const
+ {
+ if (!border) return !o.border;
+ if (!o.border) return false;
+ return *border == *o.border && precedence == o.precedence;
+ }
+
+ const BorderValue* border;
+ EBorderPrecedence precedence;
+};
+
class BorderData : public Shared<BorderData>
{
public:
@@ -799,6 +824,11 @@ public:
Length minHeight() const { return box->min_height; }
Length maxHeight() const { return box->max_height; }
+ const BorderValue& borderLeft() const { return surround->border.left; }
+ const BorderValue& borderRight() const { return surround->border.right; }
+ const BorderValue& borderTop() const { return surround->border.top; }
+ const BorderValue& borderBottom() const { return surround->border.bottom; }
+
Index: khtml/rendering/render_table.cpp
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/khtml/rendering/render_table.cpp,v
retrieving revision 1.78
diff -u -p -r1.78 khtml/rendering/render_table.cpp
--- khtml/rendering/render_table.cpp 2003/10/10 23:22:10 1.78
+++ khtml/rendering/render_table.cpp 2003/10/16 08:59:11
@@ -55,11 +55,13 @@ RenderTable::RenderTable(DOM::NodeImpl*
tCaption = 0;
head = foot = firstBody = 0;
tableLayout = 0;
-
+ m_currentBorder = 0;
+
@@ -105,6 +108,10 @@ void RenderTable::setStyle(RenderStyle *
} else
tableLayout = new AutoTableLayout(this);
}
+
+ // The table never paints its border if it collapses. It lets the cells do the painting.
+ if (collapseBorders())
+ setShouldPaintBackgroundOrBorder(false);
}
void RenderTable::addChild(RenderObject *child, RenderObject *beforeChild)
@@ -417,9 +434,8 @@ void RenderTable::paint( QPainter *p, in
#endif
if ((paintAction == PaintActionElementBackground || paintAction == PaintActionChildBackground)
- && style()->visibility() == VISIBLE) {
+ && shouldPaintBackgroundOrBorder() && style()->visibility() == VISIBLE)
paintBoxDecorations(p, _x, _y, _w, _h, _tx, _ty);
- }
// We're done. We don't bother painting any children.
if (paintAction == PaintActionElementBackground)
@@ -435,6 +451,24 @@ void RenderTable::paint( QPainter *p, in
child = child->nextSibling();
}
+ if (collapseBorders() &&
+ (paintAction == PaintActionElementBackground || paintAction == PaintActionChildBackground)
+ && style()->visibility() == VISIBLE) {
+ // Collect all the unique border styles that we want to paint in a sorted list. Once we
+ // have all the styles sorted, we then do individual passes, painting each style of border
+ // from lowest precedence to highest precedence.
+ QPtrList<CollapsedBorderValue> borderStyles;
+ borderStyles.setAutoDelete(true);
+ collectBorders(borderStyles);
+ for (uint i = 0; i < borderStyles.count(); i++) {
+ m_currentBorder = borderStyles.at(i);
+ for (child = firstChild(); child; child = child->nextSibling()) {
+ if (child->isTableSection())
+ child->paint(p, _x, _y, _w, _h, _tx, _ty, PaintActionCollapsedTableBorders);
+ }
+ }
+ }
+
#ifdef BOX_DEBUG
outlineBox(p, _tx, _ty, "blue");
#endif
@@ -641,30 +675,70 @@ RenderObject* RenderTable::removeChildNo
return RenderContainer::removeChildNode( child );
}
-#if APPLE_CHANGES
-RenderTableCell* RenderTable::cellAbove(RenderTableCell* cell) const
+int RenderTable::borderLeft() const
{
+ if (collapseBorders()) {
+ // FIXME: For strict mode, returning 0 is correct, since the table border half spills into the margin,
+ // but I'm working to get this changed. For now, follow the spec.
+ return 0;
+ }
+ return RenderBlock::borderLeft();
+}
+
+int RenderTable::borderRight() const
+{
+ if (collapseBorders()) {
+ // FIXME: For strict mode, returning 0 is correct, since the table border half spills into the margin,
+ // but I'm working to get this changed. For now, follow the spec.
+ return 0;
+ }
+ return RenderBlock::borderRight();
+}
+
+int RenderTable::borderTop() const
+{
+ if (collapseBorders()) {
+ // FIXME: For strict mode, returning 0 is correct, since the table border half spills into the margin,
+ // but I'm working to get this changed. For now, follow the spec.
+ return 0;
+ }
+ return RenderBlock::borderTop();
+}
+
+int RenderTable::borderBottom() const
+{
+ if (collapseBorders()) {
+ // FIXME: For strict mode, returning 0 is correct, since the table border half spills into the margin,
+ // but I'm working to get this changed. For now, follow the spec.
+ return 0;
+ }
+ return RenderBlock::borderBottom();
+}
+
+RenderTableCell* RenderTable::cellAbove(const RenderTableCell* cell) const
+{
// Find the section and row to look in
int r = cell->row();
- RenderTableSection *section;
- int rAbove = 0;
+ RenderTableSection* section = 0;
+ int rAbove = -1;
if (r > 0) {
// cell is not in the first row, so use the above row in its own section
section = cell->section();
rAbove = r-1;
} else {
// cell is at top of a section, use last row in previous section
- RenderObject *prevSection = cell->section()->previousSibling();
- while (prevSection && !prevSection->isTableSection()) {
- prevSection = prevSection->previousSibling();
- }
- section = static_cast<RenderTableSection *>(prevSection);
- if (section) {
- rAbove = section->numRows()-1;
+ for (RenderObject *prevSection = cell->section()->previousSibling();
+ prevSection && rAbove < 0;
+ prevSection = prevSection->previousSibling()) {
+ if (prevSection->isTableSection()) {
+ section = static_cast<RenderTableSection *>(prevSection);
+ if (section->numRows() > 0)
+ rAbove = section->numRows()-1;
+ }
}
}
- // Look up the cell in the section's grid, which required effective col index
+ // Look up the cell in the section's grid, which requires effective col index
if (section && rAbove >= 0) {
int effCol = colToEffCol(cell->col());
RenderTableCell* aboveCell;
@@ -678,8 +752,70 @@ RenderTableCell* RenderTable::cellAbove(
return 0;
}
}
-#endif
+RenderTableCell* RenderTable::cellBelow(const RenderTableCell* cell) const
+{
+ // Find the section and row to look in
+ int r = cell->row() + cell->rowSpan() - 1;
+ RenderTableSection* section = 0;
+ int rBelow = -1;
+ if (r < cell->section()->numRows()-1) {
+ // The cell is not in the last row, so use the next row in the section.
+ section = cell->section();
+ rBelow= r+1;
+ } else {
+ // The cell is at the bottom of a section. Use the first row in the next section.
+ for (RenderObject* nextSection = cell->section()->nextSibling();
+ nextSection && rBelow < 0;
+ nextSection = nextSection->nextSibling())
+ {
+ if (nextSection->isTableSection()) {
+ section = static_cast<RenderTableSection *>(nextSection);
+ if (section->numRows() > 0)
+ rBelow = 0;
+ }
+ }
+ }
+
+ // Look up the cell in the section's grid, which requires effective col index
+ if (section && rBelow >= 0) {
+ int effCol = colToEffCol(cell->col());
+ RenderTableCell* belowCell;
+ // If we hit a colspan back up to a real cell.
+ do {
+ belowCell = section->cellAt(rBelow, effCol);
+ effCol--;
+ } while (belowCell == (RenderTableCell *)-1 && effCol >=0);
+ return (belowCell == (RenderTableCell *)-1) ? 0 : belowCell;
+ } else {
+ return 0;
+ }
+}
+
+RenderTableCell* RenderTable::cellLeft(const RenderTableCell* cell) const
+{
+ RenderTableSection* section = cell->section();
+ int effCol = colToEffCol(cell->col());
+ if (effCol == 0)
+ return 0;
+
+ // If we hit a colspan back up to a real cell.
+ RenderTableCell* prevCell;
+ do {
+ prevCell = section->cellAt(cell->row(), effCol-1);
+ effCol--;
+ } while (prevCell == (RenderTableCell *)-1 && effCol >=0);
+ return (prevCell == (RenderTableCell *)-1) ? 0 : prevCell;
+}
+
+RenderTableCell* RenderTable::cellRight(const RenderTableCell* cell) const
+{
+ int effCol = colToEffCol(cell->col());
+ if (effCol == numEffCols()-1)
+ return 0;
+ return cell->section()->cellAt(cell->row(), effCol+1);
+}
+
#ifndef NDEBUG
void RenderTable::dump(QTextStream *stream, QString ind) const
{
@@ -1568,6 +1705,313 @@ void RenderTableCell::setStyle( RenderSt
}
}
+// The following rules apply for resolving conflicts and figuring out which border
+// to use.
+// (1) Borders with the 'border-style' of 'hidden' take precedence over all other conflicting
+// borders. Any border with this value suppresses all borders at this location.
+// (2) Borders with a style of 'none' have the lowest priority. Only if the border properties of all
+// the elements meeting at this edge are 'none' will the border be omitted (but note that 'none' is
+// the default value for the border style.)
+// (3) If none of the styles are 'hidden' and at least one of them is not 'none', then narrow borders
+// are discarded in favor of wider ones. If several have the same 'border-width' then styles are preferred
+// in this order: 'double', 'solid', 'dashed', 'dotted', 'ridge', 'outset', 'groove', and the lowest: 'inset'.
+// (4) If border styles differ only in color, then a style set on a cell wins over one on a row,
+// which wins over a row group, column, column group and, lastly, table. It is undefined which color
+// is used when two elements of the same type disagree.
+static const CollapsedBorderValue compareBorders(const CollapsedBorderValue& border1,
+ const CollapsedBorderValue& border2)
+{
+ // Sanity check the values passed in. If either is null, return the other.
+ if (!border2.exists()) return border1;
+ if (!border1.exists()) return border2;
+
+ // Rule #1 above.
+ if (border1.style() == BHIDDEN || border2.style() == BHIDDEN)
+ return CollapsedBorderValue(); // No border should exist at this location.
+
+ // Rule #2 above. A style of 'none' has lowest priority and always loses to any other border.
+ if (border2.style() == BNONE) return border1;
+ if (border1.style() == BNONE) return border2;
+
+ // The first part of rule #3 above. Wider borders win.
+ if (border1.width() != border2.width())
+ return border1.width() > border2.width() ? border1 : border2;
+
+ // The borders have equal width. Sort by border style.
+ if (border1.style() != border2.style())
+ return border1.style() > border2.style() ? border1 : border2;
+
+ // The border have the same width and style. Rely on precedence (cell over row over row group, etc.)
+ return border1.precedence >= border2.precedence ? border1 : border2;
+}
+
+CollapsedBorderValue RenderTableCell::collapsedLeftBorder() const
+{
+ // For border left, we need to check, in order of precedence:
+ // (1) Our left border.
+ CollapsedBorderValue result(&style()->borderLeft(), BCELL);
+
+ // (2) The previous cell's right border.
+ RenderTableCell* prevCell = table()->cellLeft(this);
+ if (prevCell) {
+ result = compareBorders(result, CollapsedBorderValue(&prevCell->style()->borderRight(), BCELL));
+ if (!result.exists()) return result;
+ }
+ else if (col() == 0) {
+ // (3) Our row's left border.
+ result = compareBorders(result, CollapsedBorderValue(&parent()->style()->borderLeft(), BROW));
+ if (!result.exists()) return result;
+
+ // (4) Our row group's left border.
+ result = compareBorders(result, CollapsedBorderValue(§ion()->style()->borderLeft(), BROWGROUP));
+ if (!result.exists()) return result;
+ }
+
+ // (5) Our column's left border.
+ RenderTableCol* colElt = table()->colElement(col());
+ if (colElt) {
+ result = compareBorders(result, CollapsedBorderValue(&colElt->style()->borderLeft(), BCOL));
+ if (!result.exists()) return result;
+ }
+
+ // (6) The previous column's right border.
+ if (col() > 0) {
+ colElt = table()->colElement(col()-1);
+ if (colElt) {
+ result = compareBorders(result, CollapsedBorderValue(&colElt->style()->borderRight(), BCOL));
+ if (!result.exists()) return result;
+ }
+ }
+
+ if (col() == 0) {
+ // (7) The table's left border.
+ result = compareBorders(result, CollapsedBorderValue(&table()->style()->borderLeft(), BTABLE));
+ if (!result.exists()) return result;
+ }
+
+ return result;
+}
+
+CollapsedBorderValue RenderTableCell::collapsedRightBorder() const
+{
+ RenderTable* tableElt = table();
+ bool inLastColumn = false;
+ int effCol = tableElt->colToEffCol(col());
+ if (effCol == tableElt->numEffCols()-1)
+ inLastColumn = true;
+
+ // For border right, we need to check, in order of precedence:
+ // (1) Our right border.
+ CollapsedBorderValue result = CollapsedBorderValue(&style()->borderRight(), BCELL);
+
+ // (2) The next cell's left border.
+ if (!inLastColumn) {
+ RenderTableCell* nextCell = tableElt->cellRight(this);
+ if (nextCell) {
+ result = compareBorders(result, CollapsedBorderValue(&nextCell->style()->borderLeft(), BCELL));
+ if (!result.exists()) return result;
+ }
+ }
+ else {
+ // (3) Our row's right border.
+ result = compareBorders(result, CollapsedBorderValue(&parent()->style()->borderRight(), BROW));
+ if (!result.exists()) return result;
+
+ // (4) Our row group's right border.
+ result = compareBorders(result, CollapsedBorderValue(§ion()->style()->borderRight(), BROWGROUP));
+ if (!result.exists()) return result;
+ }
+
+ // (5) Our column's right border.
+ RenderTableCol* colElt = table()->colElement(col());
+ if (colElt) {
+ result = compareBorders(result, CollapsedBorderValue(&colElt->style()->borderRight(), BCOL));
+ if (!result.exists()) return result;
+ }
+
+ // (6) The next column's left border.
+ if (!inLastColumn) {
+ colElt = tableElt->colElement(col()+1);
+ if (colElt) {
+ result = compareBorders(result, CollapsedBorderValue(&colElt->style()->borderLeft(), BCOL));
+ if (!result.exists()) return result;
+ }
+ }
+ else {
+ // (7) The table's right border.
+ result = compareBorders(result, CollapsedBorderValue(&tableElt->style()->borderRight(), BTABLE));
+ if (!result.exists()) return result;
+ }
+
+ return result;
+}
+
+CollapsedBorderValue RenderTableCell::collapsedTopBorder() const
+{
+ // For border top, we need to check, in order of precedence:
+ // (1) Our top border.
+ CollapsedBorderValue result = CollapsedBorderValue(&style()->borderTop(), BCELL);
+
+ RenderTableCell* prevCell = table()->cellAbove(this);
+ if (prevCell) {
+ // (2) A previous cell's bottom border.
+ result = compareBorders(result, CollapsedBorderValue(&prevCell->style()->borderBottom(), BCELL));
+ if (!result.exists()) return result;
+ }
+
+ // (3) Our row's top border.
+ result = compareBorders(result, CollapsedBorderValue(&parent()->style()->borderTop(), BROW));
+ if (!result.exists()) return result;
+
+ // (4) The previous row's bottom border.
+ if (prevCell) {
+ RenderObject* prevRow = 0;
+ if (prevCell->section() == section())
+ prevRow = parent()->previousSibling();
+ else
+ prevRow = prevCell->section()->lastChild();
+
+ if (prevRow) {
+ result = compareBorders(result, CollapsedBorderValue(&prevRow->style()->borderBottom(), BROW));
+ if (!result.exists()) return result;
+ }
+ }
+
+ // Now check row groups.
+ RenderObject* currSection = parent()->parent();
+ if (row() == 0) {
+ // (5) Our row group's top border.
+ result = compareBorders(result, CollapsedBorderValue(&currSection->style()->borderTop(), BROWGROUP));
+ if (!result.exists()) return result;
+
+ // (6) Previous row group's bottom border.
+ for (currSection = currSection->previousSibling(); currSection;
+ currSection = currSection->previousSibling()) {
+ if (currSection->isTableSection()) {
+ RenderTableSection* section = static_cast<RenderTableSection*>(currSection);
+ result = compareBorders(result, CollapsedBorderValue(§ion->style()->borderBottom(), BROWGROUP));
+ if (!result.exists()) return result;
+ }
+ }
+ }
+
+ if (!currSection) {
+ // (8) Our column's top border.
+ RenderTableCol* colElt = table()->colElement(col());
+ if (colElt) {
+ result = compareBorders(result, CollapsedBorderValue(&colElt->style()->borderTop(), BCOL));
+ if (!result.exists()) return result;
+ }
+
+ // (9) The table's top border.
+ result = compareBorders(result, CollapsedBorderValue(&table()->style()->borderTop(), BTABLE));
+ if (!result.exists()) return result;
+ }
+
+ return result;
+}
+
+CollapsedBorderValue RenderTableCell::collapsedBottomBorder() const
+{
+ // For border top, we need to check, in order of precedence:
+ // (1) Our bottom border.
+ CollapsedBorderValue result = CollapsedBorderValue(&style()->borderBottom(), BCELL);
+
+ RenderTableCell* nextCell = table()->cellBelow(this);
+ if (nextCell) {
+ // (2) A following cell's top border.
+ result = compareBorders(result, CollapsedBorderValue(&nextCell->style()->borderTop(), BCELL));
+ if (!result.exists()) return result;
+ }
+
+ // (3) Our row's bottom border. (FIXME: Deal with rowspan!)
+ result = compareBorders(result, CollapsedBorderValue(&parent()->style()->borderBottom(), BROW));
+ if (!result.exists()) return result;
+
+ // (4) The next row's top border.
+ if (nextCell) {
+ result = compareBorders(result, CollapsedBorderValue(&nextCell->parent()->style()->borderTop(), BROW));
+ if (!result.exists()) return result;
+ }
+
+ // Now check row groups.
+ RenderObject* currSection = parent()->parent();
+ if (row()+rowSpan() >= static_cast<RenderTableSection*>(currSection)->numRows()) {
+ // (5) Our row group's bottom border.
+ result = compareBorders(result, CollapsedBorderValue(&currSection->style()->borderBottom(), BROWGROUP));
+ if (!result.exists()) return result;
+
+ // (6) Following row group's top border.
+ for (currSection = currSection->nextSibling(); currSection;
+ currSection = currSection->nextSibling()) {
+ if (currSection->isTableSection()) {
+ RenderTableSection* section = static_cast<RenderTableSection*>(currSection);
+ result = compareBorders(result, CollapsedBorderValue(§ion->style()->borderTop(), BROWGROUP));
+ if (!result.exists()) return result;
+ }
+ }
+ }
+
+ if (!currSection) {
+ // (8) Our column's bottom border.
+ RenderTableCol* colElt = table()->colElement(col());
+ if (colElt) {
+ result = compareBorders(result, CollapsedBorderValue(&colElt->style()->borderBottom(), BCOL));
+ if (!result.exists()) return result;
+ }
+
+ // (9) The table's bottom border.
+ result = compareBorders(result, CollapsedBorderValue(&table()->style()->borderBottom(), BTABLE));
+ if (!result.exists()) return result;
+ }
+
+ return result;
+}
+
+int RenderTableCell::borderLeft() const
+{
+ if (table()->collapseBorders()) {
+ CollapsedBorderValue border = collapsedLeftBorder();
+ if (border.exists())
+ return int(border.width()/2.0+0.5); // Give the extra pixel to top and left.
+ return 0;
+ }
+ return RenderBlock::borderLeft();
+}
+
+int RenderTableCell::borderRight() const
+{
+ if (table()->collapseBorders()) {
+ CollapsedBorderValue border = collapsedRightBorder();
+ if (border.exists())
+ return border.width()/2;
+ return 0;
+ }
+ return RenderBlock::borderRight();
+}
+
+int RenderTableCell::borderTop() const
+{
+ if (table()->collapseBorders()) {
+ CollapsedBorderValue border = collapsedTopBorder();
+ if (border.exists())
+ return int(border.width()/2.0+0.5); // Give the extra pixel to top and left.
+ return 0;
+ }
+ return RenderBlock::borderTop();
+}
+
+int RenderTableCell::borderBottom() const
+{
+ if (table()->collapseBorders()) {
+ CollapsedBorderValue border = collapsedBottomBorder();
+ if (border.exists())
+ return border.width()/2;
+ return 0;
+ }
+ return RenderBlock::borderBottom();
+}
+
#ifdef BOX_DEBUG
#include <qpainter.h>
@@ -1603,10 +2047,168 @@ void RenderTableCell::paint(QPainter *p,
#endif
}
+static EBorderStyle collapsedBorderStyle(EBorderStyle style)
+{
+ if (style == OUTSET)
+ style = GROOVE;
+ else if (style == INSET)
+ style = RIDGE;
+ return style;
+}
+
+struct CollapsedBorder {
+ CollapsedBorder(){}
+
+ CollapsedBorderValue border;
+ RenderObject::BorderSide side;
+ bool shouldPaint;
+ int x1;
+ int y1;
+ int x2;
+ int y2;
+ EBorderStyle style;
+};
+
+class CollapsedBorders
+{
+public:
+ CollapsedBorders(int i) :count(0) {}
+
+ void addBorder(const CollapsedBorderValue& b, RenderObject::BorderSide s, bool paint,
+ int _x1, int _y1, int _x2, int _y2,
+ EBorderStyle _style)
+ {
+ if (b.exists() && paint) {
+ borders[count].border = b;
+ borders[count].side = s;
+ borders[count].shouldPaint = paint;
+ borders[count].x1 = _x1;
+ borders[count].x2 = _x2;
+ borders[count].y1 = _y1;
+ borders[count].y2 = _y2;
+ borders[count].style = _style;
+ count++;
+ }
+ }
+
+ CollapsedBorder* nextBorder() {
+ for (int i = 0; i < count; i++) {
+ if (borders[i].border.exists() && borders[i].shouldPaint) {
+ borders[i].shouldPaint = false;
+ return &borders[i];
+ }
+ }
+
+ return 0;
+ }
+
+ CollapsedBorder borders[4];
+ int count;
+};
+
+static void addBorderStyle(QPtrList<CollapsedBorderValue>& borderStyles, CollapsedBorderValue borderValue)
+{
+ if (!borderValue.exists())
+ return;
+
+ uint count = borderStyles.count();
+ if (count == 0)
+ borderStyles.append(new CollapsedBorderValue(borderValue));
+ else {
+ for (uint i = 0; i < count; i++) {
+ CollapsedBorderValue* b = borderStyles.at(i);
+ if (*b == borderValue)
+ return;
+ CollapsedBorderValue result = compareBorders(*b, borderValue);
+ if (result == *b) {
+ borderStyles.insert(i, new CollapsedBorderValue(borderValue));
+ return;
+ }
+ }
+
+ borderStyles.append(new CollapsedBorderValue(borderValue));
+ }
+}
+
+void RenderTableCell::collectBorders(QPtrList<CollapsedBorderValue>& borderStyles)
+{
+ addBorderStyle(borderStyles, collapsedLeftBorder());
+ addBorderStyle(borderStyles, collapsedRightBorder());
+ addBorderStyle(borderStyles, collapsedTopBorder());
+ addBorderStyle(borderStyles, collapsedBottomBorder());
+}
+
+void RenderTableCell::paintCollapsedBorder(QPainter* p, int _tx, int _ty, int w, int h)
+{
+ if (!table()->currentBorderStyle())
+ return;
+
+ CollapsedBorderValue leftVal = collapsedLeftBorder();
+ CollapsedBorderValue rightVal = collapsedRightBorder();
+ CollapsedBorderValue topVal = collapsedTopBorder();
+ CollapsedBorderValue bottomVal = collapsedBottomBorder();
+
+ // Adjust our x/y/width/height so that we paint the collapsed borders at the correct location.
+ int topWidth = topVal.width();
+ int bottomWidth = bottomVal.width();
+ int leftWidth = leftVal.width();
+ int rightWidth = rightVal.width();
+
+ _tx -= leftWidth/2;
+ _ty -= topWidth/2;
+ w += leftWidth/2 + int(rightWidth/2.0+0.5);
+ h += topWidth/2 + int(bottomWidth/2.0+0.5);
+
+ bool tt = topVal.isTransparent();
+ bool bt = bottomVal.isTransparent();
+ bool rt = rightVal.isTransparent();
+ bool lt = leftVal.isTransparent();
+
+ EBorderStyle ts = collapsedBorderStyle(topVal.style());
+ EBorderStyle bs = collapsedBorderStyle(bottomVal.style());
+ EBorderStyle ls = collapsedBorderStyle(leftVal.style());
+ EBorderStyle rs = collapsedBorderStyle(rightVal.style());
+
+ bool render_t = ts > BHIDDEN && !tt;
+ bool render_l = ls > BHIDDEN && !lt;
+ bool render_r = rs > BHIDDEN && !rt;
+ bool render_b = bs > BHIDDEN && !bt;
+
+ // We never paint diagonals at the joins. We simply let the border with the highest
+ // precedence paint on top of borders with lower precedence.
+ CollapsedBorders borders(4);
+ borders.addBorder(topVal, BSTop, render_t, _tx, _ty, _tx + w, _ty + topWidth, ts);
+ borders.addBorder(bottomVal, BSBottom, render_b, _tx, _ty + h - bottomWidth, _tx + w, _ty + h, bs);
+ borders.addBorder(leftVal, BSLeft, render_l, _tx, _ty, _tx + leftWidth, _ty + h, ls);
+ borders.addBorder(rightVal, BSRight, render_r, _tx + w - rightWidth, _ty, _tx + w, _ty + h, rs);
+
+ for (CollapsedBorder* border = borders.nextBorder(); border; border = borders.nextBorder()) {
+ if (border->border == *table()->currentBorderStyle())
+ drawBorder(p, border->x1, border->y1, border->x2, border->y2, border->side,
+ border->border.color(), style()->color(), border->style, 0, 0);
+ }
+}
+
+void RenderTableCell::paintObject(QPainter* p, int _x, int _y, int _w, int _h,
+ int _tx, int _ty, PaintAction paintAction)
+{
+ if (paintAction == PaintActionCollapsedTableBorders && style()->visibility() == VISIBLE) {
+ int w = width();
+ int h = height() + borderTopExtra() + borderBottomExtra();
+ _ty -= borderTopExtra();
+ paintCollapsedBorder(p, _tx, _ty, w, h);
+ }
+ else
+ RenderBlock::paintObject(p, _x, _y, _w, _h, _tx, _ty, paintAction);
+}
void RenderTableCell::paintBoxDecorations(QPainter *p,int, int _y,
- int, int _h, int _tx, int _ty)
+ int, int _h, int _tx, int _ty)
{
+ RenderTable* tableElt = table();
+ if (!tableElt->collapseBorders() && style()->emptyCells() == HIDE && !firstChild())
+ return;
+
int w = width();
int h = height() + borderTopExtra() + borderBottomExtra();
_ty -= borderTopExtra();
@@ -1657,7 +2259,7 @@ void RenderTableCell::paintBoxDecoration
if ( bg || c.isValid() )
paintBackground(p, c, bg, my, mh, _tx, _ty, w, h);
- if(style()->hasBorder())
+ if (style()->hasBorder() && !tableElt->collapseBorders())
paintBorder(p, _tx, _ty, w, h, style());
}
Index: khtml/rendering/render_table.h
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/khtml/rendering/render_table.h,v
retrieving revision 1.28
diff -u -p -r1.28 khtml/rendering/render_table.h
--- khtml/rendering/render_table.h 2003/10/10 23:22:10 1.28
+++ khtml/rendering/render_table.h 2003/10/16 08:59:12
@@ -84,10 +84,15 @@ public:
+ int borderLeft() const;
+ int borderRight() const;
+ int borderTop() const;
+ int borderBottom() const;
+
Rules getRules() const { return rules; }
const QColor &bgColor() const { return style()->backgroundColor(); }
@@ -160,10 +165,14 @@ public:
void setNeedSectionRecalc() { needSectionRecalc = true; }
virtual RenderObject* removeChildNode(RenderObject* child);
-#if APPLE_CHANGES
- virtual RenderTableCell* cellAbove(RenderTableCell* cell) const;
-#endif
+ RenderTableCell* cellAbove(const RenderTableCell* cell) const;
+ RenderTableCell* cellBelow(const RenderTableCell* cell) const;
+ RenderTableCell* cellLeft(const RenderTableCell* cell) const;
+ RenderTableCell* cellRight(const RenderTableCell* cell) const;
+
+ CollapsedBorderValue* currentBorderStyle() { return m_currentBorder; }
+
protected:
void recalcSections();
@@ -178,13 +187,17 @@ protected:
TableLayout *tableLayout;
+ CollapsedBorderValue* m_currentBorder;
+
Frame frame : 4;
Rules rules : 4;
@@ -326,6 +339,17 @@ public:
virtual void setWidth( int width );
virtual void setStyle( RenderStyle *style );
+ int borderLeft() const;
+ int borderRight() const;
+ int borderTop() const;
+ int borderBottom() const;
+
+ CollapsedBorderValue collapsedLeftBorder() const;
+ CollapsedBorderValue collapsedRightBorder() const;
+ CollapsedBorderValue collapsedTopBorder() const;
+ CollapsedBorderValue collapsedBottomBorder() const;
+ virtual void collectBorders(QPtrList<CollapsedBorderValue>& borderStyles);
+
virtual void updateFromElement();
virtual void layout();
@@ -339,6 +363,8 @@ public:
virtual void paint( QPainter* p, int x, int y,
int w, int h, int tx, int ty, PaintAction paintAction);
+ void paintCollapsedBorder(QPainter* p, int x, int y, int w, int h);
+
virtual void close();
// lie position to outside observers
@@ -359,6 +385,9 @@ public:
virtual void dump(QTextStream *stream, QString ind = "") const;
#endif
+ virtual void paintObject(QPainter *, int x, int y, int w, int h,
+ int tx, int ty, PaintAction paintAction);
+
protected:
virtual void paintBoxDecorations(QPainter *p,int _x, int _y,
int _w, int _h, int _tx, int _ty);
-------------- next part --------------
dave
More information about the Khtml-devel
mailing list