X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/d07f2e19181bc5caf29eb2338ce513be6fa42405..0d53638f7147c18153f63fdfc096b17be6e22a27:/src/richtext/richtextbuffer.cpp?ds=sidebyside diff --git a/src/richtext/richtextbuffer.cpp b/src/richtext/richtextbuffer.cpp index dbeab138df..cc52bc9a10 100644 --- a/src/richtext/richtextbuffer.cpp +++ b/src/richtext/richtextbuffer.cpp @@ -4,7 +4,6 @@ // Author: Julian Smart // Modified by: // Created: 2005-09-30 -// RCS-ID: $Id$ // Copyright: (c) Julian Smart // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// @@ -602,7 +601,7 @@ void wxRichTextObject::Invalidate(const wxRichTextRange& invalidRange) // If this is a floating object, size may not be recalculated // after floats have been collected in an early stage of Layout. // So avoid resetting the cache for floating objects during layout. - if (!IsFloating()) + if (!IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) SetCachedSize(wxDefaultSize); SetMaxSize(wxDefaultSize); SetMinSize(wxDefaultSize); @@ -806,7 +805,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 +1075,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 +1399,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 +1409,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 +1531,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 +1552,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 +1587,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 +1641,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 +1800,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 +1815,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 +1849,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 +1985,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 +2090,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 +2181,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 +2240,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 +3317,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 +3469,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 +3482,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 +3492,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 +3506,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 +3787,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 +3864,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; } @@ -3990,7 +4083,6 @@ bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange& range, wx newPara->GetAttributes().SetBulletStyle(newPara->GetAttributes().GetBulletStyle()|wxTEXT_ATTR_BULLET_STYLE_CONTINUATION); else { - // Now we need to do numbering if (renumber) { newPara->GetAttributes().SetBulletNumber(n); @@ -4007,6 +4099,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); @@ -4287,6 +4380,7 @@ 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 { + // 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) { @@ -4303,7 +4397,7 @@ bool wxRichTextParagraphLayoutBox::FindNextParagraphNumber(wxRichTextParagraph* previousParagraph = NULL; } - if (!previousParagraph->GetAttributes().HasFlag(wxTEXT_ATTR_BULLET_STYLE) || previousParagraph->GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE) + if (!previousParagraph || !previousParagraph->GetAttributes().HasFlag(wxTEXT_ATTR_BULLET_STYLE) || previousParagraph->GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE) return false; wxRichTextBuffer* buffer = GetBuffer(); @@ -4494,7 +4588,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()); @@ -4567,9 +4661,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); @@ -4586,10 +4686,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 @@ -4627,7 +4731,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) @@ -4657,8 +4761,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(); @@ -4709,7 +4813,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); } } @@ -4726,10 +4830,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 } @@ -4767,20 +4871,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.width < availableRect.width) - availableRect.width = floatAvailableRect.width; + 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; @@ -4794,7 +4902,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(); @@ -4803,7 +4911,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 @@ -4832,9 +4940,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; @@ -4873,12 +4982,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; @@ -4976,6 +5085,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); @@ -5010,7 +5157,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 +5341,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; @@ -5222,7 +5369,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) { @@ -5308,7 +5455,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) { @@ -5386,7 +5533,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()); @@ -5395,7 +5542,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) { @@ -5601,7 +5748,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; @@ -6112,7 +6259,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) @@ -6120,9 +6267,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()) @@ -6150,7 +6311,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); } @@ -6276,8 +6438,14 @@ 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|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS))) @@ -6603,7 +6771,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; @@ -6659,6 +6827,12 @@ 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(" ")); @@ -6865,15 +7039,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 ); @@ -6888,6 +7091,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) { @@ -6925,6 +7336,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() @@ -7904,6 +8316,7 @@ wxString wxRichTextBuffer::GetExtWildcard(bool combine, bool save, wxArrayInt* t return wildcard; } +#if wxUSE_FFILE && wxUSE_STREAMS /// Load a file bool wxRichTextBuffer::LoadFile(const wxString& filename, wxRichTextFileType type) { @@ -7932,7 +8345,9 @@ bool wxRichTextBuffer::SaveFile(const wxString& filename, wxRichTextFileType typ else return false; } +#endif // wxUSE_FFILE && wxUSE_STREAMS +#if wxUSE_STREAMS /// Load from a stream bool wxRichTextBuffer::LoadFile(wxInputStream& stream, wxRichTextFileType type) { @@ -7961,6 +8376,7 @@ bool wxRichTextBuffer::SaveFile(wxOutputStream& stream, wxRichTextFileType type) else return false; } +#endif // wxUSE_STREAMS /// Copy the range to the clipboard bool wxRichTextBuffer::CopyToClipboard(const wxRichTextRange& range) @@ -8190,7 +8606,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); @@ -8210,7 +8626,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(); @@ -8493,13 +8909,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 @@ -8735,10 +9151,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); @@ -8888,6 +9304,29 @@ bool wxRichTextCell::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer) return false; } +// The next 2 methods return span values. Note that the default is 1, not 0 +int wxRichTextCell::GetColspan() const +{ + int span = 1; + if (GetProperties().HasProperty(wxT("colspan"))) + { + span = GetProperties().GetPropertyLong(wxT("colspan")); + } + + return span; +} + +int wxRichTextCell::GetRowspan() const +{ + int span = 1; + if (GetProperties().HasProperty(wxT("rowspan"))) + { + span = GetProperties().GetPropertyLong(wxT("rowspan")); + } + + return span; +} + WX_DEFINE_OBJARRAY(wxRichTextObjectPtrArrayArray) IMPLEMENT_DYNAMIC_CLASS(wxRichTextTable, wxRichTextBox) @@ -8907,6 +9346,157 @@ bool wxRichTextTable::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wx WX_DECLARE_OBJARRAY(wxRect, wxRichTextRectArray); WX_DEFINE_OBJARRAY(wxRichTextRectArray); + + // Helper function for Layout() that clears the space needed by a cell with rowspan > 1 +int GetRowspanDisplacement(const wxRichTextTable* table, int row, int col, int paddingX, const wxArrayInt& colWidths) +{ + // If one or more cells above-left of this one has rowspan > 1, the affected cells below it + // will have been hidden and have width 0. As a result they are ignored by the layout algorithm, + // and all cells to their right are effectively shifted left. As a result there's no hole for + // the spanning cell to fill. + // So search back along the current row for hidden cells. However there's also the annoying issue of a + // rowspanning cell that also has colspam. So we can't rely on the rowspanning cell being directly above + // the first hidden one we come to. We also can't rely on a cell being hidden only by one type of span; + // there's nothing to stop a cell being hidden by colspan, and then again hidden from above by rowspan. + // The answer is to look above each hidden cell in turn, which I think covers all bases. + int deltaX = 0; + for (int prevcol = 0; prevcol < col; ++prevcol) + { + if (!table->GetCell(row, prevcol)->IsShown()) + { + // We've found a hidden cell. If it's hidden because of colspan to its left, it's + // already been taken into account; but not if there's a rowspanning cell above + for (int prevrow = row-1; prevrow >= 0; --prevrow) + { + wxRichTextCell* cell = table->GetCell(prevrow, prevcol); + if (cell && cell->IsShown()) + { + int rowSpan = cell->GetRowspan(); + if (rowSpan > 1 && rowSpan > (row-prevrow)) + { + // There is a rowspanning cell above above the hidden one, so we need + // to right-shift the index cell by this column's width. Furthermore, + // if the cell also colspans, we need to shift by all affected columns + for (int colSpan = 0; colSpan < cell->GetColspan(); ++colSpan) + deltaX += (colWidths[prevcol+colSpan] + paddingX); + break; + } + } + } + } + } + return deltaX; +} + + // Helper function for Layout() that expands any cell with rowspan > 1 +void ExpandCellsWithRowspan(const wxRichTextTable* table, int paddingY, int& bottomY, wxDC& dc, wxRichTextDrawingContext& context, const wxRect& availableSpace, int style) +{ + // This is called when the table's cell layout is otherwise complete. + // For any cell with rowspan > 1, expand downwards into the row(s) below. + + // Start by finding the current 'y' of the top of each row, plus the bottom of the available area for cells. + // Deduce this from the top of a visible cell in the row below. (If none are visible, the row will be invisible anyway and can be ignored.) + const int rowCount = table->GetRowCount(); + const int colCount = table->GetColumnCount(); + wxArrayInt rowTops; + rowTops.Add(0, rowCount+1); + int row; + for (row = 0; row < rowCount; ++row) + { + for (int column = 0; column < colCount; ++column) + { + wxRichTextCell* cell = table->GetCell(row, column); + if (cell && cell->IsShown()) + { + rowTops[row] = cell->GetPosition().y; + break; + } + } + } + rowTops[rowCount] = bottomY + paddingY; // The table bottom, which was passed to us + + bool needsRelay = false; + + for (row = 0; row < rowCount-1; ++row) // -1 as the bottom row can't rowspan + { + for (int col = 0; col < colCount; ++col) + { + wxRichTextCell* cell = table->GetCell(row, col); + if (cell && cell->IsShown()) + { + int span = cell->GetRowspan(); + if (span > 1) + { + span = wxMin(span, rowCount-row); // Don't try to span below the table! + if (span < 2) + continue; + + int availableHeight = rowTops[row+span] - rowTops[row] - paddingY; + wxSize newSize = wxSize(cell->GetCachedSize().GetWidth(), availableHeight); + wxRect availableCellSpace = wxRect(cell->GetPosition(), newSize); + cell->Invalidate(wxRICHTEXT_ALL); + cell->Layout(dc, context, availableCellSpace, availableSpace, style); + // Ensure there's room in the span to display its contents, else it'll overwrite lower rows + int overhang = cell->GetCachedSize().GetHeight() - availableHeight; + cell->SetCachedSize(newSize); + + if (overhang > 0) + { + // There are 3 things to get right: + // 1) The easiest is the rows below the span: they need to be downshifted by the overhang, and so does the table bottom itself + // 2) The rows within the span, including the one holding this cell, need to be deepened by their share of the overhang + // e.g. if rowspan == 3, each row should increase in depth by 1/3rd of the overhang. + // 3) The cell with the rowspan shouldn't be touched in 2); its height will be set to the whole span later. + int deltaY = overhang / span; + int spare = overhang % span; + + // Each row in the span needs to by deepened by its share of the overhang (give the first row any spare). + // This is achieved by increasing the value stored in the following row's rowTops + for (int spannedRows = 0; spannedRows < span; ++spannedRows) + { + rowTops[row+spannedRows+1] += ((deltaY * (spannedRows+1)) + (spannedRows == 0 ? spare:0)); + } + + // Any rows below the span need shifting down + for (int rowsBelow = row + span+1; rowsBelow <= rowCount; ++rowsBelow) + { + rowTops[rowsBelow] += overhang; + } + + needsRelay = true; + } + } + } + } + } + + if (!needsRelay) + return; + + // There were overflowing rowspanning cells, so layout yet again to make the increased row depths show + for (row = 0; row < rowCount; ++row) + { + for (int col = 0; col < colCount; ++col) + { + wxRichTextCell* cell = table->GetCell(row, col); + if (cell && cell->IsShown()) + { + wxPoint position(cell->GetPosition().x, rowTops[row]); + + // GetRowspan() will usually return 1, but may be greater + wxSize size(cell->GetCachedSize().GetWidth(), rowTops[row + cell->GetRowspan()] - rowTops[row] - paddingY); + + wxRect availableCellSpace = wxRect(position, size); + cell->Invalidate(wxRICHTEXT_ALL); + cell->Layout(dc, context, availableCellSpace, availableSpace, style); + cell->SetCachedSize(size); + } + } + + bottomY = rowTops[rowCount] - paddingY; + } +} + // Lays the object out. rect is the space available for layout. Often it will // be the specified overall space for this object, if trying to constrain // layout to a particular size, or it could be the total space available in the @@ -8916,7 +9506,7 @@ bool wxRichTextTable::Layout(wxDC& dc, wxRichTextDrawingContext& context, const { SetPosition(rect.GetPosition()); - // TODO: the meaty bit. Calculate sizes of all cells and rows. Try to use + // The meaty bit. Calculate sizes of all cells and rows. Try to use // minimum size if within alloted size, then divide up remaining size // between rows/cols. @@ -8930,12 +9520,13 @@ bool wxRichTextTable::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextAttr attr(GetAttributes()); context.ApplyVirtualAttributes(attr, this); + bool tableHasPercentWidth = (attr.GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE); // If we have no fixed table size, and assuming we're not pushed for // space, then we don't have to try to stretch the table to fit the contents. - bool stretchToFitTableWidth = false; - + bool stretchToFitTableWidth = tableHasPercentWidth; + int tableWidth = rect.width; - if (attr.GetTextBoxAttr().GetWidth().IsValid()) + if (attr.GetTextBoxAttr().GetWidth().IsValid() && !tableHasPercentWidth) { tableWidth = converter.GetPixels(attr.GetTextBoxAttr().GetWidth()); @@ -9032,12 +9623,9 @@ bool wxRichTextTable::Layout(wxDC& dc, wxRichTextDrawingContext& context, const { for (i = 0; i < m_colCount; i++) { - wxRichTextBox* cell = GetCell(j, i); - int colSpan = 1, rowSpan = 1; - if (cell->GetProperties().HasProperty(wxT("colspan"))) - colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan")); - if (cell->GetProperties().HasProperty(wxT("rowspan"))) - rowSpan = cell->GetProperties().GetPropertyLong(wxT("rowspan")); + wxRichTextCell* cell = GetCell(j, i); + int colSpan = cell->GetColspan(); + int rowSpan = cell->GetRowspan(); if (colSpan > 1 || rowSpan > 1) { rectArray.Add(wxRect(i, j, colSpan, rowSpan)); @@ -9049,18 +9637,16 @@ bool wxRichTextTable::Layout(wxDC& dc, wxRichTextDrawingContext& context, const { for (i = 0; i < m_colCount; i++) { - wxRichTextBox* cell = GetCell(j, i); + wxRichTextCell* cell = GetCell(j, i); if (rectArray.GetCount() == 0) { cell->Show(true); } else { - int colSpan = 1, rowSpan = 1; - if (cell->GetProperties().HasProperty(wxT("colspan"))) - colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan")); - if (cell->GetProperties().HasProperty(wxT("rowspan"))) - rowSpan = cell->GetProperties().GetPropertyLong(wxT("rowspan")); + int colSpan = cell->GetColspan(); + int rowSpan = cell->GetRowspan(); + if (colSpan > 1 || rowSpan > 1) { // Assume all spanning cells are shown @@ -9083,7 +9669,7 @@ bool wxRichTextTable::Layout(wxDC& dc, wxRichTextDrawingContext& context, const } } - // TODO: find the first spanned cell in each row that spans the most columns and doesn't + // Find the first spanned cell in each row that spans the most columns and doesn't // overlap with a spanned cell starting at a previous column position. // This means we need to keep an array of rects so we can check. However // it does also mean that some spans simply may not be taken into account @@ -9123,12 +9709,10 @@ bool wxRichTextTable::Layout(wxDC& dc, wxRichTextDrawingContext& context, const for (i = 0; i < m_colCount; i++) { - wxRichTextBox* cell = GetCell(j, i); + wxRichTextCell* cell = GetCell(j, i); if (cell->IsShown()) { - int colSpan = 1; - if (cell->GetProperties().HasProperty(wxT("colspan"))) - colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan")); + int colSpan = cell->GetColspan(); // Lay out cell to find min/max widths cell->Invalidate(wxRICHTEXT_ALL); @@ -9248,13 +9832,10 @@ bool wxRichTextTable::Layout(wxDC& dc, wxRichTextDrawingContext& context, const for (i = 0; i < m_colCount; i++) { - wxRichTextBox* cell = GetCell(j, i); + wxRichTextCell* cell = GetCell(j, i); if (cell->IsShown()) { - int colSpan = 1; - if (cell->GetProperties().HasProperty(wxT("colspan"))) - colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan")); - + int colSpan = cell->GetColspan(); if (colSpan > 1) { int spans = wxMin(colSpan, m_colCount - i); @@ -9337,7 +9918,6 @@ bool wxRichTextTable::Layout(wxDC& dc, wxRichTextDrawingContext& context, const int stretchColCount = 0; for (i = 0; i < m_colCount; i++) { - // TODO: we need to take into account min widths. // Subtract min width from width left, then // add the colShare to the min width if (colWidths[i] > 0) // absolute or proportional width has been specified @@ -9443,19 +10023,16 @@ bool wxRichTextTable::Layout(wxDC& dc, wxRichTextDrawingContext& context, const if (colWidths[i] > 0) // absolute or proportional width has been specified { - int colSpan = 1; - if (cell->GetProperties().HasProperty(wxT("colspan"))) - colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan")); - + int colSpan = cell->GetColspan(); wxRect availableCellSpace; - // TODO: take into acount spans + // Take into account spans if (colSpan > 1) { // Calculate the size of this spanning cell from its constituent columns - int xx = x; + int xx = 0; int spans = wxMin(colSpan, m_colCount - i); - for (k = i; k < spans; k++) + for (k = i; k < (i+spans); k++) { if (k != i) xx += paddingX; @@ -9469,6 +10046,10 @@ bool wxRichTextTable::Layout(wxDC& dc, wxRichTextDrawingContext& context, const // Store actual width so we can force cell to be the appropriate width on the final loop actualWidths[i] = availableCellSpace.GetWidth(); + // We now need to shift right by the width of any rowspanning cells above-left of us + int deltaX = GetRowspanDisplacement(this, j, i, paddingX, colWidths); + availableCellSpace.SetX(availableCellSpace.GetX() + deltaX); + // Lay out cell cell->Invalidate(wxRICHTEXT_ALL); cell->Layout(dc, context, availableCellSpace, availableSpace, style); @@ -9476,7 +10057,7 @@ bool wxRichTextTable::Layout(wxDC& dc, wxRichTextDrawingContext& context, const // TODO: use GetCachedSize().x to compute 'natural' size x += (availableCellSpace.GetWidth() + paddingX); - if (cell->GetCachedSize().y > maxCellHeight) + if ((cell->GetCachedSize().y > maxCellHeight) && (cell->GetRowspan() < 2)) maxCellHeight = cell->GetCachedSize().y; } } @@ -9506,6 +10087,9 @@ bool wxRichTextTable::Layout(wxDC& dc, wxRichTextDrawingContext& context, const if (j < (m_rowCount-1)) y += paddingY; } + + // Finally we need to expand any cell with rowspan > 1. We couldn't earlier; lower rows' heights weren't known + ExpandCellsWithRowspan(this, paddingY, y, dc, context, availableSpace, style); // We need to add back the margins etc. { @@ -9626,9 +10210,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. @@ -9674,6 +10258,8 @@ void wxRichTextTable::ClearTable() { m_cells.Clear(); DeleteChildren(); + m_rowCount = 0; + m_colCount = 0; } bool wxRichTextTable::CreateTable(int rows, int cols) @@ -9826,19 +10412,75 @@ bool wxRichTextTable::SetCellStyle(const wxRichTextSelection& selection, const w return true; } -bool wxRichTextTable::DeleteRows(int startRow, int noRows) +wxPosition wxRichTextTable::GetFocusedCell() const { - wxASSERT((startRow + noRows) < m_rowCount); - if ((startRow + noRows) >= m_rowCount) - return false; + wxPosition position(-1, -1); + const wxRichTextObject* focus = GetBuffer()->GetRichTextCtrl()->GetFocusObject(); - int i, j; - for (i = startRow; i < (startRow+noRows); i++) + for (int row = 0; row < GetRowCount(); ++row) { - wxRichTextObjectPtrArray& colArray = m_cells[startRow]; - for (j = 0; j < (int) colArray.GetCount(); j++) + for (int col = 0; col < GetColumnCount(); ++col) { - wxRichTextObject* cell = colArray[j]; + 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) + 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++) + { + wxRichTextObjectPtrArray& colArray = m_cells[startRow]; + for (j = 0; j < (int) colArray.GetCount(); j++) + { + wxRichTextObject* cell = colArray[j]; RemoveChild(cell, true); } @@ -9849,25 +10491,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) @@ -9878,6 +10563,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; } @@ -9887,6 +10579,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++) { @@ -9909,11 +10614,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; } @@ -9923,6 +10637,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++) { @@ -9933,6 +10660,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); @@ -9943,6 +10671,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; } @@ -10010,8 +10745,8 @@ IMPLEMENT_DYNAMIC_CLASS(wxRichTextModule, wxModule) void wxRichTextModuleInit() { wxModule* module = new wxRichTextModule; - module->Init(); wxModule::RegisterModule(module); + wxModule::InitializeModules(); } @@ -10212,7 +10947,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()); @@ -10245,7 +10980,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()); @@ -10261,14 +10996,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()); @@ -10291,12 +11032,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()); @@ -10310,10 +11055,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(); @@ -10324,7 +11080,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()); @@ -10371,7 +11131,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()); @@ -10401,7 +11161,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()); @@ -10423,7 +11183,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()); @@ -10467,7 +11227,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); } @@ -10498,13 +11258,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); @@ -10552,7 +11315,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(); @@ -10677,13 +11440,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; @@ -10693,7 +11460,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; @@ -10713,29 +11480,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) @@ -10809,6 +11583,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 @@ -10907,12 +11685,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) @@ -11480,7 +12258,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) { @@ -12451,140 +13229,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.HasFontPointSize()) + if (currentStyle.GetFontSize() != attr.GetFontSize()) { - if (currentStyle.GetFontSize() != attr.GetFontSize()) - { - // Clash of attr - mark as such - clashingAttr.AddFlag(wxTEXT_ATTR_FONT_POINT_SIZE); - currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_POINT_SIZE); - } - } - else - currentStyle.SetFontSize(attr.GetFontSize()); - } - - if (attr.HasFontPixelSize() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_PIXEL_SIZE)) - { - if (currentStyle.HasFontPixelSize()) - { - if (currentStyle.GetFontSize() != attr.GetFontSize()) - { - // Clash of attr - mark as such - clashingAttr.AddFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE); - currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE); - } + // 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)) @@ -12601,6 +13416,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)) { @@ -12616,6 +13436,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)) { @@ -12631,6 +13456,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)) { @@ -12646,6 +13476,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)) { @@ -12661,6 +13496,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)) { @@ -12676,6 +13516,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)) { @@ -12691,6 +13536,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)) { @@ -12706,6 +13556,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)) { @@ -12721,6 +13576,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)) { @@ -12736,6 +13596,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)) { @@ -12751,6 +13616,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)) { @@ -12766,6 +13636,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)) { @@ -12781,6 +13656,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)) { @@ -12796,6 +13676,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)) { @@ -12814,6 +13699,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)) { @@ -12831,6 +13721,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)) { @@ -12848,6 +13743,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)) { @@ -12887,6 +13787,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)) { @@ -12902,10 +13807,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 @@ -13219,8 +14132,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) { @@ -13236,6 +14160,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) @@ -13255,6 +14182,9 @@ wxRichTextAttr wxRichTextDrawingContext::GetVirtualAttributes(wxRichTextObject* bool wxRichTextDrawingContext::ApplyVirtualAttributes(wxRichTextAttr& attr, wxRichTextObject* obj) const { + if (!GetVirtualAttributesEnabled()) + return false; + if (HasVirtualAttributes(obj)) { wxRichTextAttr a(GetVirtualAttributes(obj)); @@ -13265,6 +14195,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) {