X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/a70eb13eb2e21b26c2bfcd3013a235e795c11772..56cd05b6467bc83f01326d4ffe630b9fb92b0572:/src/richtext/richtextbuffer.cpp diff --git a/src/richtext/richtextbuffer.cpp b/src/richtext/richtextbuffer.cpp index 82b67e7d68..cc4f3de5bc 100644 --- a/src/richtext/richtextbuffer.cpp +++ b/src/richtext/richtextbuffer.cpp @@ -4,7 +4,6 @@ // Author: Julian Smart // Modified by: // Created: 2005-09-30 -// RCS-ID: $Id$ // Copyright: (c) Julian Smart // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// @@ -42,6 +41,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" @@ -464,21 +464,6 @@ int wxRichTextFloatCollector::HitTest(wxDC& dc, wxRichTextDrawingContext& contex // 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); } @@ -616,7 +601,7 @@ void wxRichTextObject::Invalidate(const wxRichTextRange& invalidRange) // 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()) + if (!IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) SetCachedSize(wxDefaultSize); SetMaxSize(wxDefaultSize); SetMinSize(wxDefaultSize); @@ -629,7 +614,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; @@ -677,7 +662,7 @@ int wxRichTextObject::ConvertPixelsToTenthsMM(int ppi, int pixels, double scale) // Draw the borders and background for the given rectangle and attributes. // Width and height are taken to be the outer margin size, not the content. -bool wxRichTextObject::DrawBoxAttributes(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& attr, const wxRect& boxRect, int flags) +bool wxRichTextObject::DrawBoxAttributes(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& attr, const wxRect& boxRect, int flags, wxRichTextObject* obj) { // Assume boxRect is the area around the content wxRect marginRect = boxRect; @@ -707,12 +692,24 @@ bool wxRichTextObject::DrawBoxAttributes(wxDC& dc, wxRichTextBuffer* buffer, con if (flags & wxRICHTEXT_DRAW_GUIDELINES) { - wxRichTextAttr editBorderAttr = attr; + wxRichTextAttr editBorderAttr; // TODO: make guideline colour configurable editBorderAttr.GetTextBoxAttr().GetBorder().SetColour(*wxLIGHT_GREY); editBorderAttr.GetTextBoxAttr().GetBorder().SetWidth(1, wxTEXT_ATTR_UNITS_PIXELS); editBorderAttr.GetTextBoxAttr().GetBorder().SetStyle(wxTEXT_BOX_ATTR_BORDER_SOLID); + if (obj) + { + wxRichTextCell* cell = wxDynamicCast(obj, wxRichTextCell); + if (cell) + { + // This ensures that thin lines drawn by adjacent cells (left and above) + // don't get overwritten by the guidelines. + editBorderAttr.GetTextBoxAttr().GetBorder().GetLeft().Reset(); + editBorderAttr.GetTextBoxAttr().GetBorder().GetTop().Reset(); + } + } + DrawBorder(dc, buffer, editBorderAttr.GetTextBoxAttr().GetBorder(), borderRect, flags); } @@ -746,8 +743,9 @@ bool wxRichTextObject::DrawBorder(wxDC& dc, wxRichTextBuffer* buffer, const wxTe penStyle = wxLONG_DASH; wxPen pen(col, 1, penStyle); dc.SetPen(pen); - dc.DrawLine(rect.x, rect.y, rect.x, rect.y + rect.height); + // Note that the last point is not drawn. + dc.DrawLine(rect.x, rect.y, rect.x, rect.y + rect.height); } else if (borderLeft > 1) { @@ -775,8 +773,8 @@ bool wxRichTextObject::DrawBorder(wxDC& dc, wxRichTextBuffer* buffer, const wxTe penStyle = wxLONG_DASH; wxPen pen(col, 1, penStyle); dc.SetPen(pen); - dc.DrawLine(rect.x + rect.width, rect.y, rect.x + rect.width, rect.y + rect.height + 1); - + // Note that the last point is not drawn. + dc.DrawLine(rect.x + rect.width - 1, rect.y, rect.x + rect.width - 1, rect.y + rect.height); } else if (borderRight > 1) { @@ -820,7 +818,7 @@ bool wxRichTextObject::DrawBorder(wxDC& dc, wxRichTextBuffer* buffer, const wxTe if (attr.GetBottom().IsValid() && attr.GetBottom().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE) { borderBottom = converter.GetPixels(attr.GetBottom().GetWidth()); - wxColour col(attr.GetTop().GetColour()); + wxColour col(attr.GetBottom().GetColour()); // If pen width is > 1, resorts to a solid rectangle. if (borderBottom == 1) @@ -832,8 +830,7 @@ bool wxRichTextObject::DrawBorder(wxDC& dc, wxRichTextBuffer* buffer, const wxTe penStyle = wxLONG_DASH; wxPen pen(col, 1, penStyle); dc.SetPen(pen); - dc.DrawLine(rect.x, rect.y + rect.height, rect.x + rect.width, rect.y + rect.height); - + dc.DrawLine(rect.x, rect.y + rect.height - 1, rect.x + rect.width, rect.y + rect.height - 1); } else if (borderBottom > 1) { @@ -1022,7 +1019,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); } @@ -1090,7 +1087,8 @@ bool wxRichTextObject::LayoutToBestSize(wxDC& dc, wxRichTextDrawingContext& cont // If a paragraph, align the whole paragraph. // Problem with this: if we're limited by a floating object, a line may be centered // w.r.t. the smaller resulting box rather than the actual available width. - if (attr.HasAlignment() && !GetContainer()->GetFloatCollector()->HasFloats()) // FIXME: aligning whole paragraph not compatible with floating objects + // FIXME: aligning whole paragraph not compatible with floating objects + if (attr.HasAlignment() && (!wxRichTextBuffer::GetFloatingLayoutMode() || (GetContainer()->GetFloatCollector() && !GetContainer()->GetFloatCollector()->HasFloats()))) { // centering, right-justification if (attr.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE) @@ -1413,7 +1411,7 @@ wxRichTextObject* wxRichTextCompositeObject::GetChildAtPosition(long pos) const } /// Recursively merge all pieces that can be merged. -bool wxRichTextCompositeObject::Defragment(const wxRichTextRange& range) +bool wxRichTextCompositeObject::Defragment(wxRichTextDrawingContext& context, const wxRichTextRange& range) { wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst(); while (node) @@ -1423,24 +1421,84 @@ bool wxRichTextCompositeObject::Defragment(const wxRichTextRange& range) { wxRichTextCompositeObject* composite = wxDynamicCast(child, wxRichTextCompositeObject); if (composite) - composite->Defragment(); + composite->Defragment(context); - if (node->GetNext()) + // Optimization: if there are no virtual attributes, we won't need to + // to split objects in order to paint individually attributed chunks. + // So only merge in this case. + if (!context.GetVirtualAttributesEnabled()) { - wxRichTextObject* nextChild = node->GetNext()->GetData(); - if (child->CanMerge(nextChild) && child->Merge(nextChild)) + if (node->GetNext()) { - nextChild->Dereference(); - m_children.Erase(node->GetNext()); - - // Don't set node -- we'll see if we can merge again with the next - // child. + wxRichTextObject* nextChild = node->GetNext()->GetData(); + if (child->CanMerge(nextChild, context) && child->Merge(nextChild, context)) + { + nextChild->Dereference(); + m_children.Erase(node->GetNext()); + } + else + node = node->GetNext(); } else node = node->GetNext(); } else - node = node->GetNext(); + { + // If we might have virtual attributes, we first see if we have to split + // objects so that they may be painted with potential virtual attributes, + // since text objects can only draw or measure with a single attributes object + // at a time. + wxRichTextObject* childAfterSplit = child; + if (child->CanSplit(context)) + { + childAfterSplit = child->Split(context); + node = m_children.Find(childAfterSplit); + } + + if (node->GetNext()) + { + wxRichTextObject* nextChild = node->GetNext()->GetData(); + + // First split child and nextChild so we have smaller fragments to merge. + // Then Merge only has to test per-object virtual attributes + // because for an object with all the same sub-object attributes, + // then any general virtual attributes should be merged with sub-objects by + // the implementation. + + wxRichTextObject* nextChildAfterSplit = nextChild; + + if (nextChildAfterSplit->CanSplit(context)) + nextChildAfterSplit = nextChild->Split(context); + + bool splitNextChild = nextChild != nextChildAfterSplit; + + // See if we can merge this new fragment with (perhaps the first part of) the next object. + // Note that we use nextChild because if we had split nextChild, the first object always + // remains (and further parts are appended). However we must use childAfterSplit since + // it's the last part of a possibly split child. + + if (childAfterSplit->CanMerge(nextChild, context) && childAfterSplit->Merge(nextChild, context)) + { + nextChild->Dereference(); + m_children.Erase(node->GetNext()); + + // Don't set node -- we'll see if we can merge again with the next + // child. UNLESS we split this or the next child, in which case we know we have to + // move on to the end of the next child. + if (splitNextChild) + node = m_children.Find(nextChildAfterSplit); + } + else + { + if (splitNextChild) + node = m_children.Find(nextChildAfterSplit); // start from the last object in the split + else + node = node->GetNext(); + } + } + else + node = node->GetNext(); + } } else node = node->GetNext(); @@ -1485,7 +1543,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, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* partialExtents) const +bool wxRichTextCompositeObject::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, const wxPoint& position, const wxSize& parentSize, wxArrayInt* partialExtents) const { if (!range.IsWithin(GetRange())) return false; @@ -1506,7 +1564,7 @@ bool wxRichTextCompositeObject::GetRangeSize(const wxRichTextRange& range, wxSiz if (!child->GetRange().IsOutside(range)) { // Floating objects have a zero size within the paragraph. - if (child->IsFloating()) + if (child->IsFloating() && wxRichTextBuffer::GetFloatingLayoutMode()) { if (partialExtents) { @@ -1541,7 +1599,7 @@ bool wxRichTextCompositeObject::GetRangeSize(const wxRichTextRange& range, wxSiz sz.x += childSize.x; descent = wxMax(descent, childDescent); } - else if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, 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), parentSize, p)) { sz.y = wxMax(sz.y, childSize.y); sz.x += childSize.x; @@ -1595,10 +1653,17 @@ void wxRichTextCompositeObject::Invalidate(const wxRichTextRange& invalidRange) } else if (child->IsTopLevel()) { - if (invalidRange == wxRICHTEXT_NONE) - child->Invalidate(wxRICHTEXT_NONE); + if (wxRichTextBuffer::GetFloatingLayoutMode() && child->IsFloating() && GetBuffer()->GetFloatCollector() && GetBuffer()->GetFloatCollector()->HasFloat(child)) + { + // Don't invalidate subhierarchy if we've already been laid out + } else - child->Invalidate(wxRICHTEXT_ALL); // All children must be invalidated if within parent range + { + if (invalidRange == wxRICHTEXT_NONE) + child->Invalidate(wxRICHTEXT_NONE); + else + child->Invalidate(wxRICHTEXT_ALL); // All children must be invalidated if within parent range + } } else child->Invalidate(invalidRange); @@ -1657,7 +1722,6 @@ void wxRichTextParagraphLayoutBox::Init() m_invalidRange = wxRICHTEXT_ALL; - SetMargins(4); m_partialParagraph = false; m_floatCollector = NULL; } @@ -1748,7 +1812,7 @@ int wxRichTextParagraphLayoutBox::HitTest(wxDC& dc, wxRichTextDrawingContext& co return wxRICHTEXT_HITTEST_NONE; int ret = wxRICHTEXT_HITTEST_NONE; - if (m_floatCollector && (flags & wxRICHTEXT_HITTEST_NO_FLOATING_OBJECTS) == 0) + if (wxRichTextBuffer::GetFloatingLayoutMode() && m_floatCollector && (flags & wxRICHTEXT_HITTEST_NO_FLOATING_OBJECTS) == 0) ret = m_floatCollector->HitTest(dc, context, pt, textPosition, obj, flags); if (ret == wxRICHTEXT_HITTEST_NONE) @@ -1763,7 +1827,7 @@ int wxRichTextParagraphLayoutBox::HitTest(wxDC& dc, wxRichTextDrawingContext& co /// Draw the floating objects void wxRichTextParagraphLayoutBox::DrawFloats(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style) { - if (m_floatCollector) + if (wxRichTextBuffer::GetFloatingLayoutMode() && m_floatCollector) m_floatCollector->Draw(dc, context, range, selection, rect, descent, style); } @@ -1788,16 +1852,18 @@ bool wxRichTextParagraphLayoutBox::Draw(wxDC& dc, wxRichTextDrawingContext& cont context.ApplyVirtualAttributes(attr, this); int flags = style; - if (selection.IsValid() && GetParentContainer() != this && selection.WithinSelection(GetRange().GetStart(), GetParentContainer())) + if (selection.IsValid() && GetParentContainer() != this && selection.GetContainer() == this && selection.WithinSelection(GetRange().GetStart(), GetParentContainer())) flags |= wxRICHTEXT_DRAW_SELECTED; // Don't draw guidelines if at top level int theseFlags = flags; if (!GetParent()) theseFlags &= ~wxRICHTEXT_DRAW_GUIDELINES; - DrawBoxAttributes(dc, GetBuffer(), attr, thisRect, theseFlags); + DrawBoxAttributes(dc, GetBuffer(), attr, thisRect, theseFlags, this); - DrawFloats(dc, context, range, selection, rect, descent, style); + if (wxRichTextBuffer::GetFloatingLayoutMode()) + DrawFloats(dc, context, range, selection, rect, descent, style); + wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst(); while (node) { @@ -1870,6 +1936,10 @@ bool wxRichTextParagraphLayoutBox::Layout(wxDC& dc, wxRichTextDrawingContext& co 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(), attr, leftMargin, rightMargin, topMargin, bottomMargin); @@ -1927,7 +1997,8 @@ bool wxRichTextParagraphLayoutBox::Layout(wxDC& dc, wxRichTextDrawingContext& co // Gather information about only those floating objects that will not be formatted, // after which floats will be gathered per-paragraph during layout. - UpdateFloatingObjects(availableSpace, node ? node->GetData() : (wxRichTextObject*) NULL); + if (wxRichTextBuffer::GetFloatingLayoutMode()) + UpdateFloatingObjects(availableSpace, node ? node->GetData() : (wxRichTextObject*) NULL); // A way to force speedy rest-of-buffer layout (the 'else' below) bool forceQuickLayout = false; @@ -2031,7 +2102,7 @@ bool wxRichTextParagraphLayoutBox::Layout(wxDC& dc, wxRichTextDrawingContext& co maxHeight = 0; // topMargin + bottomMargin; // Check the bottom edge of any floating object - if (GetFloatCollector() && GetFloatCollector()->HasFloats()) + if (wxRichTextBuffer::GetFloatingLayoutMode() && GetFloatCollector() && GetFloatCollector()->HasFloats()) { int bottom = GetFloatCollector()->GetLastRectBottom(); if (bottom > maxHeight) @@ -2048,6 +2119,12 @@ bool wxRichTextParagraphLayoutBox::Layout(wxDC& dc, wxRichTextDrawingContext& co 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, @@ -2116,7 +2193,7 @@ bool wxRichTextParagraphLayoutBox::Layout(wxDC& dc, wxRichTextDrawingContext& co } /// Get/set the size for the given range. -bool wxRichTextParagraphLayoutBox::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* WXUNUSED(partialExtents)) const +bool wxRichTextParagraphLayoutBox::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, const wxPoint& position, const wxSize& parentSize, wxArrayInt* WXUNUSED(partialExtents)) const { wxSize sz; @@ -2175,7 +2252,7 @@ bool wxRichTextParagraphLayoutBox::GetRangeSize(const wxRichTextRange& range, wx wxSize childSize; int childDescent = 0; - child->GetRangeSize(rangeToFind, childSize, childDescent, dc, context, flags, position); + child->GetRangeSize(rangeToFind, childSize, childDescent, dc, context, flags, position, parentSize); descent = wxMax(childDescent, descent); @@ -2368,6 +2445,7 @@ wxRichTextRange wxRichTextParagraphLayoutBox::AddParagraph(const wxString& text, wxRichTextAttr* cStyle = & defaultCharStyle; wxRichTextParagraph* para = new wxRichTextParagraph(text, this, pStyle, cStyle); + para->GetAttributes().GetTextBoxAttr().Reset(); AppendChild(para); @@ -2409,6 +2487,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); @@ -2426,6 +2505,7 @@ wxRichTextRange wxRichTextParagraphLayoutBox::AddParagraphs(const wxString& text plainText->SetText(line); para = new wxRichTextParagraph(wxEmptyString, this, pStyle, cStyle); + para->GetAttributes().GetTextBoxAttr().Reset(); AppendChild(para); @@ -2475,6 +2555,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)); @@ -3248,7 +3329,17 @@ bool wxRichTextParagraphLayoutBox::SetStyle(const wxRichTextRange& range, const wxRichTextRemoveStyle(child->GetAttributes(), style); } else if (resetExistingStyle) + { + // Preserve the URL as it's not really a formatting style but a property of the object + wxString url; + if (child->GetAttributes().HasURL() && !characterAttributes.HasURL()) + url = child->GetAttributes().GetURL(); + child->GetAttributes() = characterAttributes; + + if (!url.IsEmpty()) + child->GetAttributes().SetURL(url); + } else { if (applyMinimal) @@ -3338,6 +3429,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()); @@ -3389,7 +3481,7 @@ bool wxRichTextParagraphLayoutBox::GetStyleForRange(const wxRichTextRange& range { style = wxRichTextAttr(); - wxRichTextAttr clashingAttr; + wxRichTextAttr clashingAttrPara, clashingAttrChar; wxRichTextAttr absentAttrPara, absentAttrChar; wxRichTextObjectList::compatibility_iterator node = GetChildren().GetFirst(); @@ -3402,7 +3494,7 @@ bool wxRichTextParagraphLayoutBox::GetStyleForRange(const wxRichTextRange& range { wxRichTextAttr paraStyle = para->GetCombinedAttributes(true /* use box attributes */); - CollectStyle(style, paraStyle, clashingAttr, absentAttrPara); + CollectStyle(style, paraStyle, clashingAttrPara, absentAttrPara); } else { @@ -3412,7 +3504,7 @@ bool wxRichTextParagraphLayoutBox::GetStyleForRange(const wxRichTextRange& range // First collect paragraph attributes only wxRichTextAttr paraStyle = para->GetCombinedAttributes(); paraStyle.SetFlags(paraStyle.GetFlags() & wxTEXT_ATTR_PARAGRAPH); - CollectStyle(style, paraStyle, clashingAttr, absentAttrPara); + CollectStyle(style, paraStyle, clashingAttrPara, absentAttrPara); wxRichTextObjectList::compatibility_iterator childNode = para->GetChildren().GetFirst(); @@ -3426,7 +3518,7 @@ bool wxRichTextParagraphLayoutBox::GetStyleForRange(const wxRichTextRange& range // Now collect character attributes only childStyle.SetFlags(childStyle.GetFlags() & wxTEXT_ATTR_CHARACTER); - CollectStyle(style, childStyle, clashingAttr, absentAttrChar); + CollectStyle(style, childStyle, clashingAttrChar, absentAttrChar); } childNode = childNode->GetNext(); @@ -3478,12 +3570,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 ++; } @@ -3526,7 +3618,7 @@ bool wxRichTextParagraphLayoutBox::HasParagraphAttributes(const wxRichTextRange& wxRichTextApplyStyle(textAttr, para->GetAttributes()); foundCount ++; - if (wxTextAttrEqPartial(textAttr, style)) + if (textAttr.EqPartial(style, false /* strong test */)) matchingCount ++; } } @@ -3543,7 +3635,6 @@ void wxRichTextParagraphLayoutBox::PrepareContent(wxRichTextParagraphLayoutBox& buffer->GetRichTextCtrl()->PrepareContent(container); } - /// Set character or paragraph properties bool wxRichTextParagraphLayoutBox::SetProperties(const wxRichTextRange& range, const wxRichTextProperties& properties, int flags) { @@ -3708,7 +3799,7 @@ void wxRichTextParagraphLayoutBox::Reset() wxRichTextBuffer* buffer = GetBuffer(); if (buffer && buffer->GetRichTextCtrl()) { - wxRichTextEvent event(wxEVT_COMMAND_RICHTEXT_BUFFER_RESET, buffer->GetRichTextCtrl()->GetId()); + wxRichTextEvent event(wxEVT_RICHTEXT_BUFFER_RESET, buffer->GetRichTextCtrl()->GetId()); event.SetEventObject(buffer->GetRichTextCtrl()); event.SetContainer(this); @@ -3785,8 +3876,21 @@ wxRichTextRange wxRichTextParagraphLayoutBox::GetInvalidRange(bool wholeParagrap wxRichTextParagraph* para1 = GetParagraphAtPosition(range.GetStart()); if (para1) range.SetStart(para1->GetRange().GetStart()); - // floating layout make all child should be relayout - range.SetEnd(GetOwnRange().GetEnd()); + + // FIXME: be more intelligent about this. Check if we have floating objects + // before the end of the range. But it's not clear how we can in general + // tell where it's safe to stop laying out. + // Anyway, this code is central to efficiency when laying in floating mode. + if (!wxRichTextBuffer::GetFloatingLayoutMode()) + { + wxRichTextParagraph* para2 = GetParagraphAtPosition(range.GetEnd()); + if (para2) + range.SetEnd(para2->GetRange().GetEnd()); + } + else + // Floating layout means that all children should be laid out, + // because we can't tell how the whole buffer will be affected. + range.SetEnd(GetOwnRange().GetEnd()); } return range; } @@ -3986,12 +4090,18 @@ 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); - } + if (renumber) + { + newPara->GetAttributes().SetBulletNumber(n); + } - n ++; + n ++; + } } else if (!newPara->GetAttributes().GetListStyleName().IsEmpty()) { @@ -4001,6 +4111,7 @@ bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange& range, wx newPara->GetAttributes().SetListStyleName(wxEmptyString); newPara->GetAttributes().SetLeftIndent(0, 0); newPara->GetAttributes().SetBulletText(wxEmptyString); + newPara->GetAttributes().SetBulletStyle(0); // Eliminate the main list-related attributes newPara->GetAttributes().SetFlags(newPara->GetAttributes().GetFlags() & ~wxTEXT_ATTR_LEFT_INDENT & ~wxTEXT_ATTR_BULLET_STYLE & ~wxTEXT_ATTR_BULLET_NUMBER & ~wxTEXT_ATTR_BULLET_TEXT & wxTEXT_ATTR_LIST_STYLE_NAME); @@ -4164,6 +4275,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) @@ -4197,7 +4312,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]); @@ -4276,7 +4392,24 @@ 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 { - if (!previousParagraph->GetAttributes().HasFlag(wxTEXT_ATTR_BULLET_STYLE) || previousParagraph->GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE) + // TODO: add GetNextChild/GetPreviousChild to composite + // 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 || !previousParagraph->GetAttributes().HasFlag(wxTEXT_ATTR_BULLET_STYLE) || previousParagraph->GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE) return false; wxRichTextBuffer* buffer = GetBuffer(); @@ -4375,7 +4508,7 @@ bool wxRichTextParagraph::Draw(wxDC& dc, wxRichTextDrawingContext& context, cons 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) { @@ -4467,7 +4600,7 @@ bool wxRichTextParagraph::Draw(wxDC& dc, wxRichTextDrawingContext& context, cons { wxRichTextObject* child = node2->GetData(); - if (!child->IsFloating() && child->GetRange().GetLength() > 0 && !child->GetRange().IsOutside(lineRange) && !lineRange.IsOutside(range)) + if ((!child->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && child->GetRange().GetLength() > 0 && !child->GetRange().IsOutside(lineRange) && !lineRange.IsOutside(range)) { // Draw this part of the line at the correct position wxRichTextRange objectRange(child->GetRange()); @@ -4540,9 +4673,15 @@ bool wxRichTextParagraph::Layout(wxDC& dc, wxRichTextDrawingContext& context, co // Deal with floating objects firstly before the normal layout wxRichTextBuffer* buffer = GetBuffer(); wxASSERT(buffer); + wxRichTextFloatCollector* collector = GetContainer()->GetFloatCollector(); - wxASSERT(collector); - LayoutFloat(dc, context, rect, style, collector); + + if (wxRichTextBuffer::GetFloatingLayoutMode()) + { + wxASSERT(collector != NULL); + if (collector) + LayoutFloat(dc, context, rect, parentRect, style, collector); + } wxRichTextAttr attr = GetCombinedAttributes(); context.ApplyVirtualAttributes(attr, this); @@ -4559,10 +4698,14 @@ bool wxRichTextParagraph::Layout(wxDC& dc, wxRichTextDrawingContext& context, co int lineSpacing = 0; // Let's assume line spacing of 10 is normal, 15 is 1.5, 20 is 2, etc. - if (attr.HasLineSpacing() && attr.GetLineSpacing() > 0 && attr.GetFont().IsOk()) + if (attr.HasLineSpacing() && attr.GetLineSpacing() > 0 && attr.HasFont()) { - wxCheckSetFont(dc, attr.GetFont()); - lineSpacing = (int) (double(dc.GetCharHeight()) * (double(attr.GetLineSpacing())/10.0 - 1.0)); + wxFont font(buffer->GetFontTable().FindFont(attr)); + if (font.IsOk()) + { + wxCheckSetFont(dc, font); + lineSpacing = (int) (double(dc.GetCharHeight()) * (double(attr.GetLineSpacing())/10.0 - 1.0)); + } } // Start position for each line relative to the paragraph @@ -4600,7 +4743,7 @@ bool wxRichTextParagraph::Layout(wxDC& dc, wxRichTextDrawingContext& context, co int paraDescent = 0; // This calculates the partial text extents - GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, rect.GetPosition(), & partialExtents); + GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, rect.GetPosition(), parentRect.GetSize(), & partialExtents); #else node = m_children.GetFirst(); while (node) @@ -4612,7 +4755,6 @@ bool wxRichTextParagraph::Layout(wxDC& dc, wxRichTextDrawingContext& context, co node = node->GetNext(); } - #endif // Split up lines @@ -4631,8 +4773,8 @@ bool wxRichTextParagraph::Layout(wxDC& dc, wxRichTextDrawingContext& context, co // 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->IsShown() || - (child->GetRange().GetLength() == 0 && maxHeight > spaceBeforePara) + if ((child->IsFloating() && wxRichTextBuffer::GetFloatingLayoutMode()) + || !child->IsShown() || (child->GetRange().GetLength() == 0 && maxHeight > spaceBeforePara) ) { node = node->GetNext(); @@ -4683,7 +4825,7 @@ bool wxRichTextParagraph::Layout(wxDC& dc, wxRichTextDrawingContext& context, co partialExtents.Clear(); // Recalculate the partial text extents since the child object changed size - GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, wxPoint(0,0), & partialExtents); + GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, wxPoint(0,0), parentRect.GetSize(), & partialExtents); } } @@ -4700,10 +4842,10 @@ bool wxRichTextParagraph::Layout(wxDC& dc, wxRichTextDrawingContext& context, co { #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS // Get height only, then the width using the partial extents - GetRangeSize(wxRichTextRange(lastEndPos+1, lastPosToUse), childSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_HEIGHT_ONLY); + GetRangeSize(wxRichTextRange(lastEndPos+1, lastPosToUse), childSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_HEIGHT_ONLY, wxPoint(0,0), parentRect.GetSize()); childSize.x = wxRichTextGetRangeWidth(*this, wxRichTextRange(lastEndPos+1, lastPosToUse), partialExtents); #else - GetRangeSize(wxRichTextRange(lastEndPos+1, lastPosToUse), childSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED, rect.GetPosition()); + GetRangeSize(wxRichTextRange(lastEndPos+1, lastPosToUse), childSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED, rect.GetPosition(), parentRect.GetSize()); #endif } @@ -4725,7 +4867,7 @@ bool wxRichTextParagraph::Layout(wxDC& dc, wxRichTextDrawingContext& context, co 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. @@ -4742,20 +4884,23 @@ bool wxRichTextParagraph::Layout(wxDC& dc, wxRichTextDrawingContext& context, co } lineHeight = wxMax(lineHeight, (lineDescent + lineAscent)); - wxRect floatAvailableRect = collector->GetAvailableRect(rect.y + currentPosition.y, rect.y + currentPosition.y + lineHeight); + if (wxRichTextBuffer::GetFloatingLayoutMode() && collector) + { + 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. + // Adjust availableRect to the space that is available when taking floating objects into account. - if (floatAvailableRect.x + startOffset > availableRect.x) - { - int newX = floatAvailableRect.x + startOffset; - int newW = availableRect.width - (newX - availableRect.x); - availableRect.x = newX; - availableRect.width = newW; - } + if (floatAvailableRect.x + startOffset > availableRect.x) + { + int newX = floatAvailableRect.x + startOffset; + int newW = availableRect.width - (newX - availableRect.x); + availableRect.x = newX; + availableRect.width = newW; + } - if (floatAvailableRect.width < availableRect.width) - availableRect.width = floatAvailableRect.width; + if (floatAvailableRect.width < availableRect.width) + availableRect.width = floatAvailableRect.width; + } currentPosition.x = availableRect.x - rect.x; @@ -4765,22 +4910,20 @@ bool wxRichTextParagraph::Layout(wxDC& dc, wxRichTextDrawingContext& context, co { 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, context, buffer, - attr, child->GetAttributes(), availableRect, parentRect, style); + attr, child->GetAttributes(), availableRect, parentRect.GetSize(), 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, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, wxPoint(0,0), & partialExtents); + GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, wxPoint(0,0), parentRect.GetSize(), & partialExtents); } // Go around the loop finding the available rect for the given floating objects @@ -4793,6 +4936,13 @@ bool wxRichTextParagraph::Layout(wxDC& dc, wxRichTextDrawingContext& context, co } 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 @@ -4802,17 +4952,12 @@ bool wxRichTextParagraph::Layout(wxDC& dc, wxRichTextDrawingContext& context, co if ((lineBreakInThisObject && (childSize.x + currentWidth <= availableRect.width)) || (childSize.x + currentWidth > availableRect.width) +#if 0 || ((childSize.x + currentWidth <= availableRect.width) && !node->GetNext()) - +#endif ) { - 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(); @@ -4835,7 +4980,7 @@ bool wxRichTextParagraph::Layout(wxDC& dc, wxRichTextDrawingContext& context, co // 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); @@ -4849,12 +4994,12 @@ bool wxRichTextParagraph::Layout(wxDC& dc, wxRichTextDrawingContext& context, co if (!child->IsEmpty()) { // Get height only, then the width using the partial extents - GetRangeSize(actualRange, actualSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_HEIGHT_ONLY); + GetRangeSize(actualRange, actualSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_HEIGHT_ONLY, wxPoint(0,0), parentRect.GetSize()); actualSize.x = wxRichTextGetRangeWidth(*this, actualRange, partialExtents); } else #endif - GetRangeSize(actualRange, actualSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED); + GetRangeSize(actualRange, actualSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED, wxPoint(0,0), parentRect.GetSize()); currentWidth = actualSize.x; @@ -4952,6 +5097,44 @@ bool wxRichTextParagraph::Layout(wxDC& dc, wxRichTextDrawingContext& context, co //wxASSERT(!(lastCompletedEndPos != -1 && lastCompletedEndPos < GetRange().GetEnd()-1)); + // Add the last line - it's the current pos -> last para pos + // Substract -1 because the last position is always the end-paragraph position. + if (lastCompletedEndPos <= GetRange().GetEnd()-1) + { + currentPosition.x = (lineCount == 0 ? startPositionFirstLine : startPositionSubsequentLines); + + wxRichTextLine* line = AllocateLine(lineCount); + + wxRichTextRange actualRange(lastCompletedEndPos+1, GetRange().GetEnd()-1); + + // Set relative range so we won't have to change line ranges when paragraphs are moved + line->SetRange(wxRichTextRange(actualRange.GetStart() - GetRange().GetStart(), actualRange.GetEnd() - GetRange().GetStart())); + + line->SetPosition(currentPosition); + + if (lineHeight == 0 && buffer) + { + wxFont font(buffer->GetFontTable().FindFont(attr)); + wxCheckSetFont(dc, font); + lineHeight = dc.GetCharHeight(); + } + + if (maxDescent == 0) + { + int w, h; + dc.GetTextExtent(wxT("X"), & w, &h, & maxDescent); + } + + line->SetSize(wxSize(currentWidth, lineHeight)); + line->SetDescent(maxDescent); + currentPosition.y += lineHeight; + currentPosition.y += lineSpacing; + lineCount ++; + + // Apply paragraph styles such as alignment to the wrapped line + ApplyParagraphStyle(line, attr, availableRect, dc); + } + // Remove remaining unused line objects, if any ClearUnusedLines(lineCount); @@ -4986,7 +5169,7 @@ bool wxRichTextParagraph::Layout(wxDC& dc, wxRichTextDrawingContext& context, co // 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() || !wxRichTextBuffer::GetFloatingLayoutMode()) && child->GetRange().GetLength() != 0 && !wxDynamicCast(child, wxRichTextPlainText)) { if (child->GetCachedSize().x > minWidth) minWidth = child->GetMinSize().x; @@ -5000,7 +5183,6 @@ bool wxRichTextParagraph::Layout(wxDC& dc, wxRichTextDrawingContext& context, co 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 @@ -5054,6 +5236,7 @@ void wxRichTextParagraph::ApplyParagraphStyle(wxRichTextLine* line, const wxRich return; wxPoint pos = line->GetPosition(); + wxPoint originalPos = pos; wxSize size = line->GetSize(); // centering, right-justification @@ -5069,6 +5252,22 @@ void wxRichTextParagraph::ApplyParagraphStyle(wxRichTextLine* line, const wxRich 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 @@ -5154,7 +5353,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, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* partialExtents) const +bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, const wxPoint& position, const wxSize& parentSize, wxArrayInt* partialExtents) const { if (!range.IsWithin(GetRange())) return false; @@ -5178,12 +5377,11 @@ bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange& range, wxSize& siz wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst(); while (node) { - wxRichTextObject* child = node->GetData(); if (!child->GetRange().IsOutside(range)) { // Floating objects have a zero size within the paragraph. - if (child->IsFloating()) + if (child->IsFloating() && wxRichTextBuffer::GetFloatingLayoutMode()) { if (partialExtents) { @@ -5204,7 +5402,7 @@ bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange& range, wxSize& siz rangeToUse.LimitTo(child->GetRange()); 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) { @@ -5269,7 +5467,7 @@ bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange& range, wxSize& siz partialExtents->Add(childSize.x + lastSize); } } - else if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, 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), parentSize, p)) { if (childDescent == 0) { @@ -5347,7 +5545,7 @@ bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange& range, wxSize& siz { wxRichTextObject* child = node2->GetData(); - if (!child->IsFloating() && !child->GetRange().IsOutside(lineRange)) + if ((!child->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && !child->GetRange().IsOutside(lineRange)) { wxRichTextRange rangeToUse = lineRange; rangeToUse.LimitTo(child->GetRange()); @@ -5356,7 +5554,7 @@ bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange& range, wxSize& siz wxSize childSize; int childDescent = 0; - if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y))) + if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y), parentSize)) { if (childDescent == 0) { @@ -5373,7 +5571,6 @@ bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange& range, wxSize& siz maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent)); maxLineWidth += childSize.x; } - descent = wxMax(descent, childDescent); } node2 = node2->GetNext(); @@ -5512,7 +5709,11 @@ int wxRichTextParagraph::HitTest(wxDC& dc, wxRichTextDrawingContext& context, co 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, context, pt, textPosition, obj, contextObj); @@ -5559,7 +5760,7 @@ int wxRichTextParagraph::HitTest(wxDC& dc, wxRichTextDrawingContext& context, co int paraDescent; // This calculates the partial text extents - GetRangeSize(lineRange, paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED, linePos, & partialExtents); + GetRangeSize(lineRange, paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED, linePos, wxDefaultSize, & partialExtents); int lastX = linePos.x; size_t i; @@ -6070,7 +6271,7 @@ void wxRichTextParagraph::ClearDefaultTabs() sm_defaultTabs.Clear(); } -void wxRichTextParagraph::LayoutFloat(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, int style, wxRichTextFloatCollector* floatCollector) +void wxRichTextParagraph::LayoutFloat(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style, wxRichTextFloatCollector* floatCollector) { wxRichTextObjectList::compatibility_iterator node = GetChildren().GetFirst(); while (node) @@ -6078,9 +6279,23 @@ void wxRichTextParagraph::LayoutFloat(wxDC& dc, wxRichTextDrawingContext& contex wxRichTextObject* anchored = node->GetData(); if (anchored && anchored->IsFloating() && !floatCollector->HasFloat(anchored)) { + int x = 0; + wxRichTextAttr parentAttr(GetAttributes()); + context.ApplyVirtualAttributes(parentAttr, this); +#if 1 + // 27-09-2012 + wxRect availableSpace = GetParent()->GetAvailableContentArea(dc, context, rect); + + anchored->LayoutToBestSize(dc, context, GetBuffer(), + parentAttr, anchored->GetAttributes(), + parentRect, availableSpace, + style); + wxSize size = anchored->GetCachedSize(); +#else wxSize size; - int descent, x = 0; + int descent = 0; anchored->GetRangeSize(anchored->GetRange(), size, descent, dc, context, style); +#endif int offsetY = 0; if (anchored->GetAttributes().GetTextBoxAttr().GetTop().IsValid()) @@ -6108,7 +6323,8 @@ void wxRichTextParagraph::LayoutFloat(wxDC& dc, wxRichTextDrawingContext& contex else if (anchored->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_RIGHT) x = rect.x + rect.width - size.x; - anchored->SetPosition(wxPoint(x, pos)); + //anchored->SetPosition(wxPoint(x, pos)); + anchored->Move(wxPoint(x, pos)); // should move children anchored->SetCachedSize(size); floatCollector->CollectFloat(this, anchored); } @@ -6234,11 +6450,17 @@ bool wxRichTextPlainText::Draw(wxDC& dc, wxRichTextDrawingContext& context, cons int offset = GetRange().GetStart(); - // Replace line break characters with spaces wxString str = m_text; + if (context.HasVirtualText(this)) + { + if (!context.GetVirtualText(this, str) || str.Length() != m_text.Length()) + str = m_text; + } + + // Replace line break characters with spaces 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(); @@ -6254,21 +6476,49 @@ bool wxRichTextPlainText::Draw(wxDC& dc, wxRichTextDrawingContext& context, cons 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 @@ -6507,8 +6757,8 @@ bool wxRichTextPlainText::DrawTabbedString(wxDC& dc, const wxRichTextAttr& attr, x += w; } - return true; + return true; } /// Lay the item out @@ -6533,7 +6783,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, wxRichTextDrawingContext& context, 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), const wxPoint& position, const wxSize& WXUNUSED(parentSize), wxArrayInt* partialExtents) const { if (!range.IsWithin(GetRange())) return false; @@ -6558,8 +6808,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; } @@ -6574,12 +6839,18 @@ bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange& range, wxSize& siz long len = range.GetLength(); wxString str(m_text); + if (context.HasVirtualText(this)) + { + if (!context.GetVirtualText(this, str) || str.Length() != m_text.Length()) + str = m_text; + } + wxString toReplace = wxRichTextLineBreakChar; str.Replace(toReplace, wxT(" ")); 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; @@ -6780,15 +7051,44 @@ 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 +bool wxRichTextPlainText::CanMerge(wxRichTextObject* object, wxRichTextDrawingContext& context) const { - return object->GetClassInfo() == CLASSINFO(wxRichTextPlainText) && - (m_text.empty() || (wxTextAttrEq(GetAttributes(), object->GetAttributes()) && m_properties == object->GetProperties())); + // JACS 2013-01-27 + if (!context.GetVirtualAttributesEnabled()) + { + return object->GetClassInfo() == wxCLASSINFO(wxRichTextPlainText) && + (m_text.empty() || (wxTextAttrEq(GetAttributes(), object->GetAttributes()) && m_properties == object->GetProperties())); + } + else + { + wxRichTextPlainText* otherObj = wxDynamicCast(object, wxRichTextPlainText); + if (!otherObj || m_text.empty()) + return false; + + if (!wxTextAttrEq(GetAttributes(), object->GetAttributes()) || !(m_properties == object->GetProperties())) + return false; + + // Check if differing virtual attributes makes it impossible to merge + // these strings. + + bool hasVirtualAttr1 = context.HasVirtualAttributes((wxRichTextObject*) this); + bool hasVirtualAttr2 = context.HasVirtualAttributes((wxRichTextObject*) object); + if (!hasVirtualAttr1 && !hasVirtualAttr2) + return true; + else if (hasVirtualAttr1 != hasVirtualAttr2) + return false; + else + { + wxRichTextAttr virtualAttr1 = context.GetVirtualAttributes((wxRichTextObject*) this); + wxRichTextAttr virtualAttr2 = context.GetVirtualAttributes((wxRichTextObject*) object); + return virtualAttr1 == virtualAttr2; + } + } } /// Returns true if this object merged itself with the given one. /// The calling code will then delete the given object. -bool wxRichTextPlainText::Merge(wxRichTextObject* object) +bool wxRichTextPlainText::Merge(wxRichTextObject* object, wxRichTextDrawingContext& WXUNUSED(context)) { wxRichTextPlainText* textObject = wxDynamicCast(object, wxRichTextPlainText); wxASSERT( textObject != NULL ); @@ -6803,6 +7103,214 @@ bool wxRichTextPlainText::Merge(wxRichTextObject* object) return false; } +bool wxRichTextPlainText::CanSplit(wxRichTextDrawingContext& context) const +{ + // If this object has any virtual attributes at all, whether for the whole object + // or individual ones, we should try splitting it by calling Split. + // Must be more than one character in order to be able to split. + return m_text.Length() > 1 && context.HasVirtualAttributes((wxRichTextObject*) this); +} + +wxRichTextObject* wxRichTextPlainText::Split(wxRichTextDrawingContext& context) +{ + int count = context.GetVirtualSubobjectAttributesCount(this); + if (count > 0 && GetParent()) + { + wxRichTextCompositeObject* parent = wxDynamicCast(GetParent(), wxRichTextCompositeObject); + wxRichTextObjectList::compatibility_iterator node = parent->GetChildren().Find(this); + if (node) + { + const wxRichTextAttr emptyAttr; + wxRichTextObjectList::compatibility_iterator next = node->GetNext(); + + wxArrayInt positions; + wxRichTextAttrArray attributes; + if (context.GetVirtualSubobjectAttributes(this, positions, attributes) && positions.GetCount() > 0) + { + wxASSERT(positions.GetCount() == attributes.GetCount()); + + // We will gather up runs of text with the same virtual attributes + + int len = m_text.Length(); + int i = 0; + + // runStart and runEnd represent the accumulated run with a consistent attribute + // that hasn't yet been appended + int runStart = -1; + int runEnd = -1; + wxRichTextAttr currentAttr; + wxString text = m_text; + wxRichTextPlainText* lastPlainText = this; + + for (i = 0; i < (int) positions.GetCount(); i++) + { + int pos = positions[i]; + wxASSERT(pos >= 0 && pos < len); + if (pos >= 0 && pos < len) + { + const wxRichTextAttr& attr = attributes[i]; + + if (pos == 0) + { + runStart = 0; + currentAttr = attr; + } + // Check if there was a gap from the last known attribute and this. + // In that case, we need to do something with the span of non-attributed text. + else if ((pos-1) > runEnd) + { + if (runEnd == -1) + { + // We hadn't processed anything previously, so the previous run is from the text start + // to just before this position. The current attribute remains empty. + runStart = 0; + runEnd = pos-1; + } + else + { + // If the previous attribute matches the gap's attribute (i.e., no attributes) + // then just extend the run. + if (currentAttr.IsDefault()) + { + runEnd = pos-1; + } + else + { + // We need to add an object, or reuse the existing one. + if (runStart == 0) + { + lastPlainText = this; + SetText(text.Mid(runStart, runEnd - runStart + 1)); + } + else + { + wxRichTextPlainText* obj = new wxRichTextPlainText; + lastPlainText = obj; + obj->SetAttributes(GetAttributes()); + obj->SetProperties(GetProperties()); + obj->SetParent(parent); + + obj->SetText(text.Mid(runStart, runEnd - runStart + 1)); + if (next) + parent->GetChildren().Insert(next, obj); + else + parent->GetChildren().Append(obj); + } + + runStart = runEnd+1; + runEnd = pos-1; + + currentAttr = emptyAttr; + } + } + } + + wxASSERT(runEnd == pos-1); + + // Now we only have to deal with the previous run + if (currentAttr == attr) + { + // If we still have the same attributes, then we + // simply increase the run size. + runEnd = pos; + } + else + { + if (runEnd >= 0) + { + // We need to add an object, or reuse the existing one. + if (runStart == 0) + { + lastPlainText = this; + SetText(text.Mid(runStart, runEnd - runStart + 1)); + } + else + { + wxRichTextPlainText* obj = new wxRichTextPlainText; + lastPlainText = obj; + obj->SetAttributes(GetAttributes()); + obj->SetProperties(GetProperties()); + obj->SetParent(parent); + + obj->SetText(text.Mid(runStart, runEnd - runStart + 1)); + if (next) + parent->GetChildren().Insert(next, obj); + else + parent->GetChildren().Append(obj); + } + } + + runStart = pos; + runEnd = pos; + + currentAttr = attr; + } + } + } + + // We may still have a run to add, and possibly a no-attribute text fragment after that. + // If the whole string was already a single attribute (the run covers the whole string), don't split. + if ((runStart != -1) && !(runStart == 0 && runEnd == (len-1))) + { + // If the current attribute is empty, merge the run with the next fragment + // which by definition (because it's not specified) has empty attributes. + if (currentAttr.IsDefault()) + runEnd = (len-1); + + if (runEnd < (len-1)) + { + // We need to add an object, or reuse the existing one. + if (runStart == 0) + { + lastPlainText = this; + SetText(text.Mid(runStart, runEnd - runStart + 1)); + } + else + { + wxRichTextPlainText* obj = new wxRichTextPlainText; + lastPlainText = obj; + obj->SetAttributes(GetAttributes()); + obj->SetProperties(GetProperties()); + obj->SetParent(parent); + + obj->SetText(text.Mid(runStart, runEnd - runStart + 1)); + if (next) + parent->GetChildren().Insert(next, obj); + else + parent->GetChildren().Append(obj); + } + + runStart = runEnd+1; + runEnd = (len-1); + } + + // Now the last, non-attributed fragment at the end, if any + if ((runStart < len) && !(runStart == 0 && runEnd == (len-1))) + { + wxASSERT(runStart != 0); + + wxRichTextPlainText* obj = new wxRichTextPlainText; + obj->SetAttributes(GetAttributes()); + obj->SetProperties(GetProperties()); + obj->SetParent(parent); + + obj->SetText(text.Mid(runStart, runEnd - runStart + 1)); + if (next) + parent->GetChildren().Insert(next, obj); + else + parent->GetChildren().Append(obj); + + lastPlainText = obj; + } + } + + return lastPlainText; + } + } + } + return this; +} + /// Dump to output stream for debugging void wxRichTextPlainText::Dump(wxTextOutputStream& stream) { @@ -6834,11 +7342,13 @@ long wxRichTextPlainText::GetFirstLineBreakPosition(long pos) IMPLEMENT_DYNAMIC_CLASS(wxRichTextBuffer, wxRichTextParagraphLayoutBox) -wxList wxRichTextBuffer::sm_handlers; -wxList wxRichTextBuffer::sm_drawingHandlers; -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; +bool wxRichTextBuffer::sm_floatingLayoutMode = true; /// Initialisation void wxRichTextBuffer::Init() @@ -6851,6 +7361,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 @@ -6885,6 +7398,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 @@ -6943,7 +7458,10 @@ bool wxRichTextParagraphLayoutBox::InsertParagraphsWithUndo(wxRichTextBuffer* bu /// Submit command to insert the given text bool wxRichTextBuffer::InsertTextWithUndo(long pos, const wxString& text, wxRichTextCtrl* ctrl, int flags) { - return ctrl->GetFocusObject()->InsertTextWithUndo(this, pos, text, ctrl, flags); + if (ctrl) + return ctrl->GetFocusObject()->InsertTextWithUndo(this, pos, text, ctrl, flags); + else + return wxRichTextParagraphLayoutBox::InsertTextWithUndo(this, pos, text, ctrl, flags); } /// Submit command to insert the given text @@ -7005,6 +7523,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); @@ -7032,13 +7552,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) { @@ -7088,6 +7608,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); @@ -7132,6 +7655,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); @@ -7153,6 +7679,83 @@ 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; +} + +bool wxRichTextParagraphLayoutBox::SetObjectPropertiesWithUndo(wxRichTextObject& obj, const wxRichTextProperties& properties, wxRichTextObject* objToSet) +{ + wxRichTextBuffer* buffer = GetBuffer(); + wxCHECK_MSG(buffer, false, wxT("Invalid buffer")); + wxRichTextCtrl* rtc = buffer->GetRichTextCtrl(); + wxCHECK_MSG(rtc, false, wxT("Invalid rtc")); + + wxRichTextAction* action = NULL; + wxRichTextObject* clone = NULL; + + // The object on which to set properties will usually be 'obj', but use objToSet if it's valid. + // This is necessary e.g. on setting a wxRichTextCell's properties, when obj will be the parent table + if (objToSet == NULL) + objToSet = &obj; + + if (rtc->SuppressingUndo()) + objToSet->SetProperties(properties); + else + { + clone = obj.Clone(); + objToSet->SetProperties(properties); + + // The 'true' parameter in the next line says "Ignore first time"; otherwise the objects are prematurely switched + action = new wxRichTextAction(NULL, _("Change Properties"), wxRICHTEXT_CHANGE_OBJECT, buffer, obj.GetParentContainer(), rtc, true); + action->SetOldAndNewObjects(& obj, clone); + action->SetPosition(obj.GetRange().GetStart()); + action->SetRange(obj.GetRange()); + buffer->SubmitAction(action); + } + + return true; +} + /// 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. @@ -7309,11 +7912,14 @@ bool wxRichTextBuffer::SubmitAction(wxRichTextAction* action) if (BatchingUndo() && m_batchedCommand && !SuppressingUndo()) { - wxRichTextCommand* cmd = new wxRichTextCommand(action->GetName()); - cmd->AddAction(action); - cmd->Do(); - cmd->GetActions().Clear(); - delete cmd; + if (!action->GetIgnoreFirstTime()) + { + wxRichTextCommand* cmd = new wxRichTextCommand(action->GetName()); + cmd->AddAction(action); + cmd->Do(); + cmd->GetActions().Clear(); + delete cmd; + } m_batchedCommand->AddAction(action); } @@ -7323,7 +7929,14 @@ bool wxRichTextBuffer::SubmitAction(wxRichTextAction* action) cmd->AddAction(action); // Only store it if we're not suppressing undo. - return GetCommandProcessor()->Submit(cmd, !SuppressingUndo()); + if (!action->GetIgnoreFirstTime()) + { + return GetCommandProcessor()->Submit(cmd, !SuppressingUndo()); + } + else if (!SuppressingUndo()) + { + GetCommandProcessor()->Store(cmd); // Just store it, without Do()ing anything + } } return true; @@ -7349,9 +7962,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()); @@ -7760,6 +8374,7 @@ wxString wxRichTextBuffer::GetExtWildcard(bool combine, bool save, wxArrayInt* t return wildcard; } +#if wxUSE_FFILE && wxUSE_STREAMS /// Load a file bool wxRichTextBuffer::LoadFile(const wxString& filename, wxRichTextFileType type) { @@ -7788,7 +8403,9 @@ bool wxRichTextBuffer::SaveFile(const wxString& filename, wxRichTextFileType typ else return false; } +#endif // wxUSE_FFILE && wxUSE_STREAMS +#if wxUSE_STREAMS /// Load from a stream bool wxRichTextBuffer::LoadFile(wxInputStream& stream, wxRichTextFileType type) { @@ -7817,6 +8434,7 @@ bool wxRichTextBuffer::SaveFile(wxOutputStream& stream, wxRichTextFileType type) else return false; } +#endif // wxUSE_STREAMS /// Copy the range to the clipboard bool wxRichTextBuffer::CopyToClipboard(const wxRichTextRange& range) @@ -8046,7 +8664,7 @@ bool wxRichTextBuffer::SetStyleSheetAndNotify(wxRichTextStyleSheet* sheet) if (GetRichTextCtrl()) winid = GetRichTextCtrl()->GetId(); - wxRichTextEvent event(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACING, winid); + wxRichTextEvent event(wxEVT_RICHTEXT_STYLESHEET_REPLACING, winid); event.SetEventObject(GetRichTextCtrl()); event.SetContainer(GetRichTextCtrl()->GetFocusObject()); event.SetOldStyleSheet(oldSheet); @@ -8066,7 +8684,7 @@ bool wxRichTextBuffer::SetStyleSheetAndNotify(wxRichTextStyleSheet* sheet) SetStyleSheet(sheet); - event.SetEventType(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACED); + event.SetEventType(wxEVT_RICHTEXT_STYLESHEET_REPLACED); event.SetOldStyleSheet(NULL); event.Allow(); @@ -8099,6 +8717,17 @@ int wxRichTextBuffer::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const } } +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()) @@ -8187,7 +8816,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()); @@ -8298,6 +8930,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, const wxPoint& position, const wxSize& parentSize, wxArrayInt* partialExtents) const +{ + wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType()); + if (fieldType) + return fieldType->GetRangeSize((wxRichTextField*) this, range, size, descent, dc, context, flags, position, parentSize, partialExtents); + + return wxRichTextParagraphLayoutBox::GetRangeSize(range, size, descent, dc, context, flags, position, parentSize, 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, const wxPoint& position, const wxSize& parentSize, wxArrayInt* partialExtents) const +{ + if (IsTopLevel(obj)) + return obj->wxRichTextParagraphLayoutBox::GetRangeSize(range, size, descent, dc, context, flags, position, parentSize); + 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): @@ -8311,6 +9282,22 @@ bool wxRichTextCell::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxR return wxRichTextBox::Draw(dc, context, range, selection, rect, descent, style); } +int wxRichTextCell::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags) +{ + int ret = wxRichTextParagraphLayoutBox::HitTest(dc, context, pt, textPosition, obj, contextObj, flags); + if (ret != wxRICHTEXT_HITTEST_NONE) + { + return ret; + } + else + { + textPosition = m_ownRange.GetEnd()-1; + *obj = this; + *contextObj = this; + return wxRICHTEXT_HITTEST_AFTER|wxRICHTEXT_HITTEST_OUTSIDE; + } +} + /// Copy void wxRichTextCell::Copy(const wxRichTextCell& obj) { @@ -8359,18 +9346,20 @@ bool wxRichTextCell::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer) else caption = _("Cell Properties"); + // We don't want position and floating controls for a cell. + wxRichTextSizePage::ShowPositionControls(false); + wxRichTextSizePage::ShowFloatingControls(false); + wxRichTextSizePage::ShowAlignmentControls(true); + wxRichTextObjectPropertiesDialog cellDlg(this, wxGetTopLevelParent(parent), wxID_ANY, caption); cellDlg.SetAttributes(attr); - wxRichTextSizePage* sizePage = wxDynamicCast(cellDlg.FindPage(CLASSINFO(wxRichTextSizePage)), wxRichTextSizePage); - if (sizePage) - { - // We don't want position and floating controls for a cell. - sizePage->ShowPositionControls(false); - sizePage->ShowFloatingControls(false); - } + bool ok = (cellDlg.ShowModal() == wxID_OK); - if (cellDlg.ShowModal() == wxID_OK) + wxRichTextSizePage::ShowPositionControls(true); + wxRichTextSizePage::ShowFloatingControls(true); + + if (ok) { if (multipleCells) { @@ -8391,6 +9380,29 @@ bool wxRichTextCell::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer) return false; } +// The next 2 methods return span values. Note that the default is 1, not 0 +int wxRichTextCell::GetColspan() const +{ + int span = 1; + if (GetProperties().HasProperty(wxT("colspan"))) + { + span = GetProperties().GetPropertyLong(wxT("colspan")); + } + + return span; +} + +int wxRichTextCell::GetRowspan() const +{ + int span = 1; + if (GetProperties().HasProperty(wxT("rowspan"))) + { + span = GetProperties().GetPropertyLong(wxT("rowspan")); + } + + return span; +} + WX_DEFINE_OBJARRAY(wxRichTextObjectPtrArrayArray) IMPLEMENT_DYNAMIC_CLASS(wxRichTextTable, wxRichTextBox) @@ -8404,11 +9416,197 @@ wxRichTextTable::wxRichTextTable(wxRichTextObject* parent): wxRichTextBox(parent // Draws the object. bool wxRichTextTable::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style) { - return wxRichTextBox::Draw(dc, context, range, selection, rect, descent, style); + wxRichTextBox::Draw(dc, context, range, selection, rect, descent, style); + + int colCount = GetColumnCount(); + int rowCount = GetRowCount(); + int col, row; + for (col = 0; col < colCount; col++) + { + for (row = 0; row < rowCount; row++) + { + if (row == 0 || row == (rowCount-1) || col == 0 || col == (colCount-1)) + { + wxRichTextCell* cell = GetCell(row, col); + if (cell && cell->IsShown() && !cell->GetRange().IsOutside(range)) + { + wxRect childRect(cell->GetPosition(), cell->GetCachedSize()); + wxRichTextAttr attr(cell->GetAttributes()); + if (row != 0) + attr.GetTextBoxAttr().GetBorder().GetTop().Reset(); + if (row != (rowCount-1)) + attr.GetTextBoxAttr().GetBorder().GetBottom().Reset(); + if (col != 0) + attr.GetTextBoxAttr().GetBorder().GetLeft().Reset(); + if (col != (colCount-1)) + attr.GetTextBoxAttr().GetBorder().GetRight().Reset(); + + if (attr.GetTextBoxAttr().GetBorder().IsValid()) + { + wxRect boxRect(cell->GetPosition(), cell->GetCachedSize()); + wxRect marginRect = boxRect; + wxRect contentRect, borderRect, paddingRect, outlineRect; + + cell->GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect); + cell->DrawBorder(dc, GetBuffer(), attr.GetTextBoxAttr().GetBorder(), borderRect); + } + } + } + } + } + + return true; } -WX_DECLARE_OBJARRAY(wxRect, wxRichTextRectArray); -WX_DEFINE_OBJARRAY(wxRichTextRectArray); + // Helper function for Layout() that clears the space needed by a cell with rowspan > 1 +int GetRowspanDisplacement(const wxRichTextTable* table, int row, int col, int paddingX, const wxArrayInt& colWidths) +{ + // If one or more cells above-left of this one has rowspan > 1, the affected cells below it + // will have been hidden and have width 0. As a result they are ignored by the layout algorithm, + // and all cells to their right are effectively shifted left. As a result there's no hole for + // the spanning cell to fill. + // So search back along the current row for hidden cells. However there's also the annoying issue of a + // rowspanning cell that also has colspam. So we can't rely on the rowspanning cell being directly above + // the first hidden one we come to. We also can't rely on a cell being hidden only by one type of span; + // there's nothing to stop a cell being hidden by colspan, and then again hidden from above by rowspan. + // The answer is to look above each hidden cell in turn, which I think covers all bases. + int deltaX = 0; + for (int prevcol = 0; prevcol < col; ++prevcol) + { + if (!table->GetCell(row, prevcol)->IsShown()) + { + // We've found a hidden cell. If it's hidden because of colspan to its left, it's + // already been taken into account; but not if there's a rowspanning cell above + for (int prevrow = row-1; prevrow >= 0; --prevrow) + { + wxRichTextCell* cell = table->GetCell(prevrow, prevcol); + if (cell && cell->IsShown()) + { + int rowSpan = cell->GetRowspan(); + if (rowSpan > 1 && rowSpan > (row-prevrow)) + { + // There is a rowspanning cell above above the hidden one, so we need + // to right-shift the index cell by this column's width. Furthermore, + // if the cell also colspans, we need to shift by all affected columns + for (int colSpan = 0; colSpan < cell->GetColspan(); ++colSpan) + deltaX += (colWidths[prevcol+colSpan] + paddingX); + break; + } + } + } + } + } + return deltaX; +} + + // Helper function for Layout() that expands any cell with rowspan > 1 +void ExpandCellsWithRowspan(const wxRichTextTable* table, int paddingY, int& bottomY, wxDC& dc, wxRichTextDrawingContext& context, const wxRect& availableSpace, int style) +{ + // This is called when the table's cell layout is otherwise complete. + // For any cell with rowspan > 1, expand downwards into the row(s) below. + + // Start by finding the current 'y' of the top of each row, plus the bottom of the available area for cells. + // Deduce this from the top of a visible cell in the row below. (If none are visible, the row will be invisible anyway and can be ignored.) + const int rowCount = table->GetRowCount(); + const int colCount = table->GetColumnCount(); + wxArrayInt rowTops; + rowTops.Add(0, rowCount+1); + int row; + for (row = 0; row < rowCount; ++row) + { + for (int column = 0; column < colCount; ++column) + { + wxRichTextCell* cell = table->GetCell(row, column); + if (cell && cell->IsShown()) + { + rowTops[row] = cell->GetPosition().y; + break; + } + } + } + rowTops[rowCount] = bottomY + paddingY; // The table bottom, which was passed to us + + bool needsRelay = false; + + for (row = 0; row < rowCount-1; ++row) // -1 as the bottom row can't rowspan + { + for (int col = 0; col < colCount; ++col) + { + wxRichTextCell* cell = table->GetCell(row, col); + if (cell && cell->IsShown()) + { + int span = cell->GetRowspan(); + if (span > 1) + { + span = wxMin(span, rowCount-row); // Don't try to span below the table! + if (span < 2) + continue; + + int availableHeight = rowTops[row+span] - rowTops[row] - paddingY; + wxSize newSize = wxSize(cell->GetCachedSize().GetWidth(), availableHeight); + wxRect availableCellSpace = wxRect(cell->GetPosition(), newSize); + cell->Invalidate(wxRICHTEXT_ALL); + cell->Layout(dc, context, availableCellSpace, availableSpace, style); + // Ensure there's room in the span to display its contents, else it'll overwrite lower rows + int overhang = cell->GetCachedSize().GetHeight() - availableHeight; + cell->SetCachedSize(newSize); + + if (overhang > 0) + { + // There are 3 things to get right: + // 1) The easiest is the rows below the span: they need to be downshifted by the overhang, and so does the table bottom itself + // 2) The rows within the span, including the one holding this cell, need to be deepened by their share of the overhang + // e.g. if rowspan == 3, each row should increase in depth by 1/3rd of the overhang. + // 3) The cell with the rowspan shouldn't be touched in 2); its height will be set to the whole span later. + int deltaY = overhang / span; + int spare = overhang % span; + + // Each row in the span needs to by deepened by its share of the overhang (give the first row any spare). + // This is achieved by increasing the value stored in the following row's rowTops + for (int spannedRows = 0; spannedRows < span; ++spannedRows) + { + rowTops[row+spannedRows+1] += ((deltaY * (spannedRows+1)) + (spannedRows == 0 ? spare:0)); + } + + // Any rows below the span need shifting down + for (int rowsBelow = row + span+1; rowsBelow <= rowCount; ++rowsBelow) + { + rowTops[rowsBelow] += overhang; + } + + needsRelay = true; + } + } + } + } + } + + if (!needsRelay) + return; + + // There were overflowing rowspanning cells, so layout yet again to make the increased row depths show + for (row = 0; row < rowCount; ++row) + { + for (int col = 0; col < colCount; ++col) + { + wxRichTextCell* cell = table->GetCell(row, col); + if (cell && cell->IsShown()) + { + wxPoint position(cell->GetPosition().x, rowTops[row]); + + // GetRowspan() will usually return 1, but may be greater + wxSize size(cell->GetCachedSize().GetWidth(), rowTops[row + cell->GetRowspan()] - rowTops[row] - paddingY); + + wxRect availableCellSpace = wxRect(position, size); + cell->Invalidate(wxRICHTEXT_ALL); + cell->Layout(dc, context, availableCellSpace, availableSpace, style); + cell->SetCachedSize(size); + } + } + + bottomY = rowTops[rowCount] - paddingY; + } +} // Lays the object out. rect is the space available for layout. Often it will // be the specified overall space for this object, if trying to constrain @@ -8419,7 +9617,7 @@ bool wxRichTextTable::Layout(wxDC& dc, wxRichTextDrawingContext& context, const { SetPosition(rect.GetPosition()); - // TODO: the meaty bit. Calculate sizes of all cells and rows. Try to use + // The meaty bit. Calculate sizes of all cells and rows. Try to use // minimum size if within alloted size, then divide up remaining size // between rows/cols. @@ -8433,12 +9631,13 @@ bool wxRichTextTable::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextAttr attr(GetAttributes()); context.ApplyVirtualAttributes(attr, this); + bool tableHasPercentWidth = (attr.GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE); // 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; - + bool stretchToFitTableWidth = tableHasPercentWidth; + int tableWidth = rect.width; - if (attr.GetTextBoxAttr().GetWidth().IsValid()) + if (attr.GetTextBoxAttr().GetWidth().IsValid() && !tableHasPercentWidth) { tableWidth = converter.GetPixels(attr.GetTextBoxAttr().GetWidth()); @@ -8484,9 +9683,14 @@ bool wxRichTextTable::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxArrayInt absoluteColWidths; absoluteColWidths.Add(0, m_colCount); - // wxArrayInt absoluteColWidthsSpanning(m_colCount); + wxArrayInt percentageColWidths; percentageColWidths.Add(0, m_colCount); + + // The required width of a column calculated from the content, in case we don't specify any widths + wxArrayInt maxUnspecifiedColumnWidths; + maxUnspecifiedColumnWidths.Add(0, m_colCount); + // wxArrayInt percentageColWidthsSpanning(m_colCount); // These are only relevant when the first column contains spanning information. // wxArrayInt columnSpans(m_colCount); // Each contains 1 for non-spanning cell, > 1 for spanning cell. @@ -8535,12 +9739,9 @@ bool wxRichTextTable::Layout(wxDC& dc, wxRichTextDrawingContext& context, const { for (i = 0; i < m_colCount; i++) { - wxRichTextBox* cell = GetCell(j, i); - int colSpan = 1, rowSpan = 1; - if (cell->GetProperties().HasProperty(wxT("colspan"))) - colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan")); - if (cell->GetProperties().HasProperty(wxT("rowspan"))) - rowSpan = cell->GetProperties().GetPropertyLong(wxT("rowspan")); + wxRichTextCell* cell = GetCell(j, i); + int colSpan = cell->GetColspan(); + int rowSpan = cell->GetRowspan(); if (colSpan > 1 || rowSpan > 1) { rectArray.Add(wxRect(i, j, colSpan, rowSpan)); @@ -8552,18 +9753,16 @@ bool wxRichTextTable::Layout(wxDC& dc, wxRichTextDrawingContext& context, const { for (i = 0; i < m_colCount; i++) { - wxRichTextBox* cell = GetCell(j, i); + wxRichTextCell* cell = GetCell(j, i); if (rectArray.GetCount() == 0) { cell->Show(true); } else { - int colSpan = 1, rowSpan = 1; - if (cell->GetProperties().HasProperty(wxT("colspan"))) - colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan")); - if (cell->GetProperties().HasProperty(wxT("rowspan"))) - rowSpan = cell->GetProperties().GetPropertyLong(wxT("rowspan")); + int colSpan = cell->GetColspan(); + int rowSpan = cell->GetRowspan(); + if (colSpan > 1 || rowSpan > 1) { // Assume all spanning cells are shown @@ -8586,7 +9785,7 @@ bool wxRichTextTable::Layout(wxDC& dc, wxRichTextDrawingContext& context, const } } - // TODO: find the first spanned cell in each row that spans the most columns and doesn't + // Find the first spanned cell in each row that spans the most columns and doesn't // overlap with a spanned cell starting at a previous column position. // This means we need to keep an array of rects so we can check. However // it does also mean that some spans simply may not be taken into account @@ -8626,12 +9825,10 @@ bool wxRichTextTable::Layout(wxDC& dc, wxRichTextDrawingContext& context, const for (i = 0; i < m_colCount; i++) { - wxRichTextBox* cell = GetCell(j, i); + wxRichTextCell* cell = GetCell(j, i); if (cell->IsShown()) { - int colSpan = 1; - if (cell->GetProperties().HasProperty(wxT("colspan"))) - colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan")); + int colSpan = cell->GetColspan(); // Lay out cell to find min/max widths cell->Invalidate(wxRICHTEXT_ALL); @@ -8673,6 +9870,8 @@ bool wxRichTextTable::Layout(wxDC& dc, wxRichTextDrawingContext& context, const if (cell->GetMinSize().x > 0 && absoluteCellWidth !=1 && cell->GetMinSize().x > absoluteCellWidth) absoluteCellWidth = cell->GetMinSize().x; } + else + maxUnspecifiedColumnWidths[i] = wxMax(cell->GetMaxSize().x, maxUnspecifiedColumnWidths[i]); if (absoluteCellWidth != -1) { @@ -8751,13 +9950,10 @@ bool wxRichTextTable::Layout(wxDC& dc, wxRichTextDrawingContext& context, const for (i = 0; i < m_colCount; i++) { - wxRichTextBox* cell = GetCell(j, i); + wxRichTextCell* cell = GetCell(j, i); if (cell->IsShown()) { - int colSpan = 1; - if (cell->GetProperties().HasProperty(wxT("colspan"))) - colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan")); - + int colSpan = cell->GetColspan(); if (colSpan > 1) { int spans = wxMin(colSpan, m_colCount - i); @@ -8783,6 +9979,9 @@ bool wxRichTextTable::Layout(wxDC& dc, wxRichTextDrawingContext& context, const // then we simply stop constraining the columns; instead, we'll just fit the spanning // cells to the columns later. cellWidth = cell->GetMinSize().x; + + maxUnspecifiedColumnWidths[i] = wxMax(cell->GetMaxSize().x, maxUnspecifiedColumnWidths[i]); + if (cell->GetMaxSize().x > cellWidth) cellWidth = cell->GetMaxSize().x; } @@ -8833,6 +10032,14 @@ bool wxRichTextTable::Layout(wxDC& dc, wxRichTextDrawingContext& context, const } } + // (3.1) if a column has zero width, make it the maximum unspecified width (i.e. using + // the cell's contents to calculate the width) + for (i = 0; i < m_colCount; i++) + { + if (colWidths[i] == 0) + colWidths[i] = maxUnspecifiedColumnWidths[i]; + } + // (4) Next, share any remaining space out between columns that have not yet been calculated. // TODO: take into account min widths of columns within the span int tableWidthMinusPadding = internalTableWidth - (m_colCount-1)*paddingX; @@ -8840,7 +10047,6 @@ bool wxRichTextTable::Layout(wxDC& dc, wxRichTextDrawingContext& context, const int stretchColCount = 0; for (i = 0; i < m_colCount; i++) { - // TODO: we need to take into account min widths. // Subtract min width from width left, then // add the colShare to the min width if (colWidths[i] > 0) // absolute or proportional width has been specified @@ -8897,16 +10103,7 @@ bool wxRichTextTable::Layout(wxDC& dc, wxRichTextDrawingContext& context, const } } - // TODO: if spanned cells have no specified or max width, make them the - // as big as the columns they span. Do this for all spanned cells in all - // rows, of course. Size any spanned cells left over at the end - even if they - // have width > 0, make sure they're limited to the appropriate column edge. - - /* - Sort out confusion between content width - and overall width later. For now, assume we specify overall width. - So, now we've laid out the table to fit into the given space and have used specified widths and minimum widths. @@ -8946,19 +10143,16 @@ bool wxRichTextTable::Layout(wxDC& dc, wxRichTextDrawingContext& context, const if (colWidths[i] > 0) // absolute or proportional width has been specified { - int colSpan = 1; - if (cell->GetProperties().HasProperty(wxT("colspan"))) - colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan")); - + int colSpan = cell->GetColspan(); wxRect availableCellSpace; - // TODO: take into acount spans + // Take into account spans if (colSpan > 1) { // Calculate the size of this spanning cell from its constituent columns - int xx = x; + int xx = 0; int spans = wxMin(colSpan, m_colCount - i); - for (k = i; k < spans; k++) + for (k = i; k < (i+spans); k++) { if (k != i) xx += paddingX; @@ -8972,6 +10166,10 @@ bool wxRichTextTable::Layout(wxDC& dc, wxRichTextDrawingContext& context, const // Store actual width so we can force cell to be the appropriate width on the final loop actualWidths[i] = availableCellSpace.GetWidth(); + // We now need to shift right by the width of any rowspanning cells above-left of us + int deltaX = GetRowspanDisplacement(this, j, i, paddingX, colWidths); + availableCellSpace.SetX(availableCellSpace.GetX() + deltaX); + // Lay out cell cell->Invalidate(wxRICHTEXT_ALL); cell->Layout(dc, context, availableCellSpace, availableSpace, style); @@ -8979,7 +10177,7 @@ bool wxRichTextTable::Layout(wxDC& dc, wxRichTextDrawingContext& context, const // TODO: use GetCachedSize().x to compute 'natural' size x += (availableCellSpace.GetWidth() + paddingX); - if (cell->GetCachedSize().y > maxCellHeight) + if ((cell->GetCachedSize().y > maxCellHeight) && (cell->GetRowspan() < 2)) maxCellHeight = cell->GetCachedSize().y; } } @@ -9009,6 +10207,9 @@ bool wxRichTextTable::Layout(wxDC& dc, wxRichTextDrawingContext& context, const if (j < (m_rowCount-1)) y += paddingY; } + + // Finally we need to expand any cell with rowspan > 1. We couldn't earlier; lower rows' heights weren't known + ExpandCellsWithRowspan(this, paddingY, y, dc, context, availableSpace, style); // We need to add back the margins etc. { @@ -9028,18 +10229,6 @@ bool wxRichTextTable::Layout(wxDC& dc, wxRichTextDrawingContext& context, const SetMinSize(GetCachedSize()); } - // TODO: currently we use either a fixed table width or the parent's size. - // We also want to be able to calculate the table width from its content, - // whether using fixed column widths or cell content min/max width. - // Probably need a boolean flag to say whether we need to stretch cells - // to fit the table width, or to simply use min/max cell widths. The - // trouble with this is that if cell widths are not specified, they - // will be tiny; we could use arbitrary defaults but this seems unsatisfactory. - // Anyway, ignoring that problem, we probably need to factor layout into a function - // that can can calculate the maximum unconstrained layout in case table size is - // not specified. Then LayoutToBestSize() can choose to use either parent size to - // constrain Layout(), or the previously-calculated max size to constraint layout. - return true; } @@ -9129,9 +10318,9 @@ void wxRichTextTable::CalculateRange(long start, long& end) } // Gets the range size. -bool wxRichTextTable::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* partialExtents) const +bool wxRichTextTable::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, const wxPoint& position, const wxSize& parentSize, wxArrayInt* partialExtents) const { - return wxRichTextBox::GetRangeSize(range, size, descent, dc, context, flags, position, partialExtents); + return wxRichTextBox::GetRangeSize(range, size, descent, dc, context, flags, position, parentSize, partialExtents); } // Deletes content in the given range. @@ -9177,12 +10366,17 @@ void wxRichTextTable::ClearTable() { m_cells.Clear(); DeleteChildren(); + m_rowCount = 0; + m_colCount = 0; } bool wxRichTextTable::CreateTable(int rows, int cols) { ClearTable(); + wxRichTextAttr cellattr; + cellattr.SetTextColour(GetBasicStyle().GetTextColour()); + m_rowCount = rows; m_colCount = cols; @@ -9195,6 +10389,8 @@ bool wxRichTextTable::CreateTable(int rows, int cols) for (j = 0; j < cols; j++) { wxRichTextCell* cell = new wxRichTextCell; + cell->GetAttributes() = cellattr; + AppendChild(cell); cell->AddParagraph(wxEmptyString); @@ -9329,12 +10525,67 @@ bool wxRichTextTable::SetCellStyle(const wxRichTextSelection& selection, const w return true; } +wxPosition wxRichTextTable::GetFocusedCell() const +{ + wxPosition position(-1, -1); + const wxRichTextObject* focus = GetBuffer()->GetRichTextCtrl()->GetFocusObject(); + + for (int row = 0; row < GetRowCount(); ++row) + { + for (int col = 0; col < GetColumnCount(); ++col) + { + if (GetCell(row, col) == focus) + { + position.SetRow(row); + position.SetCol(col); + return position; + } + } + } + + return position; +} + +int wxRichTextTable::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags) +{ + for (int row = 0; row < GetRowCount(); ++row) + { + for (int col = 0; col < GetColumnCount(); ++col) + { + wxRichTextCell* cell = GetCell(row, col); + if (cell->wxRichTextObject::HitTest(dc, context, pt, textPosition, obj, contextObj, flags) != wxRICHTEXT_HITTEST_NONE) + { + return cell->HitTest(dc, context, pt, textPosition, obj, contextObj, flags); + } + } + } + + return wxRICHTEXT_HITTEST_NONE; +} + bool wxRichTextTable::DeleteRows(int startRow, int noRows) { - wxASSERT((startRow + noRows) < m_rowCount); - if ((startRow + noRows) >= m_rowCount) + wxASSERT((startRow + noRows) <= m_rowCount); + if ((startRow + noRows) > m_rowCount) return false; + wxCHECK_MSG(noRows != m_rowCount, false, "Trying to delete all the cells in a table"); + + wxRichTextBuffer* buffer = GetBuffer(); + wxRichTextCtrl* rtc = buffer->GetRichTextCtrl(); + + wxRichTextAction* action = NULL; + wxRichTextTable* clone = NULL; + if (!rtc->SuppressingUndo()) + { + // Create a clone containing the current state of the table. It will be used to Undo the action + clone = wxStaticCast(this->Clone(), wxRichTextTable); + clone->SetParent(GetParent()); + action = new wxRichTextAction(NULL, _("Delete Row"), wxRICHTEXT_CHANGE_OBJECT, buffer, this, rtc); + action->SetObject(this); + action->SetPosition(GetRange().GetStart()); + } + int i, j; for (i = startRow; i < (startRow+noRows); i++) { @@ -9352,25 +10603,50 @@ bool wxRichTextTable::DeleteRows(int startRow, int noRows) m_rowCount = m_rowCount - noRows; + if (!rtc->SuppressingUndo()) + { + buffer->SubmitAction(action); + // Finally store the original-state clone; doing so earlier would cause various failures + action->StoreObject(clone); + } + return true; } bool wxRichTextTable::DeleteColumns(int startCol, int noCols) { - wxASSERT((startCol + noCols) < m_colCount); - if ((startCol + noCols) >= m_colCount) + wxASSERT((startCol + noCols) <= m_colCount); + if ((startCol + noCols) > m_colCount) return false; + wxCHECK_MSG(noCols != m_colCount, false, "Trying to delete all the cells in a table"); + + wxRichTextBuffer* buffer = GetBuffer(); + wxRichTextCtrl* rtc = buffer->GetRichTextCtrl(); + + wxRichTextAction* action = NULL; + wxRichTextTable* clone = NULL; + if (!rtc->SuppressingUndo()) + { + // Create a clone containing the current state of the table. It will be used to Undo the action + clone = wxStaticCast(this->Clone(), wxRichTextTable); + clone->SetParent(GetParent()); + action = new wxRichTextAction(NULL, _("Delete Column"), wxRICHTEXT_CHANGE_OBJECT, buffer, this, rtc); + action->SetObject(this); + action->SetPosition(GetRange().GetStart()); + } + bool deleteRows = (noCols == m_colCount); int i, j; for (i = 0; i < m_rowCount; i++) { wxRichTextObjectPtrArray& colArray = m_cells[deleteRows ? 0 : i]; - for (j = startCol; j < (startCol+noCols); j++) + for (j = 0; j < noCols; j++) { - wxRichTextObject* cell = colArray[j]; + wxRichTextObject* cell = colArray[startCol]; RemoveChild(cell, true); + colArray.RemoveAt(startCol); } if (deleteRows) @@ -9381,6 +10657,13 @@ bool wxRichTextTable::DeleteColumns(int startCol, int noCols) m_rowCount = 0; m_colCount = m_colCount - noCols; + if (!rtc->SuppressingUndo()) + { + buffer->SubmitAction(action); + // Finally store the original-state clone; doing so earlier would cause various failures + action->StoreObject(clone); + } + return true; } @@ -9390,6 +10673,24 @@ bool wxRichTextTable::AddRows(int startRow, int noRows, const wxRichTextAttr& at if (startRow > m_rowCount) return false; + wxRichTextBuffer* buffer = GetBuffer(); + wxRichTextAction* action = NULL; + wxRichTextTable* clone = NULL; + + if (!buffer->GetRichTextCtrl()->SuppressingUndo()) + { + // Create a clone containing the current state of the table. It will be used to Undo the action + clone = wxStaticCast(this->Clone(), wxRichTextTable); + clone->SetParent(GetParent()); + action = new wxRichTextAction(NULL, _("Add Row"), wxRICHTEXT_CHANGE_OBJECT, buffer, this, buffer->GetRichTextCtrl()); + action->SetObject(this); + action->SetPosition(GetRange().GetStart()); + } + + wxRichTextAttr cellattr = attr; + if (!cellattr.GetTextColour().IsOk()) + cellattr.SetTextColour(buffer->GetBasicStyle().GetTextColour()); + int i, j; for (i = 0; i < noRows; i++) { @@ -9409,14 +10710,23 @@ bool wxRichTextTable::AddRows(int startRow, int noRows, const wxRichTextAttr& at for (j = 0; j < m_colCount; j++) { wxRichTextCell* cell = new wxRichTextCell; - cell->GetAttributes() = attr; + cell->GetAttributes() = cellattr; AppendChild(cell); + cell->AddParagraph(wxEmptyString); colArray.Add(cell); } } m_rowCount = m_rowCount + noRows; + + if (!buffer->GetRichTextCtrl()->SuppressingUndo()) + { + buffer->SubmitAction(action); + // Finally store the original-state clone; doing so earlier would cause various failures + action->StoreObject(clone); + } + return true; } @@ -9426,6 +10736,24 @@ bool wxRichTextTable::AddColumns(int startCol, int noCols, const wxRichTextAttr& if (startCol > m_colCount) return false; + wxRichTextBuffer* buffer = GetBuffer(); + wxRichTextAction* action = NULL; + wxRichTextTable* clone = NULL; + + if (!buffer->GetRichTextCtrl()->SuppressingUndo()) + { + // Create a clone containing the current state of the table. It will be used to Undo the action + clone = wxStaticCast(this->Clone(), wxRichTextTable); + clone->SetParent(GetParent()); + action = new wxRichTextAction(NULL, _("Add Column"), wxRICHTEXT_CHANGE_OBJECT, buffer, this, buffer->GetRichTextCtrl()); + action->SetObject(this); + action->SetPosition(GetRange().GetStart()); + } + + wxRichTextAttr cellattr = attr; + if (!cellattr.GetTextColour().IsOk()) + cellattr.SetTextColour(buffer->GetBasicStyle().GetTextColour()); + int i, j; for (i = 0; i < m_rowCount; i++) { @@ -9433,9 +10761,10 @@ bool wxRichTextTable::AddColumns(int startCol, int noCols, const wxRichTextAttr& for (j = 0; j < noCols; j++) { wxRichTextCell* cell = new wxRichTextCell; - cell->GetAttributes() = attr; + cell->GetAttributes() = cellattr; AppendChild(cell); + cell->AddParagraph(wxEmptyString); if (startCol == m_colCount) colArray.Add(cell); @@ -9446,6 +10775,13 @@ bool wxRichTextTable::AddColumns(int startCol, int noCols, const wxRichTextAttr& m_colCount = m_colCount + noCols; + if (!buffer->GetRichTextCtrl()->SuppressingUndo()) + { + buffer->SubmitAction(action); + // Finally store the original-state clone; doing so earlier would cause various failures + action->StoreObject(clone); + } + return true; } @@ -9464,6 +10800,90 @@ bool wxRichTextTable::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer) return false; } +bool wxRichTextTableBlock::ComputeBlockForSelection(wxRichTextTable* table, wxRichTextCtrl* ctrl, bool requireCellSelection) +{ + if (!ctrl) + return false; + + ColStart() = 0; + ColEnd() = table->GetColumnCount()-1; + RowStart() = 0; + RowEnd() = table->GetRowCount()-1; + + wxRichTextSelection selection = ctrl->GetSelection(); + if (selection.IsValid() && selection.GetContainer() == table) + { + // Start with an invalid block and increase. + wxRichTextTableBlock selBlock(-1, -1, -1, -1); + wxRichTextRangeArray ranges = selection.GetRanges(); + int row, col; + for (row = 0; row < table->GetRowCount(); row++) + { + for (col = 0; col < table->GetColumnCount(); col++) + { + if (selection.WithinSelection(table->GetCell(row, col)->GetRange().GetStart())) + { + if (selBlock.ColStart() == -1) + selBlock.ColStart() = col; + if (selBlock.ColEnd() == -1) + selBlock.ColEnd() = col; + if (col < selBlock.ColStart()) + selBlock.ColStart() = col; + if (col > selBlock.ColEnd()) + selBlock.ColEnd() = col; + + if (selBlock.RowStart() == -1) + selBlock.RowStart() = row; + if (selBlock.RowEnd() == -1) + selBlock.RowEnd() = row; + if (row < selBlock.RowStart()) + selBlock.RowStart() = row; + if (row > selBlock.RowEnd()) + selBlock.RowEnd() = row; + } + } + } + + if (selBlock.RowStart() != -1 && selBlock.RowEnd() != -1 && selBlock.ColStart() != -1 && selBlock.ColEnd() != -1) + (*this) = selBlock; + } + else + { + // See if a whole cell's contents is selected, in which case we can treat the cell as selected. + // wxRTC lacks the ability to select a single cell. + wxRichTextCell* cell = wxDynamicCast(ctrl->GetFocusObject(), wxRichTextCell); + if (cell && (!requireCellSelection || (ctrl->HasSelection() && ctrl->GetSelectionRange() == cell->GetOwnRange()))) + { + int row, col; + if (table->GetCellRowColumnPosition(cell->GetRange().GetStart(), row, col)) + { + RowStart() = row; + RowEnd() = row; + ColStart() = col; + ColEnd() = col; + } + } + } + + return true; +} + +// Does this block represent the whole table? +bool wxRichTextTableBlock::IsWholeTable(wxRichTextTable* table) const +{ + return (ColStart() == 0 && RowStart() == 0 && ColEnd() == (table->GetColumnCount()-1) && RowEnd() == (table->GetRowCount()-1)); +} + +// Returns the cell focused in the table, if any +wxRichTextCell* wxRichTextTableBlock::GetFocusedCell(wxRichTextCtrl* ctrl) +{ + if (!ctrl) + return NULL; + + wxRichTextCell* cell = wxDynamicCast(ctrl->GetFocusObject(), wxRichTextCell); + return cell; +} + /* * Module to initialise and clean up handlers */ @@ -9478,12 +10898,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(); @@ -9500,8 +10933,8 @@ IMPLEMENT_DYNAMIC_CLASS(wxRichTextModule, wxModule) void wxRichTextModuleInit() { wxModule* module = new wxRichTextModule; - module->Init(); wxModule::RegisterModule(module); + wxModule::InitializeModules(); } @@ -9614,8 +11047,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()); @@ -9702,7 +11135,7 @@ bool wxRichTextAction::Do() UpdateAppearance(newCaretPosition, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions, true /* do */); wxRichTextEvent cmdEvent( - wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED, + wxEVT_RICHTEXT_CONTENT_INSERTED, m_ctrl ? m_ctrl->GetId() : -1); cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer); cmdEvent.SetRange(GetRange()); @@ -9735,7 +11168,7 @@ bool wxRichTextAction::Do() UpdateAppearance(caretPos, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions, true /* do */); wxRichTextEvent cmdEvent( - wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED, + wxEVT_RICHTEXT_CONTENT_DELETED, m_ctrl ? m_ctrl->GetId() : -1); cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer); cmdEvent.SetRange(GetRange()); @@ -9751,14 +11184,20 @@ bool wxRichTextAction::Do() { ApplyParagraphs(GetNewParagraphs()); - // 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()); - + // Invalidate the whole buffer if there were floating objects + if (wxRichTextBuffer::GetFloatingLayoutMode() && container->GetFloatingObjectCount() > 0) + m_buffer->InvalidateHierarchy(wxRICHTEXT_ALL); + else + { + // 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()); + } + UpdateAppearance(GetPosition()); wxRichTextEvent cmdEvent( - m_cmdId == wxRICHTEXT_CHANGE_STYLE ? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED : wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED, + m_cmdId == wxRICHTEXT_CHANGE_STYLE ? wxEVT_RICHTEXT_STYLE_CHANGED : wxEVT_RICHTEXT_PROPERTIES_CHANGED, m_ctrl ? m_ctrl->GetId() : -1); cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer); cmdEvent.SetRange(GetRange()); @@ -9781,12 +11220,16 @@ bool wxRichTextAction::Do() // 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()); + // Invalidate the whole buffer if there were floating objects + if (wxRichTextBuffer::GetFloatingLayoutMode() && container->GetFloatingObjectCount() > 0) + m_buffer->InvalidateHierarchy(wxRICHTEXT_ALL); + else + container->InvalidateHierarchy(GetRange()); UpdateAppearance(GetPosition()); wxRichTextEvent cmdEvent( - wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED, + wxEVT_RICHTEXT_STYLE_CHANGED, m_ctrl ? m_ctrl->GetId() : -1); cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer); cmdEvent.SetRange(GetRange()); @@ -9800,10 +11243,21 @@ bool wxRichTextAction::Do() case wxRICHTEXT_CHANGE_OBJECT: { wxRichTextObject* obj = m_objectAddress.GetObject(m_buffer); - // wxRichTextObject* obj = container->GetChildAtPosition(GetRange().GetStart()); - if (obj && m_object) + if (obj && m_object && m_ctrl) { - wxRichTextObjectList::compatibility_iterator node = container->GetChildren().Find(obj); + // The plan is to swap the current object with the stored, previous-state, clone + // We can't get 'node' from the containing buffer (as it doesn't directly store objects) + // so use the parent paragraph + wxRichTextParagraph* para = wxDynamicCast(obj->GetParent(), wxRichTextParagraph); + wxCHECK_MSG(para, false, "Invalid parent paragraph"); + + // The stored object, m_object, may have a stale parent paragraph. This would cause + // a crash during layout, so use obj's parent para, which should be the correct one. + // (An alternative would be to return the parent too from m_objectAddress.GetObject(), + // or to set obj's parent there before returning) + m_object->SetParent(para); + + wxRichTextObjectList::compatibility_iterator node = para->GetChildren().Find(obj); if (node) { wxRichTextObject* obj = node->GetData(); @@ -9812,11 +11266,21 @@ bool wxRichTextAction::Do() } } + // We can't rely on the current focus-object remaining valid, if it's e.g. a table's cell. + // And we can't cope with this in the calling code: a user may later click in the cell + // before deciding to Undo() or Redo(). So play safe and set focus to the buffer. + if (m_ctrl) + m_ctrl->SetFocusObject(m_buffer, false); + // 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()); + // Invalidate the whole buffer if there were floating objects + if (wxRichTextBuffer::GetFloatingLayoutMode() && container->GetFloatingObjectCount() > 0) + m_buffer->InvalidateHierarchy(wxRICHTEXT_ALL); + else + container->InvalidateHierarchy(GetRange()); - UpdateAppearance(GetPosition()); + UpdateAppearance(GetPosition(), true); // TODO: send new kind of modification event @@ -9851,6 +11315,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())); @@ -9860,7 +11325,7 @@ bool wxRichTextAction::Undo() UpdateAppearance(newCaretPosition, true, /* send update event */ & optimizationLineCharPositions, & optimizationLineYPositions, false /* undo */); wxRichTextEvent cmdEvent( - wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED, + wxEVT_RICHTEXT_CONTENT_DELETED, m_ctrl ? m_ctrl->GetId() : -1); cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer); cmdEvent.SetRange(GetRange()); @@ -9882,6 +11347,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()); @@ -9889,7 +11355,7 @@ bool wxRichTextAction::Undo() UpdateAppearance(GetPosition(), true, /* send update event */ & optimizationLineCharPositions, & optimizationLineYPositions, false /* undo */); wxRichTextEvent cmdEvent( - wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED, + wxEVT_RICHTEXT_CONTENT_INSERTED, m_ctrl ? m_ctrl->GetId() : -1); cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer); cmdEvent.SetRange(GetRange()); @@ -9911,7 +11377,7 @@ bool wxRichTextAction::Undo() UpdateAppearance(GetPosition()); wxRichTextEvent cmdEvent( - m_cmdId == wxRICHTEXT_CHANGE_STYLE ? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED : wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED, + m_cmdId == wxRICHTEXT_CHANGE_STYLE ? wxEVT_RICHTEXT_STYLE_CHANGED : wxEVT_RICHTEXT_PROPERTIES_CHANGED, m_ctrl ? m_ctrl->GetId() : -1); cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer); cmdEvent.SetRange(GetRange()); @@ -9955,7 +11421,7 @@ void wxRichTextAction::UpdateAppearance(long caretPosition, bool sendUpdateEvent // Refresh everything if there were floating objects or the container changed size // (we can't yet optimize in these cases, since more complex interaction with other content occurs) - if (container->GetFloatingObjectCount() > 0 || (container->GetParent() && containerRect != container->GetRect())) + if ((wxRichTextBuffer::GetFloatingLayoutMode() && container->GetFloatingObjectCount() > 0) || (container->GetParent() && containerRect != container->GetRect())) { m_ctrl->Refresh(false); } @@ -9967,8 +11433,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; @@ -9986,13 +11452,16 @@ void wxRichTextAction::UpdateAppearance(long caretPosition, bool sendUpdateEvent // find the first line which is being drawn at the same position as it was // before. Since we're talking about a simple insertion, we can assume // that the rest of the window does not need to be redrawn. + long pos = GetRange().GetStart(); - wxRichTextParagraph* para = container->GetParagraphAtPosition(GetPosition()); + wxRichTextParagraph* para = container->GetParagraphAtPosition(pos, false /* is not caret pos */); // Since we support floating layout, we should redraw the whole para instead of just // the first line touching the invalid range. if (para) { - firstY = para->GetPosition().y; + // In case something was drawn above the paragraph, + // such as a line break, allow a little extra. + firstY = para->GetPosition().y - 4; } wxRichTextObjectList::compatibility_iterator node = container->GetChildren().Find(para); @@ -10040,7 +11509,7 @@ void wxRichTextAction::UpdateAppearance(long caretPosition, bool sendUpdateEvent // Stop, we're now the same as we were foundEnd = true; - lastY = pt.y; + lastY = pt.y + line->GetSize().y; node2 = wxRichTextLineList::compatibility_iterator(); node = wxRichTextObjectList::compatibility_iterator(); @@ -10063,7 +11532,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 @@ -10165,13 +11634,17 @@ wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock& imageBlock, wxRichT SetAttributes(*charStyle); } +wxRichTextImage::~wxRichTextImage() +{ +} + void wxRichTextImage::Init() { m_originalImageSize = wxSize(-1, -1); } /// Create a cached image at the required size -bool wxRichTextImage::LoadImageCache(wxDC& dc, bool resetCache) +bool wxRichTextImage::LoadImageCache(wxDC& dc, bool resetCache, const wxSize& parentSize) { if (!m_imageBlock.IsOk()) return false; @@ -10181,7 +11654,7 @@ bool wxRichTextImage::LoadImageCache(wxDC& dc, bool resetCache) // the image so long as the new cached bitmap size hasn't changed. wxImage image; - if (resetCache || m_originalImageSize == wxSize(-1, -1)) + if (resetCache || m_originalImageSize.GetWidth() <= 0 || m_originalImageSize.GetHeight() <= 0) { m_imageCache = wxNullBitmap; @@ -10201,29 +11674,36 @@ bool wxRichTextImage::LoadImageCache(wxDC& dc, bool resetCache) int maxWidth = -1; int maxHeight = -1; - wxRichTextBuffer* buffer = GetBuffer(); - if (buffer) + wxSize sz = parentSize; + if (sz == wxDefaultSize) { - wxSize sz; - if (buffer->GetRichTextCtrl()) - { - // Subtract borders - sz = buffer->GetRichTextCtrl()->GetClientSize(); + if (GetParent() && GetParent()->GetParent()) + sz = GetParent()->GetParent()->GetCachedSize(); + } + if (sz != wxDefaultSize) + { + wxRichTextBuffer* buffer = GetBuffer(); + if (buffer) + { + // Find the actual space available when margin is taken into account 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); + if (GetParent() && GetParent()->GetParent()) + { + buffer->GetBoxRects(dc, buffer, GetParent()->GetParent()->GetAttributes(), marginRect, borderRect, contentRect, paddingRect, outlineRect); + sz = contentRect.GetSize(); + } - sz = contentRect.GetSize(); + // Use a minimum size to stop images becoming very small + parentWidth = wxMax(100, sz.GetWidth()); + parentHeight = wxMax(100, sz.GetHeight()); - // 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 (buffer->GetRichTextCtrl()) + // 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 = parentWidth; } - else - sz = buffer->GetCachedSize(); - parentWidth = sz.GetWidth(); - parentHeight = sz.GetHeight(); } if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetWidth().GetValue() > 0) @@ -10297,6 +11777,10 @@ bool wxRichTextImage::LoadImageCache(wxDC& dc, bool resetCache) width = (int) (float(m_originalImageSize.GetWidth()) * (float(height)/float(m_originalImageSize.GetHeight()))); } + // Prevent the use of zero size + width = wxMax(1, width); + height = wxMax(1, height); + if (m_imageCache.IsOk() && m_imageCache.GetWidth() == width && m_imageCache.GetHeight() == height) { // Do nothing, we didn't need to change the image cache @@ -10334,7 +11818,7 @@ bool wxRichTextImage::LoadImageCache(wxDC& dc, bool resetCache) } /// Draw the item -bool wxRichTextImage::Draw(wxDC& dc, wxRichTextDrawingContext& context, 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; @@ -10349,12 +11833,6 @@ bool wxRichTextImage::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wx DrawBoxAttributes(dc, GetBuffer(), attr, wxRect(rect.GetPosition(), GetCachedSize())); -#if 0 - int y = rect.y + (rect.height - m_imageCache.GetHeight()); - - dc.DrawBitmap(m_imageCache, rect.x, y, true); -#endif - wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight()); wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect; marginRect = rect; // outer rectangle, will calculate contentRect @@ -10401,12 +11879,12 @@ bool wxRichTextImage::Layout(wxDC& dc, wxRichTextDrawingContext& context, const /// 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, wxRichTextDrawingContext& context, 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), const wxPoint& WXUNUSED(position), const wxSize& parentSize, wxArrayInt* partialExtents) const { if (!range.IsWithin(GetRange())) return false; - if (!((wxRichTextImage*)this)->LoadImageCache(dc)) + if (!((wxRichTextImage*)this)->LoadImageCache(dc, false, parentSize)) { size.x = 0; size.y = 0; if (partialExtents) @@ -10483,12 +11961,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) { @@ -10980,7 +12452,7 @@ wxString wxRichTextImageBlock::GetExtension() const * The data object for a wxRichTextBuffer */ -const wxChar *wxRichTextBufferDataObject::ms_richTextBufferFormatId = wxT("wxShape"); +const wxChar *wxRichTextBufferDataObject::ms_richTextBufferFormatId = wxT("wxRichText"); wxRichTextBufferDataObject::wxRichTextBufferDataObject(wxRichTextBuffer* richTextBuffer) { @@ -11103,22 +12575,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 { @@ -11131,6 +12629,7 @@ IMPLEMENT_DYNAMIC_CLASS(wxRichTextFontTable, wxObject) wxRichTextFontTable::wxRichTextFontTable() { m_refData = new wxRichTextFontTableData; + m_fontScale = 1.0; } wxRichTextFontTable::wxRichTextFontTable(const wxRichTextFontTable& table) @@ -11152,13 +12651,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(); } @@ -11170,8 +12670,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() { @@ -11220,8 +12726,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; @@ -11239,36 +12754,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)) + if (!m_size.EqPartial(attr.m_size, weakTest)) return false; - if (!m_minSize.EqPartial(attr.m_minSize)) + if (!m_minSize.EqPartial(attr.m_minSize, weakTest)) return false; - if (!m_maxSize.EqPartial(attr.m_maxSize)) + 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; @@ -11493,13 +13008,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 @@ -11530,15 +13045,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; @@ -11625,10 +13148,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 @@ -11697,8 +13220,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 @@ -11795,18 +13321,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; @@ -11849,12 +13375,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; @@ -11897,100 +13423,177 @@ void wxTextAttrCollectCommonAttributes(wxTextAttr& currentStyle, const wxTextAtt long forbiddenFlags = clashingAttr.GetFlags()|absentAttr.GetFlags(); - if (attr.HasFont()) + // 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) { - if (attr.HasFontSize() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_SIZE)) + currentStyle.SetFontSize(0); + currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_SIZE); + clashingAttr.AddFlag(wxTEXT_ATTR_FONT_SIZE); + } + else + { + if (attr.HasFontPointSize() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_POINT_SIZE)) { - if (currentStyle.HasFontSize()) + if (currentStyle.HasFontPointSize()) { if (currentStyle.GetFontSize() != attr.GetFontSize()) { // Clash of attr - mark as such - clashingAttr.AddFlag(wxTEXT_ATTR_FONT_SIZE); - currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_SIZE); + clashingAttr.AddFlag(wxTEXT_ATTR_FONT_POINT_SIZE); + currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_POINT_SIZE); } } else currentStyle.SetFontSize(attr.GetFontSize()); } + else if (!attr.HasFontPointSize() && currentStyle.HasFontPointSize()) + { + clashingAttr.AddFlag(wxTEXT_ATTR_FONT_POINT_SIZE); + currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_POINT_SIZE); + } - if (attr.HasFontItalic() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_ITALIC)) + if (attr.HasFontPixelSize() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_PIXEL_SIZE)) { - if (currentStyle.HasFontItalic()) + if (currentStyle.HasFontPixelSize()) { - if (currentStyle.GetFontStyle() != attr.GetFontStyle()) + if (currentStyle.GetFontSize() != attr.GetFontSize()) { // Clash of attr - mark as such - clashingAttr.AddFlag(wxTEXT_ATTR_FONT_ITALIC); - currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC); + clashingAttr.AddFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE); + currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE); } } else - currentStyle.SetFontStyle(attr.GetFontStyle()); + currentStyle.SetFontPixelSize(attr.GetFontSize()); + } + else if (!attr.HasFontPixelSize() && currentStyle.HasFontPixelSize()) + { + clashingAttr.AddFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE); + currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE); } + } - if (attr.HasFontFamily() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_FAMILY)) + if (attr.HasFontItalic() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_ITALIC)) + { + if (currentStyle.HasFontItalic()) { - if (currentStyle.HasFontFamily()) + if (currentStyle.GetFontStyle() != attr.GetFontStyle()) { - if (currentStyle.GetFontFamily() != attr.GetFontFamily()) - { - // Clash of attr - mark as such - clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FAMILY); - currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY); - } + // Clash of attr - mark as such + clashingAttr.AddFlag(wxTEXT_ATTR_FONT_ITALIC); + currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC); + } + } + else + currentStyle.SetFontStyle(attr.GetFontStyle()); + } + else if (!attr.HasFontItalic() && currentStyle.HasFontItalic()) + { + clashingAttr.AddFlag(wxTEXT_ATTR_FONT_ITALIC); + currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC); + } + + if (attr.HasFontFamily() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_FAMILY)) + { + if (currentStyle.HasFontFamily()) + { + if (currentStyle.GetFontFamily() != attr.GetFontFamily()) + { + // Clash of attr - mark as such + clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FAMILY); + currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY); } - else - currentStyle.SetFontFamily(attr.GetFontFamily()); } + else + currentStyle.SetFontFamily(attr.GetFontFamily()); + } + else if (!attr.HasFontFamily() && currentStyle.HasFontFamily()) + { + clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FAMILY); + currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY); + } - if (attr.HasFontWeight() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_WEIGHT)) + if (attr.HasFontWeight() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_WEIGHT)) + { + if (currentStyle.HasFontWeight()) { - if (currentStyle.HasFontWeight()) + if (currentStyle.GetFontWeight() != attr.GetFontWeight()) { - if (currentStyle.GetFontWeight() != attr.GetFontWeight()) - { - // Clash of attr - mark as such - clashingAttr.AddFlag(wxTEXT_ATTR_FONT_WEIGHT); - currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT); - } + // Clash of attr - mark as such + clashingAttr.AddFlag(wxTEXT_ATTR_FONT_WEIGHT); + currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT); } - else - currentStyle.SetFontWeight(attr.GetFontWeight()); } + else + currentStyle.SetFontWeight(attr.GetFontWeight()); + } + else if (!attr.HasFontWeight() && currentStyle.HasFontWeight()) + { + clashingAttr.AddFlag(wxTEXT_ATTR_FONT_WEIGHT); + currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT); + } - if (attr.HasFontFaceName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_FACE)) + if (attr.HasFontFaceName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_FACE)) + { + if (currentStyle.HasFontFaceName()) { - if (currentStyle.HasFontFaceName()) + wxString faceName1(currentStyle.GetFontFaceName()); + wxString faceName2(attr.GetFontFaceName()); + + if (faceName1 != faceName2) { - wxString faceName1(currentStyle.GetFontFaceName()); - wxString faceName2(attr.GetFontFaceName()); + // Clash of attr - mark as such + clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FACE); + currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FACE); + } + } + else + currentStyle.SetFontFaceName(attr.GetFontFaceName()); + } + else if (!attr.HasFontFaceName() && currentStyle.HasFontFaceName()) + { + clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FACE); + currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FACE); + } - if (faceName1 != faceName2) - { - // Clash of attr - mark as such - clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FACE); - currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FACE); - } + if (attr.HasFontUnderlined() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_UNDERLINE)) + { + if (currentStyle.HasFontUnderlined()) + { + if (currentStyle.GetFontUnderlined() != attr.GetFontUnderlined()) + { + // Clash of attr - mark as such + clashingAttr.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE); + currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE); } - else - currentStyle.SetFontFaceName(attr.GetFontFaceName()); } + else + currentStyle.SetFontUnderlined(attr.GetFontUnderlined()); + } + else if (!attr.HasFontUnderlined() && currentStyle.HasFontUnderlined()) + { + clashingAttr.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE); + currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE); + } - if (attr.HasFontUnderlined() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_UNDERLINE)) + if (attr.HasFontStrikethrough() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_STRIKETHROUGH)) + { + if (currentStyle.HasFontStrikethrough()) { - if (currentStyle.HasFontUnderlined()) + if (currentStyle.GetFontStrikethrough() != attr.GetFontStrikethrough()) { - if (currentStyle.GetFontUnderlined() != attr.GetFontUnderlined()) - { - // Clash of attr - mark as such - clashingAttr.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE); - currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE); - } + // Clash of attr - mark as such + clashingAttr.AddFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH); + currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH); } - else - currentStyle.SetFontUnderlined(attr.GetFontUnderlined()); } + else + currentStyle.SetFontStrikethrough(attr.GetFontStrikethrough()); + } + else if (!attr.HasFontStrikethrough() && currentStyle.HasFontStrikethrough()) + { + clashingAttr.AddFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH); + currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH); } if (attr.HasTextColour() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_TEXT_COLOUR)) @@ -12007,6 +13610,11 @@ void wxTextAttrCollectCommonAttributes(wxTextAttr& currentStyle, const wxTextAtt else currentStyle.SetTextColour(attr.GetTextColour()); } + else if (!attr.HasTextColour() && currentStyle.HasTextColour()) + { + clashingAttr.AddFlag(wxTEXT_ATTR_TEXT_COLOUR); + currentStyle.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR); + } if (attr.HasBackgroundColour() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BACKGROUND_COLOUR)) { @@ -12022,6 +13630,11 @@ void wxTextAttrCollectCommonAttributes(wxTextAttr& currentStyle, const wxTextAtt else currentStyle.SetBackgroundColour(attr.GetBackgroundColour()); } + else if (!attr.HasBackgroundColour() && currentStyle.HasBackgroundColour()) + { + clashingAttr.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR); + currentStyle.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR); + } if (attr.HasAlignment() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_ALIGNMENT)) { @@ -12037,6 +13650,11 @@ void wxTextAttrCollectCommonAttributes(wxTextAttr& currentStyle, const wxTextAtt else currentStyle.SetAlignment(attr.GetAlignment()); } + else if (!attr.HasAlignment() && currentStyle.HasAlignment()) + { + clashingAttr.AddFlag(wxTEXT_ATTR_ALIGNMENT); + currentStyle.RemoveFlag(wxTEXT_ATTR_ALIGNMENT); + } if (attr.HasTabs() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_TABS)) { @@ -12052,6 +13670,11 @@ void wxTextAttrCollectCommonAttributes(wxTextAttr& currentStyle, const wxTextAtt else currentStyle.SetTabs(attr.GetTabs()); } + else if (!attr.HasTabs() && currentStyle.HasTabs()) + { + clashingAttr.AddFlag(wxTEXT_ATTR_TABS); + currentStyle.RemoveFlag(wxTEXT_ATTR_TABS); + } if (attr.HasLeftIndent() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LEFT_INDENT)) { @@ -12067,6 +13690,11 @@ void wxTextAttrCollectCommonAttributes(wxTextAttr& currentStyle, const wxTextAtt else currentStyle.SetLeftIndent(attr.GetLeftIndent(), attr.GetLeftSubIndent()); } + else if (!attr.HasLeftIndent() && currentStyle.HasLeftIndent()) + { + clashingAttr.AddFlag(wxTEXT_ATTR_LEFT_INDENT); + currentStyle.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT); + } if (attr.HasRightIndent() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_RIGHT_INDENT)) { @@ -12082,6 +13710,11 @@ void wxTextAttrCollectCommonAttributes(wxTextAttr& currentStyle, const wxTextAtt else currentStyle.SetRightIndent(attr.GetRightIndent()); } + else if (!attr.HasRightIndent() && currentStyle.HasRightIndent()) + { + clashingAttr.AddFlag(wxTEXT_ATTR_RIGHT_INDENT); + currentStyle.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT); + } if (attr.HasParagraphSpacingAfter() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARA_SPACING_AFTER)) { @@ -12097,6 +13730,11 @@ void wxTextAttrCollectCommonAttributes(wxTextAttr& currentStyle, const wxTextAtt else currentStyle.SetParagraphSpacingAfter(attr.GetParagraphSpacingAfter()); } + else if (!attr.HasParagraphSpacingAfter() && currentStyle.HasParagraphSpacingAfter()) + { + clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER); + currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER); + } if (attr.HasParagraphSpacingBefore() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARA_SPACING_BEFORE)) { @@ -12112,6 +13750,11 @@ void wxTextAttrCollectCommonAttributes(wxTextAttr& currentStyle, const wxTextAtt else currentStyle.SetParagraphSpacingBefore(attr.GetParagraphSpacingBefore()); } + else if (!attr.HasParagraphSpacingBefore() && currentStyle.HasParagraphSpacingBefore()) + { + clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE); + currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE); + } if (attr.HasLineSpacing() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LINE_SPACING)) { @@ -12127,6 +13770,11 @@ void wxTextAttrCollectCommonAttributes(wxTextAttr& currentStyle, const wxTextAtt else currentStyle.SetLineSpacing(attr.GetLineSpacing()); } + else if (!attr.HasLineSpacing() && currentStyle.HasLineSpacing()) + { + clashingAttr.AddFlag(wxTEXT_ATTR_LINE_SPACING); + currentStyle.RemoveFlag(wxTEXT_ATTR_LINE_SPACING); + } if (attr.HasCharacterStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_CHARACTER_STYLE_NAME)) { @@ -12142,6 +13790,11 @@ void wxTextAttrCollectCommonAttributes(wxTextAttr& currentStyle, const wxTextAtt else currentStyle.SetCharacterStyleName(attr.GetCharacterStyleName()); } + else if (!attr.HasCharacterStyleName() && currentStyle.HasCharacterStyleName()) + { + clashingAttr.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME); + currentStyle.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME); + } if (attr.HasParagraphStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARAGRAPH_STYLE_NAME)) { @@ -12157,6 +13810,11 @@ void wxTextAttrCollectCommonAttributes(wxTextAttr& currentStyle, const wxTextAtt else currentStyle.SetParagraphStyleName(attr.GetParagraphStyleName()); } + else if (!attr.HasParagraphStyleName() && currentStyle.HasParagraphStyleName()) + { + clashingAttr.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME); + currentStyle.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME); + } if (attr.HasListStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LIST_STYLE_NAME)) { @@ -12172,6 +13830,11 @@ void wxTextAttrCollectCommonAttributes(wxTextAttr& currentStyle, const wxTextAtt else currentStyle.SetListStyleName(attr.GetListStyleName()); } + else if (!attr.HasListStyleName() && currentStyle.HasListStyleName()) + { + clashingAttr.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME); + currentStyle.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME); + } if (attr.HasBulletStyle() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_STYLE)) { @@ -12187,6 +13850,11 @@ void wxTextAttrCollectCommonAttributes(wxTextAttr& currentStyle, const wxTextAtt else currentStyle.SetBulletStyle(attr.GetBulletStyle()); } + else if (!attr.HasBulletStyle() && currentStyle.HasBulletStyle()) + { + clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_STYLE); + currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE); + } if (attr.HasBulletNumber() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_NUMBER)) { @@ -12202,6 +13870,11 @@ void wxTextAttrCollectCommonAttributes(wxTextAttr& currentStyle, const wxTextAtt else currentStyle.SetBulletNumber(attr.GetBulletNumber()); } + else if (!attr.HasBulletNumber() && currentStyle.HasBulletNumber()) + { + clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NUMBER); + currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER); + } if (attr.HasBulletText() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_TEXT)) { @@ -12220,6 +13893,11 @@ void wxTextAttrCollectCommonAttributes(wxTextAttr& currentStyle, const wxTextAtt currentStyle.SetBulletFont(attr.GetBulletFont()); } } + else if (!attr.HasBulletText() && currentStyle.HasBulletText()) + { + clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_TEXT); + currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT); + } if (attr.HasBulletName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_NAME)) { @@ -12237,6 +13915,11 @@ void wxTextAttrCollectCommonAttributes(wxTextAttr& currentStyle, const wxTextAtt currentStyle.SetBulletName(attr.GetBulletName()); } } + else if (!attr.HasBulletName() && currentStyle.HasBulletName()) + { + clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NAME); + currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NAME); + } if (attr.HasURL() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_URL)) { @@ -12254,6 +13937,11 @@ void wxTextAttrCollectCommonAttributes(wxTextAttr& currentStyle, const wxTextAtt currentStyle.SetURL(attr.GetURL()); } } + else if (!attr.HasURL() && currentStyle.HasURL()) + { + clashingAttr.AddFlag(wxTEXT_ATTR_URL); + currentStyle.RemoveFlag(wxTEXT_ATTR_URL); + } if (attr.HasTextEffects() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_EFFECTS)) { @@ -12293,6 +13981,11 @@ void wxTextAttrCollectCommonAttributes(wxTextAttr& currentStyle, const wxTextAtt if (currentStyle.GetTextEffectFlags() == 0) currentStyle.RemoveFlag(wxTEXT_ATTR_EFFECTS); } + else if (!attr.HasTextEffects() && currentStyle.HasTextEffects()) + { + clashingAttr.AddFlag(wxTEXT_ATTR_EFFECTS); + currentStyle.RemoveFlag(wxTEXT_ATTR_EFFECTS); + } if (attr.HasOutlineLevel() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_OUTLINE_LEVEL)) { @@ -12308,9 +14001,16 @@ void wxTextAttrCollectCommonAttributes(wxTextAttr& currentStyle, const wxTextAtt else currentStyle.SetOutlineLevel(attr.GetOutlineLevel()); } + else if (!attr.HasOutlineLevel() && currentStyle.HasOutlineLevel()) + { + clashingAttr.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL); + currentStyle.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL); + } } WX_DEFINE_OBJARRAY(wxRichTextVariantArray); +WX_DEFINE_OBJARRAY(wxRichTextAttrArray); +WX_DEFINE_OBJARRAY(wxRichTextRectArray); IMPLEMENT_DYNAMIC_CLASS(wxRichTextProperties, wxObject) @@ -12625,8 +14325,19 @@ bool wxRichTextSelection::WithinSelection(const wxRichTextRange& range, const wx IMPLEMENT_CLASS(wxRichTextDrawingHandler, wxObject) IMPLEMENT_CLASS(wxRichTextDrawingContext, wxObject) +wxRichTextDrawingContext::wxRichTextDrawingContext(wxRichTextBuffer* buffer) +{ + Init(); + m_buffer = buffer; + if (m_buffer && m_buffer->GetRichTextCtrl()) + EnableVirtualAttributes(m_buffer->GetRichTextCtrl()->GetVirtualAttributesEnabled()); +} + bool wxRichTextDrawingContext::HasVirtualAttributes(wxRichTextObject* obj) const { + if (!GetVirtualAttributesEnabled()) + return false; + wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst(); while (node) { @@ -12642,6 +14353,9 @@ bool wxRichTextDrawingContext::HasVirtualAttributes(wxRichTextObject* obj) const wxRichTextAttr wxRichTextDrawingContext::GetVirtualAttributes(wxRichTextObject* obj) const { wxRichTextAttr attr; + if (!GetVirtualAttributesEnabled()) + return attr; + // We apply all handlers, so we can may combine several different attributes wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst(); while (node) @@ -12661,6 +14375,9 @@ wxRichTextAttr wxRichTextDrawingContext::GetVirtualAttributes(wxRichTextObject* bool wxRichTextDrawingContext::ApplyVirtualAttributes(wxRichTextAttr& attr, wxRichTextObject* obj) const { + if (!GetVirtualAttributesEnabled()) + return false; + if (HasVirtualAttributes(obj)) { wxRichTextAttr a(GetVirtualAttributes(obj)); @@ -12671,6 +14388,75 @@ bool wxRichTextDrawingContext::ApplyVirtualAttributes(wxRichTextAttr& attr, wxRi return false; } +int wxRichTextDrawingContext::GetVirtualSubobjectAttributesCount(wxRichTextObject* obj) const +{ + if (!GetVirtualAttributesEnabled()) + return 0; + + wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst(); + while (node) + { + wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData(); + int count = handler->GetVirtualSubobjectAttributesCount(obj); + if (count > 0) + return count; + + node = node->GetNext(); + } + return 0; +} + +int wxRichTextDrawingContext::GetVirtualSubobjectAttributes(wxRichTextObject* obj, wxArrayInt& positions, wxRichTextAttrArray& attributes) const +{ + if (!GetVirtualAttributesEnabled()) + return 0; + + wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst(); + while (node) + { + wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData(); + if (handler->GetVirtualSubobjectAttributes(obj, positions, attributes)) + return positions.GetCount(); + + node = node->GetNext(); + } + return 0; +} + +bool wxRichTextDrawingContext::HasVirtualText(const wxRichTextPlainText* obj) const +{ + if (!GetVirtualAttributesEnabled()) + return false; + + wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst(); + while (node) + { + wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData(); + if (handler->HasVirtualText(obj)) + return true; + + node = node->GetNext(); + } + return false; +} + +bool wxRichTextDrawingContext::GetVirtualText(const wxRichTextPlainText* obj, wxString& text) const +{ + if (!GetVirtualAttributesEnabled()) + return false; + + wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst(); + while (node) + { + wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData(); + if (handler->GetVirtualText(obj, text)) + return true; + + node = node->GetNext(); + } + return false; +} + /// Adds a handler to the end void wxRichTextBuffer::AddDrawingHandler(wxRichTextDrawingHandler *handler) { @@ -12724,5 +14510,45 @@ void wxRichTextBuffer::CleanUpDrawingHandlers() 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