From: Václav Slavík Date: Wed, 17 Mar 2004 20:47:31 +0000 (+0000) Subject: improved HTML tables layout code (patch 911377) X-Git-Url: https://git.saurik.com/wxWidgets.git/commitdiff_plain/ca16b7a98e70752eb57448571d7223052834550f improved HTML tables layout code (patch 911377) git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@26246 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- 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 honourX For President is my agenda!
Command:Go out and spread the word, we shall prevail!X
+

+ + + + + + + + + + + +
Error:Sex sells better than truth and honourX For President is my agenda!
Command:Go out and spread the word, we shall prevail!X
+

+ + + + + + + + + + + +
Error:Sex sells better than truth and honourX For President is my agenda!
Command:Go out and spread the word, we shall prevail!X
+

+ + + + + + + + + + + + + +
Error: + + + + + + + + + + + + +
Error:Sex sells better than truth and honourX 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
+

+ + + + + +
  • Just a small test
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; + // 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) + { + 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 = wxMax(m_ColsInfo[i].width * wpix / 100, - m_ColsInfo[i].minWidth); - wtemp += m_ColsInfo[i].pixwidth; + 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; + 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 - // 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++; + if (wpix < 0) + wpix = 0; + + // Assign widths 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); + // 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")))