diff --git a/include/wx/richtext/richtextbuffer.h b/include/wx/richtext/richtextbuffer.h index 2cf636a420..e4326d9bfb 100644 --- a/include/wx/richtext/richtextbuffer.h +++ b/include/wx/richtext/richtextbuffer.h @@ -304,7 +304,8 @@ enum wxTextBoxAttrFlags wxTEXT_BOX_ATTR_CLEAR = 0x00000002, wxTEXT_BOX_ATTR_COLLAPSE_BORDERS = 0x00000004, wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT = 0x00000008, - wxTEXT_BOX_ATTR_BOX_STYLE_NAME = 0x00000010 + wxTEXT_BOX_ATTR_BOX_STYLE_NAME = 0x00000010, + wxTEXT_BOX_ATTR_WHITESPACE = 0x00000020 }; /** @@ -799,7 +800,7 @@ enum wxTextBoxAttrClearStyle }; /** - Collapse mode styles. TODO: can they be switched on per side? + Collapse mode styles. */ enum wxTextBoxAttrCollapseMode { @@ -818,6 +819,21 @@ enum wxTextBoxAttrVerticalAlignment wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_BOTTOM = 3 }; +/** + Whitespace values mirroring the CSS white-space attribute. + Only wxTEXT_BOX_ATTR_WHITESPACE_NO_WRAP is currently implemented, + in table cells. + */ +enum wxTextBoxAttrWhitespaceMode +{ + wxTEXT_BOX_ATTR_WHITESPACE_NONE = 0, + wxTEXT_BOX_ATTR_WHITESPACE_NORMAL = 1, + wxTEXT_BOX_ATTR_WHITESPACE_NO_WRAP = 2, + wxTEXT_BOX_ATTR_WHITESPACE_PREFORMATTED = 3, + wxTEXT_BOX_ATTR_WHITESPACE_PREFORMATTED_LINE = 4, + wxTEXT_BOX_ATTR_WHITESPACE_PREFORMATTED_WRAP = 5 +}; + /** @class wxTextAttrBorder A class representing a rich text object border. @@ -1247,6 +1263,21 @@ public: */ bool HasCollapseBorders() const { return HasFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS); } + /** + Returns the whitespace mode. + */ + wxTextBoxAttrWhitespaceMode GetWhitespaceMode() const { return m_whitespaceMode; } + + /** + Sets the whitespace mode. + */ + void SetWhitespaceMode(wxTextBoxAttrWhitespaceMode whitespace) { m_whitespaceMode = whitespace; m_flags |= wxTEXT_BOX_ATTR_WHITESPACE; } + + /** + Returns @true if the whitespace flag is present. + */ + bool HasWhitespaceMode() const { return HasFlag(wxTEXT_BOX_ATTR_WHITESPACE); } + /** Returns the vertical alignment. */ @@ -1493,6 +1524,7 @@ public: wxTextBoxAttrClearStyle m_clearMode; wxTextBoxAttrCollapseMode m_collapseMode; wxTextBoxAttrVerticalAlignment m_verticalAlignment; + wxTextBoxAttrWhitespaceMode m_whitespaceMode; wxString m_boxStyleName; }; diff --git a/interface/wx/richtext/richtextbuffer.h b/interface/wx/richtext/richtextbuffer.h index b60d9f5a15..30de6f76ba 100644 --- a/interface/wx/richtext/richtextbuffer.h +++ b/interface/wx/richtext/richtextbuffer.h @@ -183,7 +183,9 @@ enum wxTextBoxAttrFlags wxTEXT_BOX_ATTR_FLOAT = 0x00000001, wxTEXT_BOX_ATTR_CLEAR = 0x00000002, wxTEXT_BOX_ATTR_COLLAPSE_BORDERS = 0x00000004, - wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT = 0x00000008 + wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT = 0x00000008, + wxTEXT_BOX_ATTR_BOX_STYLE_NAME = 0x00000010, + wxTEXT_BOX_ATTR_WHITESPACE = 0x00000020 }; /** @@ -651,7 +653,7 @@ enum wxTextBoxAttrClearStyle }; /** - Collapse mode styles. TODO: can they be switched on per side? + Collapse mode styles. */ enum wxTextBoxAttrCollapseMode { @@ -670,6 +672,21 @@ enum wxTextBoxAttrVerticalAlignment wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_BOTTOM = 3 }; +/** + Whitespace values mirroring the CSS white-space attribute. + Only wxTEXT_BOX_ATTR_WHITESPACE_NO_WRAP is currently implemented, + in table cells. + */ +enum wxTextBoxAttrWhitespaceMode +{ + wxTEXT_BOX_ATTR_WHITESPACE_NONE = 0, + wxTEXT_BOX_ATTR_WHITESPACE_NORMAL = 1, + wxTEXT_BOX_ATTR_WHITESPACE_NO_WRAP = 2, + wxTEXT_BOX_ATTR_WHITESPACE_PREFORMATTED = 3, + wxTEXT_BOX_ATTR_WHITESPACE_PREFORMATTED_LINE = 4, + wxTEXT_BOX_ATTR_WHITESPACE_PREFORMATTED_WRAP = 5 +}; + /** @class wxTextAttrBorder A class representing a rich text object border. @@ -1094,6 +1111,21 @@ public: */ bool HasCollapseBorders() const { return HasFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS); } + /** + Returns the whitespace mode. + */ + wxTextBoxAttrWhitespaceMode GetWhitespaceMode() const { return m_whitespaceMode; } + + /** + Sets the whitespace mode. + */ + void SetWhitespaceMode(wxTextBoxAttrWhitespaceMode whitespace) { m_whitespaceMode = whitespace; m_flags |= wxTEXT_BOX_ATTR_WHITESPACE; } + + /** + Returns @true if the whitespace flag is present. + */ + bool HasWhitespaceMode() const { return HasFlag(wxTEXT_BOX_ATTR_WHITESPACE); } + /** Returns the vertical alignment. */ @@ -1340,6 +1372,7 @@ public: wxTextBoxAttrClearStyle m_clearMode; wxTextBoxAttrCollapseMode m_collapseMode; wxTextBoxAttrVerticalAlignment m_verticalAlignment; + wxTextBoxAttrWhitespaceMode m_whitespaceMode; wxString m_boxStyleName; }; diff --git a/src/richtext/richtextbuffer.cpp b/src/richtext/richtextbuffer.cpp index e5dade846f..de274ee281 100644 --- a/src/richtext/richtextbuffer.cpp +++ b/src/richtext/richtextbuffer.cpp @@ -5200,6 +5200,14 @@ bool wxRichTextParagraph::Layout(wxDC& dc, wxRichTextDrawingContext& context, co node = node->GetNext(); } + { + // Give the minimum width at least one character width + wxFont font(buffer->GetFontTable().FindFont(attr)); + wxCheckSetFont(dc, font); + int charWidth = dc.GetCharWidth(); + minWidth = wxMax(charWidth, minWidth); + } + wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect; contentRect = wxRect(wxPoint(0, 0), wxSize(minWidth, currentPosition.y + spaceAfterPara)); GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect); @@ -9880,14 +9888,17 @@ bool wxRichTextTable::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxArrayInt maxUnspecifiedColumnWidths; maxUnspecifiedColumnWidths.Add(0, m_colCount); - // wxArrayInt percentageColWidthsSpanning(m_colCount); - // These are only relevant when the first column contains spanning information. - // wxArrayInt columnSpans(m_colCount); // Each contains 1 for non-spanning cell, > 1 for spanning cell. wxArrayInt maxColWidths; maxColWidths.Add(0, m_colCount); + wxArrayInt minColWidths; minColWidths.Add(0, m_colCount); + // Separately record the minimum width of columns with + // nowrap cells + wxArrayInt minColWidthsNoWrap; + minColWidthsNoWrap.Add(0, m_colCount); + wxSize tableSize(tableWidth, 0); int i, j, k; @@ -9895,13 +9906,11 @@ bool wxRichTextTable::Layout(wxDC& dc, wxRichTextDrawingContext& context, const for (i = 0; i < m_colCount; i++) { absoluteColWidths[i] = 0; - // absoluteColWidthsSpanning[i] = 0; percentageColWidths[i] = -1; - // percentageColWidthsSpanning[i] = -1; colWidths[i] = 0; maxColWidths[i] = 0; minColWidths[i] = 0; - // columnSpans[i] = 1; + minColWidthsNoWrap[i] = 0; } // (0) Determine which cells are visible according to spans @@ -10053,12 +10062,19 @@ bool wxRichTextTable::Layout(wxDC& dc, wxRichTextDrawingContext& context, const if (colSpan == 1 && cell->GetMaxSize().x && cell->GetMaxSize().x > maxColWidths[i]) maxColWidths[i] = cell->GetMaxSize().x; + + if (cell->GetAttributes().GetTextBoxAttr().HasWhitespaceMode() && + (cell->GetAttributes().GetTextBoxAttr().GetWhitespaceMode() == wxTEXT_BOX_ATTR_WHITESPACE_NO_WRAP)) + { + if (cell->GetMaxSize().x > minColWidthsNoWrap[i]) + minColWidthsNoWrap[i] = cell->GetMaxSize().x; + } } } } } - // (2) Allocate initial column widths from minimum widths, absolute values and proportions + // (2) Allocate initial column widths from absolute values and proportions for (i = 0; i < m_colCount; i++) { if (absoluteColWidths[i] > 0) @@ -10069,8 +10085,6 @@ bool wxRichTextTable::Layout(wxDC& dc, wxRichTextDrawingContext& context, const { colWidths[i] = percentageColWidths[i]; } - else - colWidths[i] = maxUnspecifiedColumnWidths[i]; } // (3) Process absolute or proportional widths of spanning columns, @@ -10186,18 +10200,45 @@ bool wxRichTextTable::Layout(wxDC& dc, wxRichTextDrawingContext& context, const int tableWidthMinusPadding = internalTableWidth - (m_colCount-1)*paddingX; int widthLeft = tableWidthMinusPadding; int stretchColCount = 0; - for (i = 0; i < m_colCount; i++) - { - // Subtract min width from width left, then - // add the colShare to the min width - if (colWidths[i] > 0) // absolute or proportional width has been specified - widthLeft -= colWidths[i]; - else - { - if (minColWidths[i] > 0) - widthLeft -= minColWidths[i]; + bool relaxConstraints = false; - stretchColCount ++; + size_t phase; + for (phase = 0; phase < 2; phase ++) + { + widthLeft = tableWidthMinusPadding; + stretchColCount = 0; + for (i = 0; i < m_colCount; i++) + { + // Subtract min width from width left, then + // add the colShare to the min width + if (colWidths[i] > 0) // absolute or proportional width has been specified + widthLeft -= colWidths[i]; + else + { + int minColWidth = wxMax(minColWidths[i], minColWidthsNoWrap[i]); + + // If we're at phase 2, it means we had insufficient space, so + // this time, don't take maxUnspecifiedColumnWidths into account + // since we will simply apportion the remaining space. + if (phase == 0) + minColWidth = wxMax(minColWidth, maxUnspecifiedColumnWidths[i]); + if (minColWidth > 0) + widthLeft -= minColWidth; + + // Don't allow this to stretch if we're not wrapping; give the space + // to other columns instead. + if (minColWidthsNoWrap[i] == 0) + stretchColCount ++; + } + } + if (widthLeft >= 0) + break; + else if (phase == 0) + { + relaxConstraints = true; + + // If there was insufficient space, we're now shrinking to fit the available width. + stretchToFitTableWidth = true; } } @@ -10207,6 +10248,23 @@ bool wxRichTextTable::Layout(wxDC& dc, wxRichTextDrawingContext& context, const colShare = widthLeft / stretchColCount; int colShareRemainder = widthLeft - (colShare * stretchColCount); + // Check if any columns will go below their minimum width. If so, give + // up and size columns equally to avoid rendering problems. + if (colShare < 0) + { + for (i = 0; i < m_colCount; i++) + { + int w = colWidths[i]; + if (w == 0) + w = wxMax(minColWidths[i], minColWidthsNoWrap[i]); + if ((w + colShare) < minColWidths[i]) + { + stretchColCount = 0; + break; + } + } + } + // Check we don't have enough space, in which case shrink all columns, overriding // any absolute/proportional widths // TODO: actually we would like to divide up the shrinkage according to size. @@ -10214,33 +10272,61 @@ bool wxRichTextTable::Layout(wxDC& dc, wxRichTextDrawingContext& context, const // Could first choose an arbitrary value for stretching cells, and then calculate // factors to multiply each width by. // TODO: want to record this fact and pass to an iteration that tries e.g. min widths + bool shareEqually = false; if (widthLeft < 0 || (stretchToFitTableWidth && (stretchColCount == 0))) { - colShare = tableWidthMinusPadding / m_colCount; - colShareRemainder = tableWidthMinusPadding - (colShare * m_colCount); + if (stretchColCount == 0) + { + // No columns to stretch or squash, so give up and divide space equally + colShare = tableWidthMinusPadding / m_colCount; + colShareRemainder = tableWidthMinusPadding - (colShare * m_colCount); + shareEqually = true; + } + for (i = 0; i < m_colCount; i++) { colWidths[i] = 0; - minColWidths[i] = 0; } } // We have to adjust the columns if either we need to shrink the // table to fit the parent/table width, or we explicitly set the // table width and need to stretch out the table. - if (widthLeft < 0 || stretchToFitTableWidth) + for (i = 0; i < m_colCount; i++) { - for (i = 0; i < m_colCount; i++) + if (colWidths[i] <= 0) // absolute or proportional width has not been specified { - if (colWidths[i] <= 0) // absolute or proportional width has not been specified + if (widthLeft < 0 || stretchToFitTableWidth) { - if (minColWidths[i] > 0) - colWidths[i] = minColWidths[i] + colShare; - else - colWidths[i] = colShare; + int minColWidth = wxMax(minColWidths[i], minColWidthsNoWrap[i]); + + // Don't use a value for unspecified widths if we have insufficient space, + // unless it's a nowrap cell which is likely to be small. + // Actually this code is useless because if minColWidthsNoWrap exists, + // it'll be the same value as maxUnspecifiedColumnWidths. + if (!relaxConstraints) + minColWidth = wxMax(minColWidth, maxUnspecifiedColumnWidths[i]); + + if (minColWidth > 0 && !shareEqually) + colWidths[i] = minColWidth; + + // Don't allocate extra space if not wrapping since we assume a tight fit. + // Unless shareEqually forces us to distribute space because we didn't have any + // stretchable columns. + if ((minColWidthsNoWrap[i] == 0) || shareEqually) + colWidths[i] += colShare; + if (i == (m_colCount-1)) colWidths[i] += colShareRemainder; // ensure all pixels are filled } + else + { + // We're not stretching or shrinking, so calculate the column width + // consistent with how we calculated the remaining table width previously. + int minColWidth = wxMax(minColWidths[i], minColWidthsNoWrap[i]); + minColWidth = wxMax(minColWidth, maxUnspecifiedColumnWidths[i]); + colWidths[i] = minColWidth; + } } } @@ -12858,6 +12944,7 @@ void wxTextBoxAttr::Reset() m_flags = 0; m_floatMode = wxTEXT_BOX_ATTR_FLOAT_NONE; m_clearMode = wxTEXT_BOX_ATTR_CLEAR_NONE; + m_whitespaceMode = wxTEXT_BOX_ATTR_WHITESPACE_NONE; m_collapseMode = wxTEXT_BOX_ATTR_COLLAPSE_NONE; m_verticalAlignment = wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_NONE; m_boxStyleName = wxEmptyString; @@ -12881,6 +12968,7 @@ bool wxTextBoxAttr::operator== (const wxTextBoxAttr& attr) const m_flags == attr.m_flags && m_floatMode == attr.m_floatMode && m_clearMode == attr.m_clearMode && + m_whitespaceMode == attr.m_whitespaceMode && m_collapseMode == attr.m_collapseMode && m_verticalAlignment == attr.m_verticalAlignment && @@ -12907,6 +12995,7 @@ bool wxTextBoxAttr::EqPartial(const wxTextBoxAttr& attr, bool weakTest) const (!HasClearMode() && attr.HasClearMode()) || (!HasCollapseBorders() && attr.HasCollapseBorders()) || (!HasVerticalAlignment() && attr.HasVerticalAlignment()) || + (!HasWhitespaceMode() && attr.HasWhitespaceMode()) || (!HasBoxStyleName() && attr.HasBoxStyleName()))) { return false; @@ -12923,6 +13012,9 @@ bool wxTextBoxAttr::EqPartial(const wxTextBoxAttr& attr, bool weakTest) const if (attr.HasVerticalAlignment() && HasVerticalAlignment() && (attr.GetVerticalAlignment() != GetVerticalAlignment())) return false; + if (attr.HasWhitespaceMode() && HasWhitespaceMode() && (GetWhitespaceMode() != attr.GetWhitespaceMode())) + return false; + if (attr.HasBoxStyleName() && HasBoxStyleName() && (attr.GetBoxStyleName() != GetBoxStyleName())) return false; @@ -12992,6 +13084,12 @@ bool wxTextBoxAttr::Apply(const wxTextBoxAttr& attr, const wxTextBoxAttr* compar SetVerticalAlignment(attr.GetVerticalAlignment()); } + if (attr.HasWhitespaceMode()) + { + if (!(compareWith && compareWith->HasWhitespaceMode() && compareWith->GetWhitespaceMode() == attr.GetWhitespaceMode())) + SetWhitespaceMode(attr.GetWhitespaceMode()); + } + if (attr.HasBoxStyleName()) { if (!(compareWith && compareWith->HasBoxStyleName() && compareWith->GetBoxStyleName() == attr.GetBoxStyleName())) @@ -13027,6 +13125,9 @@ bool wxTextBoxAttr::RemoveStyle(const wxTextBoxAttr& attr) if (attr.HasVerticalAlignment()) RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT); + if (attr.HasWhitespaceMode()) + RemoveFlag(wxTEXT_BOX_ATTR_WHITESPACE); + if (attr.HasBoxStyleName()) { SetBoxStyleName(wxEmptyString); @@ -13127,6 +13228,25 @@ void wxTextBoxAttr::CollectCommonAttributes(const wxTextBoxAttr& attr, wxTextBox else absentAttr.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT); + if (attr.HasWhitespaceMode()) + { + if (!clashingAttr.HasWhitespaceMode() && !absentAttr.HasWhitespaceMode()) + { + if (HasWhitespaceMode()) + { + if (GetWhitespaceMode() != attr.GetWhitespaceMode()) + { + clashingAttr.AddFlag(wxTEXT_BOX_ATTR_WHITESPACE); + RemoveFlag(wxTEXT_BOX_ATTR_WHITESPACE); + } + } + else + SetWhitespaceMode(attr.GetWhitespaceMode()); + } + } + else + absentAttr.AddFlag(wxTEXT_BOX_ATTR_WHITESPACE); + if (attr.HasBoxStyleName()) { if (!clashingAttr.HasBoxStyleName() && !absentAttr.HasBoxStyleName()) diff --git a/src/richtext/richtextxml.cpp b/src/richtext/richtextxml.cpp index cf413f42ff..b853b6fe51 100644 --- a/src/richtext/richtextxml.cpp +++ b/src/richtext/richtextxml.cpp @@ -1634,6 +1634,8 @@ bool wxRichTextXMLHelper::ImportStyle(wxRichTextAttr& attr, wxXmlNode* node, boo } else if (name == wxT("collapse-borders")) attr.GetTextBoxAttr().SetCollapseBorders((wxTextBoxAttrCollapseMode) wxAtoi(value)); + else if (name == wxT("whitespace-mode")) + attr.GetTextBoxAttr().SetWhitespaceMode((wxTextBoxAttrWhitespaceMode) wxAtoi(value)); else if (name.Contains(wxT("border-"))) { @@ -2228,6 +2230,8 @@ wxString wxRichTextXMLHelper::AddAttributes(const wxRichTextAttr& attr, bool isP if (attr.GetTextBoxAttr().HasCollapseBorders()) AddAttribute(str, wxT("collapse-borders"), (int) attr.GetTextBoxAttr().GetCollapseBorders()); + if (attr.GetTextBoxAttr().HasWhitespaceMode()) + AddAttribute(str, wxT("whitespace-mode"), (int) attr.GetTextBoxAttr().GetWhitespaceMode()); return str; } @@ -2705,6 +2709,9 @@ bool wxRichTextXMLHelper::AddAttributes(wxXmlNode* node, wxRichTextAttr& attr, b if (attr.GetTextBoxAttr().HasCollapseBorders()) AddAttribute(node, wxT("collapse-borders"), (int) attr.GetTextBoxAttr().GetCollapseBorders()); + if (attr.GetTextBoxAttr().HasWhitespaceMode()) + AddAttribute(node, wxT("whitespace-mode"), (int) attr.GetTextBoxAttr().GetWhitespaceMode()); + return true; }