diff --git a/docs/changes.txt b/docs/changes.txt
index cfc8877bec..1fb06ed883 100644
--- a/docs/changes.txt
+++ b/docs/changes.txt
@@ -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
-----
diff --git a/docs/latex/wx/htmltags.tex b/docs/latex/wx/htmltags.tex
index d31f24a204..0456fb35a2 100644
--- a/docs/latex/wx/htmltags.tex
+++ b/docs/latex/wx/htmltags.tex
@@ -130,6 +130,7 @@ TD ALIGN=[alignment]
WIDTH=[percent|pixels]
COLSPAN=[pixels]
ROWSPAN=[pixels]
+ NOWRAP
TH ALIGN=[alignment]
VALIGN=[v_alignment]
BGCOLOR=[color]
diff --git a/include/wx/html/htmlcell.h b/include/wx/html/htmlcell.h
index a73d4f9403..312b50634e 100644
--- a/include/wx/html/htmlcell.h
+++ b/include/wx/html/htmlcell.h
@@ -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)
};
diff --git a/samples/html/test/tables.htm b/samples/html/test/tables.htm
index 781983ec27..f7920ea4bb 100644
--- a/samples/html/test/tables.htm
+++ b/samples/html/test/tables.htm
@@ -109,8 +109,84 @@ kjhkj hkj hkj lkh kjh kjlh kj
+
+
+
+
-
-
+
+
+ Error: |
+ |
+ Sex sells better than truth and honour | X For President is my agenda! |
+
+
+ Command: |
+ |
+ Go out and spread the word, we shall prevail! | X |
+
+
+
+
+
+ Error: |
+ |
+ Sex sells better than truth and honour | X For President is my agenda! |
+
+
+ Command: |
+ |
+ Go out and spread the word, we shall prevail! | X |
+
+
+
+
+
+ Error: |
+ |
+ Sex sells better than truth and honour | X For President is my agenda! |
+
+
+ Command: |
+ |
+ Go out and spread the word, we shall prevail! | X |
+
+
+
+
+
+ Error: |
+ |
+
+
+
+ Error: |
+ |
+ Sex sells better than truth and honour | X For President is my agenda! |
+
+
+ Command: |
+ |
+ Go out and spread the word, we shall prevail! |
+ X |
+
+
+ |
+ X For President is my agenda! |
+
+
+ Command: |
+ |
+ Go out and spread the word, we shall prevail! |
+ X |
+
+
+
+
+
+ |
+ Really, a test! |
+
+
diff --git a/src/html/htmlcell.cpp b/src/html/htmlcell.cpp
index 7d38d30994..3bd6c6c2c3 100644
--- a/src/html/htmlcell.cpp
+++ b/src/html/htmlcell.cpp
@@ -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;
diff --git a/src/html/m_tables.cpp b/src/html/m_tables.cpp
index d1abf09252..36f364e1af 100644
--- a/src/html/m_tables.cpp
+++ b/src/html/m_tables.cpp
@@ -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")))