X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/a8a15de623fca2c9d62cbc66682efb743a0f0824..fb8d7eb7a880f1f2e32d8830f9c5e12b2536e05f:/src/richtext/richtextbuffer.cpp diff --git a/src/richtext/richtextbuffer.cpp b/src/richtext/richtextbuffer.cpp index b005494725..4847d40e1d 100644 --- a/src/richtext/richtextbuffer.cpp +++ b/src/richtext/richtextbuffer.cpp @@ -602,7 +602,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); @@ -806,7 +806,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) @@ -1076,7 +1076,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) @@ -1399,7 +1400,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) @@ -1409,24 +1410,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(); @@ -1471,7 +1532,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; @@ -1492,7 +1553,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) { @@ -1527,7 +1588,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; @@ -1581,10 +1642,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); @@ -1733,7 +1801,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) @@ -1748,7 +1816,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); } @@ -1782,7 +1850,9 @@ bool wxRichTextParagraphLayoutBox::Draw(wxDC& dc, wxRichTextDrawingContext& cont theseFlags &= ~wxRICHTEXT_DRAW_GUIDELINES; DrawBoxAttributes(dc, GetBuffer(), attr, thisRect, theseFlags); - 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) { @@ -1916,7 +1986,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; @@ -2020,7 +2091,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) @@ -2111,7 +2182,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; @@ -2170,7 +2241,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); @@ -3247,7 +3318,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) @@ -3389,7 +3470,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 +3483,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 +3493,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 +3507,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(); @@ -3707,7 +3788,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); @@ -3784,8 +3865,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; } @@ -3985,12 +4079,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()) { @@ -4000,6 +4100,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); @@ -4163,6 +4264,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) @@ -4196,7 +4301,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]); @@ -4275,7 +4381,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(); @@ -4374,7 +4497,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) { @@ -4466,7 +4589,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()); @@ -4539,9 +4662,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); @@ -4558,10 +4687,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 @@ -4599,7 +4732,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) @@ -4629,8 +4762,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(); @@ -4681,7 +4814,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); } } @@ -4698,10 +4831,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 } @@ -4739,20 +4872,24 @@ bool wxRichTextParagraph::Layout(wxDC& dc, wxRichTextDrawingContext& context, co lineAscent = wxMax(childSize.y-childDescent, maxAscent); } lineHeight = wxMax(lineHeight, (lineDescent + lineAscent)); - wxRect floatAvailableRect = collector->GetAvailableRect(rect.y + currentPosition.y, rect.y + currentPosition.y + lineHeight); - // Adjust availableRect to the space that is available when taking floating objects into account. - - if (floatAvailableRect.x + startOffset > availableRect.x) + if (wxRichTextBuffer::GetFloatingLayoutMode() && collector) { - int newX = floatAvailableRect.x + startOffset; - int newW = availableRect.width - (newX - availableRect.x); - availableRect.x = newX; - availableRect.width = newW; - } + wxRect floatAvailableRect = collector->GetAvailableRect(rect.y + currentPosition.y, rect.y + currentPosition.y + lineHeight); - if (floatAvailableRect.width < availableRect.width) - availableRect.width = floatAvailableRect.width; + // 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.width < availableRect.width) + availableRect.width = floatAvailableRect.width; + } currentPosition.x = availableRect.x - rect.x; @@ -4766,7 +4903,7 @@ bool wxRichTextParagraph::Layout(wxDC& dc, wxRichTextDrawingContext& context, co // 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(); @@ -4775,7 +4912,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); } // Go around the loop finding the available rect for the given floating objects @@ -4804,9 +4941,10 @@ 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 ) { long wrapPosition = 0; @@ -4845,12 +4983,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; @@ -4948,6 +5086,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); @@ -4982,7 +5158,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 && !wxDynamicCast(child, wxRichTextPlainText)) + if ((!child->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && child->GetRange().GetLength() != 0 && !wxDynamicCast(child, wxRichTextPlainText)) { if (child->GetCachedSize().x > minWidth) minWidth = child->GetMinSize().x; @@ -5166,7 +5342,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; @@ -5194,7 +5370,7 @@ bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange& range, wxSize& siz if (!child->GetRange().IsOutside(range)) { // Floating objects have a zero size within the paragraph. - if (child->IsFloating()) + if (child->IsFloating() && wxRichTextBuffer::GetFloatingLayoutMode()) { if (partialExtents) { @@ -5280,7 +5456,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) { @@ -5358,7 +5534,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()); @@ -5367,7 +5543,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) { @@ -5573,7 +5749,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; @@ -6084,7 +6260,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) @@ -6092,9 +6268,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()) @@ -6122,7 +6312,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); } @@ -6248,11 +6439,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(); @@ -6268,6 +6465,13 @@ 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) ) { if (textFont.IsUsingSizeInPixels()) @@ -6568,7 +6772,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; @@ -6606,6 +6810,13 @@ bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange& range, wxSize& siz 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; + } else { wxCheckSetFont(dc, font); @@ -6617,12 +6828,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; @@ -6823,15 +7040,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() == wxCLASSINFO(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 ); @@ -6846,6 +7092,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) { @@ -6883,6 +7337,7 @@ 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() @@ -8148,7 +8603,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); @@ -8168,7 +8623,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(); @@ -8451,13 +8906,13 @@ bool wxRichTextField::Layout(wxDC& dc, wxRichTextDrawingContext& context, const return wxRichTextParagraphLayoutBox::Layout(dc, context, rect, parentRect, style); } -bool wxRichTextField::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* partialExtents) const +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, partialExtents); + return fieldType->GetRangeSize((wxRichTextField*) this, range, size, descent, dc, context, flags, position, parentSize, partialExtents); - return wxRichTextParagraphLayoutBox::GetRangeSize(range, size, descent, dc, context, flags, position, partialExtents); + return wxRichTextParagraphLayoutBox::GetRangeSize(range, size, descent, dc, context, flags, position, parentSize, partialExtents); } /// Calculate range @@ -8693,10 +9148,10 @@ bool wxRichTextFieldTypeStandard::Layout(wxRichTextField* obj, wxDC& dc, wxRichT return true; } -bool wxRichTextFieldTypeStandard::GetRangeSize(wxRichTextField* obj, const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* partialExtents) const +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); + return obj->wxRichTextParagraphLayoutBox::GetRangeSize(range, size, descent, dc, context, flags, position, parentSize); else { wxSize sz = GetSize(obj, dc, context, 0); @@ -9584,9 +10039,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. @@ -9632,6 +10087,8 @@ void wxRichTextTable::ClearTable() { m_cells.Clear(); DeleteChildren(); + m_rowCount = 0; + m_colCount = 0; } bool wxRichTextTable::CreateTable(int rows, int cols) @@ -9784,12 +10241,68 @@ 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; +} + 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(); + + wxPosition position = GetFocusedCell(); + int focusCol = position.GetCol(); + int focusRow = position.GetRow(); + if (focusRow >= startRow && focusRow < (startRow+noRows)) + { + // Deleting a focused cell causes a segfault later when laying out, due to GetFocusedObject() returning an invalid object + if ((startRow + noRows) < m_rowCount) + { + // There are more rows after the one(s) to be deleted, so set focus in the first of them + rtc->SetFocusObject(GetCell(startRow + noRows, focusCol)); + } + else + { + // Otherwise set focus in the preceding row + rtc->SetFocusObject(GetCell(startRow - 1, focusCol)); + } + } + + 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++) { @@ -9807,25 +10320,68 @@ 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(); + + wxPosition position = GetFocusedCell(); + int focusCol = position.GetCol(); + int focusRow = position.GetRow(); + if (focusCol >= startCol && focusCol < (startCol+noCols)) + { + // Deleting a focused cell causes a segfault later when laying out, due to GetFocusedObject() returning an invalid object + if ((startCol + noCols) < m_colCount) + { + // There are more columns after the one(s) to be deleted, so set focus in the first of them + rtc->SetFocusObject(GetCell(focusRow, startCol + noCols)); + } + else + { + // Otherwise set focus in the preceding column + rtc->SetFocusObject(GetCell(focusRow, startCol - 1)); + } + } + + 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) @@ -9836,6 +10392,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; } @@ -9845,6 +10408,19 @@ 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()); + } + int i, j; for (i = 0; i < noRows; i++) { @@ -9867,11 +10443,20 @@ bool wxRichTextTable::AddRows(int startRow, int noRows, const wxRichTextAttr& at cell->GetAttributes() = attr; 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; } @@ -9881,6 +10466,19 @@ 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()); + } + int i, j; for (i = 0; i < m_rowCount; i++) { @@ -9891,6 +10489,7 @@ bool wxRichTextTable::AddColumns(int startCol, int noCols, const wxRichTextAttr& cell->GetAttributes() = attr; AppendChild(cell); + cell->AddParagraph(wxEmptyString); if (startCol == m_colCount) colArray.Add(cell); @@ -9901,6 +10500,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; } @@ -9968,8 +10574,8 @@ IMPLEMENT_DYNAMIC_CLASS(wxRichTextModule, wxModule) void wxRichTextModuleInit() { wxModule* module = new wxRichTextModule; - module->Init(); wxModule::RegisterModule(module); + wxModule::InitializeModules(); } @@ -10170,7 +10776,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()); @@ -10203,7 +10809,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()); @@ -10219,14 +10825,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()); @@ -10249,12 +10861,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()); @@ -10268,10 +10884,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(); @@ -10282,7 +10909,11 @@ 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()); @@ -10329,7 +10960,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()); @@ -10359,7 +10990,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()); @@ -10381,7 +11012,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()); @@ -10425,7 +11056,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); } @@ -10456,13 +11087,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); @@ -10510,7 +11144,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(); @@ -10635,13 +11269,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; @@ -10651,7 +11289,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; @@ -10671,29 +11309,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) @@ -10767,6 +11412,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 @@ -10865,12 +11514,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) @@ -11438,7 +12087,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) { @@ -12409,140 +13058,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 different font size units are being used, this is a clash. - if (((attr.GetFlags() & wxTEXT_ATTR_FONT_SIZE) | (currentStyle.GetFlags() & wxTEXT_ATTR_FONT_SIZE)) == wxTEXT_ATTR_FONT_SIZE) - { - currentStyle.SetFontSize(0); - currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_SIZE); - clashingAttr.AddFlag(wxTEXT_ATTR_FONT_SIZE); - } - else + 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 (attr.HasFontPointSize() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_POINT_SIZE)) - { - if (currentStyle.HasFontPointSize()) - { - if (currentStyle.GetFontSize() != attr.GetFontSize()) - { - // Clash of attr - mark as such - clashingAttr.AddFlag(wxTEXT_ATTR_FONT_POINT_SIZE); - currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_POINT_SIZE); - } - } - else - currentStyle.SetFontSize(attr.GetFontSize()); - } - - if (attr.HasFontPixelSize() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_PIXEL_SIZE)) + if (currentStyle.HasFontPointSize()) { - if (currentStyle.HasFontPixelSize()) + if (currentStyle.GetFontSize() != attr.GetFontSize()) { - if (currentStyle.GetFontSize() != attr.GetFontSize()) - { - // Clash of attr - mark as such - clashingAttr.AddFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE); - currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE); - } + // Clash of attr - mark as such + clashingAttr.AddFlag(wxTEXT_ATTR_FONT_POINT_SIZE); + currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_POINT_SIZE); } - else - currentStyle.SetFontPixelSize(attr.GetFontSize()); } + 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.SetFontFamily(attr.GetFontFamily()); } + 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.HasFontWeight() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_WEIGHT)) + if (attr.HasFontFamily() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_FAMILY)) + { + if (currentStyle.HasFontFamily()) { - if (currentStyle.HasFontWeight()) + if (currentStyle.GetFontFamily() != attr.GetFontFamily()) { - 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_FAMILY); + currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY); } - else - currentStyle.SetFontWeight(attr.GetFontWeight()); } + 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.HasFontFaceName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_FACE)) + if (attr.HasFontWeight() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_WEIGHT)) + { + if (currentStyle.HasFontWeight()) { - if (currentStyle.HasFontFaceName()) + if (currentStyle.GetFontWeight() != attr.GetFontWeight()) { - wxString faceName1(currentStyle.GetFontFaceName()); - wxString faceName2(attr.GetFontFaceName()); + // Clash of attr - mark as such + clashingAttr.AddFlag(wxTEXT_ATTR_FONT_WEIGHT); + currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT); + } + } + else + currentStyle.SetFontWeight(attr.GetFontWeight()); + } + else if (!attr.HasFontWeight() && currentStyle.HasFontWeight()) + { + clashingAttr.AddFlag(wxTEXT_ATTR_FONT_WEIGHT); + currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT); + } - if (faceName1 != faceName2) - { - // Clash of attr - mark as such - clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FACE); - currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FACE); - } + if (attr.HasFontFaceName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_FACE)) + { + if (currentStyle.HasFontFaceName()) + { + wxString faceName1(currentStyle.GetFontFaceName()); + wxString faceName2(attr.GetFontFaceName()); + + if (faceName1 != faceName2) + { + // Clash of attr - mark as such + clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FACE); + currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FACE); } - else - currentStyle.SetFontFaceName(attr.GetFontFaceName()); } + else + currentStyle.SetFontFaceName(attr.GetFontFaceName()); + } + else if (!attr.HasFontFaceName() && currentStyle.HasFontFaceName()) + { + clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FACE); + currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FACE); + } - if (attr.HasFontUnderlined() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_UNDERLINE)) + if (attr.HasFontUnderlined() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_UNDERLINE)) + { + if (currentStyle.HasFontUnderlined()) { - if (currentStyle.HasFontUnderlined()) + if (currentStyle.GetFontUnderlined() != attr.GetFontUnderlined()) { - 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_UNDERLINE); + currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE); } - else - currentStyle.SetFontUnderlined(attr.GetFontUnderlined()); } + 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.HasFontStrikethrough() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_STRIKETHROUGH)) + if (attr.HasFontStrikethrough() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_STRIKETHROUGH)) + { + if (currentStyle.HasFontStrikethrough()) { - if (currentStyle.HasFontStrikethrough()) + if (currentStyle.GetFontStrikethrough() != attr.GetFontStrikethrough()) { - if (currentStyle.GetFontStrikethrough() != attr.GetFontStrikethrough()) - { - // Clash of attr - mark as such - clashingAttr.AddFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH); - currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH); - } + // Clash of attr - mark as such + clashingAttr.AddFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH); + currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH); } - else - currentStyle.SetFontStrikethrough(attr.GetFontStrikethrough()); } + 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)) @@ -12559,6 +13245,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)) { @@ -12574,6 +13265,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)) { @@ -12589,6 +13285,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)) { @@ -12604,6 +13305,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)) { @@ -12619,6 +13325,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)) { @@ -12634,6 +13345,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)) { @@ -12649,6 +13365,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)) { @@ -12664,6 +13385,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)) { @@ -12679,6 +13405,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)) { @@ -12694,6 +13425,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)) { @@ -12709,6 +13445,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)) { @@ -12724,6 +13465,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)) { @@ -12739,6 +13485,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)) { @@ -12754,6 +13505,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)) { @@ -12772,6 +13528,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)) { @@ -12789,6 +13550,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)) { @@ -12806,6 +13572,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)) { @@ -12845,6 +13616,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)) { @@ -12860,10 +13636,18 @@ 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); +// JACS 2013-01-27 +WX_DEFINE_OBJARRAY(wxRichTextAttrArray); + IMPLEMENT_DYNAMIC_CLASS(wxRichTextProperties, wxObject) bool wxRichTextProperties::operator==(const wxRichTextProperties& props) const @@ -13177,8 +13961,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) { @@ -13194,6 +13989,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) @@ -13213,6 +14011,9 @@ wxRichTextAttr wxRichTextDrawingContext::GetVirtualAttributes(wxRichTextObject* bool wxRichTextDrawingContext::ApplyVirtualAttributes(wxRichTextAttr& attr, wxRichTextObject* obj) const { + if (!GetVirtualAttributesEnabled()) + return false; + if (HasVirtualAttributes(obj)) { wxRichTextAttr a(GetVirtualAttributes(obj)); @@ -13223,6 +14024,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) {