X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/bb7bbd12d311a447d0f8a72adc63c9b78464c01f..e264485aac3699e8bd876c3feeee2eb48c61a81f:/src/richtext/richtextbuffer.cpp diff --git a/src/richtext/richtextbuffer.cpp b/src/richtext/richtextbuffer.cpp index 71a54cc8af..dbeab138df 100644 --- a/src/richtext/richtextbuffer.cpp +++ b/src/richtext/richtextbuffer.cpp @@ -42,6 +42,7 @@ #include "wx/richtext/richtextstyles.h" #include "wx/richtext/richtextimagedlg.h" #include "wx/richtext/richtextsizepage.h" +#include "wx/richtext/richtextxml.h" #include "wx/listimpl.cpp" #include "wx/arrimpl.cpp" @@ -105,10 +106,10 @@ public: int GetLastRectBottom(); // Draw the floats inside a rect - void Draw(wxDC& dc, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style); + void Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style); // HitTest the floats - int HitTest(wxDC& dc, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int flags); + int HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int flags); // Get floating object count int GetFloatingObjectCount() const { return m_left.GetCount() + m_right.GetCount(); } @@ -130,9 +131,9 @@ public: static void FreeFloatRectMapArray(wxRichTextFloatRectMapArray& array); - static void DrawFloat(const wxRichTextFloatRectMapArray& array, wxDC& dc, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style); + static void DrawFloat(const wxRichTextFloatRectMapArray& array, wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style); - static int HitTestFloat(const wxRichTextFloatRectMapArray& array, wxDC& WXUNUSED(dc), const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int flags); + static int HitTestFloat(const wxRichTextFloatRectMapArray& array, wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int flags); private: wxRichTextFloatRectMapArray m_left; @@ -396,7 +397,7 @@ int wxRichTextFloatCollector::GetLastRectBottom() return ret; } -void wxRichTextFloatCollector::DrawFloat(const wxRichTextFloatRectMapArray& array, wxDC& dc, const wxRichTextRange& WXUNUSED(range), const wxRichTextSelection& selection, const wxRect& rect, int descent, int style) +void wxRichTextFloatCollector::DrawFloat(const wxRichTextFloatRectMapArray& array, wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& WXUNUSED(range), const wxRichTextSelection& selection, const wxRect& rect, int descent, int style) { int start = rect.y; int end = rect.y + rect.height; @@ -411,20 +412,20 @@ void wxRichTextFloatCollector::DrawFloat(const wxRichTextFloatRectMapArray& arra { wxRichTextObject* obj = array[i]->anchor; wxRichTextRange r = obj->GetRange(); - obj->Draw(dc, r, selection, wxRect(obj->GetPosition(), obj->GetCachedSize()), descent, style); + obj->Draw(dc, context, r, selection, wxRect(obj->GetPosition(), obj->GetCachedSize()), descent, style); i++; } } -void wxRichTextFloatCollector::Draw(wxDC& dc, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style) +void wxRichTextFloatCollector::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style) { if (m_left.GetCount() > 0) - DrawFloat(m_left, dc, range, selection, rect, descent, style); + DrawFloat(m_left, dc, context, range, selection, rect, descent, style); if (m_right.GetCount() > 0) - DrawFloat(m_right, dc, range, selection, rect, descent, style); + DrawFloat(m_right, dc, context, range, selection, rect, descent, style); } -int wxRichTextFloatCollector::HitTestFloat(const wxRichTextFloatRectMapArray& array, wxDC& WXUNUSED(dc), const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int WXUNUSED(flags)) +int wxRichTextFloatCollector::HitTestFloat(const wxRichTextFloatRectMapArray& array, wxDC& WXUNUSED(dc), wxRichTextDrawingContext& WXUNUSED(context), const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int WXUNUSED(flags)) { int i; if (array.GetCount() == 0) @@ -451,12 +452,12 @@ int wxRichTextFloatCollector::HitTestFloat(const wxRichTextFloatRectMapArray& ar return wxRICHTEXT_HITTEST_NONE; } -int wxRichTextFloatCollector::HitTest(wxDC& dc, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int flags) +int wxRichTextFloatCollector::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int flags) { - int ret = HitTestFloat(m_left, dc, pt, textPosition, obj, flags); + int ret = HitTestFloat(m_left, dc, context, pt, textPosition, obj, flags); if (ret == wxRICHTEXT_HITTEST_NONE) { - ret = HitTestFloat(m_right, dc, pt, textPosition, obj, flags); + ret = HitTestFloat(m_right, dc, context, pt, textPosition, obj, flags); } return ret; } @@ -464,21 +465,6 @@ int wxRichTextFloatCollector::HitTest(wxDC& dc, const wxPoint& pt, long& textPos // Helpers for efficiency inline void wxCheckSetFont(wxDC& dc, const wxFont& font) { - // JACS: did I do this some time ago when testing? Should we re-enable it? -#if 0 - const wxFont& font1 = dc.GetFont(); - if (font1.IsOk() && font.IsOk()) - { - if (font1.GetPointSize() == font.GetPointSize() && - font1.GetFamily() == font.GetFamily() && - font1.GetStyle() == font.GetStyle() && - font1.GetWeight() == font.GetWeight() && - font1.GetUnderlined() == font.GetUnderlined() && - font1.GetFamily() == font.GetFamily() && - font1.GetFaceName() == font.GetFaceName()) - return; - } -#endif dc.SetFont(font); } @@ -598,11 +584,13 @@ int wxRichTextObject::GetBottomMargin() const // Calculate the available content space in the given rectangle, given the // margins, border and padding specified in the object's attributes. -wxRect wxRichTextObject::GetAvailableContentArea(wxDC& dc, const wxRect& outerRect) const +wxRect wxRichTextObject::GetAvailableContentArea(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& outerRect) const { wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect; marginRect = outerRect; - GetBoxRects(dc, GetBuffer(), GetAttributes(), marginRect, borderRect, contentRect, paddingRect, outlineRect); + wxRichTextAttr attr(GetAttributes()); + context.ApplyVirtualAttributes(attr, (wxRichTextObject*) this); + GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect); return contentRect; } @@ -611,7 +599,11 @@ void wxRichTextObject::Invalidate(const wxRichTextRange& invalidRange) { if (invalidRange != wxRICHTEXT_NONE) { - SetCachedSize(wxDefaultSize); + // If this is a floating object, size may not be recalculated + // after floats have been collected in an early stage of Layout. + // So avoid resetting the cache for floating objects during layout. + if (!IsFloating()) + SetCachedSize(wxDefaultSize); SetMaxSize(wxDefaultSize); SetMinSize(wxDefaultSize); } @@ -623,7 +615,7 @@ int wxRichTextObject::ConvertTenthsMMToPixels(wxDC& dc, int units) const // Unscale double scale = 1.0; if (GetBuffer()) - scale = GetBuffer()->GetScale(); + scale = GetBuffer()->GetScale() / GetBuffer()->GetDimensionScale(); int p = ConvertTenthsMMToPixels(dc.GetPPI().x, units, scale); return p; @@ -1016,7 +1008,7 @@ void wxRichTextObject::Dump(wxTextOutputStream& stream) wxRichTextBuffer* wxRichTextObject::GetBuffer() const { const wxRichTextObject* obj = this; - while (obj && !obj->IsKindOf(CLASSINFO(wxRichTextBuffer))) + while (obj && !wxDynamicCast(obj, wxRichTextBuffer)) obj = obj->GetParent(); return wxDynamicCast(obj, wxRichTextBuffer); } @@ -1038,7 +1030,7 @@ wxPoint wxRichTextObject::GetAbsolutePosition() const // Hit-testing: returns a flag indicating hit test details, plus // information about position -int wxRichTextObject::HitTest(wxDC& WXUNUSED(dc), const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int WXUNUSED(flags)) +int wxRichTextObject::HitTest(wxDC& WXUNUSED(dc), wxRichTextDrawingContext& WXUNUSED(context), const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int WXUNUSED(flags)) { if (!IsShown()) return wxRICHTEXT_HITTEST_NONE; @@ -1058,14 +1050,14 @@ int wxRichTextObject::HitTest(wxDC& WXUNUSED(dc), const wxPoint& pt, long& textP // Lays out the object first with a given amount of space, and then if no width was specified in attr, // lays out the object again using the maximum ('best') size -bool wxRichTextObject::LayoutToBestSize(wxDC& dc, wxRichTextBuffer* buffer, +bool wxRichTextObject::LayoutToBestSize(wxDC& dc, wxRichTextDrawingContext& context, wxRichTextBuffer* buffer, const wxRichTextAttr& parentAttr, const wxRichTextAttr& attr, const wxRect& availableParentSpace, const wxRect& availableContainerSpace, int style) { wxRect availableChildRect = AdjustAvailableSpace(dc, buffer, parentAttr, attr, availableParentSpace, availableContainerSpace); wxRect originalAvailableRect = availableChildRect; - Layout(dc, availableChildRect, availableContainerSpace, style); + Layout(dc, context, availableChildRect, availableContainerSpace, style); wxSize maxSize = GetMaxSize(); @@ -1087,17 +1079,17 @@ bool wxRichTextObject::LayoutToBestSize(wxDC& dc, wxRichTextBuffer* buffer, if (attr.HasAlignment() && !GetContainer()->GetFloatCollector()->HasFloats()) // FIXME: aligning whole paragraph not compatible with floating objects { // centering, right-justification - if (GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_CENTRE) + if (attr.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE) { availableChildRect.x = (originalAvailableRect.GetWidth() - availableChildRect.GetWidth())/2 + availableChildRect.x; } - else if (GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_RIGHT) + else if (attr.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT) { availableChildRect.x = availableChildRect.x + originalAvailableRect.GetWidth() - availableChildRect.GetWidth(); } } - Layout(dc, availableChildRect, availableContainerSpace, style); + Layout(dc, context, availableChildRect, availableContainerSpace, style); } /* @@ -1227,7 +1219,7 @@ void wxRichTextCompositeObject::Copy(const wxRichTextCompositeObject& obj) /// Hit-testing: returns a flag indicating hit test details, plus /// information about position -int wxRichTextCompositeObject::HitTest(wxDC& dc, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags) +int wxRichTextCompositeObject::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags) { if (!IsShown()) return wxRICHTEXT_HITTEST_NONE; @@ -1240,13 +1232,13 @@ int wxRichTextCompositeObject::HitTest(wxDC& dc, const wxPoint& pt, long& textPo if (child->IsShown() && child->IsTopLevel() && (flags & wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS)) { // Just check if we hit the overall object - int ret = child->wxRichTextObject::HitTest(dc, pt, textPosition, obj, contextObj, flags); + int ret = child->wxRichTextObject::HitTest(dc, context, pt, textPosition, obj, contextObj, flags); if (ret != wxRICHTEXT_HITTEST_NONE) return ret; } else if (child->IsShown()) { - int ret = child->HitTest(dc, pt, textPosition, obj, contextObj, flags); + int ret = child->HitTest(dc, context, pt, textPosition, obj, contextObj, flags); if (ret != wxRICHTEXT_HITTEST_NONE) return ret; } @@ -1258,7 +1250,7 @@ int wxRichTextCompositeObject::HitTest(wxDC& dc, const wxPoint& pt, long& textPo } /// Finds the absolute position and row height for the given character position -bool wxRichTextCompositeObject::FindPosition(wxDC& dc, long index, wxPoint& pt, int* height, bool forceLineStart) +bool wxRichTextCompositeObject::FindPosition(wxDC& dc, wxRichTextDrawingContext& context, long index, wxPoint& pt, int* height, bool forceLineStart) { wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst(); while (node) @@ -1269,7 +1261,7 @@ bool wxRichTextCompositeObject::FindPosition(wxDC& dc, long index, wxPoint& pt, // such as a text box, because the character position will no longer // apply. By definition, a top-level object has its own range of // character positions. - if (!child->IsTopLevel() && child->FindPosition(dc, index, pt, height, forceLineStart)) + if (!child->IsTopLevel() && child->FindPosition(dc, context, index, pt, height, forceLineStart)) return true; node = node->GetNext(); @@ -1479,7 +1471,7 @@ void wxRichTextCompositeObject::Dump(wxTextOutputStream& stream) /// Get/set the object size for the given range. Returns false if the range /// is invalid for this object. -bool wxRichTextCompositeObject::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position, wxArrayInt* partialExtents) const +bool wxRichTextCompositeObject::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* partialExtents) const { if (!range.IsWithin(GetRange())) return false; @@ -1535,7 +1527,7 @@ bool wxRichTextCompositeObject::GetRangeSize(const wxRichTextRange& range, wxSiz sz.x += childSize.x; descent = wxMax(descent, childDescent); } - else if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, flags, wxPoint(position.x + sz.x, position.y), p)) + else if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y), p)) { sz.y = wxMax(sz.y, childSize.y); sz.x += childSize.x; @@ -1651,7 +1643,6 @@ void wxRichTextParagraphLayoutBox::Init() m_invalidRange = wxRICHTEXT_ALL; - SetMargins(4); m_partialParagraph = false; m_floatCollector = NULL; } @@ -1736,17 +1727,17 @@ void wxRichTextParagraphLayoutBox::UpdateRanges() } // HitTest -int wxRichTextParagraphLayoutBox::HitTest(wxDC& dc, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags) +int wxRichTextParagraphLayoutBox::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags) { if (!IsShown()) return wxRICHTEXT_HITTEST_NONE; int ret = wxRICHTEXT_HITTEST_NONE; if (m_floatCollector && (flags & wxRICHTEXT_HITTEST_NO_FLOATING_OBJECTS) == 0) - ret = m_floatCollector->HitTest(dc, pt, textPosition, obj, flags); + ret = m_floatCollector->HitTest(dc, context, pt, textPosition, obj, flags); if (ret == wxRICHTEXT_HITTEST_NONE) - return wxRichTextCompositeObject::HitTest(dc, pt, textPosition, obj, contextObj, flags); + return wxRichTextCompositeObject::HitTest(dc, context, pt, textPosition, obj, contextObj, flags); else { *contextObj = this; @@ -1755,10 +1746,10 @@ int wxRichTextParagraphLayoutBox::HitTest(wxDC& dc, const wxPoint& pt, long& tex } /// Draw the floating objects -void wxRichTextParagraphLayoutBox::DrawFloats(wxDC& dc, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style) +void wxRichTextParagraphLayoutBox::DrawFloats(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style) { if (m_floatCollector) - m_floatCollector->Draw(dc, range, selection, rect, descent, style); + m_floatCollector->Draw(dc, context, range, selection, rect, descent, style); } void wxRichTextParagraphLayoutBox::MoveAnchoredObjectToParagraph(wxRichTextParagraph* from, wxRichTextParagraph* to, wxRichTextObject* obj) @@ -1771,13 +1762,16 @@ void wxRichTextParagraphLayoutBox::MoveAnchoredObjectToParagraph(wxRichTextParag } /// Draw the item -bool wxRichTextParagraphLayoutBox::Draw(wxDC& dc, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style) +bool wxRichTextParagraphLayoutBox::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style) { if (!IsShown()) return true; wxRect thisRect(GetPosition(), GetCachedSize()); + wxRichTextAttr attr(GetAttributes()); + context.ApplyVirtualAttributes(attr, this); + int flags = style; if (selection.IsValid() && GetParentContainer() != this && selection.WithinSelection(GetRange().GetStart(), GetParentContainer())) flags |= wxRICHTEXT_DRAW_SELECTED; @@ -1786,9 +1780,9 @@ bool wxRichTextParagraphLayoutBox::Draw(wxDC& dc, const wxRichTextRange& range, int theseFlags = flags; if (!GetParent()) theseFlags &= ~wxRICHTEXT_DRAW_GUIDELINES; - DrawBoxAttributes(dc, GetBuffer(), GetAttributes(), thisRect, theseFlags); + DrawBoxAttributes(dc, GetBuffer(), attr, thisRect, theseFlags); - DrawFloats(dc, range, selection, rect, descent, style); + DrawFloats(dc, context, range, selection, rect, descent, style); wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst(); while (node) { @@ -1813,7 +1807,7 @@ bool wxRichTextParagraphLayoutBox::Draw(wxDC& dc, const wxRichTextRange& range, // Skip } else - child->Draw(dc, childRange, selection, rect, descent, style); + child->Draw(dc, context, childRange, selection, rect, descent, style); } node = node->GetNext(); @@ -1822,7 +1816,7 @@ bool wxRichTextParagraphLayoutBox::Draw(wxDC& dc, const wxRichTextRange& range, } /// Lay the item out -bool wxRichTextParagraphLayoutBox::Layout(wxDC& dc, const wxRect& rect, const wxRect& parentRect, int style) +bool wxRichTextParagraphLayoutBox::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style) { SetPosition(rect.GetPosition()); @@ -1832,6 +1826,9 @@ bool wxRichTextParagraphLayoutBox::Layout(wxDC& dc, const wxRect& rect, const wx wxRect availableSpace; bool formatRect = (style & wxRICHTEXT_LAYOUT_SPECIFIED_RECT) == wxRICHTEXT_LAYOUT_SPECIFIED_RECT; + wxRichTextAttr attr(GetAttributes()); + context.ApplyVirtualAttributes(attr, this); + // If only laying out a specific area, the passed rect has a different meaning: // the visible part of the buffer. This is used in wxRichTextCtrl::OnSize, // so that during a size, only the visible part will be relaid out, or @@ -1840,7 +1837,7 @@ bool wxRichTextParagraphLayoutBox::Layout(wxDC& dc, const wxRect& rect, const wx if (formatRect) { wxRect rect2(0, 0, rect.width, rect.height); - availableSpace = GetAvailableContentArea(dc, rect2); + availableSpace = GetAvailableContentArea(dc, context, rect2); // Invalidate the part of the buffer from the first visible line // to the end. If other parts of the buffer are currently invalid, @@ -1855,11 +1852,15 @@ bool wxRichTextParagraphLayoutBox::Layout(wxDC& dc, const wxRect& rect, const wx } else { - availableSpace = GetAvailableContentArea(dc, rect); + availableSpace = GetAvailableContentArea(dc, context, rect); } + // Fix the width if we're at the top level + if (!GetParent()) + attr.GetTextBoxAttr().GetWidth().SetValue(rect.GetWidth(), wxTEXT_ATTR_UNITS_PIXELS); + int leftMargin, rightMargin, topMargin, bottomMargin; - wxRichTextObject::GetTotalMargin(dc, GetBuffer(), GetAttributes(), leftMargin, rightMargin, + wxRichTextObject::GetTotalMargin(dc, GetBuffer(), attr, leftMargin, rightMargin, topMargin, bottomMargin); int maxWidth = 0; @@ -1872,8 +1873,8 @@ bool wxRichTextParagraphLayoutBox::Layout(wxDC& dc, const wxRect& rect, const wx int maxMinWidth = 0; // If we have vertical alignment, we must recalculate everything. - bool hasVerticalAlignment = (GetAttributes().GetTextBoxAttr().HasVerticalAlignment() && - (GetAttributes().GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP)); + bool hasVerticalAlignment = (attr.GetTextBoxAttr().HasVerticalAlignment() && + (attr.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP)); wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst(); @@ -1952,8 +1953,8 @@ bool wxRichTextParagraphLayoutBox::Layout(wxDC& dc, const wxRect& rect, const wx { // Lays out the object first with a given amount of space, and then if no width was specified in attr, // lays out the object again using the minimum size - child->LayoutToBestSize(dc, GetBuffer(), - GetAttributes(), child->GetAttributes(), availableSpace, rect, style&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT); + child->LayoutToBestSize(dc, context, GetBuffer(), + attr, child->GetAttributes(), availableSpace, rect, style&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT); // Layout must set the cached size availableSpace.y += child->GetCachedSize().y; @@ -1986,8 +1987,8 @@ bool wxRichTextParagraphLayoutBox::Layout(wxDC& dc, const wxRect& rect, const wx { // Lays out the object first with a given amount of space, and then if no width was specified in attr, // lays out the object again using the minimum size - child->LayoutToBestSize(dc, GetBuffer(), - GetAttributes(), child->GetAttributes(), availableSpace, rect, style&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT); + child->LayoutToBestSize(dc, context, GetBuffer(), + attr, child->GetAttributes(), availableSpace, rect, style&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT); //child->Layout(dc, availableChildRect, style); } @@ -2018,9 +2019,17 @@ bool wxRichTextParagraphLayoutBox::Layout(wxDC& dc, const wxRect& rect, const wx else maxHeight = 0; // topMargin + bottomMargin; - if (GetAttributes().GetTextBoxAttr().GetSize().GetWidth().IsValid()) + // Check the bottom edge of any floating object + if (GetFloatCollector() && GetFloatCollector()->HasFloats()) + { + int bottom = GetFloatCollector()->GetLastRectBottom(); + if (bottom > maxHeight) + maxHeight = bottom; + } + + if (attr.GetTextBoxAttr().GetSize().GetWidth().IsValid()) { - wxRect r = AdjustAvailableSpace(dc, GetBuffer(), wxRichTextAttr() /* not used */, GetAttributes(), parentRect, parentRect); + wxRect r = AdjustAvailableSpace(dc, GetBuffer(), wxRichTextAttr() /* not used */, attr, parentRect, parentRect); int w = r.GetWidth(); // Convert external to content rect @@ -2028,6 +2037,12 @@ bool wxRichTextParagraphLayoutBox::Layout(wxDC& dc, const wxRect& rect, const wx maxWidth = wxMax(maxWidth, w); maxMaxWidth = wxMax(maxMaxWidth, w); } + else + { + // TODO: Make sure the layout box's position reflects + // the position of the children, but without + // breaking layout of a box within a paragraph. + } // TODO: (also in para layout) should set the // object's size to an absolute one if specified, @@ -2037,7 +2052,7 @@ bool wxRichTextParagraphLayoutBox::Layout(wxDC& dc, const wxRect& rect, const wx { wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect; contentRect = wxRect(wxPoint(0, 0), wxSize(maxWidth, maxHeight)); - GetBoxRects(dc, GetBuffer(), GetAttributes(), marginRect, borderRect, contentRect, paddingRect, outlineRect); + GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect); SetCachedSize(marginRect.GetSize()); } @@ -2045,7 +2060,7 @@ bool wxRichTextParagraphLayoutBox::Layout(wxDC& dc, const wxRect& rect, const wx { wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect; contentRect = wxRect(wxPoint(0, 0), wxSize(maxMaxWidth, maxHeight)); // Actually max height is a lie, we can't know it - GetBoxRects(dc, GetBuffer(), GetAttributes(), marginRect, borderRect, contentRect, paddingRect, outlineRect); + GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect); SetMaxSize(marginRect.GetSize()); } @@ -2053,22 +2068,22 @@ bool wxRichTextParagraphLayoutBox::Layout(wxDC& dc, const wxRect& rect, const wx { wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect; contentRect = wxRect(wxPoint(0, 0), wxSize(maxMinWidth, maxHeight)); // Actually max height is a lie, we can't know it - GetBoxRects(dc, GetBuffer(), GetAttributes(), marginRect, borderRect, contentRect, paddingRect, outlineRect); + GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect); SetMinSize(marginRect.GetSize()); } - if (GetAttributes().GetTextBoxAttr().HasVerticalAlignment() && - (GetAttributes().GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP)) + if (attr.GetTextBoxAttr().HasVerticalAlignment() && + (attr.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP)) { int yOffset = 0; int leftOverSpace = availableSpace.height - topMargin - bottomMargin - maxHeight; if (leftOverSpace > 0) { - if (GetAttributes().GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_CENTRE) + if (attr.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_CENTRE) { yOffset = (leftOverSpace/2); } - else if (GetAttributes().GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_BOTTOM) + else if (attr.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_BOTTOM) { yOffset = leftOverSpace; } @@ -2096,7 +2111,7 @@ bool wxRichTextParagraphLayoutBox::Layout(wxDC& dc, const wxRect& rect, const wx } /// Get/set the size for the given range. -bool wxRichTextParagraphLayoutBox::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position, wxArrayInt* WXUNUSED(partialExtents)) const +bool wxRichTextParagraphLayoutBox::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* WXUNUSED(partialExtents)) const { wxSize sz; @@ -2155,7 +2170,7 @@ bool wxRichTextParagraphLayoutBox::GetRangeSize(const wxRichTextRange& range, wx wxSize childSize; int childDescent = 0; - child->GetRangeSize(rangeToFind, childSize, childDescent, dc, flags, position); + child->GetRangeSize(rangeToFind, childSize, childDescent, dc, context, flags, position); descent = wxMax(childDescent, descent); @@ -2348,6 +2363,7 @@ wxRichTextRange wxRichTextParagraphLayoutBox::AddParagraph(const wxString& text, wxRichTextAttr* cStyle = & defaultCharStyle; wxRichTextParagraph* para = new wxRichTextParagraph(text, this, pStyle, cStyle); + para->GetAttributes().GetTextBoxAttr().Reset(); AppendChild(para); @@ -2389,6 +2405,7 @@ wxRichTextRange wxRichTextParagraphLayoutBox::AddParagraphs(const wxString& text size_t len = text.length(); wxString line; wxRichTextParagraph* para = new wxRichTextParagraph(wxEmptyString, this, pStyle, cStyle); + para->GetAttributes().GetTextBoxAttr().Reset(); AppendChild(para); @@ -2406,6 +2423,7 @@ wxRichTextRange wxRichTextParagraphLayoutBox::AddParagraphs(const wxString& text plainText->SetText(line); para = new wxRichTextParagraph(wxEmptyString, this, pStyle, cStyle); + para->GetAttributes().GetTextBoxAttr().Reset(); AppendChild(para); @@ -2455,6 +2473,7 @@ wxRichTextRange wxRichTextParagraphLayoutBox::AddImage(const wxImage& image, wxR wxRichTextAttr* cStyle = & defaultCharStyle; wxRichTextParagraph* para = new wxRichTextParagraph(this, pStyle); + para->GetAttributes().GetTextBoxAttr().Reset(); AppendChild(para); para->AppendChild(new wxRichTextImage(image, this, cStyle)); @@ -3318,6 +3337,7 @@ bool wxRichTextParagraphLayoutBox::DoGetStyle(long position, wxRichTextAttr& sty { // Start with the base style style = GetAttributes(); + style.GetTextBoxAttr().Reset(); // Apply the paragraph style wxRichTextApplyStyle(style, obj->GetAttributes()); @@ -3458,12 +3478,12 @@ bool wxRichTextParagraphLayoutBox::HasCharacterAttributes(const wxRichTextRange& if (childRange.GetLength() == 0 && GetRange().GetLength() == 1) childRange.SetEnd(childRange.GetEnd()+1); - if (!childRange.IsOutside(range) && child->IsKindOf(CLASSINFO(wxRichTextPlainText))) + if (!childRange.IsOutside(range) && wxDynamicCast(child, wxRichTextPlainText)) { foundCount ++; wxRichTextAttr textAttr = para->GetCombinedAttributes(child->GetAttributes()); - if (wxTextAttrEqPartial(textAttr, style)) + if (textAttr.EqPartial(style, false /* strong test - attributes must be valid in both objects */)) matchingCount ++; } @@ -3506,7 +3526,7 @@ bool wxRichTextParagraphLayoutBox::HasParagraphAttributes(const wxRichTextRange& wxRichTextApplyStyle(textAttr, para->GetAttributes()); foundCount ++; - if (wxTextAttrEqPartial(textAttr, style)) + if (textAttr.EqPartial(style, false /* strong test */)) matchingCount ++; } } @@ -3516,6 +3536,170 @@ bool wxRichTextParagraphLayoutBox::HasParagraphAttributes(const wxRichTextRange& return foundCount == matchingCount && foundCount != 0; } +void wxRichTextParagraphLayoutBox::PrepareContent(wxRichTextParagraphLayoutBox& container) +{ + wxRichTextBuffer* buffer = GetBuffer(); + if (buffer && buffer->GetRichTextCtrl()) + buffer->GetRichTextCtrl()->PrepareContent(container); +} + +/// Set character or paragraph properties +bool wxRichTextParagraphLayoutBox::SetProperties(const wxRichTextRange& range, const wxRichTextProperties& properties, int flags) +{ + wxRichTextBuffer* buffer = GetBuffer(); + + bool withUndo = ((flags & wxRICHTEXT_SETPROPERTIES_WITH_UNDO) != 0); + bool parasOnly = ((flags & wxRICHTEXT_SETPROPERTIES_PARAGRAPHS_ONLY) != 0); + bool charactersOnly = ((flags & wxRICHTEXT_SETPROPERTIES_CHARACTERS_ONLY) != 0); + bool resetExistingProperties = ((flags & wxRICHTEXT_SETPROPERTIES_RESET) != 0); + bool removeProperties = ((flags & wxRICHTEXT_SETPROPERTIES_REMOVE) != 0); + + // If we are associated with a control, make undoable; otherwise, apply immediately + // to the data. + + bool haveControl = (buffer->GetRichTextCtrl() != NULL); + + wxRichTextAction* action = NULL; + + if (haveControl && withUndo) + { + action = new wxRichTextAction(NULL, _("Change Properties"), wxRICHTEXT_CHANGE_PROPERTIES, buffer, this, buffer->GetRichTextCtrl()); + action->SetRange(range); + action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition()); + } + + wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst(); + while (node) + { + wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph); + // wxASSERT (para != NULL); + + if (para && para->GetChildCount() > 0) + { + // Stop searching if we're beyond the range of interest + if (para->GetRange().GetStart() > range.GetEnd()) + break; + + if (!para->GetRange().IsOutside(range)) + { + // We'll be using a copy of the paragraph to make style changes, + // not updating the buffer directly. + wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL); + + if (haveControl && withUndo) + { + newPara = new wxRichTextParagraph(*para); + action->GetNewParagraphs().AppendChild(newPara); + + // Also store the old ones for Undo + action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para)); + } + else + newPara = para; + + if (parasOnly) + { + if (removeProperties) + { + // Removes the given style from the paragraph + // TODO + newPara->GetProperties().RemoveProperties(properties); + } + else if (resetExistingProperties) + newPara->GetProperties() = properties; + else + newPara->GetProperties().MergeProperties(properties); + } + + // When applying paragraph styles dynamically, don't change the text objects' attributes + // since they will computed as needed. Only apply the character styling if it's _only_ + // character styling. This policy is subject to change and might be put under user control. + + // Hm. we might well be applying a mix of paragraph and character styles, in which + // case we _do_ want to apply character styles regardless of what para styles are set. + // But if we're applying a paragraph style, which has some character attributes, but + // we only want the paragraphs to hold this character style, then we _don't_ want to + // apply the character style. So we need to be able to choose. + + if (!parasOnly && charactersOnly && range.GetStart() != newPara->GetRange().GetEnd()) + { + wxRichTextRange childRange(range); + childRange.LimitTo(newPara->GetRange()); + + // Find the starting position and if necessary split it so + // we can start applying different properties. + // TODO: check that the properties actually change or are different + // from properties outside of range + wxRichTextObject* firstObject wxDUMMY_INITIALIZE(NULL); + wxRichTextObject* lastObject wxDUMMY_INITIALIZE(NULL); + + if (childRange.GetStart() == newPara->GetRange().GetStart()) + firstObject = newPara->GetChildren().GetFirst()->GetData(); + else + firstObject = newPara->SplitAt(range.GetStart()); + + // Increment by 1 because we're apply the style one _after_ the split point + long splitPoint = childRange.GetEnd(); + if (splitPoint != newPara->GetRange().GetEnd()) + splitPoint ++; + + // Find last object + if (splitPoint == newPara->GetRange().GetEnd()) + lastObject = newPara->GetChildren().GetLast()->GetData(); + else + // lastObject is set as a side-effect of splitting. It's + // returned as the object before the new object. + (void) newPara->SplitAt(splitPoint, & lastObject); + + wxASSERT(firstObject != NULL); + wxASSERT(lastObject != NULL); + + if (!firstObject || !lastObject) + continue; + + wxRichTextObjectList::compatibility_iterator firstNode = newPara->GetChildren().Find(firstObject); + wxRichTextObjectList::compatibility_iterator lastNode = newPara->GetChildren().Find(lastObject); + + wxASSERT(firstNode); + wxASSERT(lastNode); + + wxRichTextObjectList::compatibility_iterator node2 = firstNode; + + while (node2) + { + wxRichTextObject* child = node2->GetData(); + + if (removeProperties) + { + // Removes the given properties from the paragraph + child->GetProperties().RemoveProperties(properties); + } + else if (resetExistingProperties) + child->GetProperties() = properties; + else + { + child->GetProperties().MergeProperties(properties); + } + + if (node2 == lastNode) + break; + + node2 = node2->GetNext(); + } + } + } + } + + node = node->GetNext(); + } + + // Do action, or delay it until end of batch. + if (haveControl && withUndo) + buffer->SubmitAction(action); + + return true; +} + void wxRichTextParagraphLayoutBox::Reset() { Clear(); @@ -3532,6 +3716,8 @@ void wxRichTextParagraphLayoutBox::Reset() AddParagraph(wxEmptyString); + PrepareContent(*this); + InvalidateHierarchy(wxRICHTEXT_ALL); } @@ -3799,12 +3985,19 @@ bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange& range, wx wxRichTextApplyStyle(newPara->GetAttributes(), listStyle); // Now we need to do numbering - if (renumber) + // Preserve the existing list item continuation bullet style, if any + if (para->GetAttributes().HasBulletStyle() && (para->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION)) + newPara->GetAttributes().SetBulletStyle(newPara->GetAttributes().GetBulletStyle()|wxTEXT_ATTR_BULLET_STYLE_CONTINUATION); + else { - newPara->GetAttributes().SetBulletNumber(n); - } + // Now we need to do numbering + if (renumber) + { + newPara->GetAttributes().SetBulletNumber(n); + } - n ++; + n ++; + } } else if (!newPara->GetAttributes().GetListStyleName().IsEmpty()) { @@ -3977,6 +4170,10 @@ bool wxRichTextParagraphLayoutBox::DoNumberList(const wxRichTextRange& range, co wxRichTextAttr listStyle(defToUse->GetCombinedStyleForLevel(thisLevel, styleSheet)); wxRichTextApplyStyle(newPara->GetAttributes(), listStyle); + // Preserve the existing list item continuation bullet style, if any + if (para->GetAttributes().HasBulletStyle() && (para->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION)) + newPara->GetAttributes().SetBulletStyle(newPara->GetAttributes().GetBulletStyle()|wxTEXT_ATTR_BULLET_STYLE_CONTINUATION); + // OK, we've (re)applied the style, now let's get the numbering right. if (currentLevel == -1) @@ -4010,7 +4207,8 @@ bool wxRichTextParagraphLayoutBox::DoNumberList(const wxRichTextRange& range, co } else { - levels[currentLevel] ++; + if (!(para->GetAttributes().HasBulletStyle() && (para->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION))) + levels[currentLevel] ++; } newPara->GetAttributes().SetBulletNumber(levels[currentLevel]); @@ -4089,6 +4287,22 @@ bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy, const wxRichTextRa /// position of the paragraph that it had to start looking from. bool wxRichTextParagraphLayoutBox::FindNextParagraphNumber(wxRichTextParagraph* previousParagraph, wxRichTextAttr& attr) const { + // Search for a paragraph that isn't a continuation paragraph (no bullet) + while (previousParagraph && previousParagraph->GetAttributes().HasBulletStyle() && previousParagraph->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION) + { + wxRichTextObjectList::compatibility_iterator node = ((wxRichTextCompositeObject*) previousParagraph->GetParent())->GetChildren().Find(previousParagraph); + if (node) + { + node = node->GetPrevious(); + if (node) + previousParagraph = wxDynamicCast(node->GetData(), wxRichTextParagraph); + else + previousParagraph = NULL; + } + else + previousParagraph = NULL; + } + if (!previousParagraph->GetAttributes().HasFlag(wxTEXT_ATTR_BULLET_STYLE) || previousParagraph->GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE) return false; @@ -4172,7 +4386,7 @@ wxRichTextParagraph::~wxRichTextParagraph() } /// Draw the item -bool wxRichTextParagraph::Draw(wxDC& dc, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int WXUNUSED(descent), int style) +bool wxRichTextParagraph::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int WXUNUSED(descent), int style) { if (!IsShown()) return true; @@ -4182,19 +4396,20 @@ bool wxRichTextParagraph::Draw(wxDC& dc, const wxRichTextRange& range, const wxR // for all paragraphs). But generally box attributes are likely to be // different for different objects. wxRect paraRect = GetRect(); - DrawBoxAttributes(dc, GetBuffer(), GetAttributes(), paraRect); - wxRichTextAttr attr = GetCombinedAttributes(); + context.ApplyVirtualAttributes(attr, this); + + DrawBoxAttributes(dc, GetBuffer(), attr, paraRect); // Draw the bullet, if any - if (attr.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE) + if ((attr.GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE) == 0 && (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION) == 0) { if (attr.GetLeftSubIndent() != 0) { int spaceBeforePara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingBefore()); int leftIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftIndent()); - wxRichTextAttr bulletAttr(GetCombinedAttributes()); + wxRichTextAttr bulletAttr(attr); // Combine with the font of the first piece of content, if one is specified if (GetChildren().GetCount() > 0) @@ -4302,13 +4517,13 @@ bool wxRichTextParagraph::Draw(wxDC& dc, const wxRichTextRange& range, const wxR #endif { int descent = 0; - child->GetRangeSize(objectRange, objectSize, descent, dc, wxRICHTEXT_UNFORMATTED, objectPosition); + child->GetRangeSize(objectRange, objectSize, descent, dc, context, wxRICHTEXT_UNFORMATTED, objectPosition); } } // Use the child object's width, but the whole line's height wxRect childRect(objectPosition, wxSize(objectSize.x, line->GetSize().y)); - child->Draw(dc, objectRange, selection, childRect, maxDescent, style); + child->Draw(dc, context, objectRange, selection, childRect, maxDescent, style); objectPosition.x += objectSize.x; i ++; @@ -4347,16 +4562,17 @@ static int wxRichTextGetRangeWidth(const wxRichTextParagraph& para, const wxRich } /// Lay the item out -bool wxRichTextParagraph::Layout(wxDC& dc, const wxRect& rect, const wxRect& parentRect, int style) +bool wxRichTextParagraph::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style) { // Deal with floating objects firstly before the normal layout wxRichTextBuffer* buffer = GetBuffer(); wxASSERT(buffer); wxRichTextFloatCollector* collector = GetContainer()->GetFloatCollector(); wxASSERT(collector); - LayoutFloat(dc, rect, style, collector); + LayoutFloat(dc, context, rect, style, collector); wxRichTextAttr attr = GetCombinedAttributes(); + context.ApplyVirtualAttributes(attr, this); // ClearLines(); @@ -4411,7 +4627,7 @@ bool wxRichTextParagraph::Layout(wxDC& dc, const wxRect& rect, const wxRect& par int paraDescent = 0; // This calculates the partial text extents - GetRangeSize(GetRange(), paraSize, paraDescent, dc, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, rect.GetPosition(), & partialExtents); + GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, rect.GetPosition(), & partialExtents); #else node = m_children.GetFirst(); while (node) @@ -4419,11 +4635,10 @@ bool wxRichTextParagraph::Layout(wxDC& dc, const wxRect& rect, const wxRect& par wxRichTextObject* child = node->GetData(); //child->SetCachedSize(wxDefaultSize); - child->Layout(dc, rect, style); + child->Layout(dc, context, rect, style); node = node->GetNext(); } - #endif // Split up lines @@ -4486,15 +4701,15 @@ bool wxRichTextParagraph::Layout(wxDC& dc, const wxRect& rect, const wxRect& par // lays out the object again using the minimum size // The position will be determined by its location in its line, // and not by the child's actual position. - child->LayoutToBestSize(dc, buffer, - GetAttributes(), child->GetAttributes(), availableRect, parentRect, style); + child->LayoutToBestSize(dc, context, buffer, + attr, child->GetAttributes(), availableRect, parentRect, style); if (oldSize != child->GetCachedSize()) { partialExtents.Clear(); // Recalculate the partial text extents since the child object changed size - GetRangeSize(GetRange(), paraSize, paraDescent, dc, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, wxPoint(0,0), & partialExtents); + GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, wxPoint(0,0), & partialExtents); } } @@ -4511,10 +4726,10 @@ bool wxRichTextParagraph::Layout(wxDC& dc, const wxRect& rect, const wxRect& par { #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS // Get height only, then the width using the partial extents - GetRangeSize(wxRichTextRange(lastEndPos+1, lastPosToUse), childSize, childDescent, dc, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_HEIGHT_ONLY); + GetRangeSize(wxRichTextRange(lastEndPos+1, lastPosToUse), childSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_HEIGHT_ONLY); childSize.x = wxRichTextGetRangeWidth(*this, wxRichTextRange(lastEndPos+1, lastPosToUse), partialExtents); #else - GetRangeSize(wxRichTextRange(lastEndPos+1, lastPosToUse), childSize, childDescent, dc, wxRICHTEXT_UNFORMATTED, rect.GetPosition()); + GetRangeSize(wxRichTextRange(lastEndPos+1, lastPosToUse), childSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED, rect.GetPosition()); #endif } @@ -4536,13 +4751,22 @@ bool wxRichTextParagraph::Layout(wxDC& dc, const wxRect& rect, const wxRect& par wxRect oldAvailableRect = availableRect; // Available width depends on the floating objects and the line height. - // Note: the floating objects may be placed vertically along the two side of + // Note: the floating objects may be placed vertically along the two sides of // buffer, so we may have different available line widths with different // [startY, endY]. So, we can't determine how wide the available // space is until we know the exact line height. - lineDescent = wxMax(childDescent, maxDescent); - lineAscent = wxMax(childSize.y-childDescent, maxAscent); - lineHeight = lineDescent + lineAscent; + if (childDescent == 0) + { + lineHeight = wxMax(lineHeight, childSize.y); + lineDescent = maxDescent; + lineAscent = maxAscent; + } + else + { + lineDescent = wxMax(childDescent, maxDescent); + lineAscent = wxMax(childSize.y-childDescent, maxAscent); + } + lineHeight = wxMax(lineHeight, (lineDescent + lineAscent)); wxRect floatAvailableRect = collector->GetAvailableRect(rect.y + currentPosition.y, rect.y + currentPosition.y + lineHeight); // Adjust availableRect to the space that is available when taking floating objects into account. @@ -4566,22 +4790,20 @@ bool wxRichTextParagraph::Layout(wxDC& dc, const wxRect& rect, const wxRect& par { wxSize oldSize = child->GetCachedSize(); - //child->SetCachedSize(wxDefaultSize); // Lays out the object first with a given amount of space, and then if no width was specified in attr, // lays out the object again using the minimum size child->Invalidate(wxRICHTEXT_ALL); - child->LayoutToBestSize(dc, buffer, - GetAttributes(), child->GetAttributes(), availableRect, parentRect, style); + child->LayoutToBestSize(dc, context, buffer, + attr, child->GetAttributes(), availableRect, parentRect, style); childSize = child->GetCachedSize(); childDescent = child->GetDescent(); - //child->SetPosition(availableRect.GetPosition()); if (oldSize != child->GetCachedSize()) { partialExtents.Clear(); // Recalculate the partial text extents since the child object changed size - GetRangeSize(GetRange(), paraSize, paraDescent, dc, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, wxPoint(0,0), & partialExtents); + GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, wxPoint(0,0), & partialExtents); } // Go around the loop finding the available rect for the given floating objects @@ -4594,6 +4816,13 @@ bool wxRichTextParagraph::Layout(wxDC& dc, const wxRect& rect, const wxRect& par } while (doLoop); + if (child->IsTopLevel()) + { + // We can move it to the correct position at this point + // TODO: probably need to add margin + child->Move(GetPosition() + wxPoint(currentWidth + (wxMax(leftIndent, leftIndent + leftSubIndent)), currentPosition.y)); + } + // Cases: // 1) There was a line break BEFORE the natural break // 2) There was a line break AFTER the natural break @@ -4608,12 +4837,6 @@ bool wxRichTextParagraph::Layout(wxDC& dc, const wxRect& rect, const wxRect& par ) { - if (child->IsTopLevel()) - { - // We can move it to the correct position at this point - child->Move(GetPosition() + wxPoint(currentWidth, currentPosition.y)); - } - long wrapPosition = 0; if ((childSize.x + currentWidth <= availableRect.width) && !node->GetNext() && !lineBreakInThisObject) wrapPosition = child->GetRange().GetEnd(); @@ -4624,7 +4847,7 @@ bool wxRichTextParagraph::Layout(wxDC& dc, const wxRect& rect, const wxRect& par // Note: one object must contains only one wxTextAtrr, so the line height will not // change inside one object. Thus, we can pass the remain line width to the // FindWrapPosition function. - if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos+1, child->GetRange().GetEnd()), dc, availableRect.width, wrapPosition, & partialExtents)) + if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos+1, child->GetRange().GetEnd()), dc, context, availableRect.width, wrapPosition, & partialExtents)) { // If the function failed, just cut it off at the end of this child. wrapPosition = child->GetRange().GetEnd(); @@ -4636,7 +4859,7 @@ bool wxRichTextParagraph::Layout(wxDC& dc, const wxRect& rect, const wxRect& par // Line end position shouldn't be the same as the end, or greater. if (wrapPosition >= GetRange().GetEnd()) - wrapPosition = GetRange().GetEnd()-1; + wrapPosition = wxMax(0, GetRange().GetEnd()-1); // wxLogDebug(wxT("Split at %ld"), wrapPosition); @@ -4644,25 +4867,29 @@ bool wxRichTextParagraph::Layout(wxDC& dc, const wxRect& rect, const wxRect& par wxSize actualSize; wxRichTextRange actualRange(lastCompletedEndPos+1, wrapPosition); - /// Use previous descent, not the wrapping descent we just found, since this may be too big - /// for the fragment we're about to add. - childDescent = maxDescent; + childDescent = 0; #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS if (!child->IsEmpty()) { // Get height only, then the width using the partial extents - GetRangeSize(actualRange, actualSize, childDescent, dc, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_HEIGHT_ONLY); + GetRangeSize(actualRange, actualSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_HEIGHT_ONLY); actualSize.x = wxRichTextGetRangeWidth(*this, actualRange, partialExtents); } else #endif - GetRangeSize(actualRange, actualSize, childDescent, dc, wxRICHTEXT_UNFORMATTED); + GetRangeSize(actualRange, actualSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED); currentWidth = actualSize.x; - maxDescent = wxMax(childDescent, maxDescent); - maxAscent = wxMax(actualSize.y-childDescent, maxAscent); - lineHeight = maxDescent + maxAscent; + + // The descent for the whole line at this point, is the correct max descent + maxDescent = childDescent; + // Maximum ascent + maxAscent = actualSize.y-childDescent; + + // lineHeight is given by the height for the whole line, since it will + // take into account ascend/descend. + lineHeight = actualSize.y; if (lineHeight == 0 && buffer) { @@ -4698,7 +4925,7 @@ bool wxRichTextParagraph::Layout(wxDC& dc, const wxRect& rect, const wxRect& par lineCount ++; - // TODO: account for zero-length objects, such as fields + // TODO: account for zero-length objects // wxASSERT(wrapPosition > lastCompletedEndPos); lastEndPos = wrapPosition; @@ -4725,9 +4952,20 @@ bool wxRichTextParagraph::Layout(wxDC& dc, const wxRect& rect, const wxRect& par { // We still fit, so don't add a line, and keep going currentWidth += childSize.x; - maxDescent = wxMax(childDescent, maxDescent); - maxAscent = wxMax(childSize.y-childDescent, maxAscent); - lineHeight = maxDescent + maxAscent; + + if (childDescent == 0) + { + // An object with a zero descend value wants to take up the whole + // height regardless of baseline + lineHeight = wxMax(lineHeight, childSize.y); + } + else + { + maxDescent = wxMax(childDescent, maxDescent); + maxAscent = wxMax(childSize.y-childDescent, maxAscent); + } + + lineHeight = wxMax(lineHeight, (maxDescent + maxAscent)); maxWidth = wxMax(maxWidth, currentWidth+startOffset); lastEndPos = child->GetRange().GetEnd(); @@ -4745,7 +4983,7 @@ bool wxRichTextParagraph::Layout(wxDC& dc, const wxRect& rect, const wxRect& par { wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect; contentRect = wxRect(wxPoint(0, 0), wxSize(maxWidth, currentPosition.y + spaceAfterPara)); - GetBoxRects(dc, buffer, GetAttributes(), marginRect, borderRect, contentRect, paddingRect, outlineRect); + GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect); SetCachedSize(marginRect.GetSize()); } @@ -4755,7 +4993,7 @@ bool wxRichTextParagraph::Layout(wxDC& dc, const wxRect& rect, const wxRect& par { wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect; contentRect = wxRect(wxPoint(0, 0), wxSize(paraSize.x + wxMax(leftIndent, leftIndent + leftSubIndent) + rightIndent, currentPosition.y + spaceAfterPara)); - GetBoxRects(dc, buffer, GetAttributes(), marginRect, borderRect, contentRect, paddingRect, outlineRect); + GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect); SetMaxSize(marginRect.GetSize()); } @@ -4772,7 +5010,7 @@ bool wxRichTextParagraph::Layout(wxDC& dc, const wxRect& rect, const wxRect& par // If floating, ignore. We already laid out floats. // Also ignore if empty object, except if we haven't got any // size yet. - if (!child->IsFloating() && child->GetRange().GetLength() != 0 && !child->IsKindOf(CLASSINFO(wxRichTextPlainText))) + if (!child->IsFloating() && child->GetRange().GetLength() != 0 && !wxDynamicCast(child, wxRichTextPlainText)) { if (child->GetCachedSize().x > minWidth) minWidth = child->GetMinSize().x; @@ -4782,11 +5020,10 @@ bool wxRichTextParagraph::Layout(wxDC& dc, const wxRect& rect, const wxRect& par wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect; contentRect = wxRect(wxPoint(0, 0), wxSize(minWidth, currentPosition.y + spaceAfterPara)); - GetBoxRects(dc, buffer, GetAttributes(), marginRect, borderRect, contentRect, paddingRect, outlineRect); + GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect); SetMinSize(marginRect.GetSize()); } - #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING // Use the text extents to calculate the size of each fragment in each line @@ -4840,21 +5077,38 @@ void wxRichTextParagraph::ApplyParagraphStyle(wxRichTextLine* line, const wxRich return; wxPoint pos = line->GetPosition(); + wxPoint originalPos = pos; wxSize size = line->GetSize(); // centering, right-justification - if (attr.HasAlignment() && GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_CENTRE) + if (attr.HasAlignment() && attr.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE) { int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent()); pos.x = (rect.GetWidth() - rightIndent - size.x)/2 + pos.x; line->SetPosition(pos); } - else if (attr.HasAlignment() && GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_RIGHT) + else if (attr.HasAlignment() && attr.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT) { int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent()); pos.x = pos.x + rect.GetWidth() - size.x - rightIndent; line->SetPosition(pos); } + + if (pos != originalPos) + { + wxPoint inc = pos - originalPos; + + wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst(); + + while (node) + { + wxRichTextObject* child = node->GetData(); + if (child->IsTopLevel() && !child->GetRange().IsOutside(line->GetAbsoluteRange())) + child->Move(child->GetPosition() + inc); + + node = node->GetNext(); + } + } } /// Insert text at the given position @@ -4940,7 +5194,7 @@ void wxRichTextParagraph::ClearLines() /// Get/set the object size for the given range. Returns false if the range /// is invalid for this object. -bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position, wxArrayInt* partialExtents) const +bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* partialExtents) const { if (!range.IsWithin(GetRange())) return false; @@ -4948,8 +5202,6 @@ bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange& range, wxSize& siz if (flags & wxRICHTEXT_UNFORMATTED) { // Just use unformatted data, assume no line breaks - // TODO: take into account line breaks - wxSize sz; wxArrayInt childExtents; @@ -4959,10 +5211,13 @@ bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange& range, wxSize& siz else p = NULL; + int maxDescent = 0; + int maxAscent = 0; + int maxLineHeight = 0; + wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst(); while (node) { - wxRichTextObject* child = node->GetData(); if (!child->GetRange().IsOutside(range)) { @@ -4986,36 +5241,61 @@ bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange& range, wxSize& siz wxRichTextRange rangeToUse = range; rangeToUse.LimitTo(child->GetRange()); -#if 0 - if (child->IsTopLevel()) - rangeToUse = child->GetOwnRange(); -#endif int childDescent = 0; - // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we're already cached the size, + // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we've already cached the size, // but it's only going to be used after caching has taken place. if ((flags & wxRICHTEXT_HEIGHT_ONLY) && child->GetCachedSize().y != 0) { childDescent = child->GetDescent(); childSize = child->GetCachedSize(); - sz.y = wxMax(sz.y, childSize.y); + if (childDescent == 0) + { + maxLineHeight = wxMax(maxLineHeight, childSize.y); + } + else + { + maxDescent = wxMax(maxDescent, childDescent); + maxAscent = wxMax(maxAscent, (childSize.y - childDescent)); + } + + maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent)); + + sz.y = wxMax(sz.y, maxLineHeight); sz.x += childSize.x; - descent = wxMax(descent, childDescent); + descent = maxDescent; } else if (child->IsTopLevel()) { childDescent = child->GetDescent(); childSize = child->GetCachedSize(); - sz.y = wxMax(sz.y, childSize.y); + if (childDescent == 0) + { + maxLineHeight = wxMax(maxLineHeight, childSize.y); + } + else + { + maxDescent = wxMax(maxDescent, childDescent); + maxAscent = wxMax(maxAscent, (childSize.y - childDescent)); + } + + maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent)); + + sz.y = wxMax(sz.y, maxLineHeight); sz.x += childSize.x; - descent = wxMax(descent, childDescent); + descent = maxDescent; + + // FIXME: this won't change the original values. + // Should we be calling GetRangeSize above instead of using cached values? +#if 0 if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange())) { child->SetCachedSize(childSize); child->SetDescent(childDescent); } +#endif if (partialExtents) { @@ -5028,11 +5308,23 @@ bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange& range, wxSize& siz partialExtents->Add(childSize.x + lastSize); } } - else if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, flags, wxPoint(position.x + sz.x, position.y), p)) + else if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y), p)) { - sz.y = wxMax(sz.y, childSize.y); + if (childDescent == 0) + { + maxLineHeight = wxMax(maxLineHeight, childSize.y); + } + else + { + maxDescent = wxMax(maxDescent, childDescent); + maxAscent = wxMax(maxAscent, (childSize.y - childDescent)); + } + + maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent)); + + sz.y = wxMax(sz.y, maxLineHeight); sz.x += childSize.x; - descent = wxMax(descent, childDescent); + descent = maxDescent; if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange())) { @@ -5084,7 +5376,10 @@ bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange& range, wxSize& siz wxRichTextRange lineRange = line->GetAbsoluteRange(); if (!lineRange.IsOutside(range)) { - wxSize lineSize; + int maxDescent = 0; + int maxAscent = 0; + int maxLineHeight = 0; + int maxLineWidth = 0; wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst(); while (node2) @@ -5100,20 +5395,33 @@ bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange& range, wxSize& siz wxSize childSize; int childDescent = 0; - if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, flags, wxPoint(position.x + sz.x, position.y))) + if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y))) { - lineSize.y = wxMax(lineSize.y, childSize.y); - lineSize.x += childSize.x; + if (childDescent == 0) + { + // Assume that if descent is zero, this child can occupy the full line height + // and does not need space for the line's maximum descent. So we influence + // the overall max line height only. + maxLineHeight = wxMax(maxLineHeight, childSize.y); + } + else + { + maxAscent = wxMax(maxAscent, (childSize.y - childDescent)); + maxDescent = wxMax(maxAscent, childDescent); + } + maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent)); + maxLineWidth += childSize.x; } - descent = wxMax(descent, childDescent); } node2 = node2->GetNext(); } + descent = wxMax(descent, maxDescent); + // Increase size by a line (TODO: paragraph spacing) - sz.y += lineSize.y; - sz.x = wxMax(sz.x, lineSize.x); + sz.y += maxLineHeight; + sz.x = wxMax(sz.x, maxLineWidth); } node = node->GetNext(); } @@ -5123,7 +5431,7 @@ bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange& range, wxSize& siz } /// Finds the absolute position and row height for the given character position -bool wxRichTextParagraph::FindPosition(wxDC& dc, long index, wxPoint& pt, int* height, bool forceLineStart) +bool wxRichTextParagraph::FindPosition(wxDC& dc, wxRichTextDrawingContext& context, long index, wxPoint& pt, int* height, bool forceLineStart) { if (index == -1) { @@ -5199,7 +5507,7 @@ bool wxRichTextParagraph::FindPosition(wxDC& dc, long index, wxPoint& pt, int* h // then we can add this size to the line start position and // paragraph start position to find the actual position. - if (GetRangeSize(r, rangeSize, descent, dc, wxRICHTEXT_UNFORMATTED, line->GetPosition()+ GetPosition())) + if (GetRangeSize(r, rangeSize, descent, dc, context, wxRICHTEXT_UNFORMATTED, line->GetPosition()+ GetPosition())) { pt.x = line->GetPosition().x + GetPosition().x + rangeSize.x; *height = line->GetSize().y; @@ -5217,7 +5525,7 @@ bool wxRichTextParagraph::FindPosition(wxDC& dc, long index, wxPoint& pt, int* h /// Hit-testing: returns a flag indicating hit test details, plus /// information about position -int wxRichTextParagraph::HitTest(wxDC& dc, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags) +int wxRichTextParagraph::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags) { if (!IsShown()) return wxRICHTEXT_HITTEST_NONE; @@ -5234,7 +5542,7 @@ int wxRichTextParagraph::HitTest(wxDC& dc, const wxPoint& pt, long& textPosition { long tmpPos; wxRichTextObject* tempObj, *tempContextObj; - if (GetParent() && GetParent()->wxRichTextObject::HitTest(dc, pt, tmpPos, & tempObj, & tempContextObj, flags) == wxRICHTEXT_HITTEST_NONE) + if (GetParent() && GetParent()->wxRichTextObject::HitTest(dc, context, pt, tmpPos, & tempObj, & tempContextObj, flags) == wxRICHTEXT_HITTEST_NONE) return wxRICHTEXT_HITTEST_NONE; } @@ -5242,10 +5550,14 @@ int wxRichTextParagraph::HitTest(wxDC& dc, const wxPoint& pt, long& textPosition while (objNode) { wxRichTextObject* child = objNode->GetData(); - if (child->IsTopLevel() && ((flags & wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS) == 0)) + // Don't recurse if we have wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS, + // and also, if this seems composite but actually is marked as atomic, + // don't recurse. + if (child->IsTopLevel() && ((flags & wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS) == 0) && + (! (((flags & wxRICHTEXT_HITTEST_HONOUR_ATOMIC) != 0) && child->IsAtomic()))) { { - int hitTest = child->HitTest(dc, pt, textPosition, obj, contextObj); + int hitTest = child->HitTest(dc, context, pt, textPosition, obj, contextObj); if (hitTest != wxRICHTEXT_HITTEST_NONE) return hitTest; } @@ -5289,7 +5601,7 @@ int wxRichTextParagraph::HitTest(wxDC& dc, const wxPoint& pt, long& textPosition int paraDescent; // This calculates the partial text extents - GetRangeSize(lineRange, paraSize, paraDescent, dc, wxRICHTEXT_UNFORMATTED, linePos, & partialExtents); + GetRangeSize(lineRange, paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED, linePos, & partialExtents); int lastX = linePos.x; size_t i; @@ -5327,7 +5639,7 @@ int wxRichTextParagraph::HitTest(wxDC& dc, const wxPoint& pt, long& textPosition wxRichTextRange rangeToUse(lineRange.GetStart(), i); - GetRangeSize(rangeToUse, childSize, descent, dc, wxRICHTEXT_UNFORMATTED, linePos); + GetRangeSize(rangeToUse, childSize, descent, dc, context, wxRICHTEXT_UNFORMATTED, linePos); int nextX = childSize.x + linePos.x; @@ -5529,7 +5841,7 @@ bool wxRichTextParagraph::GetContiguousPlainText(wxString& text, const wxRichTex } /// Find a suitable wrap position. -bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange& range, wxDC& dc, int availableSpace, long& wrapPosition, wxArrayInt* partialExtents) +bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange& range, wxDC& dc, wxRichTextDrawingContext& context, int availableSpace, long& wrapPosition, wxArrayInt* partialExtents) { if (range.GetLength() <= 0) return false; @@ -5571,7 +5883,7 @@ bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange& range, wxDC& d if (minPos == maxPos) { int descent = 0; - GetRangeSize(wxRichTextRange(range.GetStart(), minPos), sz, descent, dc, wxRICHTEXT_UNFORMATTED); + GetRangeSize(wxRichTextRange(range.GetStart(), minPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED); if (sz.x > availableSpace) breakPosition = minPos - 1; @@ -5580,13 +5892,13 @@ bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange& range, wxDC& d else if ((maxPos - minPos) == 1) { int descent = 0; - GetRangeSize(wxRichTextRange(range.GetStart(), minPos), sz, descent, dc, wxRICHTEXT_UNFORMATTED); + GetRangeSize(wxRichTextRange(range.GetStart(), minPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED); if (sz.x > availableSpace) breakPosition = minPos - 1; else { - GetRangeSize(wxRichTextRange(range.GetStart(), maxPos), sz, descent, dc, wxRICHTEXT_UNFORMATTED); + GetRangeSize(wxRichTextRange(range.GetStart(), maxPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED); if (sz.x > availableSpace) breakPosition = maxPos-1; } @@ -5597,7 +5909,7 @@ bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange& range, wxDC& d long nextPos = minPos + ((maxPos - minPos) / 2); int descent = 0; - GetRangeSize(wxRichTextRange(range.GetStart(), nextPos), sz, descent, dc, wxRICHTEXT_UNFORMATTED); + GetRangeSize(wxRichTextRange(range.GetStart(), nextPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED); if (sz.x > availableSpace) { @@ -5800,7 +6112,7 @@ void wxRichTextParagraph::ClearDefaultTabs() sm_defaultTabs.Clear(); } -void wxRichTextParagraph::LayoutFloat(wxDC& dc, const wxRect& rect, int style, wxRichTextFloatCollector* floatCollector) +void wxRichTextParagraph::LayoutFloat(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, int style, wxRichTextFloatCollector* floatCollector) { wxRichTextObjectList::compatibility_iterator node = GetChildren().GetFirst(); while (node) @@ -5810,7 +6122,7 @@ void wxRichTextParagraph::LayoutFloat(wxDC& dc, const wxRect& rect, int style, w { wxSize size; int descent, x = 0; - anchored->GetRangeSize(anchored->GetRange(), size, descent, dc, style); + anchored->GetRangeSize(anchored->GetRange(), size, descent, dc, context, style); int offsetY = 0; if (anchored->GetAttributes().GetTextBoxAttr().GetTop().IsValid()) @@ -5939,12 +6251,13 @@ wxRichTextPlainText::wxRichTextPlainText(const wxString& text, wxRichTextObject* #define WIDTH_FOR_DEFAULT_TABS 50 /// Draw the item -bool wxRichTextPlainText::Draw(wxDC& dc, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int WXUNUSED(style)) +bool wxRichTextPlainText::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int WXUNUSED(style)) { wxRichTextParagraph* para = wxDynamicCast(GetParent(), wxRichTextParagraph); wxASSERT (para != NULL); wxRichTextAttr textAttr(para ? para->GetCombinedAttributes(GetAttributes(), false /* no box attributes */) : GetAttributes()); + context.ApplyVirtualAttributes(textAttr, this); // Let's make the assumption for now that for content in a paragraph, including // text, we never have a discontinuous selection. So we only deal with a @@ -5967,7 +6280,7 @@ bool wxRichTextPlainText::Draw(wxDC& dc, const wxRichTextRange& range, const wxR wxString str = m_text; wxString toRemove = wxRichTextLineBreakChar; str.Replace(toRemove, wxT(" ")); - if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS)) + if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & (wxTEXT_ATTR_EFFECT_CAPITALS|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS))) str.MakeUpper(); long len = range.GetLength(); @@ -5983,21 +6296,49 @@ bool wxRichTextPlainText::Draw(wxDC& dc, const wxRichTextRange& range, const wxR int x, y; if ( textFont.IsOk() ) { + if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SMALL_CAPITALS)) + { + textFont.SetPointSize((int) (textFont.GetPointSize()*0.75)); + wxCheckSetFont(dc, textFont); + charHeight = dc.GetCharHeight(); + } + if ( textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT) ) { - double size = static_cast(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR; - textFont.SetPointSize( static_cast(size) ); - x = rect.x; - y = rect.y; + if (textFont.IsUsingSizeInPixels()) + { + double size = static_cast(textFont.GetPixelSize().y) / wxSCRIPT_MUL_FACTOR; + textFont.SetPixelSize(wxSize(0, static_cast(size))); + x = rect.x; + y = rect.y; + } + else + { + double size = static_cast(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR; + textFont.SetPointSize(static_cast(size)); + x = rect.x; + y = rect.y; + } wxCheckSetFont(dc, textFont); } else if ( textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT) ) { - double size = static_cast(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR; - textFont.SetPointSize( static_cast(size) ); - x = rect.x; - int sub_height = static_cast( static_cast(charHeight) / wxSCRIPT_MUL_FACTOR); - y = rect.y + (rect.height - sub_height + (descent - m_descent)); + if (textFont.IsUsingSizeInPixels()) + { + double size = static_cast(textFont.GetPixelSize().y) / wxSCRIPT_MUL_FACTOR; + textFont.SetPixelSize(wxSize(0, static_cast(size))); + x = rect.x; + int sub_height = static_cast(static_cast(charHeight) / wxSCRIPT_MUL_FACTOR); + y = rect.y + (rect.height - sub_height + (descent - m_descent)); + } + else + { + double size = static_cast(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR; + textFont.SetPointSize(static_cast(size)); + x = rect.x; + int sub_height = static_cast(static_cast(charHeight) / wxSCRIPT_MUL_FACTOR); + y = rect.y + (rect.height - sub_height + (descent - m_descent)); + } wxCheckSetFont(dc, textFont); } else @@ -6236,16 +6577,16 @@ bool wxRichTextPlainText::DrawTabbedString(wxDC& dc, const wxRichTextAttr& attr, x += w; } - return true; + return true; } /// Lay the item out -bool wxRichTextPlainText::Layout(wxDC& dc, const wxRect& WXUNUSED(rect), const wxRect& WXUNUSED(parentRect), int WXUNUSED(style)) +bool wxRichTextPlainText::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& WXUNUSED(rect), const wxRect& WXUNUSED(parentRect), int WXUNUSED(style)) { // Only lay out if we haven't already cached the size if (m_size.x == -1) - GetRangeSize(GetRange(), m_size, m_descent, dc, 0, wxPoint(0, 0)); + GetRangeSize(GetRange(), m_size, m_descent, dc, context, 0, wxPoint(0, 0)); m_maxSize = m_size; // Eventually we want to have a reasonable estimate of minimum size. m_minSize = wxSize(0, 0); @@ -6262,7 +6603,7 @@ void wxRichTextPlainText::Copy(const wxRichTextPlainText& obj) /// Get/set the object size for the given range. Returns false if the range /// is invalid for this object. -bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int WXUNUSED(flags), wxPoint position, wxArrayInt* partialExtents) const +bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int WXUNUSED(flags), wxPoint position, wxArrayInt* partialExtents) const { if (!range.IsWithin(GetRange())) return false; @@ -6273,6 +6614,7 @@ bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange& range, wxSize& siz int relativeX = position.x - GetParent()->GetPosition().x; wxRichTextAttr textAttr(para ? para->GetCombinedAttributes(GetAttributes()) : GetAttributes()); + context.ApplyVirtualAttributes(textAttr, (wxRichTextObject*) this); // Always assume unformatted text, since at this level we have no knowledge // of line breaks - and we don't need it, since we'll calculate size within @@ -6286,8 +6628,23 @@ bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange& range, wxSize& siz || (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT) ) ) { wxFont textFont = font; - double size = static_cast(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR; - textFont.SetPointSize( static_cast(size) ); + if (textFont.IsUsingSizeInPixels()) + { + double size = static_cast(textFont.GetPixelSize().y) / wxSCRIPT_MUL_FACTOR; + textFont.SetPixelSize(wxSize(0, static_cast(size))); + } + else + { + double size = static_cast(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR; + textFont.SetPointSize(static_cast(size)); + } + wxCheckSetFont(dc, textFont); + bScript = true; + } + else if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SMALL_CAPITALS)) + { + wxFont textFont = font; + textFont.SetPointSize((int) (textFont.GetPointSize()*0.75)); wxCheckSetFont(dc, textFont); bScript = true; } @@ -6307,7 +6664,7 @@ bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange& range, wxSize& siz wxString stringChunk = str.Mid(startPos, (size_t) len); - if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS)) + if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & (wxTEXT_ATTR_EFFECT_CAPITALS|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS))) stringChunk.MakeUpper(); wxCoord w, h; @@ -6459,6 +6816,7 @@ wxRichTextObject* wxRichTextPlainText::DoSplit(long pos) wxRichTextPlainText* newObject = new wxRichTextPlainText(secondPart); newObject->SetAttributes(GetAttributes()); + newObject->SetProperties(GetProperties()); newObject->SetRange(wxRichTextRange(pos, GetRange().GetEnd())); GetRange().SetEnd(pos-1); @@ -6509,8 +6867,8 @@ wxString wxRichTextPlainText::GetTextForRange(const wxRichTextRange& range) cons /// Returns true if this object can merge itself with the given one. bool wxRichTextPlainText::CanMerge(wxRichTextObject* object) const { - return object->GetClassInfo() == CLASSINFO(wxRichTextPlainText) && - (m_text.empty() || wxTextAttrEq(GetAttributes(), object->GetAttributes())); + return object->GetClassInfo() == wxCLASSINFO(wxRichTextPlainText) && + (m_text.empty() || (wxTextAttrEq(GetAttributes(), object->GetAttributes()) && m_properties == object->GetProperties())); } /// Returns true if this object merged itself with the given one. @@ -6561,10 +6919,12 @@ long wxRichTextPlainText::GetFirstLineBreakPosition(long pos) IMPLEMENT_DYNAMIC_CLASS(wxRichTextBuffer, wxRichTextParagraphLayoutBox) -wxList wxRichTextBuffer::sm_handlers; -wxRichTextRenderer* wxRichTextBuffer::sm_renderer = NULL; -int wxRichTextBuffer::sm_bulletRightMargin = 20; -float wxRichTextBuffer::sm_bulletProportion = (float) 0.3; +wxList wxRichTextBuffer::sm_handlers; +wxList wxRichTextBuffer::sm_drawingHandlers; +wxRichTextFieldTypeHashMap wxRichTextBuffer::sm_fieldTypes; +wxRichTextRenderer* wxRichTextBuffer::sm_renderer = NULL; +int wxRichTextBuffer::sm_bulletRightMargin = 20; +float wxRichTextBuffer::sm_bulletProportion = (float) 0.3; /// Initialisation void wxRichTextBuffer::Init() @@ -6577,6 +6937,9 @@ void wxRichTextBuffer::Init() m_suppressUndo = 0; m_handlerFlags = 0; m_scale = 1.0; + m_dimensionScale = 1.0; + m_fontScale = 1.0; + SetMargins(4); } /// Initialisation @@ -6611,6 +6974,8 @@ void wxRichTextBuffer::Copy(const wxRichTextBuffer& obj) m_batchedCommand = NULL; m_suppressUndo = obj.m_suppressUndo; m_invalidRange = obj.m_invalidRange; + m_dimensionScale = obj.m_dimensionScale; + m_fontScale = obj.m_fontScale; } /// Push style sheet to top of stack @@ -6731,6 +7096,8 @@ bool wxRichTextParagraphLayoutBox::InsertNewlineWithUndo(wxRichTextBuffer* buffe } wxRichTextAttr attr(buffer->GetDefaultStyle()); + // Don't include box attributes such as margins + attr.GetTextBoxAttr().Reset(); wxRichTextParagraph* newPara = new wxRichTextParagraph(wxEmptyString, this, & attr); action->GetNewParagraphs().AppendChild(newPara); @@ -6758,13 +7125,13 @@ bool wxRichTextParagraphLayoutBox::InsertNewlineWithUndo(wxRichTextBuffer* buffe action->SetPosition(pos); - // Use the default character style // Use the default character style if (!buffer->GetDefaultStyle().IsDefault() && newPara->GetChildren().GetFirst()) { // Check whether the default style merely reflects the paragraph/basic style, // in which case don't apply it. wxRichTextAttr defaultStyle(buffer->GetDefaultStyle()); + defaultStyle.GetTextBoxAttr().Reset(); wxRichTextAttr toApply; if (para) { @@ -6814,6 +7181,9 @@ bool wxRichTextParagraphLayoutBox::InsertImageWithUndo(wxRichTextBuffer* buffer, wxRichTextAttr attr(buffer->GetDefaultStyle()); + // Don't include box attributes such as margins + attr.GetTextBoxAttr().Reset(); + wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr); if (p) newPara->SetAttributes(*p); @@ -6858,6 +7228,9 @@ wxRichTextObject* wxRichTextParagraphLayoutBox::InsertObjectWithUndo(wxRichTextB wxRichTextAttr attr(buffer->GetDefaultStyle()); + // Don't include box attributes such as margins + attr.GetTextBoxAttr().Reset(); + wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr); if (p) newPara->SetAttributes(*p); @@ -6879,6 +7252,50 @@ wxRichTextObject* wxRichTextParagraphLayoutBox::InsertObjectWithUndo(wxRichTextB return obj; } +wxRichTextField* wxRichTextParagraphLayoutBox::InsertFieldWithUndo(wxRichTextBuffer* buffer, long pos, const wxString& fieldType, + const wxRichTextProperties& properties, + wxRichTextCtrl* ctrl, int flags, + const wxRichTextAttr& textAttr) +{ + wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Field"), wxRICHTEXT_INSERT, buffer, this, ctrl, false); + + wxRichTextAttr* p = NULL; + wxRichTextAttr paraAttr; + if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE) + { + paraAttr = GetStyleForNewParagraph(buffer, pos); + if (!paraAttr.IsDefault()) + p = & paraAttr; + } + + wxRichTextAttr attr(buffer->GetDefaultStyle()); + + // Don't include box attributes such as margins + attr.GetTextBoxAttr().Reset(); + + wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr); + if (p) + newPara->SetAttributes(*p); + + wxRichTextField* fieldObject = new wxRichTextField(); + fieldObject->wxRichTextObject::SetProperties(properties); + fieldObject->SetFieldType(fieldType); + fieldObject->SetAttributes(textAttr); + newPara->AppendChild(fieldObject); + action->GetNewParagraphs().AppendChild(newPara); + action->GetNewParagraphs().UpdateRanges(); + action->GetNewParagraphs().SetPartialParagraph(true); + action->SetPosition(pos); + + // Set the range we'll need to delete in Undo + action->SetRange(wxRichTextRange(pos, pos)); + + buffer->SubmitAction(action); + + wxRichTextField* obj = wxDynamicCast(GetLeafObjectAtPosition(pos), wxRichTextField); + return obj; +} + /// Get the style that is appropriate for a new paragraph at this position. /// If the previous paragraph has a paragraph style name, look up the next-paragraph /// style. @@ -7030,6 +7447,9 @@ bool wxRichTextBuffer::EndBatchUndo() /// Submit immediately, or delay according to whether collapsing is on bool wxRichTextBuffer::SubmitAction(wxRichTextAction* action) { + if (action && !action->GetNewParagraphs().IsEmpty()) + PrepareContent(action->GetNewParagraphs()); + if (BatchingUndo() && m_batchedCommand && !SuppressingUndo()) { wxRichTextCommand* cmd = new wxRichTextCommand(action->GetName()); @@ -7072,9 +7492,10 @@ bool wxRichTextBuffer::EndSuppressUndo() bool wxRichTextBuffer::BeginStyle(const wxRichTextAttr& style) { wxRichTextAttr newStyle(GetDefaultStyle()); + newStyle.GetTextBoxAttr().Reset(); // Save the old default style - m_attributeStack.Append((wxObject*) new wxRichTextAttr(GetDefaultStyle())); + m_attributeStack.Append((wxObject*) new wxRichTextAttr(newStyle)); wxRichTextApplyStyle(newStyle, style); newStyle.SetFlags(style.GetFlags()|newStyle.GetFlags()); @@ -7806,9 +8227,9 @@ void wxRichTextBuffer::SetRenderer(wxRichTextRenderer* renderer) /// Hit-testing: returns a flag indicating hit test details, plus /// information about position -int wxRichTextBuffer::HitTest(wxDC& dc, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags) +int wxRichTextBuffer::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags) { - int ret = wxRichTextParagraphLayoutBox::HitTest(dc, pt, textPosition, obj, contextObj, flags); + int ret = wxRichTextParagraphLayoutBox::HitTest(dc, context, pt, textPosition, obj, contextObj, flags); if (ret != wxRICHTEXT_HITTEST_NONE) { return ret; @@ -7822,6 +8243,17 @@ int wxRichTextBuffer::HitTest(wxDC& dc, const wxPoint& pt, long& textPosition, w } } +void wxRichTextBuffer::SetFontScale(double fontScale) +{ + m_fontScale = fontScale; + m_fontTable.SetFontScale(fontScale); +} + +void wxRichTextBuffer::SetDimensionScale(double dimScale) +{ + m_dimensionScale = dimScale; +} + bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph* paragraph, wxDC& dc, const wxRichTextAttr& bulletAttr, const wxRect& rect) { if (bulletAttr.GetTextColour().IsOk()) @@ -7910,7 +8342,10 @@ bool wxRichTextStdRenderer::DrawTextBullet(wxRichTextParagraph* paragraph, wxDC& if ((attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL) && !attr.GetBulletFont().IsEmpty() && attr.HasFont()) { wxRichTextAttr fontAttr; - fontAttr.SetFontSize(attr.GetFontSize()); + if (attr.HasFontPixelSize()) + fontAttr.SetFontPixelSize(attr.GetFontSize()); + else + fontAttr.SetFontPointSize(attr.GetFontSize()); fontAttr.SetFontStyle(attr.GetFontStyle()); fontAttr.SetFontWeight(attr.GetFontWeight()); fontAttr.SetFontUnderlined(attr.GetFontUnderlined()); @@ -7985,7 +8420,7 @@ wxRichTextBox::wxRichTextBox(wxRichTextObject* parent): } /// Draw the item -bool wxRichTextBox::Draw(wxDC& dc, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style) +bool wxRichTextBox::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style) { if (!IsShown()) return true; @@ -7995,7 +8430,7 @@ bool wxRichTextBox::Draw(wxDC& dc, const wxRichTextRange& range, const wxRichTex // so we can apply commands (properties, delete, ...) to objects such as text boxes and images. // Ultimately we would like to be able to interactively resize an active object // using drag handles. - return wxRichTextParagraphLayoutBox::Draw(dc, range, selection, rect, descent, style); + return wxRichTextParagraphLayoutBox::Draw(dc, context, range, selection, rect, descent, style); } /// Copy @@ -8021,6 +8456,345 @@ bool wxRichTextBox::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer) return false; } +/*! + * wxRichTextField + */ + +IMPLEMENT_DYNAMIC_CLASS(wxRichTextField, wxRichTextParagraphLayoutBox) + +wxRichTextField::wxRichTextField(const wxString& fieldType, wxRichTextObject* parent): + wxRichTextParagraphLayoutBox(parent) +{ + SetFieldType(fieldType); +} + +/// Draw the item +bool wxRichTextField::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style) +{ + if (!IsShown()) + return true; + + wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType()); + if (fieldType && fieldType->Draw(this, dc, context, range, selection, rect, descent, style)) + return true; + + // Fallback; but don't draw guidelines. + style &= ~wxRICHTEXT_DRAW_GUIDELINES; + return wxRichTextParagraphLayoutBox::Draw(dc, context, range, selection, rect, descent, style); +} + +bool wxRichTextField::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style) +{ + wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType()); + if (fieldType && fieldType->Layout(this, dc, context, rect, parentRect, style)) + return true; + + // Fallback + return wxRichTextParagraphLayoutBox::Layout(dc, context, rect, parentRect, style); +} + +bool wxRichTextField::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* partialExtents) const +{ + wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType()); + if (fieldType) + return fieldType->GetRangeSize((wxRichTextField*) this, range, size, descent, dc, context, flags, position, partialExtents); + + return wxRichTextParagraphLayoutBox::GetRangeSize(range, size, descent, dc, context, flags, position, partialExtents); +} + +/// Calculate range +void wxRichTextField::CalculateRange(long start, long& end) +{ + if (IsTopLevel()) + wxRichTextParagraphLayoutBox::CalculateRange(start, end); + else + wxRichTextObject::CalculateRange(start, end); +} + +/// Copy +void wxRichTextField::Copy(const wxRichTextField& obj) +{ + wxRichTextParagraphLayoutBox::Copy(obj); + + UpdateField(GetBuffer()); +} + +// Edit properties via a GUI +bool wxRichTextField::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer) +{ + wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType()); + if (fieldType) + return fieldType->EditProperties(this, parent, buffer); + + return false; +} + +bool wxRichTextField::CanEditProperties() const +{ + wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType()); + if (fieldType) + return fieldType->CanEditProperties((wxRichTextField*) this); + + return false; +} + +wxString wxRichTextField::GetPropertiesMenuLabel() const +{ + wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType()); + if (fieldType) + return fieldType->GetPropertiesMenuLabel((wxRichTextField*) this); + + return wxEmptyString; +} + +bool wxRichTextField::UpdateField(wxRichTextBuffer* buffer) +{ + wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType()); + if (fieldType) + return fieldType->UpdateField(buffer, (wxRichTextField*) this); + + return false; +} + +bool wxRichTextField::IsTopLevel() const +{ + wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType()); + if (fieldType) + return fieldType->IsTopLevel((wxRichTextField*) this); + + return true; +} + +IMPLEMENT_CLASS(wxRichTextFieldType, wxObject) + +IMPLEMENT_CLASS(wxRichTextFieldTypeStandard, wxRichTextFieldType) + +wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString& name, const wxString& label, int displayStyle) +{ + Init(); + + SetName(name); + SetLabel(label); + SetDisplayStyle(displayStyle); +} + +wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString& name, const wxBitmap& bitmap, int displayStyle) +{ + Init(); + + SetName(name); + SetBitmap(bitmap); + SetDisplayStyle(displayStyle); +} + +void wxRichTextFieldTypeStandard::Init() +{ + m_displayStyle = wxRICHTEXT_FIELD_STYLE_RECTANGLE; + m_font = wxFont(6, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL); + m_textColour = *wxWHITE; + m_borderColour = *wxBLACK; + m_backgroundColour = *wxBLACK; + m_verticalPadding = 1; + m_horizontalPadding = 3; + m_horizontalMargin = 2; + m_verticalMargin = 0; +} + +void wxRichTextFieldTypeStandard::Copy(const wxRichTextFieldTypeStandard& field) +{ + wxRichTextFieldType::Copy(field); + + m_label = field.m_label; + m_displayStyle = field.m_displayStyle; + m_font = field.m_font; + m_textColour = field.m_textColour; + m_borderColour = field.m_borderColour; + m_backgroundColour = field.m_backgroundColour; + m_verticalPadding = field.m_verticalPadding; + m_horizontalPadding = field.m_horizontalPadding; + m_horizontalMargin = field.m_horizontalMargin; + m_bitmap = field.m_bitmap; +} + +bool wxRichTextFieldTypeStandard::Draw(wxRichTextField* obj, wxDC& dc, wxRichTextDrawingContext& WXUNUSED(context), const wxRichTextRange& WXUNUSED(range), const wxRichTextSelection& selection, const wxRect& rect, int descent, int WXUNUSED(style)) +{ + if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_COMPOSITE) + return false; // USe default composite drawing + else // if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_RECTANGLE || m_displayStyle == wxRICHTEXT_FIELD_STYLE_NOBORDER) + { + int borderSize = 1; + + wxPen borderPen(m_borderColour, 1, wxSOLID); + wxBrush backgroundBrush(m_backgroundColour); + wxColour textColour(m_textColour); + + if (selection.WithinSelection(obj->GetRange().GetStart(), obj)) + { + wxColour highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT)); + wxColour highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT)); + + borderPen = wxPen(highlightTextColour, 1, wxSOLID); + backgroundBrush = wxBrush(highlightColour); + + wxCheckSetBrush(dc, backgroundBrush); + wxCheckSetPen(dc, wxPen(highlightColour, 1, wxSOLID)); + dc.DrawRectangle(rect); + } + + if (m_displayStyle != wxRICHTEXT_FIELD_STYLE_NO_BORDER) + borderSize = 0; + + // objectRect is the area where the content is drawn, after margins around it have been taken into account + wxRect objectRect = wxRect(wxPoint(rect.x + m_horizontalMargin, rect.y + wxMax(0, rect.height - descent - obj->GetCachedSize().y)), + wxSize(obj->GetCachedSize().x - 2*m_horizontalMargin - borderSize, obj->GetCachedSize().y)); + + // clientArea is where the text is actually written + wxRect clientArea = objectRect; + + if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_RECTANGLE) + { + dc.SetPen(borderPen); + dc.SetBrush(backgroundBrush); + dc.DrawRoundedRectangle(objectRect, 4.0); + } + else if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_START_TAG) + { + int arrowLength = objectRect.height/2; + clientArea.width -= (arrowLength - m_horizontalPadding); + + wxPoint pts[5]; + pts[0].x = objectRect.x; pts[0].y = objectRect.y; + pts[1].x = objectRect.x + objectRect.width - arrowLength; pts[1].y = objectRect.y; + pts[2].x = objectRect.x + objectRect.width; pts[2].y = objectRect.y + (objectRect.height/2); + pts[3].x = objectRect.x + objectRect.width - arrowLength; pts[3].y = objectRect.y + objectRect.height; + pts[4].x = objectRect.x; pts[4].y = objectRect.y + objectRect.height; + dc.SetPen(borderPen); + dc.SetBrush(backgroundBrush); + dc.DrawPolygon(5, pts); + } + else if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_END_TAG) + { + int arrowLength = objectRect.height/2; + clientArea.width -= (arrowLength - m_horizontalPadding); + clientArea.x += (arrowLength - m_horizontalPadding); + + wxPoint pts[5]; + pts[0].x = objectRect.x + objectRect.width; pts[0].y = objectRect.y; + pts[1].x = objectRect.x + arrowLength; pts[1].y = objectRect.y; + pts[2].x = objectRect.x; pts[2].y = objectRect.y + (objectRect.height/2); + pts[3].x = objectRect.x + arrowLength; pts[3].y = objectRect.y + objectRect.height; + pts[4].x = objectRect.x + objectRect.width; pts[4].y = objectRect.y + objectRect.height; + dc.SetPen(borderPen); + dc.SetBrush(backgroundBrush); + dc.DrawPolygon(5, pts); + } + + if (m_bitmap.IsOk()) + { + int x = clientArea.x + (clientArea.width - m_bitmap.GetWidth())/2; + int y = clientArea.y + m_verticalPadding; + dc.DrawBitmap(m_bitmap, x, y, true); + + if (selection.WithinSelection(obj->GetRange().GetStart(), obj)) + { + wxCheckSetBrush(dc, *wxBLACK_BRUSH); + wxCheckSetPen(dc, *wxBLACK_PEN); + dc.SetLogicalFunction(wxINVERT); + dc.DrawRectangle(wxRect(x, y, m_bitmap.GetWidth(), m_bitmap.GetHeight())); + dc.SetLogicalFunction(wxCOPY); + } + } + else + { + wxString label(m_label); + if (label.IsEmpty()) + label = wxT("??"); + int w, h, maxDescent; + dc.SetFont(m_font); + dc.GetTextExtent(m_label, & w, &h, & maxDescent); + dc.SetTextForeground(textColour); + + int x = clientArea.x + (clientArea.width - w)/2; + int y = clientArea.y + (clientArea.height - (h - maxDescent))/2; + dc.DrawText(m_label, x, y); + } + } + + return true; +} + +bool wxRichTextFieldTypeStandard::Layout(wxRichTextField* obj, wxDC& dc, wxRichTextDrawingContext& context, const wxRect& WXUNUSED(rect), const wxRect& WXUNUSED(parentRect), int style) +{ + if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_COMPOSITE) + return false; // USe default composite layout + + wxSize size = GetSize(obj, dc, context, style); + obj->SetCachedSize(size); + obj->SetMinSize(size); + obj->SetMaxSize(size); + return true; +} + +bool wxRichTextFieldTypeStandard::GetRangeSize(wxRichTextField* obj, const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* partialExtents) const +{ + if (IsTopLevel(obj)) + return obj->wxRichTextParagraphLayoutBox::GetRangeSize(range, size, descent, dc, context, flags, position); + else + { + wxSize sz = GetSize(obj, dc, context, 0); + if (partialExtents) + { + int lastSize; + if (partialExtents->GetCount() > 0) + lastSize = (*partialExtents)[partialExtents->GetCount()-1]; + else + lastSize = 0; + partialExtents->Add(lastSize + sz.x); + } + size = sz; + return true; + } +} + +wxSize wxRichTextFieldTypeStandard::GetSize(wxRichTextField* WXUNUSED(obj), wxDC& dc, wxRichTextDrawingContext& WXUNUSED(context), int WXUNUSED(style)) const +{ + int borderSize = 1; + int w = 0, h = 0, maxDescent = 0; + + wxSize sz; + if (m_bitmap.IsOk()) + { + w = m_bitmap.GetWidth(); + h = m_bitmap.GetHeight(); + + sz = wxSize(w + m_horizontalMargin*2, h + m_verticalMargin*2); + } + else + { + wxString label(m_label); + if (label.IsEmpty()) + label = wxT("??"); + dc.SetFont(m_font); + dc.GetTextExtent(label, & w, &h, & maxDescent); + + sz = wxSize(w + m_horizontalPadding*2 + m_horizontalMargin*2, h + m_verticalPadding *2 + m_verticalMargin*2); + } + + if (m_displayStyle != wxRICHTEXT_FIELD_STYLE_NO_BORDER) + { + sz.x += borderSize*2; + sz.y += borderSize*2; + } + + if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_START_TAG || m_displayStyle == wxRICHTEXT_FIELD_STYLE_END_TAG) + { + // Add space for the arrow + sz.x += (sz.y/2 - m_horizontalPadding); + } + + return sz; +} + IMPLEMENT_DYNAMIC_CLASS(wxRichTextCell, wxRichTextBox) wxRichTextCell::wxRichTextCell(wxRichTextObject* parent): @@ -8029,9 +8803,9 @@ wxRichTextCell::wxRichTextCell(wxRichTextObject* parent): } /// Draw the item -bool wxRichTextCell::Draw(wxDC& dc, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style) +bool wxRichTextCell::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style) { - return wxRichTextBox::Draw(dc, range, selection, rect, descent, style); + return wxRichTextBox::Draw(dc, context, range, selection, rect, descent, style); } /// Copy @@ -8085,7 +8859,7 @@ bool wxRichTextCell::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer) wxRichTextObjectPropertiesDialog cellDlg(this, wxGetTopLevelParent(parent), wxID_ANY, caption); cellDlg.SetAttributes(attr); - wxRichTextSizePage* sizePage = wxDynamicCast(cellDlg.FindPage(CLASSINFO(wxRichTextSizePage)), wxRichTextSizePage); + wxRichTextSizePage* sizePage = wxDynamicCast(cellDlg.FindPage(wxCLASSINFO(wxRichTextSizePage)), wxRichTextSizePage); if (sizePage) { // We don't want position and floating controls for a cell. @@ -8125,9 +8899,9 @@ wxRichTextTable::wxRichTextTable(wxRichTextObject* parent): wxRichTextBox(parent } // Draws the object. -bool wxRichTextTable::Draw(wxDC& dc, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style) +bool wxRichTextTable::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style) { - return wxRichTextBox::Draw(dc, range, selection, rect, descent, style); + return wxRichTextBox::Draw(dc, context, range, selection, rect, descent, style); } WX_DECLARE_OBJARRAY(wxRect, wxRichTextRectArray); @@ -8138,7 +8912,7 @@ WX_DEFINE_OBJARRAY(wxRichTextRectArray); // layout to a particular size, or it could be the total space available in the // parent. rect is the overall size, so we must subtract margins and padding. // to get the actual available space. -bool wxRichTextTable::Layout(wxDC& dc, const wxRect& rect, const wxRect& WXUNUSED(parentRect), int style) +bool wxRichTextTable::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& WXUNUSED(parentRect), int style) { SetPosition(rect.GetPosition()); @@ -8150,17 +8924,20 @@ bool wxRichTextTable::Layout(wxDC& dc, const wxRect& rect, const wxRect& WXUNUSE wxRichTextBuffer* buffer = GetBuffer(); if (buffer) scale = buffer->GetScale(); - wxRect availableSpace = GetAvailableContentArea(dc, rect); + wxRect availableSpace = GetAvailableContentArea(dc, context, rect); wxTextAttrDimensionConverter converter(dc, scale, availableSpace.GetSize()); + wxRichTextAttr attr(GetAttributes()); + context.ApplyVirtualAttributes(attr, this); + // If we have no fixed table size, and assuming we're not pushed for // space, then we don't have to try to stretch the table to fit the contents. bool stretchToFitTableWidth = false; int tableWidth = rect.width; - if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid()) + if (attr.GetTextBoxAttr().GetWidth().IsValid()) { - tableWidth = converter.GetPixels(GetAttributes().GetTextBoxAttr().GetWidth()); + tableWidth = converter.GetPixels(attr.GetTextBoxAttr().GetWidth()); // Fixed table width, so we do want to stretch columns out if necessary. stretchToFitTableWidth = true; @@ -8171,17 +8948,17 @@ bool wxRichTextTable::Layout(wxDC& dc, const wxRect& rect, const wxRect& WXUNUSE // Get internal padding int paddingLeft = 0, paddingTop = 0; - if (GetAttributes().GetTextBoxAttr().GetPadding().GetLeft().IsValid()) - paddingLeft = converter.GetPixels(GetAttributes().GetTextBoxAttr().GetPadding().GetLeft()); - if (GetAttributes().GetTextBoxAttr().GetPadding().GetTop().IsValid()) - paddingTop = converter.GetPixels(GetAttributes().GetTextBoxAttr().GetPadding().GetTop()); + if (attr.GetTextBoxAttr().GetPadding().GetLeft().IsValid()) + paddingLeft = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetLeft()); + if (attr.GetTextBoxAttr().GetPadding().GetTop().IsValid()) + paddingTop = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetTop()); // Assume that left and top padding are also used for inter-cell padding. int paddingX = paddingLeft; int paddingY = paddingTop; int totalLeftMargin = 0, totalRightMargin = 0, totalTopMargin = 0, totalBottomMargin = 0; - GetTotalMargin(dc, buffer, GetAttributes(), totalLeftMargin, totalRightMargin, totalTopMargin, totalBottomMargin); + GetTotalMargin(dc, buffer, attr, totalLeftMargin, totalRightMargin, totalTopMargin, totalBottomMargin); // Internal table width - the area for content int internalTableWidth = tableWidth - totalLeftMargin - totalRightMargin; @@ -8204,7 +8981,7 @@ bool wxRichTextTable::Layout(wxDC& dc, const wxRect& rect, const wxRect& WXUNUSE wxArrayInt absoluteColWidths; absoluteColWidths.Add(0, m_colCount); - // wxArrayInt absoluteColWidthsSpanning(m_colCount); + wxArrayInt percentageColWidths; percentageColWidths.Add(0, m_colCount); // wxArrayInt percentageColWidthsSpanning(m_colCount); @@ -8355,7 +9132,7 @@ bool wxRichTextTable::Layout(wxDC& dc, const wxRect& rect, const wxRect& WXUNUSE // Lay out cell to find min/max widths cell->Invalidate(wxRICHTEXT_ALL); - cell->Layout(dc, availableSpace, availableSpace, style); + cell->Layout(dc, context, availableSpace, availableSpace, style); if (colSpan == 1) { @@ -8694,7 +9471,7 @@ bool wxRichTextTable::Layout(wxDC& dc, const wxRect& rect, const wxRect& WXUNUSE // Lay out cell cell->Invalidate(wxRICHTEXT_ALL); - cell->Layout(dc, availableCellSpace, availableSpace, style); + cell->Layout(dc, context, availableCellSpace, availableSpace, style); // TODO: use GetCachedSize().x to compute 'natural' size @@ -8715,7 +9492,7 @@ bool wxRichTextTable::Layout(wxDC& dc, const wxRect& rect, const wxRect& WXUNUSE wxRect availableCellSpace = wxRect(cell->GetPosition(), wxSize(actualWidths[i], maxCellHeight)); // Lay out cell with new height cell->Invalidate(wxRICHTEXT_ALL); - cell->Layout(dc, availableCellSpace, availableSpace, style); + cell->Layout(dc, context, availableCellSpace, availableSpace, style); // Make sure the cell size really is the appropriate size, // not the calculated box size @@ -8734,7 +9511,7 @@ bool wxRichTextTable::Layout(wxDC& dc, const wxRect& rect, const wxRect& WXUNUSE { wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect; contentRect = wxRect(wxPoint(0, 0), wxSize(maxRight - availableSpace.x, y - availableSpace.y)); - GetBoxRects(dc, GetBuffer(), GetAttributes(), marginRect, borderRect, contentRect, paddingRect, outlineRect); + GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect); SetCachedSize(marginRect.GetSize()); } @@ -8764,14 +9541,14 @@ bool wxRichTextTable::Layout(wxDC& dc, const wxRect& rect, const wxRect& WXUNUSE } // Finds the absolute position and row height for the given character position -bool wxRichTextTable::FindPosition(wxDC& dc, long index, wxPoint& pt, int* height, bool forceLineStart) +bool wxRichTextTable::FindPosition(wxDC& dc, wxRichTextDrawingContext& context, long index, wxPoint& pt, int* height, bool forceLineStart) { wxRichTextCell* child = GetCell(index+1); if (child) { // Find the position at the start of the child cell, since the table doesn't // have any caret position of its own. - return child->FindPosition(dc, -1, pt, height, forceLineStart); + return child->FindPosition(dc, context, -1, pt, height, forceLineStart); } else return false; @@ -8849,9 +9626,9 @@ void wxRichTextTable::CalculateRange(long start, long& end) } // Gets the range size. -bool wxRichTextTable::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position, wxArrayInt* partialExtents) const +bool wxRichTextTable::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* partialExtents) const { - return wxRichTextBox::GetRangeSize(range, size, descent, dc, flags, position, partialExtents); + return wxRichTextBox::GetRangeSize(range, size, descent, dc, context, flags, position, partialExtents); } // Deletes content in the given range. @@ -9198,11 +9975,25 @@ public: wxRichTextBuffer::SetRenderer(new wxRichTextStdRenderer); wxRichTextBuffer::InitStandardHandlers(); wxRichTextParagraph::InitDefaultTabs(); + + wxRichTextXMLHandler::RegisterNodeName(wxT("text"), wxT("wxRichTextPlainText")); + wxRichTextXMLHandler::RegisterNodeName(wxT("symbol"), wxT("wxRichTextPlainText")); + wxRichTextXMLHandler::RegisterNodeName(wxT("image"), wxT("wxRichTextImage")); + wxRichTextXMLHandler::RegisterNodeName(wxT("paragraph"), wxT("wxRichTextParagraph")); + wxRichTextXMLHandler::RegisterNodeName(wxT("paragraphlayout"), wxT("wxRichTextParagraphLayoutBox")); + wxRichTextXMLHandler::RegisterNodeName(wxT("textbox"), wxT("wxRichTextBox")); + wxRichTextXMLHandler::RegisterNodeName(wxT("cell"), wxT("wxRichTextCell")); + wxRichTextXMLHandler::RegisterNodeName(wxT("table"), wxT("wxRichTextTable")); + wxRichTextXMLHandler::RegisterNodeName(wxT("field"), wxT("wxRichTextField")); + return true; } void OnExit() { wxRichTextBuffer::CleanUpHandlers(); + wxRichTextBuffer::CleanUpDrawingHandlers(); + wxRichTextBuffer::CleanUpFieldTypes(); + wxRichTextXMLHandler::ClearNodeToClassMap(); wxRichTextDecimalToRoman(-1); wxRichTextParagraph::ClearDefaultTabs(); wxRichTextCtrl::ClearAvailableFontNames(); @@ -9333,8 +10124,8 @@ void wxRichTextAction::CalculateRefreshOptimizations(wxArrayInt& optimizationLin // first, but of course this means we'll be doing it twice. if (!m_buffer->IsDirty() && m_ctrl) // can only do optimisation if the buffer is already laid out correctly { - wxSize clientSize = m_ctrl->GetClientSize(); - wxPoint firstVisiblePt = m_ctrl->GetFirstVisiblePoint(); + wxSize clientSize = m_ctrl->GetUnscaledSize(m_ctrl->GetClientSize()); + wxPoint firstVisiblePt = m_ctrl->GetUnscaledPoint(m_ctrl->GetFirstVisiblePoint()); int lastY = firstVisiblePt.y + clientSize.y; wxRichTextParagraph* para = container->GetParagraphAtPosition(GetRange().GetStart()); @@ -9466,6 +10257,7 @@ bool wxRichTextAction::Do() break; } case wxRICHTEXT_CHANGE_STYLE: + case wxRICHTEXT_CHANGE_PROPERTIES: { ApplyParagraphs(GetNewParagraphs()); @@ -9476,7 +10268,7 @@ bool wxRichTextAction::Do() UpdateAppearance(GetPosition()); wxRichTextEvent cmdEvent( - wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED, + m_cmdId == wxRICHTEXT_CHANGE_STYLE ? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED : wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED, m_ctrl ? m_ctrl->GetId() : -1); cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer); cmdEvent.SetRange(GetRange()); @@ -9569,6 +10361,7 @@ bool wxRichTextAction::Undo() container->DeleteRange(GetRange()); container->UpdateRanges(); + // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object, // Layout() would stop prematurely at the top level. container->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart())); @@ -9600,6 +10393,7 @@ bool wxRichTextAction::Undo() container->InsertFragment(GetRange().GetStart(), m_oldParagraphs); container->UpdateRanges(); + // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object, // Layout() would stop prematurely at the top level. container->InvalidateHierarchy(GetRange()); @@ -9619,6 +10413,7 @@ bool wxRichTextAction::Undo() break; } case wxRICHTEXT_CHANGE_STYLE: + case wxRICHTEXT_CHANGE_PROPERTIES: { ApplyParagraphs(GetOldParagraphs()); // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object, @@ -9628,7 +10423,7 @@ bool wxRichTextAction::Undo() UpdateAppearance(GetPosition()); wxRichTextEvent cmdEvent( - wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED, + m_cmdId == wxRICHTEXT_CHANGE_STYLE ? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED : wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED, m_ctrl ? m_ctrl->GetId() : -1); cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer); cmdEvent.SetRange(GetRange()); @@ -9684,8 +10479,8 @@ void wxRichTextAction::UpdateAppearance(long caretPosition, bool sendUpdateEvent { size_t i; - wxSize clientSize = m_ctrl->GetClientSize(); - wxPoint firstVisiblePt = m_ctrl->GetFirstVisiblePoint(); + wxSize clientSize = m_ctrl->GetUnscaledSize(m_ctrl->GetClientSize()); + wxPoint firstVisiblePt = m_ctrl->GetUnscaledPoint(m_ctrl->GetFirstVisiblePoint()); // Start/end positions int firstY = 0; @@ -9780,7 +10575,7 @@ void wxRichTextAction::UpdateAppearance(long caretPosition, bool sendUpdateEvent lastY = firstVisiblePt.y + clientSize.y; // Convert to device coordinates - wxRect rect(m_ctrl->GetPhysicalPoint(wxPoint(firstVisiblePt.x, firstY)), wxSize(clientSize.x, lastY - firstY)); + wxRect rect(m_ctrl->GetPhysicalPoint(m_ctrl->GetScaledPoint(wxPoint(firstVisiblePt.x, firstY))), m_ctrl->GetScaledSize(wxSize(clientSize.x, lastY - firstY))); m_ctrl->RefreshRect(rect); } else @@ -9867,6 +10662,7 @@ IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage, wxRichTextObject) wxRichTextImage::wxRichTextImage(const wxImage& image, wxRichTextObject* parent, wxRichTextAttr* charStyle): wxRichTextObject(parent) { + Init(); m_imageBlock.MakeImageBlockDefaultQuality(image, wxBITMAP_TYPE_PNG); if (charStyle) SetAttributes(*charStyle); @@ -9875,40 +10671,155 @@ wxRichTextImage::wxRichTextImage(const wxImage& image, wxRichTextObject* parent, wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock& imageBlock, wxRichTextObject* parent, wxRichTextAttr* charStyle): wxRichTextObject(parent) { + Init(); m_imageBlock = imageBlock; if (charStyle) SetAttributes(*charStyle); } +void wxRichTextImage::Init() +{ + m_originalImageSize = wxSize(-1, -1); +} + /// Create a cached image at the required size bool wxRichTextImage::LoadImageCache(wxDC& dc, bool resetCache) { - if (resetCache || !m_imageCache.IsOk() /* || m_imageCache.GetWidth() != size.x || m_imageCache.GetHeight() != size.y */) + if (!m_imageBlock.IsOk()) + return false; + + // If we have an original image size, use that to compute the cached bitmap size + // instead of loading the image each time. This way we can avoid loading + // the image so long as the new cached bitmap size hasn't changed. + + wxImage image; + if (resetCache || m_originalImageSize == wxSize(-1, -1)) { - if (!m_imageBlock.IsOk()) - return false; + m_imageCache = wxNullBitmap; - wxImage image; m_imageBlock.Load(image); if (!image.IsOk()) return false; - int width = image.GetWidth(); - int height = image.GetHeight(); + m_originalImageSize = wxSize(image.GetWidth(), image.GetHeight()); + } + + int width = m_originalImageSize.GetWidth(); + int height = m_originalImageSize.GetHeight(); + + int parentWidth = 0; + int parentHeight = 0; + + int maxWidth = -1; + int maxHeight = -1; - if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetWidth().GetValue() > 0) + wxRichTextBuffer* buffer = GetBuffer(); + if (buffer) + { + wxSize sz; + if (buffer->GetRichTextCtrl()) { - if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM) - width = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetWidth().GetValue()); - else - width = GetAttributes().GetTextBoxAttr().GetWidth().GetValue(); + // Subtract borders + sz = buffer->GetRichTextCtrl()->GetClientSize(); + + wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect; + marginRect = wxRect(0, 0, sz.x, sz.y); + buffer->GetBoxRects(dc, buffer, buffer->GetAttributes(), marginRect, borderRect, contentRect, paddingRect, outlineRect); + + sz = contentRect.GetSize(); + + // Start with a maximum width of the control size, even if not specified by the content, + // to minimize the amount of picture overlapping the right-hand side + maxWidth = sz.x; } - if (GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetHeight().GetValue() > 0) + else + sz = buffer->GetCachedSize(); + parentWidth = sz.GetWidth(); + parentHeight = sz.GetHeight(); + } + + if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetWidth().GetValue() > 0) + { + if (parentWidth > 0 && GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE) + width = (int) ((GetAttributes().GetTextBoxAttr().GetWidth().GetValue() * parentWidth)/100.0); + else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM) + width = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetWidth().GetValue()); + else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS) + width = GetAttributes().GetTextBoxAttr().GetWidth().GetValue(); + } + + // Limit to max width + + if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() > 0) + { + int mw = -1; + + if (parentWidth > 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE) + mw = (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() * parentWidth)/100.0); + else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM) + mw = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue()); + else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS) + mw = GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue(); + + // If we already have a smaller max width due to the constraints of the control size, + // don't use the larger max width. + if (mw != -1 && ((maxWidth == -1) || (mw < maxWidth))) + maxWidth = mw; + } + + if (maxWidth > 0 && width > maxWidth) + width = maxWidth; + + // Preserve the aspect ratio + if (width != m_originalImageSize.GetWidth()) + height = (int) (float(m_originalImageSize.GetHeight()) * (float(width)/float(m_originalImageSize.GetWidth()))); + + if (GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetHeight().GetValue() > 0) + { + if (parentHeight > 0 && GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE) + height = (int) ((GetAttributes().GetTextBoxAttr().GetHeight().GetValue() * parentHeight)/100.0); + else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM) + height = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetHeight().GetValue()); + else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS) + height = GetAttributes().GetTextBoxAttr().GetHeight().GetValue(); + + // Preserve the aspect ratio + if (height != m_originalImageSize.GetHeight()) + width = (int) (float(m_originalImageSize.GetWidth()) * (float(height)/float(m_originalImageSize.GetHeight()))); + } + + // Limit to max height + + if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() > 0) + { + if (parentHeight > 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE) + maxHeight = (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() * parentHeight)/100.0); + else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM) + maxHeight = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue()); + else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS) + maxHeight = GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue(); + } + + if (maxHeight > 0 && height > maxHeight) + { + height = maxHeight; + + // Preserve the aspect ratio + if (height != m_originalImageSize.GetHeight()) + width = (int) (float(m_originalImageSize.GetWidth()) * (float(height)/float(m_originalImageSize.GetHeight()))); + } + + if (m_imageCache.IsOk() && m_imageCache.GetWidth() == width && m_imageCache.GetHeight() == height) + { + // Do nothing, we didn't need to change the image cache + } + else + { + if (!image.IsOk()) { - if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM) - height = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetHeight().GetValue()); - else - height = GetAttributes().GetTextBoxAttr().GetHeight().GetValue(); + m_imageBlock.Load(image); + if (!image.IsOk()) + return false; } if (image.GetWidth() == width && image.GetHeight() == height) @@ -9935,7 +10846,7 @@ bool wxRichTextImage::LoadImageCache(wxDC& dc, bool resetCache) } /// Draw the item -bool wxRichTextImage::Draw(wxDC& dc, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int WXUNUSED(descent), int WXUNUSED(style)) +bool wxRichTextImage::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& WXUNUSED(range), const wxRichTextSelection& selection, const wxRect& rect, int WXUNUSED(descent), int WXUNUSED(style)) { if (!IsShown()) return true; @@ -9945,22 +10856,19 @@ bool wxRichTextImage::Draw(wxDC& dc, const wxRichTextRange& range, const wxRichT if (!LoadImageCache(dc)) return false; - DrawBoxAttributes(dc, GetBuffer(), GetAttributes(), wxRect(rect.GetPosition(), GetCachedSize())); + wxRichTextAttr attr(GetAttributes()); + context.ApplyVirtualAttributes(attr, this); -#if 0 - int y = rect.y + (rect.height - m_imageCache.GetHeight()); - - dc.DrawBitmap(m_imageCache, rect.x, y, true); -#endif + DrawBoxAttributes(dc, GetBuffer(), attr, wxRect(rect.GetPosition(), GetCachedSize())); wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight()); wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect; marginRect = rect; // outer rectangle, will calculate contentRect - GetBoxRects(dc, GetBuffer(), GetAttributes(), marginRect, borderRect, contentRect, paddingRect, outlineRect); + GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect); dc.DrawBitmap(m_imageCache, contentRect.x, contentRect.y, true); - if (selection.WithinSelection(range.GetStart(), this)) + if (selection.WithinSelection(GetRange().GetStart(), this)) { wxCheckSetBrush(dc, *wxBLACK_BRUSH); wxCheckSetPen(dc, *wxBLACK_PEN); @@ -9973,7 +10881,7 @@ bool wxRichTextImage::Draw(wxDC& dc, const wxRichTextRange& range, const wxRichT } /// Lay the item out -bool wxRichTextImage::Layout(wxDC& dc, const wxRect& rect, const wxRect& WXUNUSED(parentRect), int WXUNUSED(style)) +bool wxRichTextImage::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& WXUNUSED(parentRect), int WXUNUSED(style)) { if (!LoadImageCache(dc)) return false; @@ -9981,7 +10889,11 @@ bool wxRichTextImage::Layout(wxDC& dc, const wxRect& rect, const wxRect& WXUNUSE wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight()); wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect; contentRect = wxRect(wxPoint(0,0), imageSize); - GetBoxRects(dc, GetBuffer(), GetAttributes(), marginRect, borderRect, contentRect, paddingRect, outlineRect); + + wxRichTextAttr attr(GetAttributes()); + context.ApplyVirtualAttributes(attr, this); + + GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect); wxSize overallSize = marginRect.GetSize(); @@ -9995,7 +10907,7 @@ bool wxRichTextImage::Layout(wxDC& dc, const wxRect& rect, const wxRect& WXUNUSE /// Get/set the object size for the given range. Returns false if the range /// is invalid for this object. -bool wxRichTextImage::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& WXUNUSED(descent), wxDC& dc, int WXUNUSED(flags), wxPoint WXUNUSED(position), wxArrayInt* partialExtents) const +bool wxRichTextImage::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& WXUNUSED(descent), wxDC& dc, wxRichTextDrawingContext& context, int WXUNUSED(flags), wxPoint WXUNUSED(position), wxArrayInt* partialExtents) const { if (!range.IsWithin(GetRange())) return false; @@ -10008,10 +10920,13 @@ bool wxRichTextImage::GetRangeSize(const wxRichTextRange& range, wxSize& size, i return false; } + wxRichTextAttr attr(GetAttributes()); + context.ApplyVirtualAttributes(attr, (wxRichTextObject*) this); + wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight()); wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect; contentRect = wxRect(wxPoint(0,0), imageSize); - GetBoxRects(dc, GetBuffer(), GetAttributes(), marginRect, borderRect, contentRect, paddingRect, outlineRect); + GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect); wxSize overallSize = marginRect.GetSize(); @@ -10043,6 +10958,7 @@ void wxRichTextImage::Copy(const wxRichTextImage& obj) wxRichTextObject::Copy(obj); m_imageBlock = obj.m_imageBlock; + m_originalImageSize = obj.m_originalImageSize; } /// Edit properties via a GUI @@ -10073,12 +10989,6 @@ bool wxTextAttrEq(const wxRichTextAttr& attr1, const wxRichTextAttr& attr2) return (attr1 == attr2); } -// Partial equality test taking flags into account -bool wxTextAttrEqPartial(const wxRichTextAttr& attr1, const wxRichTextAttr& attr2) -{ - return attr1.EqPartial(attr2); -} - /// Compare tabs bool wxRichTextTabsEq(const wxArrayInt& tabs1, const wxArrayInt& tabs2) { @@ -10693,22 +11603,48 @@ class wxRichTextFontTableData: public wxObjectRefData public: wxRichTextFontTableData() {} - wxFont FindFont(const wxRichTextAttr& fontSpec); + wxFont FindFont(const wxRichTextAttr& fontSpec, double fontScale); wxRichTextFontTableHashMap m_hashMap; }; -wxFont wxRichTextFontTableData::FindFont(const wxRichTextAttr& fontSpec) +wxFont wxRichTextFontTableData::FindFont(const wxRichTextAttr& fontSpec, double fontScale) { wxString facename(fontSpec.GetFontFaceName()); - wxString spec(wxString::Format(wxT("%d-%d-%d-%d-%s-%d"), fontSpec.GetFontSize(), fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), (int) fontSpec.GetFontUnderlined(), facename.c_str(), (int) fontSpec.GetFontEncoding())); - wxRichTextFontTableHashMap::iterator entry = m_hashMap.find(spec); + int fontSize = fontSpec.GetFontSize(); + if (fontScale != 1.0) + fontSize = (int) ((double(fontSize) * fontScale) + 0.5); + + wxString units; + if (fontSpec.HasFontPixelSize() && !fontSpec.HasFontPointSize()) + units = wxT("px"); + else + units = wxT("pt"); + wxString spec = wxString::Format(wxT("%d-%s-%d-%d-%d-%d-%s-%d"), + fontSize, units.c_str(), fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), (int) fontSpec.GetFontUnderlined(), (int) fontSpec.GetFontStrikethrough(), + facename.c_str(), (int) fontSpec.GetFontEncoding()); + + wxRichTextFontTableHashMap::iterator entry = m_hashMap.find(spec); if ( entry == m_hashMap.end() ) { - wxFont font(fontSpec.GetFontSize(), wxDEFAULT, fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), fontSpec.GetFontUnderlined(), facename.c_str()); - m_hashMap[spec] = font; - return font; + if (fontSpec.HasFontPixelSize() && !fontSpec.HasFontPointSize()) + { + wxFont font(wxSize(0, fontSize), wxFONTFAMILY_DEFAULT, fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), fontSpec.GetFontUnderlined(), facename); + if (fontSpec.HasFontStrikethrough() && fontSpec.GetFontStrikethrough()) + font.SetStrikethrough(true); + m_hashMap[spec] = font; + return font; + } + else + { + wxFont font(fontSize, wxFONTFAMILY_DEFAULT, fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), fontSpec.GetFontUnderlined(), facename.c_str()); + if (fontSpec.HasFontStrikethrough() && fontSpec.GetFontStrikethrough()) + font.SetStrikethrough(true); + + m_hashMap[spec] = font; + return font; + } } else { @@ -10721,6 +11657,7 @@ IMPLEMENT_DYNAMIC_CLASS(wxRichTextFontTable, wxObject) wxRichTextFontTable::wxRichTextFontTable() { m_refData = new wxRichTextFontTableData; + m_fontScale = 1.0; } wxRichTextFontTable::wxRichTextFontTable(const wxRichTextFontTable& table) @@ -10742,13 +11679,14 @@ bool wxRichTextFontTable::operator == (const wxRichTextFontTable& table) const void wxRichTextFontTable::operator= (const wxRichTextFontTable& table) { Ref(table); + m_fontScale = table.m_fontScale; } wxFont wxRichTextFontTable::FindFont(const wxRichTextAttr& fontSpec) { wxRichTextFontTableData* data = (wxRichTextFontTableData*) m_refData; if (data) - return data->FindFont(fontSpec); + return data->FindFont(fontSpec, m_fontScale); else return wxFont(); } @@ -10760,8 +11698,14 @@ void wxRichTextFontTable::Clear() data->m_hashMap.clear(); } -// wxTextBoxAttr +void wxRichTextFontTable::SetFontScale(double fontScale) +{ + if (fontScale != m_fontScale) + Clear(); + m_fontScale = fontScale; +} +// wxTextBoxAttr void wxTextBoxAttr::Reset() { @@ -10777,6 +11721,8 @@ void wxTextBoxAttr::Reset() m_position.Reset(); m_size.Reset(); + m_minSize.Reset(); + m_maxSize.Reset(); m_border.Reset(); m_outline.Reset(); @@ -10797,6 +11743,8 @@ bool wxTextBoxAttr::operator== (const wxTextBoxAttr& attr) const m_position == attr.m_position && m_size == attr.m_size && + m_minSize == attr.m_minSize && + m_maxSize == attr.m_maxSize && m_border == attr.m_border && m_outline == attr.m_outline && @@ -10806,8 +11754,17 @@ bool wxTextBoxAttr::operator== (const wxTextBoxAttr& attr) const } // Partial equality test -bool wxTextBoxAttr::EqPartial(const wxTextBoxAttr& attr) const +bool wxTextBoxAttr::EqPartial(const wxTextBoxAttr& attr, bool weakTest) const { + if (!weakTest && + ((!HasFloatMode() && attr.HasFloatMode()) || + (!HasClearMode() && attr.HasClearMode()) || + (!HasCollapseBorders() && attr.HasCollapseBorders()) || + (!HasVerticalAlignment() && attr.HasVerticalAlignment()) || + (!HasBoxStyleName() && attr.HasBoxStyleName()))) + { + return false; + } if (attr.HasFloatMode() && HasFloatMode() && (GetFloatMode() != attr.GetFloatMode())) return false; @@ -10825,27 +11782,36 @@ bool wxTextBoxAttr::EqPartial(const wxTextBoxAttr& attr) const // Position - if (!m_position.EqPartial(attr.m_position)) + if (!m_position.EqPartial(attr.m_position, weakTest)) + return false; + + // Size + + if (!m_size.EqPartial(attr.m_size, weakTest)) + return false; + if (!m_minSize.EqPartial(attr.m_minSize, weakTest)) + return false; + if (!m_maxSize.EqPartial(attr.m_maxSize, weakTest)) return false; // Margins - if (!m_margins.EqPartial(attr.m_margins)) + if (!m_margins.EqPartial(attr.m_margins, weakTest)) return false; // Padding - if (!m_padding.EqPartial(attr.m_padding)) + if (!m_padding.EqPartial(attr.m_padding, weakTest)) return false; // Border - if (!GetBorder().EqPartial(attr.GetBorder())) + if (!GetBorder().EqPartial(attr.GetBorder(), weakTest)) return false; // Outline - if (!GetOutline().EqPartial(attr.GetOutline())) + if (!GetOutline().EqPartial(attr.GetOutline(), weakTest)) return false; return true; @@ -10891,6 +11857,8 @@ bool wxTextBoxAttr::Apply(const wxTextBoxAttr& attr, const wxTextBoxAttr* compar m_position.Apply(attr.m_position, compareWith ? (& attr.m_position) : (const wxTextAttrDimensions*) NULL); m_size.Apply(attr.m_size, compareWith ? (& attr.m_size) : (const wxTextAttrSize*) NULL); + m_minSize.Apply(attr.m_minSize, compareWith ? (& attr.m_minSize) : (const wxTextAttrSize*) NULL); + m_maxSize.Apply(attr.m_maxSize, compareWith ? (& attr.m_maxSize) : (const wxTextAttrSize*) NULL); m_border.Apply(attr.m_border, compareWith ? (& attr.m_border) : (const wxTextAttrBorders*) NULL); m_outline.Apply(attr.m_outline, compareWith ? (& attr.m_outline) : (const wxTextAttrBorders*) NULL); @@ -10924,6 +11892,8 @@ bool wxTextBoxAttr::RemoveStyle(const wxTextBoxAttr& attr) m_position.RemoveStyle(attr.m_position); m_size.RemoveStyle(attr.m_size); + m_minSize.RemoveStyle(attr.m_minSize); + m_maxSize.RemoveStyle(attr.m_maxSize); m_border.RemoveStyle(attr.m_border); m_outline.RemoveStyle(attr.m_outline); @@ -11035,6 +12005,8 @@ void wxTextBoxAttr::CollectCommonAttributes(const wxTextBoxAttr& attr, wxTextBox m_position.CollectCommonAttributes(attr.m_position, clashingAttr.m_position, absentAttr.m_position); m_size.CollectCommonAttributes(attr.m_size, clashingAttr.m_size, absentAttr.m_size); + m_minSize.CollectCommonAttributes(attr.m_minSize, clashingAttr.m_minSize, absentAttr.m_minSize); + m_maxSize.CollectCommonAttributes(attr.m_maxSize, clashingAttr.m_maxSize, absentAttr.m_maxSize); m_border.CollectCommonAttributes(attr.m_border, clashingAttr.m_border, absentAttr.m_border); m_outline.CollectCommonAttributes(attr.m_outline, clashingAttr.m_outline, absentAttr.m_outline); @@ -11043,7 +12015,7 @@ void wxTextBoxAttr::CollectCommonAttributes(const wxTextBoxAttr& attr, wxTextBox bool wxTextBoxAttr::IsDefault() const { return GetFlags() == 0 && !m_border.IsValid() && !m_outline.IsValid() && - !m_size.GetWidth().IsValid() && !m_size.GetHeight().IsValid() && + !m_size.IsValid() && !m_minSize.IsValid() && !m_maxSize.IsValid() && !m_position.IsValid() && !m_padding.IsValid() && !m_margins.IsValid(); } @@ -11064,13 +12036,13 @@ bool wxRichTextAttr::operator==(const wxRichTextAttr& attr) const return (m_textBoxAttr == attr.m_textBoxAttr); } -// Partial equality test taking comparison object into account -bool wxRichTextAttr::EqPartial(const wxRichTextAttr& attr) const +// Partial equality test +bool wxRichTextAttr::EqPartial(const wxRichTextAttr& attr, bool weakTest) const { - if (!(wxTextAttr::EqPartial(attr))) + if (!(wxTextAttr::EqPartial(attr, weakTest))) return false; - return m_textBoxAttr.EqPartial(attr.m_textBoxAttr); + return m_textBoxAttr.EqPartial(attr.m_textBoxAttr, weakTest); } // Merges the given attributes. If compareWith @@ -11101,15 +12073,23 @@ void wxRichTextAttr::CollectCommonAttributes(const wxRichTextAttr& attr, wxRichT } // Partial equality test -bool wxTextAttrBorder::EqPartial(const wxTextAttrBorder& border) const +bool wxTextAttrBorder::EqPartial(const wxTextAttrBorder& border, bool weakTest) const { - if (border.HasStyle() && !HasStyle() && (border.GetStyle() != GetStyle())) + if (!weakTest && + ((!HasStyle() && border.HasStyle()) || + (!HasColour() && border.HasColour()) || + (!HasWidth() && border.HasWidth()))) + { + return false; + } + + if (border.HasStyle() && HasStyle() && (border.GetStyle() != GetStyle())) return false; - if (border.HasColour() && !HasColour() && (border.GetColourLong() != GetColourLong())) + if (border.HasColour() && HasColour() && (border.GetColourLong() != GetColourLong())) return false; - if (border.HasWidth() && !HasWidth() && !(border.GetWidth() == GetWidth())) + if (border.HasWidth() && HasWidth() && !(border.GetWidth() == GetWidth())) return false; return true; @@ -11196,10 +12176,10 @@ void wxTextAttrBorder::CollectCommonAttributes(const wxTextAttrBorder& attr, wxT } // Partial equality test -bool wxTextAttrBorders::EqPartial(const wxTextAttrBorders& borders) const +bool wxTextAttrBorders::EqPartial(const wxTextAttrBorders& borders, bool weakTest) const { - return m_left.EqPartial(borders.m_left) && m_right.EqPartial(borders.m_right) && - m_top.EqPartial(borders.m_top) && m_bottom.EqPartial(borders.m_bottom); + return m_left.EqPartial(borders.m_left, weakTest) && m_right.EqPartial(borders.m_right, weakTest) && + m_top.EqPartial(borders.m_top, weakTest) && m_bottom.EqPartial(borders.m_bottom, weakTest); } // Apply border to 'this', but not if the same as compareWith @@ -11268,8 +12248,11 @@ void wxTextAttrBorders::SetWidth(const wxTextAttrDimension& width) } // Partial equality test -bool wxTextAttrDimension::EqPartial(const wxTextAttrDimension& dim) const +bool wxTextAttrDimension::EqPartial(const wxTextAttrDimension& dim, bool weakTest) const { + if (!weakTest && !IsValid() && dim.IsValid()) + return false; + if (dim.IsValid() && IsValid() && !((*this) == dim)) return false; else @@ -11366,18 +12349,18 @@ int wxTextAttrDimensionConverter::GetTenthsMM(const wxTextAttrDimension& dim) co } // Partial equality test -bool wxTextAttrDimensions::EqPartial(const wxTextAttrDimensions& dims) const +bool wxTextAttrDimensions::EqPartial(const wxTextAttrDimensions& dims, bool weakTest) const { - if (!m_left.EqPartial(dims.m_left)) + if (!m_left.EqPartial(dims.m_left, weakTest)) return false; - if (!m_right.EqPartial(dims.m_right)) + if (!m_right.EqPartial(dims.m_right, weakTest)) return false; - if (!m_top.EqPartial(dims.m_top)) + if (!m_top.EqPartial(dims.m_top, weakTest)) return false; - if (!m_bottom.EqPartial(dims.m_bottom)) + if (!m_bottom.EqPartial(dims.m_bottom, weakTest)) return false; return true; @@ -11420,12 +12403,12 @@ void wxTextAttrDimensions::CollectCommonAttributes(const wxTextAttrDimensions& a } // Partial equality test -bool wxTextAttrSize::EqPartial(const wxTextAttrSize& size) const +bool wxTextAttrSize::EqPartial(const wxTextAttrSize& size, bool weakTest) const { - if (!m_width.EqPartial(size.m_width)) + if (!m_width.EqPartial(size.m_width, weakTest)) return false; - if (!m_height.EqPartial(size.m_height)) + if (!m_height.EqPartial(size.m_height, weakTest)) return false; return true; @@ -11470,19 +12453,44 @@ void wxTextAttrCollectCommonAttributes(wxTextAttr& currentStyle, const wxTextAtt if (attr.HasFont()) { - if (attr.HasFontSize() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_SIZE)) + // If different font size units are being used, this is a clash. + if (((attr.GetFlags() & wxTEXT_ATTR_FONT_SIZE) | (currentStyle.GetFlags() & wxTEXT_ATTR_FONT_SIZE)) == wxTEXT_ATTR_FONT_SIZE) + { + currentStyle.SetFontSize(0); + currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_SIZE); + clashingAttr.AddFlag(wxTEXT_ATTR_FONT_SIZE); + } + else { - if (currentStyle.HasFontSize()) + if (attr.HasFontPointSize() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_POINT_SIZE)) { - if (currentStyle.GetFontSize() != attr.GetFontSize()) + if (currentStyle.HasFontPointSize()) { - // Clash of attr - mark as such - clashingAttr.AddFlag(wxTEXT_ATTR_FONT_SIZE); - currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_SIZE); + if (currentStyle.GetFontSize() != attr.GetFontSize()) + { + // Clash of attr - mark as such + clashingAttr.AddFlag(wxTEXT_ATTR_FONT_POINT_SIZE); + currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_POINT_SIZE); + } } + else + currentStyle.SetFontSize(attr.GetFontSize()); + } + + if (attr.HasFontPixelSize() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_PIXEL_SIZE)) + { + if (currentStyle.HasFontPixelSize()) + { + if (currentStyle.GetFontSize() != attr.GetFontSize()) + { + // Clash of attr - mark as such + clashingAttr.AddFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE); + currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE); + } + } + else + currentStyle.SetFontPixelSize(attr.GetFontSize()); } - else - currentStyle.SetFontSize(attr.GetFontSize()); } if (attr.HasFontItalic() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_ITALIC)) @@ -11562,6 +12570,21 @@ void wxTextAttrCollectCommonAttributes(wxTextAttr& currentStyle, const wxTextAtt else currentStyle.SetFontUnderlined(attr.GetFontUnderlined()); } + + if (attr.HasFontStrikethrough() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_STRIKETHROUGH)) + { + if (currentStyle.HasFontStrikethrough()) + { + if (currentStyle.GetFontStrikethrough() != attr.GetFontStrikethrough()) + { + // Clash of attr - mark as such + clashingAttr.AddFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH); + currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH); + } + } + else + currentStyle.SetFontStrikethrough(attr.GetFontStrikethrough()); + } } if (attr.HasTextColour() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_TEXT_COLOUR)) @@ -11927,6 +12950,18 @@ int wxRichTextProperties::Find(const wxString& name) const return -1; } +bool wxRichTextProperties::Remove(const wxString& name) +{ + int idx = Find(name); + if (idx != -1) + { + m_properties.RemoveAt(idx); + return true; + } + else + return false; +} + wxVariant* wxRichTextProperties::FindOrCreateProperty(const wxString& name) { int idx = Find(name); @@ -12015,6 +13050,26 @@ void wxRichTextProperties::SetProperty(const wxString& name, bool value) SetProperty(name, wxVariant(value, name)); } +void wxRichTextProperties::RemoveProperties(const wxRichTextProperties& properties) +{ + size_t i; + for (i = 0; i < properties.GetCount(); i++) + { + wxString name = properties.GetProperties()[i].GetName(); + if (HasProperty(name)) + Remove(name); + } +} + +void wxRichTextProperties::MergeProperties(const wxRichTextProperties& properties) +{ + size_t i; + for (i = 0; i < properties.GetCount(); i++) + { + SetProperty(properties.GetProperties()[i]); + } +} + wxRichTextObject* wxRichTextObjectAddress::GetObject(wxRichTextParagraphLayoutBox* topLevelContainer) const { if (m_address.GetCount() == 0) @@ -12161,6 +13216,147 @@ bool wxRichTextSelection::WithinSelection(const wxRichTextRange& range, const wx return false; } +IMPLEMENT_CLASS(wxRichTextDrawingHandler, wxObject) +IMPLEMENT_CLASS(wxRichTextDrawingContext, wxObject) + +bool wxRichTextDrawingContext::HasVirtualAttributes(wxRichTextObject* obj) const +{ + wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst(); + while (node) + { + wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData(); + if (handler->HasVirtualAttributes(obj)) + return true; + + node = node->GetNext(); + } + return false; +} + +wxRichTextAttr wxRichTextDrawingContext::GetVirtualAttributes(wxRichTextObject* obj) const +{ + wxRichTextAttr attr; + // We apply all handlers, so we can may combine several different attributes + wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst(); + while (node) + { + wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData(); + if (handler->HasVirtualAttributes(obj)) + { + bool success = handler->GetVirtualAttributes(attr, obj); + wxASSERT(success); + wxUnusedVar(success); + } + + node = node->GetNext(); + } + return attr; +} + +bool wxRichTextDrawingContext::ApplyVirtualAttributes(wxRichTextAttr& attr, wxRichTextObject* obj) const +{ + if (HasVirtualAttributes(obj)) + { + wxRichTextAttr a(GetVirtualAttributes(obj)); + attr.Apply(a); + return true; + } + else + return false; +} + +/// Adds a handler to the end +void wxRichTextBuffer::AddDrawingHandler(wxRichTextDrawingHandler *handler) +{ + sm_drawingHandlers.Append(handler); +} + +/// Inserts a handler at the front +void wxRichTextBuffer::InsertDrawingHandler(wxRichTextDrawingHandler *handler) +{ + sm_drawingHandlers.Insert( handler ); +} + +/// Removes a handler +bool wxRichTextBuffer::RemoveDrawingHandler(const wxString& name) +{ + wxRichTextDrawingHandler *handler = FindDrawingHandler(name); + if (handler) + { + sm_drawingHandlers.DeleteObject(handler); + delete handler; + return true; + } + else + return false; +} + +wxRichTextDrawingHandler* wxRichTextBuffer::FindDrawingHandler(const wxString& name) +{ + wxList::compatibility_iterator node = sm_drawingHandlers.GetFirst(); + while (node) + { + wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData(); + if (handler->GetName().Lower() == name.Lower()) return handler; + + node = node->GetNext(); + } + return NULL; +} + +void wxRichTextBuffer::CleanUpDrawingHandlers() +{ + wxList::compatibility_iterator node = sm_drawingHandlers.GetFirst(); + while (node) + { + wxRichTextDrawingHandler* handler = (wxRichTextDrawingHandler*)node->GetData(); + wxList::compatibility_iterator next = node->GetNext(); + delete handler; + node = next; + } + + sm_drawingHandlers.Clear(); +} + +void wxRichTextBuffer::AddFieldType(wxRichTextFieldType *fieldType) +{ + sm_fieldTypes[fieldType->GetName()] = fieldType; +} + +bool wxRichTextBuffer::RemoveFieldType(const wxString& name) +{ + wxRichTextFieldTypeHashMap::iterator it = sm_fieldTypes.find(name); + if (it == sm_fieldTypes.end()) + return false; + else + { + wxRichTextFieldType* fieldType = it->second; + sm_fieldTypes.erase(it); + delete fieldType; + return true; + } +} + +wxRichTextFieldType *wxRichTextBuffer::FindFieldType(const wxString& name) +{ + wxRichTextFieldTypeHashMap::iterator it = sm_fieldTypes.find(name); + if (it == sm_fieldTypes.end()) + return NULL; + else + return it->second; +} + +void wxRichTextBuffer::CleanUpFieldTypes() +{ + wxRichTextFieldTypeHashMap::iterator it; + for( it = sm_fieldTypes.begin(); it != sm_fieldTypes.end(); ++it ) + { + wxRichTextFieldType* fieldType = it->second; + delete fieldType; + } + + sm_fieldTypes.clear(); +} #endif // wxUSE_RICHTEXT