« Return to Thread: Patch for when rowspan or colspan equals 0

Re: Patch for when rowspan or colspan equals 0

by Bugzilla from carlos_licea@hotmail.com :: Rate this Message:

Reply to Author | View in Thread

On Lunes 06 Abril 2009 21:55:33 Germain Garand escribió:

> Le samedi 4 avril 2009, Carlos Licea a écrit :
> > Dear list,
> > I've been working on my patch for implementing proper colspan and
> > rowspan behaviour.
> > Forget almost completely my previous mail, here's a new way of doing it,
> > way better than the old one, which has no known corner-cases, it even
> > handles properly when both colspan and rowspan equals 0 in the same cell,
> > whereas the last one did but I couldn't catch them till recently.
>
> Hi Carlos,
> nice work so far!
>
Thanks, I believe is shaping up nicely, we still have some work to do, as you
uncovered, though.

> > So the algorithm seems to work quite well actually, except for one
> > problem, for rowspan > 2 for some reason it stops growing the cell even
> > though we set the rowspan properly! (I believe but my believings have
> > been proven wrong in the past ;).
>
> errm I'm getting Qt assert failures (index out of range) in QVector's []
> operator with all your rowspan testcases, so some logic is indeed wrong.
>
You were right at IRC, I missed the case when you already found the cell which
has a colspan = 0 and you want to insert it in a new column, if I'm correct it
just happens when the colspan=0 is in the first row.

> it sounds like this could all stem from:
>
> +    ensureRows( cRow + (rSpan)? rSpan : 1 );
>
> ? surely, you meant:
>
> +    ensureRows( cRow + (rSpan? rSpan : 1) );
>
Changed, though... is there a real difference? I didn't get any compiling
errors.

> - the html/html_tableimpl.cpp bit about changing the lowest allowed value
> for the span attribute of the col element's :
>
> @@ -964,7 +964,7 @@
>      {
>      case ATTR_SPAN:
>          _span = attr->val() ? attr->val()->toInt() : 1;
> -        if (_span < 1) _span = 1;
> +        if (_span < 0) _span = 1;
>
> looks wrong actually... the HTMl 4.01 specification mandates that this
> value be > 0
>
Changed it back.

> - could you setup a boolean flag for when no zero rowspan/colspan has been
> encountered yet, and then skip entirely the map lookup logic and
> effColToCol calculation as an optimization?
>
Done.

> - you should clear your two maps on recalculation (up in recalcCells()
> probably)
>
Done. Though for it to work properly we must make sure that the previous cells
aren't somehow used, otherwise we would have a span different than 0 and we
would have a 'normal' cell instead of a special case cell.

> - this does not cover exactly the 4.01 specification for colspan,
> where the spanning of colspan=0 cells should be limited to the current
> colgroup.
> Figuring the extent of the current colgroup is not really straightforward
> however, as colgroup (and col) are sort of descriptive tags that have no
> real rendered  counterpart.
>
> Yet we have (zero dimensions) render objects for them in the render tree.
>
> e.g:
> <table>
>   <colgroup span=2></colgroup>
>   <colgroup span=3></colgroup>
>   <colgroup>
>      <col span=2>
>      <col>
>   </colgroup>
> </table>
>
I need to check that use case...

> defining 3 colgroups spanning 8 logical cells in total,
> will give you a render tree as such:
>
>     RenderTable: <table>
>       RenderTableCol: <colgroup>
>       RenderTableCol: <colgroup>
>       RenderTableCol: <colgroup>
>         RenderTableCol:<col>
>         RenderTableCol:<col>
>
>
> See for instance RenderTable::colElement for a method that's walking this
> structure.
I'll have a look.

>
> A full table matching that colgroup definition would be for instance:
> <table>
>   <colgroup span=2></colgroup>
>   <colgroup span=3></colgroup>
>   <colgroup>
>      <col span=2>
>      <col>
>   </colgroup>
>
>   <tr>
>     <td colspan=2>1st colgroup</td>
>     <td>2nd</td><td>col</td><td>group</td>
>     <td colspan=3>3rd colgroup</td>
>   </tr>
>
> </table>
>
So this is a real table which I can test against?
--
Carlos



[tables.diff]

Index: html/html_tableimpl.cpp
===================================================================
--- html/html_tableimpl.cpp (revisión: 950904)
+++ html/html_tableimpl.cpp (copia de trabajo)
@@ -210,7 +210,7 @@
     lastSection = section;
     if ( index < 0 ) //append/last mode
         return 0;
-    
+
     long rows   = section->numRows();
     if ( index >= rows ) {
         section =  0;
@@ -428,7 +428,7 @@
 
     return cellChanged;
 }
-
+
 void HTMLTableElementImpl::parseAttribute(AttributeImpl *attr)
 {
     // ### to CSS!!
@@ -910,14 +910,14 @@
         // limit this to something not causing an overflow with short int
         if(rSpan < 0 || rSpan > 1024) rSpan = 1;
         if (renderer())
-            renderer()->updateFromElement();                    
+            renderer()->updateFromElement();
         break;
     case ATTR_COLSPAN:
         cSpan = attr->val() ? attr->val()->toInt() : 1;
         // limit this to something not causing an overflow with short int
         if(cSpan < 0 || cSpan > 1024) cSpan = 1;
         if (renderer())
-            renderer()->updateFromElement();        
+            renderer()->updateFromElement();
         break;
     case ATTR_NOWRAP:
         if (attr->val() != 0)
Index: rendering/render_table.cpp
===================================================================
--- rendering/render_table.cpp (revisión: 950904)
+++ rendering/render_table.cpp (copia de trabajo)
@@ -182,7 +182,7 @@
                     head = static_cast<RenderTableSection *>(child);
                 } else {
                     resetSectionPointerIfNotBefore(firstBody, beforeChild);
-                    if (!firstBody)
+                    if (!firstBody)
                         firstBody = static_cast<RenderTableSection*>(child);
                 }
             }
@@ -761,10 +761,10 @@
                 maxCols = sectionCols;
         }
     }
-    
+
     columns.resize(maxCols);
     columnPos.resize(maxCols+1);
-    
+
     needSectionRecalc = false;
     setNeedsLayout(true);
 }
@@ -1028,6 +1028,7 @@
 
 RenderTableSection::RenderTableSection(DOM::NodeImpl* node)
     : RenderBox(node)
+    , containsSpansZero(false)
 {
     // init RenderObject attributes
     setInline(false);   // our object is not Inline
@@ -1190,44 +1191,130 @@
  }
     }
 
+    //What:Postion the cell
+    //How: we take care of special case when colspan or rowspan equals 0
+    //after that position the cell, that is just to tell where is it and tell
+    //other cells where they can't be located (marking the cells as -1)
+    //later taking the span into account (and in other function) the cell is
+    //then painted (that's why we need to set the colspan and rowspan properly
+    //when any of them is cero
+
     // make sure we have enough rows
-    ensureRows( cRow + rSpan );
+    ensureRows( cRow + (rSpan? rSpan : 1 ) );
 
     grid[cRow].rowRenderer = row;
 
     int col = cCol;
     // tell the cell where it is
     RenderTableCell *set = cell;
-    while ( cSpan ) {
- int currentSpan;
- if ( cCol >= nCols ) {
-    table()->appendColumn( cSpan );
-    currentSpan = cSpan;
- } else {
-    if ( cSpan < columns[cCol].span )
- table()->splitColumn( cCol, cSpan );
-    currentSpan = columns[cCol].span;
- }
- int r = 0;
- while ( r < rSpan ) {
-    if ( !cellAt( cRow + r, cCol ) ) {
-// qDebug("    adding cell at %d, %d",  cRow + r, cCol );
- cellAt( cRow + r, cCol ) = set;
-    }
-    r++;
- }
- cCol++;
- cSpan -= currentSpan;
- set = (RenderTableCell *)-1;
+//     kDebug()<<"row"<<cRow<<"col"<<cCol;
+//     kDebug()<<"cSpan"<<cSpan<<"rSpan"<<rSpan;
+
+    //check wether we are in a column or in a row with span = 0
+    QList< int > columnsToAvoid;
+    if( containsSpansZero ) {
+        int actualCurrentCol = table()->effColToCol(cCol);
+        if( cellsWithColSpanZero.contains( actualCurrentCol ) ) {
+            //No need to check if we have enough column, we already found the first cell
+            //(when colspan="0", and as such, we've already inserted it
+            while ( RenderTableCell* cell = cellsWithColSpanZero.take( actualCurrentCol ) ) {
+                int cellRow = cell->row();
+                if( !cellAt( cellRow, actualCurrentCol ) ) {
+                    cellAt( cellRow, actualCurrentCol ) = (RenderTableCell *)-1;
+                }
+                cell->setColSpan( cell->colSpan() + 1 );
+                cellsWithColSpanZero.insertMulti(actualCurrentCol + 1, cell );
+            }
+        }
+
+        if( cellsWithRowSpanZero.contains( cRow ) ) {
+            //make sure we have enough columns
+            if( cCol >= nCols ) {
+                table()->appendColumn( cCol - nCols );
+                nCols = columns.size();
+            }
+            while( RenderTableCell* cell = cellsWithRowSpanZero.take( cRow ) ) {
+                int cellCol = cell->col();
+                if( !cellAt( cRow, cellCol ) ) {
+                    cellAt( cRow, cellCol ) = cell;
+                }
+                cell->setRowSpan( cell->rowSpan() + 1 );
+                columnsToAvoid << cellCol;
+                cellsWithRowSpanZero.insertMulti( cRow + 1, cell );
+//                 kDebug()<<"final rowspan"<<cell->rowSpan();
+            }
+        }
     }
+
+    //Save the column if the its span is 0
+    if( cSpan == 0 ) {
+        //mark it to be inserted in the next column
+        if( cCol >= nCols ) {
+            table()->appendColumn( cCol - nCols );
+            nCols = columns.size();
+        }
+        int finalSpan = 0;
+        for( int i = cCol; i < nCols; ++i) {
+            if ( !cellAt( cRow, cCol ) ) {
+                cellAt( cRow, cCol ) = cell;
+            }
+            ++finalSpan;
+        }
+        cellsWithColSpanZero.insertMulti( table()->effColToCol(cCol) + finalSpan, cell );
+        cell->setColSpan( finalSpan );
+        containsSpansZero = true;
+    }
+
+    if( rSpan == 0 ) {
+        if( cCol >= nCols ) {
+            table()->appendColumn( cCol - nCols );
+            nCols = columns.size();
+        }
+        //mark is as inserted in next row
+        cellsWithRowSpanZero.insertMulti( cRow + 1, cell );
+        if ( !cellAt( cRow, cCol ) ) {
+            cellAt( cRow, cCol ) = cell;
+        }
+        cell->setRowSpan( 1 );
+        containsSpansZero = true;
+    }
+
+     if( rSpan ) {
+        while ( cSpan ) {
+            int currentSpan;
+            if ( cCol >= nCols ) {
+                table()->appendColumn( cSpan );
+                currentSpan = cSpan;
+            } else {
+                if ( cSpan < columns[cCol].span ) {
+                    table()->splitColumn( cCol, cSpan );
+                }
+                currentSpan = columns[cCol].span;
+            }
+            int r = 0;
+
+            if( columnsToAvoid.contains(cCol) )
+                ++cCol;
+
+            while ( r < rSpan ) {
+                if ( !cellAt( cRow + r, cCol ) ) {
+    // qDebug("    adding cell at %d, %d",  cRow + r, cCol );
+                    cellAt( cRow + r, cCol ) = set;
+                }
+                ++r;
+            }
+            ++cCol;
+            cSpan -= currentSpan;
+            set = (RenderTableCell *)-1;
+        }
+     }
+
     if ( cell ) {
- cell->setRow( cRow );
- cell->setCol( table()->effColToCol( col ) );
+        cell->setRow( cRow );
+        cell->setCol( table()->effColToCol( col ) );
     }
 }
 
-
-
 void RenderTableSection::setCellWidths()
 {
 #ifdef DEBUG_LAYOUT
@@ -1781,7 +1868,7 @@
  if ( !cell || cell == (RenderTableCell *)-1 || nextrow && (*nextrow)[c] == cell )
     continue;
                 RenderObject* rowr = cell->parent();
-                int rtx = tx+rowr->xPos();
+                int rtx = tx+rowr->xPos();
                 int rty = ty+rowr->yPos();
 #ifdef TABLE_PRINT
  kDebug( 6040 ) << "painting cell " << r << "/" << c;
@@ -1826,23 +1913,25 @@
 int RenderTableSection::numColumns() const
 {
     int result = 0;
-    
+
     for (int r = 0; r < numRows(); ++r) {
         for (int c = result; c < table()->numEffCols(); ++c) {
             if (cellAt(r, c))
                 result = c;
         }
     }
-    
+
     return result + 1;
 }
-
+
 void RenderTableSection::recalcCells()
 {
     cCol = 0;
     cRow = -1;
     clearGrid();
     grid.resize( 0 );
+    cellsWithColSpanZero.clear();
+    cellsWithRowSpanZero.clear();
 
     for (RenderObject *row = firstChild(); row; row = row->nextSibling()) {
         if (row->isTableRow())  {
@@ -1854,6 +1943,7 @@
             for (RenderObject *cell = row->firstChild(); cell; cell = cell->nextSibling())
                 if (cell->isTableCell())
                     addCell( static_cast<RenderTableCell *>(cell), static_cast<RenderTableRow *>(row) );
+
  }
     }
     needCellRecalc = false;
@@ -2563,7 +2653,7 @@
     RenderTableCol* colElt = table()->colElement(col() + (rtl ? colSpan() - 1 : 0), &startColEdge, &endColEdge);
     if (colElt && (!rtl ? startColEdge : endColEdge)) {
         result = compareBorders(result, CollapsedBorderValue(&colElt->style()->borderLeft(), BCOL));
-        if (!result.exists())
+        if (!result.exists())
             return result;
         if (colElt->parent()->isTableCol() && (!rtl ? !colElt->previousSibling() : !colElt->nextSibling())) {
             result = compareBorders(result, CollapsedBorderValue(&colElt->parent()->style()->borderLeft(), BCOLGROUP));
Index: rendering/render_table.h
===================================================================
--- rendering/render_table.h (revisión: 950904)
+++ rendering/render_table.h (copia de trabajo)
@@ -273,6 +273,12 @@
 
     int numColumns() const;
 
+    //QMap< nextColumnToInsert, cell >
+    QMap< int, RenderTableCell* > cellsWithColSpanZero;
+    //QMap< nextRowToInsert, cell >
+    QMap< int, RenderTableCell* > cellsWithRowSpanZero;
+    bool containsSpansZero;
+
     signed short cRow;
     ushort cCol;
     bool needCellRecalc;

 « Return to Thread: Patch for when rowspan or colspan equals 0