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:
parent
d43699ef11
commit
ca16b7a98e
@ -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
|
||||
-----
|
||||
|
@ -130,6 +130,7 @@ TD ALIGN=[alignment]
|
||||
WIDTH=[percent|pixels]
|
||||
COLSPAN=[pixels]
|
||||
ROWSPAN=[pixels]
|
||||
NOWRAP
|
||||
TH ALIGN=[alignment]
|
||||
VALIGN=[v_alignment]
|
||||
BGCOLOR=[color]
|
||||
|
@ -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)
|
||||
};
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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")))
|
||||
|
Loading…
Reference in New Issue
Block a user