X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/345c78ca5fa57ea6c7dc7e6a05496a8e82aa2b50..2a45803fc3877afd0ae3ce356dfe216505165882:/src/richtext/richtextbuffer.cpp diff --git a/src/richtext/richtextbuffer.cpp b/src/richtext/richtextbuffer.cpp index 6567878d42..536ca8b67e 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); @@ -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,85 @@ 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(); + wxRichTextObjectList::compatibility_iterator nextNode = node->GetNext(); + + // 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(); @@ -1492,7 +1554,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) { @@ -1581,10 +1643,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 +1802,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 +1817,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 +1851,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 +1987,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 +2092,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) @@ -3389,7 +3461,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 +3474,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 +3484,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 +3498,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(); @@ -3784,8 +3856,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 +4070,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 +4091,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 +4255,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 +4292,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 +4372,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 +4488,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 +4580,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 +4653,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); @@ -4629,8 +4749,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(); @@ -4739,20 +4859,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); + + // 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; + if (floatAvailableRect.width < availableRect.width) + availableRect.width = floatAvailableRect.width; + } currentPosition.x = availableRect.x - rect.x; @@ -4831,7 +4955,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); @@ -4982,7 +5106,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; @@ -5194,7 +5318,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) { @@ -5358,7 +5482,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()); @@ -6084,7 +6208,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 +6216,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 +6260,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 +6387,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 +6413,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()) @@ -6606,6 +6758,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 +6776,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 +6988,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 ); @@ -6846,6 +7040,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 +7285,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() @@ -8817,7 +9220,7 @@ bool wxRichTextCell::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer) wxRichTextObjectPropertiesDialog cellDlg(this, wxGetTopLevelParent(parent), wxID_ANY, caption); cellDlg.SetAttributes(attr); - wxRichTextSizePage* sizePage = wxDynamicCast(cellDlg.FindPage(CLASSINFO(wxRichTextSizePage)), wxRichTextSizePage); + wxRichTextSizePage* sizePage = wxDynamicCast(cellDlg.FindPage(wxCLASSINFO(wxRichTextSizePage)), wxRichTextSizePage); if (sizePage) { // We don't want position and floating controls for a cell. @@ -10219,9 +10622,15 @@ 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()); @@ -10249,7 +10658,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()); @@ -10282,7 +10695,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()); @@ -10425,7 +10842,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); } @@ -10651,7 +11068,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; @@ -10680,6 +11097,10 @@ bool wxRichTextImage::LoadImageCache(wxDC& dc, bool resetCache) // Subtract borders sz = buffer->GetRichTextCtrl()->GetClientSize(); + // Use a minimum size to stop images becoming very small + sz.x = wxMax(sz.x, 100); + sz.y = wxMax(sz.y, 100); + 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); @@ -10767,6 +11188,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 @@ -12409,7 +12834,14 @@ 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) + { + 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)) { @@ -12425,6 +12857,11 @@ void wxTextAttrCollectCommonAttributes(wxTextAttr& currentStyle, const wxTextAtt 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.HasFontPixelSize() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_PIXEL_SIZE)) { @@ -12440,99 +12877,134 @@ void wxTextAttrCollectCommonAttributes(wxTextAttr& currentStyle, const wxTextAtt else 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.HasFontItalic() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_ITALIC)) + if (attr.HasFontItalic() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_ITALIC)) + { + if (currentStyle.HasFontItalic()) { - if (currentStyle.HasFontItalic()) + if (currentStyle.GetFontStyle() != attr.GetFontStyle()) { - if (currentStyle.GetFontStyle() != attr.GetFontStyle()) - { - // Clash of attr - mark as such - clashingAttr.AddFlag(wxTEXT_ATTR_FONT_ITALIC); - currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC); - } + // Clash of attr - mark as such + clashingAttr.AddFlag(wxTEXT_ATTR_FONT_ITALIC); + currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC); } - else - currentStyle.SetFontStyle(attr.GetFontStyle()); } + 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 (attr.HasFontFamily() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_FAMILY)) + { + if (currentStyle.HasFontFamily()) { - if (currentStyle.HasFontFamily()) + if (currentStyle.GetFontFamily() != attr.GetFontFamily()) { - 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_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()); + 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); - } + 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)) @@ -12549,6 +13021,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)) { @@ -12564,6 +13041,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)) { @@ -12579,6 +13061,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)) { @@ -12594,6 +13081,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)) { @@ -12609,6 +13101,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)) { @@ -12624,6 +13121,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)) { @@ -12639,6 +13141,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)) { @@ -12654,6 +13161,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)) { @@ -12669,6 +13181,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)) { @@ -12684,6 +13201,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)) { @@ -12699,6 +13221,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)) { @@ -12714,6 +13241,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)) { @@ -12729,6 +13261,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)) { @@ -12744,6 +13281,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)) { @@ -12762,6 +13304,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)) { @@ -12779,6 +13326,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)) { @@ -12796,6 +13348,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)) { @@ -12835,6 +13392,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)) { @@ -12850,10 +13412,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 @@ -13167,8 +13737,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) { @@ -13184,6 +13765,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) @@ -13203,6 +13787,9 @@ wxRichTextAttr wxRichTextDrawingContext::GetVirtualAttributes(wxRichTextObject* bool wxRichTextDrawingContext::ApplyVirtualAttributes(wxRichTextAttr& attr, wxRichTextObject* obj) const { + if (!GetVirtualAttributesEnabled()) + return false; + if (HasVirtualAttributes(obj)) { wxRichTextAttr a(GetVirtualAttributes(obj)); @@ -13213,6 +13800,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) {