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)
}
/// 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)
{
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();
/// 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;
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;
}
/// 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;
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);
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)
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;
}
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
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)
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);
}
}
{
#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
}
// 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();
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
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;
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;
//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);
/// 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;
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)
{
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)
{
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;
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)))
/// 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;
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(" "));
}
/// 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 );
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)
{
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
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);
}
// 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.
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;
// the image so long as the new cached bitmap size hasn't changed.
wxImage image;
- if (resetCache || m_originalImageSize.GetWidth() <= 0 || m_originalImageSize.GetHeight() <= -1)
+ if (resetCache || m_originalImageSize.GetWidth() <= 0 || m_originalImageSize.GetHeight() <= 0)
{
m_imageCache = wxNullBitmap;
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)
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
/// 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)
* The data object for a wxRichTextBuffer
*/
-const wxChar *wxRichTextBufferDataObject::ms_richTextBufferFormatId = wxT("wxShape");
+const wxChar *wxRichTextBufferDataObject::ms_richTextBufferFormatId = wxT("wxRichText");
wxRichTextBufferDataObject::wxRichTextBufferDataObject(wxRichTextBuffer* richTextBuffer)
{
WX_DEFINE_OBJARRAY(wxRichTextVariantArray);
+// JACS 2013-01-27
+WX_DEFINE_OBJARRAY(wxRichTextAttrArray);
+
IMPLEMENT_DYNAMIC_CLASS(wxRichTextProperties, wxObject)
bool wxRichTextProperties::operator==(const wxRichTextProperties& props) const
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)
{
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)
bool wxRichTextDrawingContext::ApplyVirtualAttributes(wxRichTextAttr& attr, wxRichTextObject* obj) const
{
+ if (!GetVirtualAttributesEnabled())
+ return false;
+
if (HasVirtualAttributes(obj))
{
wxRichTextAttr a(GetVirtualAttributes(obj));
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)
{