improved HTML tables layout code (patch 911377)

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@26246 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Václav Slavík 2004-03-17 20:47:31 +00:00
parent d43699ef11
commit ca16b7a98e
6 changed files with 285 additions and 41 deletions

View File

@ -93,6 +93,10 @@ wxMSW:
- wxRegConf couldn't read global settings without admin privileges and didn't
even try to do it by default -- now it does
wxHTML:
- improved tables layout algorithm (Tim Kosse)
2.5.1
-----

View File

@ -130,6 +130,7 @@ TD ALIGN=[alignment]
WIDTH=[percent|pixels]
COLSPAN=[pixels]
ROWSPAN=[pixels]
NOWRAP
TH ALIGN=[alignment]
VALIGN=[v_alignment]
BGCOLOR=[color]

View File

@ -172,6 +172,11 @@ public:
int GetPosX() const {return m_PosX;}
int GetPosY() const {return m_PosY;}
int GetWidth() const {return m_Width;}
// Returns the maximum possible length of the cell.
// Call Layout at least once before using GetMaxTotalWidth()
virtual int GetMaxTotalWidth() const { return m_Width; }
int GetHeight() const {return m_Height;}
int GetDescent() const {return m_Descent;}
@ -428,6 +433,10 @@ public:
// below first/last terminal cell). For internal use only.
void RemoveExtraSpacing(bool top, bool bottom);
// Returns the maximum possible length of the container.
// Call Layout at least once before using GetMaxTotalWidth()
virtual int GetMaxTotalWidth() const { return m_MaxTotalWidth; }
protected:
void UpdateRenderingStatePre(wxHtmlRenderingInfo& info,
wxHtmlCell *cell) const;
@ -457,7 +466,10 @@ protected:
int m_LastLayout;
// if != -1 then call to Layout may be no-op
// if previous call to Layout has same argument
int m_MaxTotalWidth;
// Maximum possible length if ignoring line wrap
DECLARE_ABSTRACT_CLASS(wxHtmlContainerCell)
DECLARE_NO_COPY_CLASS(wxHtmlContainerCell)
};

View File

@ -109,8 +109,84 @@ kjhkj hkj hkj lkh kjh kjlh kj</TD>
</TABLE>
<p>
</p>
<hr size="8"/>
<table cellspacing='0' cellpadding='0'>
<tr align='left'>
<td width="30%" valign='top'>Error:</td>
<td width='5'></td>
<td>Sex sells better than <b>truth and honour</b></td><td align='right'>X For President is my agenda!</td>
</tr>
<tr align='left'>
<td valign='top'>Command:</td>
<td width='5'></td>
<td>Go out and spread the word, <b>we shall prevail!</b></td><td>X</td>
</tr>
</table>
<br><br>
<table cellspacing='0' cellpadding='0'>
<tr align='left'>
<td valign='top'>Error:</td>
<td width='5'></td>
<td>Sex sells better than <b>truth and honour</b></td><td align='right'>X For President is my agenda!</td>
</tr>
<tr align='left'>
<td valign='top'>Command:</td>
<td width='5'></td>
<td>Go out and spread the word, <b>we shall prevail!</b></td><td>X</td>
</tr>
</table>
<br><br>
<table width="200" cellspacing='0' cellpadding='0'>
<tr align='left'>
<td valign='top'>Error:</td>
<td width='5'></td>
<td nowrap>Sex sells better than <b>truth and honour</b></td><td align='right'>X For President is my agenda!</td>
</tr>
<tr align='left'>
<td valign='top'>Command:</td>
<td width='5'></td>
<td>Go out and spread the word, <b>we shall prevail!</b></td><td>X</td>
</tr>
</table>
<br><br>
<table cellspacing='0' cellpadding='0'>
<tr align='left'>
<td valign='top'>Error:</td>
<td width='5'></td>
<td>
<table cellspacing='0' cellpadding='0'>
<tr align='left'>
<td valign='top'>Error:</td>
<td width='5'></td>
<td>Sex sells better than <b>truth and honour</b></td><td align='right'>X For President is my agenda!</td>
</tr>
<tr align='left'>
<td valign='top'>Command:</td>
<td width='5'></td>
<td>Go out and spread the word, <b>we shall prevail!</b></td>
<td>X</td>
</tr>
</table>
</td>
<td align='right'>X For President is my agenda!</td>
</tr>
<tr align='left'>
<td valign='top'>Command:</td>
<td width='5'></td>
<td>Go out and spread the word, <b>we shall prevail!</b></td>
<td>X</td>
</tr>
</table>
<br><br>
<table cellspacing='0' cellpadding='0'>
<tr>
<td><ul><li>Just a small test</li></ul></td>
<td>Really, a test!</td>
</tr>
</table>

View File

@ -529,6 +529,7 @@ wxHtmlContainerCell::wxHtmlContainerCell(wxHtmlContainerCell *parent) : wxHtmlCe
{
m_Cells = m_LastCell = NULL;
m_Parent = parent;
m_MaxTotalWidth = 0;
if (m_Parent) m_Parent->InsertCell(this);
m_AlignHor = wxHTML_ALIGN_LEFT;
m_AlignVer = wxHTML_ALIGN_BOTTOM;
@ -644,6 +645,8 @@ void wxHtmlContainerCell::Layout(int w)
int ysizeup = 0, ysizedown = 0;
int MaxLineWidth = 0;
int xcnt = 0;
int curLineWidth = 0;
m_MaxTotalWidth = 0;
/*
@ -698,6 +701,20 @@ void wxHtmlContainerCell::Layout(int w)
// layout nonbreakable run of cells:
cell->SetPos(xpos, ybasicpos + cell->GetDescent());
xpos += cell->GetWidth();
if (!cell->IsTerminalCell())
{
// Container cell indicates new line
if (curLineWidth > m_MaxTotalWidth)
m_MaxTotalWidth = curLineWidth;
if (wxMax(cell->GetWidth(), cell->GetMaxTotalWidth()) > m_MaxTotalWidth)
m_MaxTotalWidth = cell->GetMaxTotalWidth();
curLineWidth = 0;
}
else
// Normal cell, add maximum cell width to line width
curLineWidth += cell->GetMaxTotalWidth();
cell = cell->GetNext();
xcnt++;
@ -785,6 +802,10 @@ void wxHtmlContainerCell::Layout(int w)
m_Height = m_MinHeight;
}
if (curLineWidth > m_MaxTotalWidth)
m_MaxTotalWidth = curLineWidth;
m_MaxTotalWidth += s_indent + ((m_IndentRight < 0) ? (-m_IndentRight * m_Width / 100) : m_IndentRight);
MaxLineWidth += s_indent + ((m_IndentRight < 0) ? (-m_IndentRight * m_Width / 100) : m_IndentRight);
if (m_Width < MaxLineWidth) m_Width = MaxLineWidth;

View File

@ -22,14 +22,6 @@
#ifndef WXPRECOMP
#endif
/*
REMARKS:
1. This version of m_tables doesn't support auto-layout algorithm.
This means that all columns are of same width unless explicitly specified.
*/
#include "wx/html/forcelnk.h"
#include "wx/html/m_templ.h"
@ -73,6 +65,7 @@ struct cellStruct
int colspan, rowspan;
int minheight, valign;
cellState flag;
bool nowrap;
};
@ -346,6 +339,12 @@ void wxHtmlTableCell::AddCell(wxHtmlContainerCell *cell, const wxHtmlTag& tag)
else m_CellInfo[r][c].valign = wxHTML_ALIGN_CENTER;
}
// nowrap
if (tag.HasParam(wxT("NOWRAP")))
m_CellInfo[r][c].nowrap = true;
else
m_CellInfo[r][c].nowrap = false;
cell->SetIndent(m_Padding, wxHTML_INDENT_ALL, wxHTML_UNITS_PIXELS);
}
@ -353,6 +352,8 @@ void wxHtmlTableCell::ComputeMinMaxWidths()
{
if (m_NumCols == 0 || m_ColsInfo[0].minWidth != -1) return;
m_MaxTotalWidth = 0;
int percentage = 0;
for (int c = 0; c < m_NumCols; c++)
{
for (int r = 0; r < m_NumRows; r++)
@ -361,21 +362,41 @@ void wxHtmlTableCell::ComputeMinMaxWidths()
if (cell.flag == cellUsed)
{
cell.cont->Layout(2*m_Padding + 1);
int width = cell.cont->GetWidth();
int maxWidth = cell.cont->GetMaxTotalWidth();
int width = cell.nowrap?maxWidth:cell.cont->GetWidth();
width -= (cell.colspan-1) * m_Spacing;
maxWidth -= (cell.colspan-1) * m_Spacing;
// HTML 4.0 says it is acceptable to distribute min/max
width /= cell.colspan;
for (int j = 0; j < cell.colspan; j++)
maxWidth /= cell.colspan;
for (int j = 0; j < cell.colspan; j++) {
if (width > m_ColsInfo[c+j].minWidth)
m_ColsInfo[c+j].minWidth = width;
if (maxWidth > m_ColsInfo[c+j].maxWidth)
m_ColsInfo[c+j].maxWidth = maxWidth;
}
}
}
// Calculate maximum table width, required for nested tables
if (m_ColsInfo[c].units == wxHTML_UNITS_PIXELS)
m_MaxTotalWidth += wxMax(m_ColsInfo[c].width, m_ColsInfo[c].minWidth);
else if ((m_ColsInfo[c].units == wxHTML_UNITS_PERCENT) && (m_ColsInfo[c].width != 0))
percentage += m_ColsInfo[c].width;
else
m_MaxTotalWidth += m_ColsInfo[c].maxWidth;
}
// FIXME -- compute maxWidth as well. Not needed yet, so there's no
// point in computing it.
}
if (percentage >= 100)
{
// Table would have infinite length
// Make it ridiculous large
m_MaxTotalWidth = 0xFFFFFF;
}
else
m_MaxTotalWidth = m_MaxTotalWidth * 100 / (100 - percentage);
m_MaxTotalWidth += (m_NumCols + 1) * m_Spacing;
}
void wxHtmlTableCell::Layout(int w)
{
@ -391,8 +412,18 @@ void wxHtmlTableCell::Layout(int w)
if (m_WidthFloatUnits == wxHTML_UNITS_PERCENT)
{
if (m_WidthFloat < 0) m_Width = (100 + m_WidthFloat) * w / 100;
else m_Width = m_WidthFloat * w / 100;
if (m_WidthFloat < 0)
{
if (m_WidthFloat < -100)
m_WidthFloat = -100;
m_Width = (100 + m_WidthFloat) * w / 100;
}
else
{
if (m_WidthFloat > 100)
m_WidthFloat = 100;
m_Width = m_WidthFloat * w / 100;
}
}
else
{
@ -407,7 +438,10 @@ void wxHtmlTableCell::Layout(int w)
*/
/* 1. setup columns widths: */
/* 1. setup columns widths:
The algorithm tries to keep the table size less than w if possible.
*/
{
int wpix = m_Width - (m_NumCols + 1) * m_Spacing;
int i, j;
@ -421,29 +455,103 @@ void wxHtmlTableCell::Layout(int w)
wpix -= m_ColsInfo[i].pixwidth;
}
// 1b. setup floating-width columns:
int wtemp = 0;
for (i = 0; i < m_NumCols; i++)
if ((m_ColsInfo[i].units == wxHTML_UNITS_PERCENT) && (m_ColsInfo[i].width != 0))
{
m_ColsInfo[i].pixwidth = wxMax(m_ColsInfo[i].width * wpix / 100,
m_ColsInfo[i].minWidth);
wtemp += m_ColsInfo[i].pixwidth;
}
wpix -= wtemp;
// 1c. setup defalut columns (no width specification supplied):
// FIXME: This algorithm doesn't conform to HTML standard : it assigns
// equal widths instead of optimal
for (i = j = 0; i < m_NumCols; i++)
if (m_ColsInfo[i].width == 0) j++;
// 1b. Calculate maximum possible width if line wrapping would be disabled
// Recalculate total width if m_WidthFloat is zero to keep tables as small
// as possible.
int maxWidth = 0;
for (i = 0; i < m_NumCols; i++)
if (m_ColsInfo[i].width == 0)
{
// FIXME: this is not optimal, because if we allocate more than
// wpix/j pixels to one column, we should try to allocate
// smaller place to other columns
m_ColsInfo[i].pixwidth = wxMax(wpix/j, m_ColsInfo[i].minWidth);
maxWidth += m_ColsInfo[i].maxWidth;
}
if (!m_WidthFloat)
{
// Recalculate table width since no table width was initially given
int newWidth = m_Width - wpix + maxWidth;
// Make sure that floating-width columns will have the right size.
// Calculate sum of all floating-width columns
int percentage = 0;
for (i = 0; i < m_NumCols; i++)
if ((m_ColsInfo[i].units == wxHTML_UNITS_PERCENT) && (m_ColsInfo[i].width != 0))
percentage += m_ColsInfo[i].width;
if (percentage >= 100)
newWidth = w;
else
newWidth = newWidth * 100 / (100 - percentage);
newWidth = wxMin(newWidth, w - (m_NumCols + 1) * m_Spacing);
wpix -= m_Width - newWidth;
m_Width = newWidth;
}
// 1c. setup floating-width columns:
int wtemp = wpix;
for (i = 0; i < m_NumCols; i++)
if ((m_ColsInfo[i].units == wxHTML_UNITS_PERCENT) && (m_ColsInfo[i].width != 0))
{
m_ColsInfo[i].pixwidth = wxMin(m_ColsInfo[i].width, 100) * wpix / 100;
// Make sure to leave enough space for the other columns
int minRequired = 0;
for (j = 0; j < m_NumCols; j++)
{
if ((m_ColsInfo[j].units == wxHTML_UNITS_PERCENT && j > i) ||
!m_ColsInfo[j].width)
minRequired += m_ColsInfo[j].minWidth;
}
m_ColsInfo[i].pixwidth = wxMax(wxMin(wtemp - minRequired, m_ColsInfo[i].pixwidth), m_ColsInfo[i].minWidth);
wtemp -= m_ColsInfo[i].pixwidth;
}
wpix = wtemp;
// 1d. setup default columns (no width specification supplied):
// The algorithm assigns calculates the maximum possible width if line
// wrapping would be disabled and assigns column width as a fraction
// based upon the maximum width of a column
// FIXME: I'm not sure if this algorithm is conform to HTML standard,
// though it seems to be much better than the old one
for (i = j = 0; i < m_NumCols; i++)
if (m_ColsInfo[i].width == 0) j++;
if (wpix < 0)
wpix = 0;
// Assign widths
for (i = 0; i < m_NumCols; i++)
if (m_ColsInfo[i].width == 0)
{
// Assign with, make sure not to drop below minWidth
if (maxWidth)
m_ColsInfo[i].pixwidth = wpix * (m_ColsInfo[i].maxWidth / (float)maxWidth) + 0.5;
else
m_ColsInfo[i].pixwidth = wpix / j;
// Make sure to leave enough space for the other columns
int minRequired = 0;
int r;
for (r = i + 1; r < m_NumCols; r++)
{
if (!m_ColsInfo[r].width)
minRequired += m_ColsInfo[r].minWidth;
}
m_ColsInfo[i].pixwidth = wxMax(wxMin(wpix - minRequired, m_ColsInfo[i].pixwidth), m_ColsInfo[i].minWidth);
if (maxWidth)
{
if (m_ColsInfo[i].pixwidth > (wpix * (m_ColsInfo[i].maxWidth / (float)maxWidth) + 0.5))
{
int diff = m_ColsInfo[i].pixwidth - (wpix * m_ColsInfo[i].maxWidth / (float)maxWidth + 0.5);
maxWidth += diff - m_ColsInfo[i].maxWidth;
}
else
maxWidth -= m_ColsInfo[i].maxWidth;
}
wpix -= m_ColsInfo[i].pixwidth;
}
}
@ -556,8 +664,30 @@ TAG_HANDLER_BEGIN(TABLE, "TABLE,TR,TD,TH")
oldcont = c = m_WParser->OpenContainer();
c->SetWidthFloat(tag, m_WParser->GetPixelScale());
m_Table = new wxHtmlTableCell(c, tag, m_WParser->GetPixelScale());
m_Table = new wxHtmlTableCell(c, tag);
// width:
{
if (tag.HasParam(wxT("WIDTH")))
{
wxString wd = tag.GetParam(wxT("WIDTH"));
if (wd[wd.Length()-1] == wxT('%'))
{
int width = 0;
wxSscanf(wd.c_str(), wxT("%i%%"), &width);
m_Table->SetWidthFloat(width, wxHTML_UNITS_PERCENT);
}
else
{
int width = 0;
wxSscanf(wd.c_str(), wxT("%i"), &width);
m_Table->SetWidthFloat(m_WParser->GetPixelScale() * width, wxHTML_UNITS_PIXELS);
}
}
else
m_Table->SetWidthFloat(0, wxHTML_UNITS_PIXELS);
}
int oldAlign = m_WParser->GetAlign();
m_tAlign = wxEmptyString;
if (tag.HasParam(wxT("ALIGN")))