// Switch off if the platform doesn't like it for some reason
#define wxRICHTEXT_USE_OPTIMIZED_DRAWING 1
+// Use GetPartialTextExtents for platforms that support it natively
+#define wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS 1
+
const wxChar wxRichTextLineBreakChar = (wxChar) 29;
// Helpers for efficiency
inline void wxCheckSetFont(wxDC& dc, const wxFont& font)
{
+#if 0
const wxFont& font1 = dc.GetFont();
if (font1.IsOk() && font.IsOk())
{
font1.GetFaceName() == font.GetFaceName())
return;
}
+#endif
dc.SetFont(font);
}
}
/// Recursively merge all pieces that can be merged.
-bool wxRichTextCompositeObject::Defragment()
+bool wxRichTextCompositeObject::Defragment(const wxRichTextRange& range)
{
wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
while (node)
{
wxRichTextObject* child = node->GetData();
- wxRichTextCompositeObject* composite = wxDynamicCast(child, wxRichTextCompositeObject);
- if (composite)
- composite->Defragment();
-
- if (node->GetNext())
+ if (range == wxRICHTEXT_ALL || !child->GetRange().IsOutside(range))
{
- wxRichTextObject* nextChild = node->GetNext()->GetData();
- if (child->CanMerge(nextChild) && child->Merge(nextChild))
+ wxRichTextCompositeObject* composite = wxDynamicCast(child, wxRichTextCompositeObject);
+ if (composite)
+ composite->Defragment();
+
+ if (node->GetNext())
{
- nextChild->Dereference();
- m_children.Erase(node->GetNext());
+ wxRichTextObject* nextChild = node->GetNext()->GetData();
+ if (child->CanMerge(nextChild) && child->Merge(nextChild))
+ {
+ nextChild->Dereference();
+ m_children.Erase(node->GetNext());
- // Don't set node -- we'll see if we can merge again with the next
- // child.
+ // Don't set node -- we'll see if we can merge again with the next
+ // child.
+ }
+ else
+ node = node->GetNext();
}
else
node = node->GetNext();
}
/// Get/set the size for the given range. Assume only has one child.
-bool wxRichTextBox::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position) const
+bool wxRichTextBox::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position, wxArrayInt* partialExtents) const
{
wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
if (node)
{
wxRichTextObject* child = node->GetData();
- return child->GetRangeSize(range, size, descent, dc, flags, position);
+ return child->GetRangeSize(range, size, descent, dc, flags, position, partialExtents);
}
else
return false;
// Skip
}
else
- child->Draw(dc, range, selectionRange, childRect, descent, style);
+ child->Draw(dc, range, selectionRange, rect, descent, style);
}
node = node->GetNext();
}
/// Get/set the size for the given range.
-bool wxRichTextParagraphLayoutBox::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position) const
+bool wxRichTextParagraphLayoutBox::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position, wxArrayInt* WXUNUSED(partialExtents)) const
{
wxSize sz;
wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
while (node)
{
- // child is a paragraph
- wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
- wxASSERT (child != NULL);
-
- wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
- while (node2)
+ wxRichTextObject* obj = (wxRichTextObject*) node->GetData();
+ if (obj->GetRange().Contains(pos))
{
- wxRichTextLine* line = node2->GetData();
+ // child is a paragraph
+ wxRichTextParagraph* child = wxDynamicCast(obj, wxRichTextParagraph);
+ wxASSERT (child != NULL);
- wxRichTextRange range = line->GetAbsoluteRange();
+ wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
+ while (node2)
+ {
+ wxRichTextLine* line = node2->GetData();
- if (range.Contains(pos) ||
+ wxRichTextRange range = line->GetAbsoluteRange();
- // If the position is end-of-paragraph, then return the last line of
- // of the paragraph.
- (range.GetEnd() == child->GetRange().GetEnd()-1) && (pos == child->GetRange().GetEnd()))
- return line;
+ if (range.Contains(pos) ||
- node2 = node2->GetNext();
+ // If the position is end-of-paragraph, then return the last line of
+ // of the paragraph.
+ ((range.GetEnd() == child->GetRange().GetEnd()-1) && (pos == child->GetRange().GetEnd())))
+ return line;
+
+ node2 = node2->GetNext();
+ }
}
node = node->GetNext();
// we only want the paragraphs to hold this character style, then we _don't_ want to
// apply the character style. So we need to be able to choose.
- // if (!paragraphStyle && characterStyle && range.GetStart() != newPara->GetRange().GetEnd())
- if (!parasOnly && characterStyle && range.GetStart() != newPara->GetRange().GetEnd())
+ if (!parasOnly && (characterStyle|charactersOnly) && range.GetStart() != newPara->GetRange().GetEnd())
{
wxRichTextRange childRange(range);
childRange.LimitTo(newPara->GetRange());
/// Combines 'style' with 'currentStyle' for the purpose of summarising the attributes of a range of
/// content.
-bool wxRichTextParagraphLayoutBox::CollectStyle(wxTextAttr& currentStyle, const wxTextAttr& style, long& multipleStyleAttributes, int& multipleTextEffectAttributes)
+bool wxRichTextParagraphLayoutBox::CollectStyle(wxTextAttr& currentStyle, const wxTextAttr& style, long& multipleStyleAttributes, int& multipleTextEffectAttributes, int& absentStyleAttributes, int& absentTextEffectAttributes)
{
+ absentStyleAttributes |= (~style.GetFlags() & wxTEXT_ATTR_ALL);
+ absentTextEffectAttributes |= (~style.GetTextEffectFlags() & 0xFFFF);
+
if (style.HasFont())
{
- if (style.HasFontSize() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_FONT_SIZE))
+ if (style.HasFontSize() && !wxHasStyle(multipleStyleAttributes|absentStyleAttributes, wxTEXT_ATTR_FONT_SIZE))
{
if (currentStyle.HasFontSize())
{
}
}
- if (style.HasFontItalic() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_FONT_ITALIC))
+ if (style.HasFontItalic() && !wxHasStyle(multipleStyleAttributes|absentStyleAttributes, wxTEXT_ATTR_FONT_ITALIC))
{
if (currentStyle.HasFontItalic())
{
}
}
- if (style.HasFontWeight() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_FONT_WEIGHT))
+ if (style.HasFontWeight() && !wxHasStyle(multipleStyleAttributes|absentStyleAttributes, wxTEXT_ATTR_FONT_WEIGHT))
{
if (currentStyle.HasFontWeight())
{
}
}
- if (style.HasFontFaceName() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_FONT_FACE))
+ if (style.HasFontFaceName() && !wxHasStyle(multipleStyleAttributes|absentStyleAttributes, wxTEXT_ATTR_FONT_FACE))
{
if (currentStyle.HasFontFaceName())
{
}
}
- if (style.HasFontUnderlined() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_FONT_UNDERLINE))
+ if (style.HasFontUnderlined() && !wxHasStyle(multipleStyleAttributes|absentStyleAttributes, wxTEXT_ATTR_FONT_UNDERLINE))
{
if (currentStyle.HasFontUnderlined())
{
}
}
- if (style.HasTextColour() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_TEXT_COLOUR))
+ if (style.HasTextColour() && !wxHasStyle(multipleStyleAttributes|absentStyleAttributes, wxTEXT_ATTR_TEXT_COLOUR))
{
if (currentStyle.HasTextColour())
{
currentStyle.SetTextColour(style.GetTextColour());
}
- if (style.HasBackgroundColour() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_BACKGROUND_COLOUR))
+ if (style.HasBackgroundColour() && !wxHasStyle(multipleStyleAttributes|absentStyleAttributes, wxTEXT_ATTR_BACKGROUND_COLOUR))
{
if (currentStyle.HasBackgroundColour())
{
currentStyle.SetBackgroundColour(style.GetBackgroundColour());
}
- if (style.HasAlignment() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_ALIGNMENT))
+ if (style.HasAlignment() && !wxHasStyle(multipleStyleAttributes|absentStyleAttributes, wxTEXT_ATTR_ALIGNMENT))
{
if (currentStyle.HasAlignment())
{
currentStyle.SetAlignment(style.GetAlignment());
}
- if (style.HasTabs() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_TABS))
+ if (style.HasTabs() && !wxHasStyle(multipleStyleAttributes|absentStyleAttributes, wxTEXT_ATTR_TABS))
{
if (currentStyle.HasTabs())
{
currentStyle.SetTabs(style.GetTabs());
}
- if (style.HasLeftIndent() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_LEFT_INDENT))
+ if (style.HasLeftIndent() && !wxHasStyle(multipleStyleAttributes|absentStyleAttributes, wxTEXT_ATTR_LEFT_INDENT))
{
if (currentStyle.HasLeftIndent())
{
currentStyle.SetLeftIndent(style.GetLeftIndent(), style.GetLeftSubIndent());
}
- if (style.HasRightIndent() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_RIGHT_INDENT))
+ if (style.HasRightIndent() && !wxHasStyle(multipleStyleAttributes|absentStyleAttributes, wxTEXT_ATTR_RIGHT_INDENT))
{
if (currentStyle.HasRightIndent())
{
currentStyle.SetRightIndent(style.GetRightIndent());
}
- if (style.HasParagraphSpacingAfter() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_PARA_SPACING_AFTER))
+ if (style.HasParagraphSpacingAfter() && !wxHasStyle(multipleStyleAttributes|absentStyleAttributes, wxTEXT_ATTR_PARA_SPACING_AFTER))
{
if (currentStyle.HasParagraphSpacingAfter())
{
currentStyle.SetParagraphSpacingAfter(style.GetParagraphSpacingAfter());
}
- if (style.HasParagraphSpacingBefore() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_PARA_SPACING_BEFORE))
+ if (style.HasParagraphSpacingBefore() && !wxHasStyle(multipleStyleAttributes|absentStyleAttributes, wxTEXT_ATTR_PARA_SPACING_BEFORE))
{
if (currentStyle.HasParagraphSpacingBefore())
{
currentStyle.SetParagraphSpacingBefore(style.GetParagraphSpacingBefore());
}
- if (style.HasLineSpacing() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_LINE_SPACING))
+ if (style.HasLineSpacing() && !wxHasStyle(multipleStyleAttributes|absentStyleAttributes, wxTEXT_ATTR_LINE_SPACING))
{
if (currentStyle.HasLineSpacing())
{
currentStyle.SetLineSpacing(style.GetLineSpacing());
}
- if (style.HasCharacterStyleName() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_CHARACTER_STYLE_NAME))
+ if (style.HasCharacterStyleName() && !wxHasStyle(multipleStyleAttributes|absentStyleAttributes, wxTEXT_ATTR_CHARACTER_STYLE_NAME))
{
if (currentStyle.HasCharacterStyleName())
{
currentStyle.SetCharacterStyleName(style.GetCharacterStyleName());
}
- if (style.HasParagraphStyleName() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_PARAGRAPH_STYLE_NAME))
+ if (style.HasParagraphStyleName() && !wxHasStyle(multipleStyleAttributes|absentStyleAttributes, wxTEXT_ATTR_PARAGRAPH_STYLE_NAME))
{
if (currentStyle.HasParagraphStyleName())
{
currentStyle.SetParagraphStyleName(style.GetParagraphStyleName());
}
- if (style.HasListStyleName() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_LIST_STYLE_NAME))
+ if (style.HasListStyleName() && !wxHasStyle(multipleStyleAttributes|absentStyleAttributes, wxTEXT_ATTR_LIST_STYLE_NAME))
{
if (currentStyle.HasListStyleName())
{
currentStyle.SetListStyleName(style.GetListStyleName());
}
- if (style.HasBulletStyle() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_BULLET_STYLE))
+ if (style.HasBulletStyle() && !wxHasStyle(multipleStyleAttributes|absentStyleAttributes, wxTEXT_ATTR_BULLET_STYLE))
{
if (currentStyle.HasBulletStyle())
{
currentStyle.SetBulletStyle(style.GetBulletStyle());
}
- if (style.HasBulletNumber() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_BULLET_NUMBER))
+ if (style.HasBulletNumber() && !wxHasStyle(multipleStyleAttributes|absentStyleAttributes, wxTEXT_ATTR_BULLET_NUMBER))
{
if (currentStyle.HasBulletNumber())
{
currentStyle.SetBulletNumber(style.GetBulletNumber());
}
- if (style.HasBulletText() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_BULLET_TEXT))
+ if (style.HasBulletText() && !wxHasStyle(multipleStyleAttributes|absentStyleAttributes, wxTEXT_ATTR_BULLET_TEXT))
{
if (currentStyle.HasBulletText())
{
}
}
- if (style.HasBulletName() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_BULLET_NAME))
+ if (style.HasBulletName() && !wxHasStyle(multipleStyleAttributes|absentStyleAttributes, wxTEXT_ATTR_BULLET_NAME))
{
if (currentStyle.HasBulletName())
{
}
}
- if (style.HasURL() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_URL))
+ if (style.HasURL() && !wxHasStyle(multipleStyleAttributes|absentStyleAttributes, wxTEXT_ATTR_URL))
{
if (currentStyle.HasURL())
{
}
}
- if (style.HasTextEffects() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_EFFECTS))
+ if (style.HasTextEffects() && !wxHasStyle(multipleStyleAttributes|absentStyleAttributes, wxTEXT_ATTR_EFFECTS))
{
if (currentStyle.HasTextEffects())
{
// We need to find the bits in the new style that are different:
// just look at those bits that are specified by the new style.
+ // We need to remove the bits and flags that are not common between current style
+ // and new style. In so doing we need to take account of the styles absent from one or more of the
+ // previous styles.
+
int currentRelevantTextEffects = currentStyle.GetTextEffects() & style.GetTextEffectFlags();
int newRelevantTextEffects = style.GetTextEffects() & style.GetTextEffectFlags();
currentStyle.SetTextEffects(style.GetTextEffects());
currentStyle.SetTextEffectFlags(style.GetTextEffectFlags());
}
+
+ // Mask out the flags and values that cannot be common because they were absent in one or more objecrs
+ // that we've looked at so far
+ currentStyle.SetTextEffects(currentStyle.GetTextEffects() & ~absentTextEffectAttributes);
+ currentStyle.SetTextEffectFlags(currentStyle.GetTextEffectFlags() & ~absentTextEffectAttributes);
+
+ if (currentStyle.GetTextEffectFlags() == 0)
+ currentStyle.SetFlags(currentStyle.GetFlags() & ~wxTEXT_ATTR_EFFECTS);
}
- if (style.HasOutlineLevel() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_OUTLINE_LEVEL))
+ if (style.HasOutlineLevel() && !wxHasStyle(multipleStyleAttributes|absentStyleAttributes, wxTEXT_ATTR_OUTLINE_LEVEL))
{
if (currentStyle.HasOutlineLevel())
{
long multipleStyleAttributes = 0;
int multipleTextEffectAttributes = 0;
+ int absentStyleAttributesPara = 0;
+ int absentStyleAttributesChar = 0;
+ int absentTextEffectAttributesPara = 0;
+ int absentTextEffectAttributesChar = 0;
+
wxRichTextObjectList::compatibility_iterator node = GetChildren().GetFirst();
while (node)
{
{
wxTextAttr paraStyle = para->GetCombinedAttributes();
- CollectStyle(style, paraStyle, multipleStyleAttributes, multipleTextEffectAttributes);
+ CollectStyle(style, paraStyle, multipleStyleAttributes, multipleTextEffectAttributes, absentStyleAttributesPara, absentTextEffectAttributesPara);
}
else
{
// First collect paragraph attributes only
wxTextAttr paraStyle = para->GetCombinedAttributes();
paraStyle.SetFlags(paraStyle.GetFlags() & wxTEXT_ATTR_PARAGRAPH);
- CollectStyle(style, paraStyle, multipleStyleAttributes, multipleTextEffectAttributes);
+ CollectStyle(style, paraStyle, multipleStyleAttributes, multipleTextEffectAttributes, absentStyleAttributesPara, absentTextEffectAttributesPara);
wxRichTextObjectList::compatibility_iterator childNode = para->GetChildren().GetFirst();
// Now collect character attributes only
childStyle.SetFlags(childStyle.GetFlags() & wxTEXT_ATTR_CHARACTER);
- CollectStyle(style, childStyle, multipleStyleAttributes, multipleTextEffectAttributes);
+ CollectStyle(style, childStyle, multipleStyleAttributes, multipleTextEffectAttributes, absentStyleAttributesChar, absentTextEffectAttributesChar);
}
childNode = childNode->GetNext();
}
/// Draw the item
-bool wxRichTextParagraph::Draw(wxDC& dc, const wxRichTextRange& range, const wxRichTextRange& selectionRange, const wxRect& WXUNUSED(rect), int WXUNUSED(descent), int style)
+bool wxRichTextParagraph::Draw(wxDC& dc, const wxRichTextRange& range, const wxRichTextRange& selectionRange, const wxRect& rect, int WXUNUSED(descent), int style)
{
wxTextAttr attr = GetCombinedAttributes();
wxRichTextLine* line = node->GetData();
wxRichTextRange lineRange = line->GetAbsoluteRange();
- int maxDescent = line->GetDescent();
-
// Lines are specified relative to the paragraph
wxPoint linePosition = line->GetPosition() + GetPosition();
- wxPoint objectPosition = linePosition;
- // Loop through objects until we get to the one within range
- wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
- while (node2)
+ // Don't draw if off the screen
+ if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) != 0) || ((linePosition.y + line->GetSize().y) >= rect.y && linePosition.y <= rect.y + rect.height))
{
- wxRichTextObject* child = node2->GetData();
+ wxPoint objectPosition = linePosition;
+ int maxDescent = line->GetDescent();
+
+ // Loop through objects until we get to the one within range
+ wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
- if (!child->GetRange().IsOutside(lineRange) && !lineRange.IsOutside(range))
+ int i = 0;
+ while (node2)
{
- // Draw this part of the line at the correct position
- wxRichTextRange objectRange(child->GetRange());
- objectRange.LimitTo(lineRange);
+ wxRichTextObject* child = node2->GetData();
- wxSize objectSize;
- int descent = 0;
- child->GetRangeSize(objectRange, objectSize, descent, dc, wxRICHTEXT_UNFORMATTED, objectPosition);
+ if (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());
+ objectRange.LimitTo(lineRange);
- // Use the child object's width, but the whole line's height
- wxRect childRect(objectPosition, wxSize(objectSize.x, line->GetSize().y));
- child->Draw(dc, objectRange, selectionRange, childRect, maxDescent, style);
+ wxSize objectSize;
+#if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING && wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
+ if (i < (int) line->GetObjectSizes().GetCount())
+ {
+ objectSize.x = line->GetObjectSizes()[(size_t) i];
+ }
+ else
+#endif
+ {
+ int descent = 0;
+ child->GetRangeSize(objectRange, objectSize, descent, dc, wxRICHTEXT_UNFORMATTED, objectPosition);
+ }
- objectPosition.x += objectSize.x;
- }
- else if (child->GetRange().GetStart() > lineRange.GetEnd())
- // Can break out of inner loop now since we've passed this line's range
- break;
+ // Use the child object's width, but the whole line's height
+ wxRect childRect(objectPosition, wxSize(objectSize.x, line->GetSize().y));
+ child->Draw(dc, objectRange, selectionRange, childRect, maxDescent, style);
- node2 = node2->GetNext();
+ objectPosition.x += objectSize.x;
+ i ++;
+ }
+ 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();
+ }
}
node = node->GetNext();
return true;
}
+// Get the range width using partial extents calculated for the whole paragraph.
+static int wxRichTextGetRangeWidth(const wxRichTextParagraph& para, const wxRichTextRange& range, const wxArrayInt& partialExtents)
+{
+ wxASSERT(partialExtents.GetCount() >= (size_t) range.GetLength());
+
+ if (partialExtents.GetCount() < (size_t) range.GetLength())
+ return 0;
+
+ int leftMostPos = 0;
+ if (range.GetStart() - para.GetRange().GetStart() > 0)
+ leftMostPos = partialExtents[range.GetStart() - para.GetRange().GetStart() - 1];
+
+ int rightMostPos = partialExtents[range.GetEnd() - para.GetRange().GetStart()];
+
+ int w = rightMostPos - leftMostPos;
+
+ return w;
+}
+
/// Lay the item out
bool wxRichTextParagraph::Layout(wxDC& dc, const wxRect& rect, int style)
{
int lineCount = 0;
- wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
+ 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();
node = node->GetNext();
}
+#endif
+
// Split up lines
// We may need to go back to a previous child, in which case create the new line,
{
wxRichTextObject* child = node->GetData();
+ if (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?
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
+ }
// Cases:
// 1) There was a line break BEFORE the natural break
// Find a place to wrap. This may walk back to previous children,
// for example if a word spans several objects.
- if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos+1, child->GetRange().GetEnd()), dc, availableSpaceForText, wrapPosition))
+ if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos+1, child->GetRange().GetEnd()), dc, availableSpaceForText, wrapPosition, & partialExtents))
{
// If the function failed, just cut it off at the end of this child.
wrapPosition = child->GetRange().GetEnd();
// 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;
lineHeight = wxMax(lineHeight, actualSize.y);
maxDescent = wxMax(childDescent, maxDescent);
ClearUnusedLines(lineCount);
// Apply styles to wrapped lines
- ApplyParagraphStyle(attr, rect);
+ ApplyParagraphStyle(attr, rect, dc);
SetCachedSize(wxSize(maxWidth, currentPosition.y + spaceBeforePara + 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 wxTextAttr& attr, const wxRect& rect)
+void wxRichTextParagraph::ApplyParagraphStyle(const wxTextAttr& attr, const wxRect& rect, wxDC& dc)
{
if (!attr.HasAlignment())
return;
// centering, right-justification
if (attr.HasAlignment() && GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_CENTRE)
{
- pos.x = (rect.GetWidth() - size.x)/2 + pos.x;
+ int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
+ pos.x = (rect.GetWidth() - (pos.x - rect.x) - rightIndent - size.x)/2 + pos.x;
+ // Lines are relative to the paragraph position
+ pos.x -= GetPosition().x;
line->SetPosition(pos);
}
else if (attr.HasAlignment() && GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_RIGHT)
{
- pos.x = pos.x + rect.GetWidth() - size.x;
+ int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
+ pos.x = rect.x + rect.GetWidth() - size.x - rightIndent;
+ // Lines are relative to the paragraph position
+ pos.x -= GetPosition().x;
line->SetPosition(pos);
}
/// 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) const
+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;
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))
{
rangeToUse.LimitTo(child->GetRange());
int childDescent = 0;
- if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, flags, wxPoint(position.x + sz.x, position.y)))
+ // 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();
}
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 + lastX;
+ 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++)
lastX = nextX;
}
}
+#endif
}
}
text += textObj->GetTextForRange(range);
}
else
- return true;
+ {
+ text += wxT(" ");
+ }
}
node = node->GetNext();
text = textObj->GetTextForRange(range) + text;
}
else
- return true;
+ {
+ text = wxT(" ") + text;
+ }
}
node = node->GetPrevious();
}
/// Find a suitable wrap position.
-bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange& range, wxDC& dc, int availableSpace, long& wrapPosition)
+bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange& range, wxDC& dc, int availableSpace, long& wrapPosition, wxArrayInt* partialExtents)
{
+ if (range.GetLength() <= 0)
+ return false;
+
// Find the first position where the line exceeds the available space.
wxSize sz;
long breakPosition = range.GetEnd();
- // Binary chop for speed
- long minPos = range.GetStart();
- long maxPos = range.GetEnd();
- while (true)
+#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
+ if (partialExtents && partialExtents->GetCount() >= (size_t) (GetRange().GetLength()-1)) // the final position in a paragraph is the newline
{
- if (minPos == maxPos)
- {
- int descent = 0;
- GetRangeSize(wxRichTextRange(range.GetStart(), minPos), sz, descent, dc, wxRICHTEXT_UNFORMATTED);
+ int widthBefore;
- if (sz.x > availableSpace)
- breakPosition = minPos - 1;
- break;
- }
- else if ((maxPos - minPos) == 1)
+ if (range.GetStart() > GetRange().GetStart())
+ widthBefore = (*partialExtents)[range.GetStart() - GetRange().GetStart() - 1];
+ else
+ widthBefore = 0;
+
+ size_t i;
+ for (i = (size_t) range.GetStart(); i <= (size_t) range.GetEnd(); i++)
{
- int descent = 0;
- GetRangeSize(wxRichTextRange(range.GetStart(), minPos), sz, descent, dc, wxRICHTEXT_UNFORMATTED);
+ int widthFromStartOfThisRange = (*partialExtents)[i - GetRange().GetStart()] - widthBefore;
- if (sz.x > availableSpace)
- breakPosition = minPos - 1;
- else
+ if (widthFromStartOfThisRange > availableSpace)
{
- GetRangeSize(wxRichTextRange(range.GetStart(), maxPos), sz, descent, dc, wxRICHTEXT_UNFORMATTED);
- if (sz.x > availableSpace)
- breakPosition = maxPos-1;
+ breakPosition = i-1;
+ break;
}
- break;
}
- else
+ }
+ else
+#endif
+ {
+ // Binary chop for speed
+ long minPos = range.GetStart();
+ long maxPos = range.GetEnd();
+ while (true)
{
- long nextPos = minPos + ((maxPos - minPos) / 2);
-
- int descent = 0;
- GetRangeSize(wxRichTextRange(range.GetStart(), nextPos), sz, descent, dc, wxRICHTEXT_UNFORMATTED);
+ if (minPos == maxPos)
+ {
+ int descent = 0;
+ GetRangeSize(wxRichTextRange(range.GetStart(), minPos), sz, descent, dc, wxRICHTEXT_UNFORMATTED);
- if (sz.x > availableSpace)
+ if (sz.x > availableSpace)
+ breakPosition = minPos - 1;
+ break;
+ }
+ else if ((maxPos - minPos) == 1)
{
- maxPos = nextPos;
+ int descent = 0;
+ GetRangeSize(wxRichTextRange(range.GetStart(), minPos), sz, descent, dc, wxRICHTEXT_UNFORMATTED);
+
+ if (sz.x > availableSpace)
+ breakPosition = minPos - 1;
+ else
+ {
+ GetRangeSize(wxRichTextRange(range.GetStart(), maxPos), sz, descent, dc, wxRICHTEXT_UNFORMATTED);
+ if (sz.x > availableSpace)
+ breakPosition = maxPos-1;
+ }
+ break;
}
else
{
- minPos = nextPos;
+ long nextPos = minPos + ((maxPos - minPos) / 2);
+
+ int descent = 0;
+ GetRangeSize(wxRichTextRange(range.GetStart(), nextPos), sz, descent, dc, wxRICHTEXT_UNFORMATTED);
+
+ if (sz.x > availableSpace)
+ {
+ maxPos = nextPos;
+ }
+ else
+ {
+ minPos = nextPos;
+ }
}
}
}
m_pos = wxPoint(0, 0);
m_size = wxSize(0, 0);
m_descent = 0;
+#if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
+ m_objectSizes.Clear();
+#endif
}
/// Copy
void wxRichTextLine::Copy(const wxRichTextLine& obj)
{
m_range = obj.m_range;
+#if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
+ m_objectSizes = obj.m_objectSizes;
+#endif
}
/// Get the absolute object position
long len = range.GetLength();
wxString stringChunk = str.Mid(range.GetStart() - offset, (size_t) len);
- int charHeight = dc.GetCharHeight();
-
- int x = rect.x;
- int y = rect.y + (rect.height - charHeight - (descent - m_descent));
-
// Test for the optimized situations where all is selected, or none
// is selected.
- wxFont font(GetBuffer()->GetFontTable().FindFont(textAttr));
- wxCheckSetFont(dc, font);
+ wxFont textFont(GetBuffer()->GetFontTable().FindFont(textAttr));
+ wxCheckSetFont(dc, textFont);
+ int charHeight = dc.GetCharHeight();
+
+ int x, y;
+ if ( textFont.Ok() )
+ {
+ if ( textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT) )
+ {
+ double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
+ textFont.SetPointSize( static_cast<int>(size) );
+ x = rect.x;
+ y = rect.y;
+ wxCheckSetFont(dc, textFont);
+ }
+ else if ( textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT) )
+ {
+ double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
+ textFont.SetPointSize( static_cast<int>(size) );
+ x = rect.x;
+ int sub_height = static_cast<int>( static_cast<double>(charHeight) / wxSCRIPT_MUL_FACTOR);
+ y = rect.y + (rect.height - sub_height + (descent - m_descent));
+ wxCheckSetFont(dc, textFont);
+ }
+ else
+ {
+ x = rect.x;
+ y = rect.y + (rect.height - charHeight - (descent - m_descent));
+ }
+ }
+ else
+ {
+ x = rect.x;
+ y = rect.y + (rect.height - charHeight - (descent - m_descent));
+ }
// (a) All selected.
if (selectionRange.GetStart() <= range.GetStart() && selectionRange.GetEnd() >= range.GetEnd())
// (c) Part selected, part not
// Let's draw unselected chunk, selected chunk, then unselected chunk.
- dc.SetBackgroundMode(wxTRANSPARENT);
+ dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
// 1. Initial unselected chunk, if any, up until start of selection.
if (selectionRange.GetStart() > range.GetStart() && selectionRange.GetStart() <= range.GetEnd())
wxCheckSetBrush(dc, wxBrush(highlightColour));
wxCheckSetPen(dc, wxPen(highlightColour));
dc.SetTextForeground(highlightTextColour);
- dc.SetBackgroundMode(wxTRANSPARENT);
+ dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
}
else
{
if (attr.HasFlag(wxTEXT_ATTR_BACKGROUND_COLOUR) && attr.GetBackgroundColour().IsOk())
{
- dc.SetBackgroundMode(wxSOLID);
+ dc.SetBackgroundMode(wxBRUSHSTYLE_SOLID);
dc.SetTextBackground(attr.GetBackgroundColour());
}
else
- dc.SetBackgroundMode(wxTRANSPARENT);
+ dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
}
while (hasTabs)
/// 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, int WXUNUSED(flags), wxPoint position) const
+bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int WXUNUSED(flags), wxPoint position, wxArrayInt* partialExtents) const
{
if (!range.IsWithin(GetRange()))
return false;
// of line breaks - and we don't need it, since we'll calculate size within
// formatted text by doing it in chunks according to the line ranges
+ bool bScript(false);
wxFont font(GetBuffer()->GetFontTable().FindFont(textAttr));
- wxCheckSetFont(dc, font);
+ if (font.Ok())
+ {
+ if ( textAttr.HasTextEffects() && ( (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT)
+ || (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT) ) )
+ {
+ wxFont textFont = font;
+ double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
+ textFont.SetPointSize( static_cast<int>(size) );
+ wxCheckSetFont(dc, textFont);
+ bScript = true;
+ }
+ else
+ {
+ wxCheckSetFont(dc, font);
+ }
+ }
+ bool haveDescent = false;
int startPos = range.GetStart() - GetRange().GetStart();
long len = range.GetLength();
while (stringChunk.Find(wxT('\t')) >= 0)
{
+ int absoluteWidth = 0;
+
// the string has a tab
// break up the string at the Tab
wxString stringFragment = stringChunk.BeforeFirst(wxT('\t'));
stringChunk = stringChunk.AfterFirst(wxT('\t'));
- dc.GetTextExtent(stringFragment, & w, & h);
- width += w;
- int absoluteWidth = width + position.x;
+
+ if (partialExtents)
+ {
+ int oldWidth;
+ if (partialExtents->GetCount() > 0)
+ oldWidth = (*partialExtents)[partialExtents->GetCount()-1];
+ else
+ oldWidth = 0;
+
+ // Add these partial extents
+ wxArrayInt p;
+ dc.GetPartialTextExtents(stringFragment, p);
+ size_t j;
+ for (j = 0; j < p.GetCount(); j++)
+ partialExtents->Add(oldWidth + p[j]);
+
+ if (partialExtents->GetCount() > 0)
+ absoluteWidth = (*partialExtents)[(*partialExtents).GetCount()-1] + position.x;
+ else
+ absoluteWidth = position.x;
+ }
+ else
+ {
+ dc.GetTextExtent(stringFragment, & w, & h);
+ width += w;
+ absoluteWidth = width + position.x;
+ haveDescent = true;
+ }
bool notFound = true;
for (int i = 0; i < tabCount && notFound; ++i)
notFound = false;
width = nextTabPos - position.x;
+
+ if (partialExtents)
+ partialExtents->Add(width);
}
}
}
}
- dc.GetTextExtent(stringChunk, & w, & h, & descent);
- width += w;
- size = wxSize(width, dc.GetCharHeight());
+
+ if (!stringChunk.IsEmpty())
+ {
+ if (partialExtents)
+ {
+ int oldWidth;
+ if (partialExtents->GetCount() > 0)
+ oldWidth = (*partialExtents)[partialExtents->GetCount()-1];
+ else
+ oldWidth = 0;
+
+ // Add these partial extents
+ wxArrayInt p;
+ dc.GetPartialTextExtents(stringChunk, p);
+ size_t j;
+ for (j = 0; j < p.GetCount(); j++)
+ partialExtents->Add(oldWidth + p[j]);
+ }
+ else
+ {
+ dc.GetTextExtent(stringChunk, & w, & h, & descent);
+ width += w;
+ haveDescent = true;
+ }
+ }
+
+ if (partialExtents)
+ {
+ int charHeight = dc.GetCharHeight();
+ if ((*partialExtents).GetCount() > 0)
+ w = (*partialExtents)[partialExtents->GetCount()-1];
+ else
+ w = 0;
+ size = wxSize(w, charHeight);
+ }
+ else
+ {
+ size = wxSize(width, dc.GetCharHeight());
+ }
+
+ if (!haveDescent)
+ dc.GetTextExtent(wxT("X"), & w, & h, & descent);
+
+ if ( bScript )
+ dc.SetFont(font);
return true;
}
action->GetNewParagraphs() = paragraphs;
+ if (p && !p->IsDefault())
+ {
+ for (wxRichTextObjectList::compatibility_iterator node = action->GetNewParagraphs().GetChildren().GetFirst(); node; node = node->GetNext())
+ {
+ wxRichTextObject* child = node->GetData();
+ child->SetAttributes(*p);
+ }
+ }
+
action->SetPosition(pos);
wxRichTextRange range = wxRichTextRange(pos, pos + paragraphs.GetRange().GetEnd() - 1);
{
if (para && para->GetRange().GetEnd() == pos)
pos1 ++;
+
+ // Now see if we need to number the paragraph.
if (newPara->GetAttributes().HasBulletNumber())
- newPara->GetAttributes().SetBulletNumber(newPara->GetAttributes().GetBulletNumber()+1);
+ {
+ wxRichTextAttr numberingAttr;
+ if (FindNextParagraphNumber(para, numberingAttr))
+ wxRichTextApplyStyle(newPara->GetAttributes(), (const wxRichTextAttr&) numberingAttr);
+ }
}
action->SetPosition(pos);
}
}
}
+
+ // Also apply list style if present
+ if (lookUpNewParaStyle && !para->GetAttributes().GetListStyleName().IsEmpty() && GetStyleSheet())
+ {
+ wxRichTextListStyleDefinition* listDef = GetStyleSheet()->FindListStyle(para->GetAttributes().GetListStyleName());
+ if (listDef)
+ {
+ int thisIndent = para->GetAttributes().GetLeftIndent();
+ int thisLevel = para->GetAttributes().HasOutlineLevel() ? para->GetAttributes().GetOutlineLevel() : listDef->FindLevelForIndent(thisIndent);
+
+ // Apply the overall list style, and item style for this level
+ wxRichTextAttr listStyle(listDef->GetCombinedStyleForLevel(thisLevel, GetStyleSheet()));
+ wxRichTextApplyStyle(attr, listStyle);
+ attr.SetOutlineLevel(thisLevel);
+ if (para->GetAttributes().HasBulletNumber())
+ attr.SetBulletNumber(para->GetAttributes().GetBulletNumber());
+ }
+ }
+
if (!foundAttributes)
{
attr = para->GetAttributes();
attr.SetFlags(flags);
}
- // Now see if we need to number the paragraph.
- if (attr.HasBulletStyle())
- {
- wxTextAttr numberingAttr;
- if (FindNextParagraphNumber(para, numberingAttr))
- wxRichTextApplyStyle(attr, (const wxTextAttr&) numberingAttr);
- }
-
return attr;
}
else
SetDefaultStyle(newStyle);
- // wxLogDebug("Default style size = %d", GetDefaultStyle().GetFont().GetPointSize());
-
return true;
}
}
/// Finds a handler by filename or, if supplied, type
-wxRichTextFileHandler *wxRichTextBuffer::FindHandlerFilenameOrType(const wxString& filename, int imageType)
+wxRichTextFileHandler *wxRichTextBuffer::FindHandlerFilenameOrType(const wxString& filename,
+ wxRichTextFileType imageType)
{
if (imageType != wxRICHTEXT_TYPE_ANY)
return FindHandler(imageType);
else if (!filename.IsEmpty())
{
wxString path, file, ext;
- wxSplitPath(filename, & path, & file, & ext);
+ wxFileName::SplitPath(filename, & path, & file, & ext);
return FindHandler(ext, imageType);
}
else
}
/// Finds a handler by extension and type
-wxRichTextFileHandler* wxRichTextBuffer::FindHandler(const wxString& extension, int type)
+wxRichTextFileHandler* wxRichTextBuffer::FindHandler(const wxString& extension, wxRichTextFileType type)
{
wxList::compatibility_iterator node = sm_handlers.GetFirst();
while (node)
}
/// Finds a handler by type
-wxRichTextFileHandler* wxRichTextBuffer::FindHandler(int type)
+wxRichTextFileHandler* wxRichTextBuffer::FindHandler(wxRichTextFileType type)
{
wxList::compatibility_iterator node = sm_handlers.GetFirst();
while (node)
while (node)
{
wxRichTextFileHandler* handler = (wxRichTextFileHandler*) node->GetData();
- if (handler->IsVisible() && ((save && handler->CanSave()) || !save && handler->CanLoad()))
+ if (handler->IsVisible() && ((save && handler->CanSave()) || (!save && handler->CanLoad())))
{
if (combine)
{
}
/// Load a file
-bool wxRichTextBuffer::LoadFile(const wxString& filename, int type)
+bool wxRichTextBuffer::LoadFile(const wxString& filename, wxRichTextFileType type)
{
wxRichTextFileHandler* handler = FindHandlerFilenameOrType(filename, type);
if (handler)
}
/// Save a file
-bool wxRichTextBuffer::SaveFile(const wxString& filename, int type)
+bool wxRichTextBuffer::SaveFile(const wxString& filename, wxRichTextFileType type)
{
wxRichTextFileHandler* handler = FindHandlerFilenameOrType(filename, type);
if (handler)
}
/// Load from a stream
-bool wxRichTextBuffer::LoadFile(wxInputStream& stream, int type)
+bool wxRichTextBuffer::LoadFile(wxInputStream& stream, wxRichTextFileType type)
{
wxRichTextFileHandler* handler = FindHandler(type);
if (handler)
}
/// Save to a stream
-bool wxRichTextBuffer::SaveFile(wxOutputStream& stream, int type)
+bool wxRichTextBuffer::SaveFile(wxOutputStream& stream, wxRichTextFileType type)
{
wxRichTextFileHandler* handler = FindHandler(type);
if (handler)
wxRichTextBuffer* richTextBuffer = data.GetRichTextBuffer();
if (richTextBuffer)
{
- InsertParagraphsWithUndo(position+1, *richTextBuffer, GetRichTextCtrl(), wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE);
+ InsertParagraphsWithUndo(position+1, *richTextBuffer, GetRichTextCtrl(), 0);
if (GetRichTextCtrl())
GetRichTextCtrl()->ShowPosition(position + richTextBuffer->GetRange().GetEnd());
delete richTextBuffer;
#else
wxString text2 = text;
#endif
- InsertTextWithUndo(position+1, text2, GetRichTextCtrl());
+ InsertTextWithUndo(position+1, text2, GetRichTextCtrl(), wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE);
if (GetRichTextCtrl())
GetRichTextCtrl()->ShowPosition(position + text2.Length());
if (action->GetNewParagraphs().GetChildCount() == 1)
action->GetNewParagraphs().SetPartialParagraph(true);
- action->SetPosition(position);
+ action->SetPosition(position+1);
// Set the range we'll need to delete in Undo
- action->SetRange(wxRichTextRange(position, position));
+ action->SetRange(wxRichTextRange(position+1, position+1));
SubmitAction(action);
if (attr.GetTextColour().Ok())
dc.SetTextForeground(attr.GetTextColour());
- dc.SetBackgroundMode(wxTRANSPARENT);
+ dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
int charHeight = dc.GetCharHeight();
wxCoord tw, th;
{
}
+void wxRichTextAction::CalculateRefreshOptimizations(wxArrayInt& optimizationLineCharPositions, wxArrayInt& optimizationLineYPositions)
+{
+ // Store a list of line start character and y positions so we can figure out which area
+ // we need to refresh
+
+#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
+ // NOTE: we're assuming that the buffer is laid out correctly at this point.
+ // If we had several actions, which only invalidate and leave layout until the
+ // paint handler is called, then this might not be true. So we may need to switch
+ // optimisation on only when we're simply adding text and not simultaneously
+ // deleting a selection, for example. Or, we make sure the buffer is laid out correctly
+ // first, but of course this means we'll be doing it twice.
+ if (!m_buffer->GetDirty() && m_ctrl) // can only do optimisation if the buffer is already laid out correctly
+ {
+ wxSize clientSize = m_ctrl->GetClientSize();
+ wxPoint firstVisiblePt = m_ctrl->GetFirstVisiblePoint();
+ int lastY = firstVisiblePt.y + clientSize.y;
+
+ wxRichTextParagraph* para = m_buffer->GetParagraphAtPosition(GetRange().GetStart());
+ wxRichTextObjectList::compatibility_iterator node = m_buffer->GetChildren().Find(para);
+ while (node)
+ {
+ wxRichTextParagraph* child = (wxRichTextParagraph*) node->GetData();
+ wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
+ while (node2)
+ {
+ wxRichTextLine* line = node2->GetData();
+ wxPoint pt = line->GetAbsolutePosition();
+ wxRichTextRange range = line->GetAbsoluteRange();
+
+ if (pt.y > lastY)
+ {
+ node2 = wxRichTextLineList::compatibility_iterator();
+ node = wxRichTextObjectList::compatibility_iterator();
+ }
+ else if (range.GetStart() > GetPosition() && pt.y >= firstVisiblePt.y)
+ {
+ optimizationLineCharPositions.Add(range.GetStart());
+ optimizationLineYPositions.Add(pt.y);
+ }
+
+ if (node2)
+ node2 = node2->GetNext();
+ }
+
+ if (node)
+ node = node->GetNext();
+ }
+ }
+#endif
+}
+
bool wxRichTextAction::Do()
{
m_buffer->Modify(true);
wxArrayInt optimizationLineYPositions;
#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
- // NOTE: we're assuming that the buffer is laid out correctly at this point.
- // If we had several actions, which only invalidate and leave layout until the
- // paint handler is called, then this might not be true. So we may need to switch
- // optimisation on only when we're simply adding text and not simultaneously
- // deleting a selection, for example. Or, we make sure the buffer is laid out correctly
- // first, but of course this means we'll be doing it twice.
- if (!m_buffer->GetDirty() && m_ctrl) // can only do optimisation if the buffer is already laid out correctly
- {
- wxSize clientSize = m_ctrl->GetClientSize();
- wxPoint firstVisiblePt = m_ctrl->GetFirstVisiblePoint();
- int lastY = firstVisiblePt.y + clientSize.y;
-
- wxRichTextParagraph* para = m_buffer->GetParagraphAtPosition(GetRange().GetStart());
- wxRichTextObjectList::compatibility_iterator node = m_buffer->GetChildren().Find(para);
- while (node)
- {
- wxRichTextParagraph* child = (wxRichTextParagraph*) node->GetData();
- wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
- while (node2)
- {
- wxRichTextLine* line = node2->GetData();
- wxPoint pt = line->GetAbsolutePosition();
- wxRichTextRange range = line->GetAbsoluteRange();
-
- if (pt.y > lastY)
- {
- node2 = wxRichTextLineList::compatibility_iterator();
- node = wxRichTextObjectList::compatibility_iterator();
- }
- else if (range.GetStart() > GetPosition() && pt.y >= firstVisiblePt.y)
- {
- optimizationLineCharPositions.Add(range.GetStart());
- optimizationLineYPositions.Add(pt.y);
- }
-
- if (node2)
- node2 = node2->GetNext();
- }
-
- if (node)
- node = node->GetNext();
- }
- }
+ CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
#endif
m_buffer->InsertFragment(GetRange().GetStart(), m_newParagraphs);
m_buffer->UpdateRanges();
- m_buffer->Invalidate(wxRichTextRange(GetRange().GetStart()-1, GetRange().GetEnd()));
+ m_buffer->Invalidate(wxRichTextRange(wxMax(0, GetRange().GetStart()-1), GetRange().GetEnd()));
long newCaretPosition = GetPosition() + m_newParagraphs.GetRange().GetLength();
newCaretPosition = wxMin(newCaretPosition, (m_buffer->GetRange().GetEnd()-1));
- if (optimizationLineCharPositions.GetCount() > 0)
- UpdateAppearance(newCaretPosition, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions);
- else
- UpdateAppearance(newCaretPosition, true /* send update event */);
+ UpdateAppearance(newCaretPosition, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions, true /* do */);
wxRichTextEvent cmdEvent(
wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED,
}
case wxRICHTEXT_DELETE:
{
+ wxArrayInt optimizationLineCharPositions;
+ wxArrayInt optimizationLineYPositions;
+
+#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
+ CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
+#endif
+
m_buffer->DeleteRange(GetRange());
m_buffer->UpdateRanges();
m_buffer->Invalidate(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
if (caretPos >= m_buffer->GetRange().GetEnd())
caretPos --;
- UpdateAppearance(caretPos, true /* send update event */);
+ UpdateAppearance(caretPos, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions, true /* do */);
wxRichTextEvent cmdEvent(
wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED,
{
case wxRICHTEXT_INSERT:
{
+ wxArrayInt optimizationLineCharPositions;
+ wxArrayInt optimizationLineYPositions;
+
+#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
+ CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
+#endif
+
m_buffer->DeleteRange(GetRange());
m_buffer->UpdateRanges();
m_buffer->Invalidate(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
long newCaretPosition = GetPosition() - 1;
- UpdateAppearance(newCaretPosition, true /* send update event */);
+ UpdateAppearance(newCaretPosition, true, /* send update event */ & optimizationLineCharPositions, & optimizationLineYPositions, false /* undo */);
wxRichTextEvent cmdEvent(
wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED,
}
case wxRICHTEXT_DELETE:
{
+ wxArrayInt optimizationLineCharPositions;
+ wxArrayInt optimizationLineYPositions;
+
+#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
+ CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
+#endif
+
m_buffer->InsertFragment(GetRange().GetStart(), m_oldParagraphs);
m_buffer->UpdateRanges();
m_buffer->Invalidate(GetRange());
- UpdateAppearance(GetPosition(), true /* send update event */);
+ UpdateAppearance(GetPosition(), true, /* send update event */ & optimizationLineCharPositions, & optimizationLineYPositions, false /* undo */);
wxRichTextEvent cmdEvent(
wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED,
}
/// Update the control appearance
-void wxRichTextAction::UpdateAppearance(long caretPosition, bool sendUpdateEvent, wxArrayInt* optimizationLineCharPositions, wxArrayInt* optimizationLineYPositions)
+void wxRichTextAction::UpdateAppearance(long caretPosition, bool sendUpdateEvent, wxArrayInt* optimizationLineCharPositions, wxArrayInt* optimizationLineYPositions, bool isDoCmd)
{
if (m_ctrl)
{
if (!m_ctrl->IsFrozen())
{
m_ctrl->LayoutContent();
- m_ctrl->PositionCaret();
#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
// Find refresh rectangle if we are in a position to optimise refresh
- if (m_cmdId == wxRICHTEXT_INSERT && optimizationLineCharPositions && optimizationLineCharPositions->GetCount() > 0)
+ if ((m_cmdId == wxRICHTEXT_INSERT || m_cmdId == wxRICHTEXT_DELETE) && optimizationLineCharPositions)
{
size_t i;
int firstY = 0;
int lastY = firstVisiblePt.y + clientSize.y;
- bool foundStart = false;
bool foundEnd = false;
// position offset - how many characters were inserted
int positionOffset = GetRange().GetLength();
+ // Determine whether this is Do or Undo, and adjust positionOffset accordingly
+ if ((m_cmdId == wxRICHTEXT_DELETE && isDoCmd) || (m_cmdId == wxRICHTEXT_INSERT && !isDoCmd))
+ positionOffset = - positionOffset;
+
// 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.
wxRichTextParagraph* para = m_buffer->GetParagraphAtPosition(GetPosition());
+ if (para)
+ {
+ // Find line containing GetPosition().
+ wxRichTextLine* line = NULL;
+ wxRichTextLineList::compatibility_iterator node2 = para->GetLines().GetFirst();
+ while (node2)
+ {
+ wxRichTextLine* l = node2->GetData();
+ wxRichTextRange range = l->GetAbsoluteRange();
+ if (range.Contains(GetRange().GetStart()-1))
+ {
+ line = l;
+ break;
+ }
+ node2 = node2->GetNext();
+ }
+
+ if (line)
+ {
+ // Step back a couple of lines to where we can be sure of reformatting correctly
+ wxRichTextLineList::compatibility_iterator lineNode = para->GetLines().Find(line);
+ if (lineNode)
+ {
+ lineNode = lineNode->GetPrevious();
+ if (lineNode)
+ {
+ line = (wxRichTextLine*) lineNode->GetData();
+ lineNode = lineNode->GetPrevious();
+ if (lineNode)
+ line = (wxRichTextLine*) lineNode->GetData();
+ }
+ }
+
+ firstY = line->GetAbsolutePosition().y;
+ }
+ }
+
wxRichTextObjectList::compatibility_iterator node = m_buffer->GetChildren().Find(para);
while (node)
{
node2 = wxRichTextLineList::compatibility_iterator();
node = wxRichTextObjectList::compatibility_iterator();
}
- else
+ // Detect last line in the buffer
+ else if (!node2->GetNext() && para->GetRange().Contains(m_buffer->GetRange().GetEnd()))
{
- if (!foundStart)
- {
- firstY = pt.y - firstVisiblePt.y;
- foundStart = true;
- }
+ foundEnd = true;
+ lastY = pt.y + line->GetSize().y;
+ node2 = wxRichTextLineList::compatibility_iterator();
+ node = wxRichTextObjectList::compatibility_iterator();
+
+ break;
+ }
+ else
+ {
// search for this line being at the same position as before
for (i = 0; i < optimizationLineCharPositions->GetCount(); i++)
{
{
// Stop, we're now the same as we were
foundEnd = true;
- lastY = pt.y - firstVisiblePt.y;
+
+ lastY = pt.y;
node2 = wxRichTextLineList::compatibility_iterator();
node = wxRichTextObjectList::compatibility_iterator();
node = node->GetNext();
}
- if (!foundStart)
- firstY = firstVisiblePt.y;
+ firstY = wxMax(firstVisiblePt.y, firstY);
if (!foundEnd)
lastY = firstVisiblePt.y + clientSize.y;
- wxRect rect(firstVisiblePt.x, firstY, firstVisiblePt.x + clientSize.x, lastY - firstY);
+ // Convert to device coordinates
+ wxRect rect(m_ctrl->GetPhysicalPoint(wxPoint(firstVisiblePt.x, firstY)), wxSize(clientSize.x, lastY - firstY));
m_ctrl->RefreshRect(rect);
-
- // TODO: we need to make sure that lines are only drawn if in the update region. The rect
- // passed to Draw is currently used in different ways (to pass the position the content should
- // be drawn at as well as the relevant region).
}
else
#endif
m_ctrl->Refresh(false);
+#if wxRICHTEXT_USE_OWN_CARET
+ m_ctrl->PositionCaret();
+#endif
if (sendUpdateEvent)
wxTextCtrl::SendTextUpdatedEvent(m_ctrl);
}
/// Make block from the wxImage
bool wxRichTextImage::MakeBlock()
{
- if (m_imageBlock.GetImageType() == wxBITMAP_TYPE_ANY || m_imageBlock.GetImageType() == -1)
- m_imageBlock.SetImageType(wxBITMAP_TYPE_PNG);
+ wxBitmapType type = m_imageBlock.GetImageType();
+ if ( type == wxBITMAP_TYPE_ANY || type == wxBITMAP_TYPE_INVALID )
+ m_imageBlock.SetImageType(type = wxBITMAP_TYPE_PNG);
- m_imageBlock.MakeImageBlock(m_image, m_imageBlock.GetImageType());
+ m_imageBlock.MakeImageBlock(m_image, type);
return m_imageBlock.Ok();
}
/// 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& WXUNUSED(dc), int WXUNUSED(flags), wxPoint WXUNUSED(position)) const
+bool wxRichTextImage::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& WXUNUSED(descent), wxDC& WXUNUSED(dc), int WXUNUSED(flags), wxPoint WXUNUSED(position), wxArrayInt* partialExtents) const
{
if (!range.IsWithin(GetRange()))
return false;
+ if (!m_image.Ok())
+ ((wxRichTextImage*) this)->LoadFromBlock();
+
+ if (partialExtents)
+ {
+ if (m_image.Ok())
+ partialExtents->Add(m_image.GetWidth());
+ else
+ partialExtents->Add(0);
+ }
+
if (!m_image.Ok())
return false;
bool wxRichTextFileHandler::CanHandle(const wxString& filename) const
{
wxString path, file, ext;
- wxSplitPath(filename, & path, & file, & ext);
+ wxFileName::SplitPath(filename, & path, & file, & ext);
return (ext.Lower() == GetExtension());
}
{
m_data = NULL;
m_dataSize = 0;
- m_imageType = -1;
+ m_imageType = wxBITMAP_TYPE_INVALID;
}
void wxRichTextImageBlock::Clear()
delete[] m_data;
m_data = NULL;
m_dataSize = 0;
- m_imageType = -1;
+ m_imageType = wxBITMAP_TYPE_INVALID;
}
// If it's not a JPEG we can make use of 'image', already scaled, so we don't have to
// load the image a 2nd time.
-bool wxRichTextImageBlock::MakeImageBlock(const wxString& filename, int imageType, wxImage& image, bool convertToJPEG)
+bool wxRichTextImageBlock::MakeImageBlock(const wxString& filename, wxBitmapType imageType,
+ wxImage& image, bool convertToJPEG)
{
m_imageType = imageType;
wxString filenameToRead(filename);
bool removeFile = false;
- if (imageType == -1)
+ if (imageType == wxBITMAP_TYPE_INVALID)
return false; // Could not determine image type
if ((imageType != wxBITMAP_TYPE_JPEG) && convertToJPEG)
{
- wxString tempFile;
- bool success = wxGetTempFileName(_("image"), tempFile) ;
+ wxString tempFile =
+ wxFileName::CreateTempFileName(_("image"));
- wxASSERT(success);
-
- wxUnusedVar(success);
+ wxASSERT(!tempFile.IsEmpty());
image.SaveFile(tempFile, wxBITMAP_TYPE_JPEG);
filenameToRead = tempFile;
// Make an image block from the wxImage in the given
// format.
-bool wxRichTextImageBlock::MakeImageBlock(wxImage& image, int imageType, int quality)
+bool wxRichTextImageBlock::MakeImageBlock(wxImage& image, wxBitmapType imageType, int quality)
{
m_imageType = imageType;
image.SetOption(wxT("quality"), quality);
- if (imageType == -1)
+ if (imageType == wxBITMAP_TYPE_INVALID)
return false; // Could not determine image type
- wxString tempFile;
- bool success = wxGetTempFileName(_("image"), tempFile) ;
-
- wxASSERT(success);
- wxUnusedVar(success);
+ wxString tempFile = wxFileName::CreateTempFileName(_("image")) ;
+ wxASSERT(!tempFile.IsEmpty());
if (!image.SaveFile(tempFile, m_imageType))
{
wxMemoryInputStream mstream(m_data, m_dataSize);
bool success = image.LoadFile(mstream, GetImageType());
#else
- wxString tempFile;
- bool success = wxGetTempFileName(_("image"), tempFile) ;
- wxASSERT(success);
+ wxString tempFile = wxFileName::CreateTempFileName(_("image"));
+ wxASSERT(!tempFile.IsEmpty());
if (!WriteBlock(tempFile, m_data, m_dataSize))
{
}
// Read data in hex from a stream
-bool wxRichTextImageBlock::ReadHex(wxInputStream& stream, int length, int imageType)
+bool wxRichTextImageBlock::ReadHex(wxInputStream& stream, int length, wxBitmapType imageType)
{
int dataSize = length/2;
if (m_data)
delete[] m_data;
- wxChar str[2];
+ // create a null terminated temporary string:
+ char str[3];
+ str[2] = '\0';
+
m_data = new unsigned char[dataSize];
int i;
for (i = 0; i < dataSize; i ++)
}
wxRichTextFontTable::wxRichTextFontTable(const wxRichTextFontTable& table)
+ : wxObject()
{
(*this) = table;
}