+ wxRichTextAttr attr = GetCombinedAttributes();
+
+ // ClearLines();
+
+ // Increase the size of the paragraph due to spacing
+ int spaceBeforePara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingBefore());
+ int spaceAfterPara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingAfter());
+ int leftIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftIndent());
+ int leftSubIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftSubIndent());
+ int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
+
+ 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().Ok())
+ {
+ wxCheckSetFont(dc, attr.GetFont());
+ lineSpacing = (int) (double(dc.GetCharHeight()) * (double(attr.GetLineSpacing())/10.0 - 1.0));
+ }
+
+ // Start position for each line relative to the paragraph
+ int startPositionFirstLine = leftIndent;
+ int startPositionSubsequentLines = leftIndent + leftSubIndent;
+ wxRect availableRect;
+
+ // If we have a bullet in this paragraph, the start position for the first line's text
+ // is actually leftIndent + leftSubIndent.
+ if (attr.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE)
+ startPositionFirstLine = startPositionSubsequentLines;
+
+ long lastEndPos = GetRange().GetStart()-1;
+ long lastCompletedEndPos = lastEndPos;
+
+ int currentWidth = 0;
+ SetPosition(rect.GetPosition());
+
+ wxPoint currentPosition(0, spaceBeforePara); // We will calculate lines relative to paragraph
+ int lineHeight = 0;
+ int maxWidth = 0;
+ int maxAscent = 0;
+ int maxDescent = 0;
+ int lineCount = 0;
+ int lineAscent = 0;
+ int lineDescent = 0;
+
+ wxRichTextObjectList::compatibility_iterator node;
+
+#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
+ wxUnusedVar(style);
+ wxArrayInt partialExtents;
+
+ wxSize paraSize;
+ int paraDescent;
+
+ // This calculates the partial text extents
+ GetRangeSize(GetRange(), paraSize, paraDescent, dc, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, wxPoint(0,0), & partialExtents);
+#else
+ node = m_children.GetFirst();
+ while (node)
+ {
+ wxRichTextObject* child = node->GetData();
+
+ child->SetCachedSize(wxDefaultSize);
+ child->Layout(dc, rect, style);
+
+ node = node->GetNext();
+ }
+
+#endif
+
+ // Split up lines
+
+ // We may need to go back to a previous child, in which case create the new line,
+ // find the child corresponding to the start position of the string, and
+ // continue.
+
+ node = m_children.GetFirst();
+ while (node)
+ {
+ wxRichTextObject* child = node->GetData();
+
+ // If floating, ignore. We already laid out floats.
+ if (child->IsFloating() || child->GetRange().GetLength() == 0)
+ {
+ node = node->GetNext();
+ continue;
+ }
+
+ // If this is e.g. a composite text box, it will need to be laid out itself.
+ // But if just a text fragment or image, for example, this will
+ // do nothing. NB: won't we need to set the position after layout?
+ // since for example if position is dependent on vertical line size, we
+ // can't tell the position until the size is determined. So possibly introduce
+ // another layout phase.
+
+ // We may only be looking at part of a child, if we searched back for wrapping
+ // and found a suitable point some way into the child. So get the size for the fragment
+ // if necessary.
+
+ long nextBreakPos = GetFirstLineBreakPosition(lastEndPos+1);
+ long lastPosToUse = child->GetRange().GetEnd();
+ bool lineBreakInThisObject = (nextBreakPos > -1 && nextBreakPos <= child->GetRange().GetEnd());
+
+ if (lineBreakInThisObject)
+ lastPosToUse = nextBreakPos;
+
+ wxSize childSize;
+ int childDescent = 0;
+
+ if ((nextBreakPos == -1) && (lastEndPos == child->GetRange().GetStart() - 1)) // i.e. we want to get the whole thing
+ {
+ childSize = child->GetCachedSize();
+ childDescent = child->GetDescent();
+ }
+ else
+ {
+#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
+ // Get height only, then the width using the partial extents
+ GetRangeSize(wxRichTextRange(lastEndPos+1, lastPosToUse), childSize, childDescent, dc, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_HEIGHT_ONLY);
+ childSize.x = wxRichTextGetRangeWidth(*this, wxRichTextRange(lastEndPos+1, lastPosToUse), partialExtents);
+#else
+ GetRangeSize(wxRichTextRange(lastEndPos+1, lastPosToUse), childSize, childDescent, dc, wxRICHTEXT_UNFORMATTED, rect.GetPosition());
+#endif
+ }
+
+ // Available width depends on the floating objects and the line height
+ // Note: the floating objects may be placed vertically along the two side of
+ // buffer, so we may have different available line width with different
+ // [startY, endY]. So, we should can't determine how wide the available
+ // space is until we know the exact line height.
+ lineDescent = wxMax(childDescent, maxDescent);
+ lineAscent = wxMax(childSize.y-childDescent, maxAscent);
+ lineHeight = lineDescent + lineAscent;
+ availableRect = collector->GetAvailableRect(rect.y + currentPosition.y, rect.y + currentPosition.y + lineHeight);
+
+ currentPosition.x = (lineCount == 0 ? availableRect.x + startPositionFirstLine : availableRect.x + startPositionSubsequentLines);
+
+ // Cases:
+ // 1) There was a line break BEFORE the natural break
+ // 2) There was a line break AFTER the natural break
+ // 3) The child still fits (carry on)
+
+ if ((lineBreakInThisObject && (childSize.x + currentWidth <= availableRect.width)) ||
+ (childSize.x + currentWidth > availableRect.width))
+ {
+ long wrapPosition = 0;
+
+ int indent = lineCount == 0 ? startPositionFirstLine : startPositionSubsequentLines;
+ indent += rightIndent;
+ // Find a place to wrap. This may walk back to previous children,
+ // for example if a word spans several objects.
+ // Note: one object must contains only one wxTextAtrr, so the line height will not
+ // change inside one object. Thus, we can pass the remain line width to the
+ // FindWrapPosition function.
+ if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos+1, child->GetRange().GetEnd()), dc, availableRect.width - indent, wrapPosition, & partialExtents))
+ {
+ // If the function failed, just cut it off at the end of this child.
+ wrapPosition = child->GetRange().GetEnd();
+ }
+
+ // FindWrapPosition can still return a value that will put us in an endless wrapping loop
+ if (wrapPosition <= lastCompletedEndPos)
+ wrapPosition = wxMax(lastCompletedEndPos+1,child->GetRange().GetEnd());
+
+ // wxLogDebug(wxT("Split at %ld"), wrapPosition);
+
+ // Let's find the actual size of the current line now
+ wxSize actualSize;
+ wxRichTextRange actualRange(lastCompletedEndPos+1, wrapPosition);
+
+ /// Use previous descent, not the wrapping descent we just found, since this may be too big
+ /// for the fragment we're about to add.
+ childDescent = maxDescent;
+
+#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
+ // Get height only, then the width using the partial extents
+ GetRangeSize(actualRange, actualSize, childDescent, dc, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_HEIGHT_ONLY);
+ actualSize.x = wxRichTextGetRangeWidth(*this, actualRange, partialExtents);
+#else
+ GetRangeSize(actualRange, actualSize, childDescent, dc, wxRICHTEXT_UNFORMATTED);
+#endif
+
+ currentWidth = actualSize.x;
+ maxDescent = wxMax(childDescent, maxDescent);
+ maxAscent = wxMax(actualSize.y-childDescent, maxAscent);
+ lineHeight = maxDescent + maxAscent;
+
+ // Add a new line
+ wxRichTextLine* line = AllocateLine(lineCount);
+
+ // 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);
+ line->SetSize(wxSize(currentWidth, lineHeight));
+ line->SetDescent(maxDescent);
+
+ // Now move down a line. TODO: add margins, spacing
+ currentPosition.y += lineHeight;
+ currentPosition.y += lineSpacing;
+ currentWidth = 0;
+ maxDescent = 0;
+ maxAscent = 0;
+ maxWidth = wxMax(maxWidth, currentWidth);
+
+ lineCount ++;
+
+ // TODO: account for zero-length objects, such as fields
+ wxASSERT(wrapPosition > lastCompletedEndPos);
+
+ lastEndPos = wrapPosition;
+ lastCompletedEndPos = lastEndPos;
+
+ lineHeight = 0;
+
+ // May need to set the node back to a previous one, due to searching back in wrapping
+ wxRichTextObject* childAfterWrapPosition = FindObjectAtPosition(wrapPosition+1);
+ if (childAfterWrapPosition)
+ node = m_children.Find(childAfterWrapPosition);
+ else
+ node = node->GetNext();
+ }
+ else
+ {
+ // We still fit, so don't add a line, and keep going
+ currentWidth += childSize.x;
+ maxDescent = wxMax(childDescent, maxDescent);
+ maxAscent = wxMax(childSize.y-childDescent, maxAscent);
+ lineHeight = maxDescent + maxAscent;
+
+ maxWidth = wxMax(maxWidth, currentWidth);
+ lastEndPos = child->GetRange().GetEnd();
+
+ node = node->GetNext();
+ }
+ }
+
+ // 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 ? availableRect.x + startPositionFirstLine : availableRect.x + 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 && GetBuffer())
+ {
+ wxFont font(GetBuffer()->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 ++;
+ }
+
+ // Remove remaining unused line objects, if any
+ ClearUnusedLines(lineCount);
+
+ // Apply styles to wrapped lines
+ ApplyParagraphStyle(attr, rect, dc);
+
+ SetCachedSize(wxSize(maxWidth, currentPosition.y + spaceAfterPara));
+
+ m_dirty = false;
+
+#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
+#if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
+ // Use the text extents to calculate the size of each fragment in each line
+ wxRichTextLineList::compatibility_iterator lineNode = m_cachedLines.GetFirst();
+ while (lineNode)
+ {
+ wxRichTextLine* line = lineNode->GetData();
+ wxRichTextRange lineRange = line->GetAbsoluteRange();
+
+ // Loop through objects until we get to the one within range
+ wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
+
+ while (node2)
+ {
+ wxRichTextObject* child = node2->GetData();
+
+ if (child->GetRange().GetLength() > 0 && !child->GetRange().IsOutside(lineRange))
+ {
+ wxRichTextRange rangeToUse = lineRange;
+ rangeToUse.LimitTo(child->GetRange());
+
+ // Find the size of the child from the text extents, and store in an array
+ // for drawing later
+ int left = 0;
+ if (rangeToUse.GetStart() > GetRange().GetStart())
+ left = partialExtents[(rangeToUse.GetStart()-1) - GetRange().GetStart()];
+ int right = partialExtents[rangeToUse.GetEnd() - GetRange().GetStart()];
+ int sz = right - left;
+ line->GetObjectSizes().Add(sz);
+ }
+ else if (child->GetRange().GetStart() > lineRange.GetEnd())
+ // Can break out of inner loop now since we've passed this line's range
+ break;
+
+ node2 = node2->GetNext();
+ }
+
+ lineNode = lineNode->GetNext();
+ }
+#endif
+#endif
+
+ return true;
+}
+
+/// Apply paragraph styles, such as centering, to wrapped lines
+void wxRichTextParagraph::ApplyParagraphStyle(const wxRichTextAttr& attr, const wxRect& rect, wxDC& dc)
+{
+ if (!attr.HasAlignment())
+ return;
+
+ wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
+ while (node)
+ {
+ wxRichTextLine* line = node->GetData();
+
+ wxPoint pos = line->GetPosition();
+ wxSize size = line->GetSize();
+
+ // centering, right-justification
+ if (attr.HasAlignment() && GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_CENTRE)
+ {
+ int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
+ pos.x = (rect.GetWidth() - pos.x - rightIndent - size.x)/2 + pos.x;
+ line->SetPosition(pos);
+ }
+ else if (attr.HasAlignment() && GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_RIGHT)
+ {
+ int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
+ pos.x = rect.GetWidth() - size.x - rightIndent;
+ line->SetPosition(pos);
+ }
+
+ node = node->GetNext();
+ }
+}
+
+/// Insert text at the given position
+bool wxRichTextParagraph::InsertText(long pos, const wxString& text)
+{
+ wxRichTextObject* childToUse = NULL;
+ wxRichTextObjectList::compatibility_iterator nodeToUse = wxRichTextObjectList::compatibility_iterator();
+
+ wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
+ while (node)
+ {
+ wxRichTextObject* child = node->GetData();
+ if (child->GetRange().Contains(pos) && child->GetRange().GetLength() > 0)
+ {
+ childToUse = child;
+ nodeToUse = node;
+ break;
+ }
+
+ node = node->GetNext();
+ }
+
+ if (childToUse)
+ {
+ wxRichTextPlainText* textObject = wxDynamicCast(childToUse, wxRichTextPlainText);
+ if (textObject)
+ {
+ int posInString = pos - textObject->GetRange().GetStart();
+
+ wxString newText = textObject->GetText().Mid(0, posInString) +
+ text + textObject->GetText().Mid(posInString);
+ textObject->SetText(newText);
+
+ int textLength = text.length();
+
+ textObject->SetRange(wxRichTextRange(textObject->GetRange().GetStart(),
+ textObject->GetRange().GetEnd() + textLength));
+
+ // Increment the end range of subsequent fragments in this paragraph.
+ // We'll set the paragraph range itself at a higher level.
+
+ wxRichTextObjectList::compatibility_iterator node = nodeToUse->GetNext();
+ while (node)
+ {
+ wxRichTextObject* child = node->GetData();
+ child->SetRange(wxRichTextRange(textObject->GetRange().GetStart() + textLength,
+ textObject->GetRange().GetEnd() + textLength));
+
+ node = node->GetNext();
+ }
+
+ return true;
+ }
+ else
+ {
+ // TODO: if not a text object, insert at closest position, e.g. in front of it
+ }
+ }
+ else
+ {
+ // Add at end.
+ // Don't pass parent initially to suppress auto-setting of parent range.
+ // We'll do that at a higher level.
+ wxRichTextPlainText* textObject = new wxRichTextPlainText(text, this);
+
+ AppendChild(textObject);
+ return true;
+ }
+
+ return false;
+}
+
+void wxRichTextParagraph::Copy(const wxRichTextParagraph& obj)
+{
+ wxRichTextCompositeObject::Copy(obj);
+}
+
+/// Clear the cached lines
+void wxRichTextParagraph::ClearLines()
+{
+ WX_CLEAR_LIST(wxRichTextLineList, m_cachedLines);
+}
+
+/// 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, int flags, wxPoint position, wxArrayInt* partialExtents) const
+{
+ if (!range.IsWithin(GetRange()))
+ return false;
+
+ if (flags & wxRICHTEXT_UNFORMATTED)
+ {
+ // Just use unformatted data, assume no line breaks
+ // TODO: take into account line breaks
+
+ wxSize sz;
+
+ wxArrayInt childExtents;
+ wxArrayInt* p;
+ if (partialExtents)
+ p = & childExtents;
+ else
+ p = NULL;
+
+ wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
+ while (node)
+ {
+
+ wxRichTextObject* child = node->GetData();
+ if (!child->GetRange().IsOutside(range))
+ {
+ // Floating objects have a zero size within the paragraph.
+ if (child->IsFloating())
+ {
+ if (partialExtents)
+ {
+ int lastSize;
+ if (partialExtents->GetCount() > 0)
+ lastSize = (*partialExtents)[partialExtents->GetCount()-1];
+ else
+ lastSize = 0;
+
+ partialExtents->Add(0 /* zero size */ + lastSize);
+ }
+ }
+ else
+ {
+ wxSize childSize;
+
+ wxRichTextRange rangeToUse = range;
+ rangeToUse.LimitTo(child->GetRange());
+ int childDescent = 0;
+
+ // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we're already cached the size,
+ // but it's only going to be used after caching has taken place.
+ if ((flags & wxRICHTEXT_HEIGHT_ONLY) && child->GetCachedSize().y != 0)
+ {
+ childDescent = child->GetDescent();
+ childSize = child->GetCachedSize();
+
+ sz.y = wxMax(sz.y, childSize.y);
+ sz.x += childSize.x;
+ descent = wxMax(descent, childDescent);
+ }
+ else if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, flags, wxPoint(position.x + sz.x, position.y), p))
+ {
+ sz.y = wxMax(sz.y, childSize.y);
+ sz.x += childSize.x;
+ descent = wxMax(descent, childDescent);
+
+ if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange()))
+ {
+ child->SetCachedSize(childSize);
+ child->SetDescent(childDescent);
+ }
+
+ if (partialExtents)
+ {
+ int lastSize;
+ if (partialExtents->GetCount() > 0)
+ lastSize = (*partialExtents)[partialExtents->GetCount()-1];
+ else
+ lastSize = 0;
+
+ size_t i;
+ for (i = 0; i < childExtents.GetCount(); i++)
+ {
+ partialExtents->Add(childExtents[i] + lastSize);
+ }
+ }
+ }
+ }
+
+ if (p)
+ p->Clear();
+ }
+
+ node = node->GetNext();
+ }
+ size = sz;
+ }
+ else
+ {
+ // Use formatted data, with line breaks
+ wxSize sz;
+
+ // We're going to loop through each line, and then for each line,
+ // call GetRangeSize for the fragment that comprises that line.
+ // Only we have to do that multiple times within the line, because
+ // the line may be broken into pieces. For now ignore line break commands
+ // (so we can assume that getting the unformatted size for a fragment
+ // within a line is the actual size)
+
+ wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
+ while (node)
+ {
+ wxRichTextLine* line = node->GetData();
+ wxRichTextRange lineRange = line->GetAbsoluteRange();
+ if (!lineRange.IsOutside(range))
+ {
+ wxSize lineSize;
+
+ wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
+ while (node2)
+ {
+ wxRichTextObject* child = node2->GetData();
+
+ if (!child->IsFloating() && !child->GetRange().IsOutside(lineRange))
+ {
+ wxRichTextRange rangeToUse = lineRange;
+ rangeToUse.LimitTo(child->GetRange());
+
+ wxSize childSize;
+ int childDescent = 0;
+ if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, flags, wxPoint(position.x + sz.x, position.y)))
+ {
+ lineSize.y = wxMax(lineSize.y, childSize.y);
+ lineSize.x += childSize.x;
+ }
+ descent = wxMax(descent, childDescent);
+ }
+
+ node2 = node2->GetNext();
+ }
+
+ // Increase size by a line (TODO: paragraph spacing)
+ sz.y += lineSize.y;
+ sz.x = wxMax(sz.x, lineSize.x);
+ }
+ node = node->GetNext();
+ }
+ size = sz;
+ }
+ return true;
+}
+
+/// Finds the absolute position and row height for the given character position
+bool wxRichTextParagraph::FindPosition(wxDC& dc, long index, wxPoint& pt, int* height, bool forceLineStart)
+{
+ if (index == -1)
+ {
+ wxRichTextLine* line = ((wxRichTextParagraphLayoutBox*)GetParent())->GetLineAtPosition(0);
+ if (line)
+ *height = line->GetSize().y;
+ else
+ *height = dc.GetCharHeight();
+
+ // -1 means 'the start of the buffer'.
+ pt = GetPosition();
+ if (line)
+ pt = pt + line->GetPosition();
+
+ return true;
+ }
+
+ // The final position in a paragraph is taken to mean the position
+ // at the start of the next paragraph.
+ if (index == GetRange().GetEnd())
+ {
+ wxRichTextParagraphLayoutBox* parent = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
+ wxASSERT( parent != NULL );
+
+ // Find the height at the next paragraph, if any
+ wxRichTextLine* line = parent->GetLineAtPosition(index + 1);
+ if (line)
+ {
+ *height = line->GetSize().y;
+ pt = line->GetAbsolutePosition();
+ }
+ else
+ {
+ *height = dc.GetCharHeight();
+ int indent = ConvertTenthsMMToPixels(dc, m_attributes.GetLeftIndent());
+ pt = wxPoint(indent, GetCachedSize().y);
+ }
+
+ return true;
+ }
+
+ if (index < GetRange().GetStart() || index > GetRange().GetEnd())
+ return false;
+
+ wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
+ while (node)
+ {
+ wxRichTextLine* line = node->GetData();
+ wxRichTextRange lineRange = line->GetAbsoluteRange();
+ if (index >= lineRange.GetStart() && index <= lineRange.GetEnd())
+ {
+ // If this is the last point in the line, and we're forcing the
+ // returned value to be the start of the next line, do the required
+ // thing.
+ if (index == lineRange.GetEnd() && forceLineStart)
+ {
+ if (node->GetNext())
+ {
+ wxRichTextLine* nextLine = node->GetNext()->GetData();
+ *height = nextLine->GetSize().y;
+ pt = nextLine->GetAbsolutePosition();
+ return true;
+ }
+ }
+
+ pt.y = line->GetPosition().y + GetPosition().y;
+
+ wxRichTextRange r(lineRange.GetStart(), index);
+ wxSize rangeSize;
+ int descent = 0;
+
+ // We find the size of the line up to this point,
+ // then we can add this size to the line start position and
+ // paragraph start position to find the actual position.
+
+ if (GetRangeSize(r, rangeSize, descent, dc, wxRICHTEXT_UNFORMATTED, line->GetPosition()+ GetPosition()))
+ {
+ pt.x = line->GetPosition().x + GetPosition().x + rangeSize.x;
+ *height = line->GetSize().y;
+
+ return true;
+ }
+
+ }
+
+ node = node->GetNext();
+ }
+
+ return false;
+}
+
+/// Hit-testing: returns a flag indicating hit test details, plus
+/// information about position
+int wxRichTextParagraph::HitTest(wxDC& dc, const wxPoint& pt, long& textPosition)
+{
+ wxPoint paraPos = GetPosition();
+
+ wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
+ while (node)
+ {
+ wxRichTextLine* line = node->GetData();
+ wxPoint linePos = paraPos + line->GetPosition();
+ wxSize lineSize = line->GetSize();
+ wxRichTextRange lineRange = line->GetAbsoluteRange();
+
+ if (pt.y <= linePos.y + lineSize.y)
+ {
+ if (pt.x < linePos.x)
+ {
+ textPosition = lineRange.GetStart();
+ return wxRICHTEXT_HITTEST_BEFORE|wxRICHTEXT_HITTEST_OUTSIDE;
+ }
+ else if (pt.x >= (linePos.x + lineSize.x))
+ {
+ textPosition = lineRange.GetEnd();
+ return wxRICHTEXT_HITTEST_AFTER|wxRICHTEXT_HITTEST_OUTSIDE;
+ }
+ else
+ {
+#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
+ wxArrayInt partialExtents;
+
+ wxSize paraSize;
+ int paraDescent;
+
+ // This calculates the partial text extents
+ GetRangeSize(lineRange, paraSize, paraDescent, dc, wxRICHTEXT_UNFORMATTED, wxPoint(0,0), & partialExtents);
+
+ int lastX = linePos.x;
+ size_t i;
+ for (i = 0; i < partialExtents.GetCount(); i++)
+ {
+ int nextX = partialExtents[i] + linePos.x;
+
+ if (pt.x >= lastX && pt.x <= nextX)
+ {
+ textPosition = i + lineRange.GetStart(); // minus 1?
+
+ // So now we know it's between i-1 and i.
+ // Let's see if we can be more precise about
+ // which side of the position it's on.
+
+ int midPoint = (nextX + lastX)/2;
+ if (pt.x >= midPoint)
+ return wxRICHTEXT_HITTEST_AFTER;
+ else
+ return wxRICHTEXT_HITTEST_BEFORE;
+ }
+
+ lastX = nextX;
+ }
+#else
+ long i;
+ int lastX = linePos.x;
+ for (i = lineRange.GetStart(); i <= lineRange.GetEnd(); i++)
+ {
+ wxSize childSize;
+ int descent = 0;
+
+ wxRichTextRange rangeToUse(lineRange.GetStart(), i);
+
+ GetRangeSize(rangeToUse, childSize, descent, dc, wxRICHTEXT_UNFORMATTED, linePos);
+
+ int nextX = childSize.x + linePos.x;
+
+ if (pt.x >= lastX && pt.x <= nextX)
+ {
+ textPosition = i;
+
+ // So now we know it's between i-1 and i.
+ // Let's see if we can be more precise about
+ // which side of the position it's on.
+
+ int midPoint = (nextX + lastX)/2;
+ if (pt.x >= midPoint)
+ return wxRICHTEXT_HITTEST_AFTER;
+ else
+ return wxRICHTEXT_HITTEST_BEFORE;
+ }
+ else
+ {
+ lastX = nextX;
+ }
+ }
+#endif
+ }
+ }
+
+ node = node->GetNext();
+ }
+
+ return wxRICHTEXT_HITTEST_NONE;
+}
+
+/// Split an object at this position if necessary, and return
+/// the previous object, or NULL if inserting at beginning.
+wxRichTextObject* wxRichTextParagraph::SplitAt(long pos, wxRichTextObject** previousObject)
+{
+ wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
+ while (node)
+ {
+ wxRichTextObject* child = node->GetData();
+
+ if (pos == child->GetRange().GetStart())
+ {
+ if (previousObject)
+ {
+ if (node->GetPrevious())
+ *previousObject = node->GetPrevious()->GetData();
+ else
+ *previousObject = NULL;
+ }
+
+ return child;
+ }
+
+ if (child->GetRange().Contains(pos))
+ {
+ // This should create a new object, transferring part of
+ // the content to the old object and the rest to the new object.
+ wxRichTextObject* newObject = child->DoSplit(pos);
+
+ // If we couldn't split this object, just insert in front of it.
+ if (!newObject)
+ {
+ // Maybe this is an empty string, try the next one
+ // return child;
+ }
+ else
+ {
+ // Insert the new object after 'child'
+ if (node->GetNext())
+ m_children.Insert(node->GetNext(), newObject);
+ else
+ m_children.Append(newObject);
+ newObject->SetParent(this);
+
+ if (previousObject)
+ *previousObject = child;
+
+ return newObject;
+ }
+ }
+
+ node = node->GetNext();
+ }
+ if (previousObject)
+ *previousObject = NULL;
+ return NULL;
+}
+
+/// Move content to a list from obj on
+void wxRichTextParagraph::MoveToList(wxRichTextObject* obj, wxList& list)
+{
+ wxRichTextObjectList::compatibility_iterator node = m_children.Find(obj);
+ while (node)
+ {
+ wxRichTextObject* child = node->GetData();
+ list.Append(child);
+
+ wxRichTextObjectList::compatibility_iterator oldNode = node;