WX_DEFINE_LIST(wxRichTextObjectList)
WX_DEFINE_LIST(wxRichTextLineList)
+// Switch off if the platform doesn't like it for some reason
+#define wxRICHTEXT_USE_OPTIMIZED_DRAWING 1
+
/*!
* wxRichTextObject
* This is the base for drawable objects.
{
wxRect childRect(child->GetPosition(), child->GetCachedSize());
- if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) == 0) && childRect.GetTop() > rect.GetBottom() || childRect.GetBottom() < rect.GetTop())
+ if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) == 0) && childRect.GetTop() > rect.GetBottom())
+ {
+ // Stop drawing
+ break;
+ }
+ else if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) == 0) && childRect.GetBottom() < rect.GetTop())
{
// Skip
}
{
#if wxRICHTEXT_USE_DYNAMIC_STYLES
// Don't use the base style, just the default style, and the base style will
- // be combined at display time
- wxTextAttrEx style(GetDefaultStyle());
+ // be combined at display time.
+ // Divide into paragraph and character styles.
+
+ wxTextAttrEx defaultCharStyle;
+ wxTextAttrEx defaultParaStyle;
+ wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle, defaultCharStyle);
#else
wxTextAttrEx style(GetAttributes());
// then the attributes will remain the 'basic style' (i.e. the
// layout box's style).
wxRichTextApplyStyle(style, GetDefaultStyle());
+
+ wxTextAttrEx defaultCharStyle = style;
+ wxTextAttrEx defaultParaStyle = style;
#endif
- wxRichTextParagraph* para = new wxRichTextParagraph(text, this, & style);
- if (paraStyle)
- para->SetAttributes(*paraStyle);
+ wxTextAttrEx* pStyle = paraStyle ? paraStyle : (wxTextAttrEx*) & defaultParaStyle;
+ wxTextAttrEx* cStyle = & defaultCharStyle;
+
+ wxRichTextParagraph* para = new wxRichTextParagraph(text, this, pStyle, cStyle);
AppendChild(para);
{
#if wxRICHTEXT_USE_DYNAMIC_STYLES
// Don't use the base style, just the default style, and the base style will
- // be combined at display time
- wxTextAttrEx style(GetDefaultStyle());
+ // be combined at display time.
+ // Divide into paragraph and character styles.
+
+ wxTextAttrEx defaultCharStyle;
+ wxTextAttrEx defaultParaStyle;
+ wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle, defaultCharStyle);
#else
wxTextAttrEx style(GetAttributes());
- //wxLogDebug("Initial style = %s", style.GetFont().GetFaceName());
- //wxLogDebug("Initial size = %d", style.GetFont().GetPointSize());
-
// Apply default style. If the style has no attributes set,
// then the attributes will remain the 'basic style' (i.e. the
// layout box's style).
wxRichTextApplyStyle(style, GetDefaultStyle());
- //wxLogDebug("Style after applying default style = %s", style.GetFont().GetFaceName());
- //wxLogDebug("Size after applying default style = %d", style.GetFont().GetPointSize());
+ wxTextAttrEx defaultCharStyle = style;
+ wxTextAttrEx defaultParaStyle = style;
#endif
+ wxTextAttrEx* pStyle = paraStyle ? paraStyle : (wxTextAttrEx*) & defaultParaStyle;
+ wxTextAttrEx* cStyle = & defaultCharStyle;
+
wxRichTextParagraph* firstPara = NULL;
wxRichTextParagraph* lastPara = NULL;
size_t i = 0;
size_t len = text.length();
wxString line;
- wxRichTextParagraph* para = new wxRichTextParagraph(wxEmptyString, this, & style);
- if (paraStyle)
- para->SetAttributes(*paraStyle);
+ wxRichTextParagraph* para = new wxRichTextParagraph(wxEmptyString, this, pStyle, cStyle);
AppendChild(para);
wxRichTextPlainText* plainText = (wxRichTextPlainText*) para->GetChildren().GetFirst()->GetData();
plainText->SetText(line);
- para = new wxRichTextParagraph(wxEmptyString, this, & style);
- if (paraStyle)
- para->SetAttributes(*paraStyle);
+ para = new wxRichTextParagraph(wxEmptyString, this, pStyle, cStyle);
AppendChild(para);
- //if (!firstPara)
- // firstPara = para;
-
lastPara = para;
line = wxEmptyString;
}
plainText->SetText(line);
}
-/*
- if (firstPara)
- range.SetStart(firstPara->GetRange().GetStart());
- else if (lastPara)
- range.SetStart(lastPara->GetRange().GetStart());
-
- if (lastPara)
- range.SetEnd(lastPara->GetRange().GetEnd());
- else if (firstPara)
- range.SetEnd(firstPara->GetRange().GetEnd());
-*/
-
UpdateRanges();
SetDirty(false);
{
#if wxRICHTEXT_USE_DYNAMIC_STYLES
// Don't use the base style, just the default style, and the base style will
- // be combined at display time
- wxTextAttrEx style(GetDefaultStyle());
+ // be combined at display time.
+ // Divide into paragraph and character styles.
+
+ wxTextAttrEx defaultCharStyle;
+ wxTextAttrEx defaultParaStyle;
+ wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle, defaultCharStyle);
#else
wxTextAttrEx style(GetAttributes());
// then the attributes will remain the 'basic style' (i.e. the
// layout box's style).
wxRichTextApplyStyle(style, GetDefaultStyle());
+
+ wxTextAttrEx defaultCharStyle = style;
+ wxTextAttrEx defaultParaStyle = style;
#endif
- wxRichTextParagraph* para = new wxRichTextParagraph(this, & style);
- AppendChild(para);
- para->AppendChild(new wxRichTextImage(image, this));
+ wxTextAttrEx* pStyle = paraStyle ? paraStyle : (wxTextAttrEx*) & defaultParaStyle;
+ wxTextAttrEx* cStyle = & defaultCharStyle;
- if (paraStyle)
- para->SetAttributes(*paraStyle);
+ wxRichTextParagraph* para = new wxRichTextParagraph(this, pStyle);
+ AppendChild(para);
+ para->AppendChild(new wxRichTextImage(image, this, cStyle));
UpdateRanges();
SetDirty(true);
/// Insert fragment into this box at the given position. If partialParagraph is true,
/// it is assumed that the last (or only) paragraph is just a piece of data with no paragraph
/// marker.
-/// TODO: if fragment is inserted inside styled fragment, must apply that style to
-/// to the data (if it has a default style, anyway).
bool wxRichTextParagraphLayoutBox::InsertFragment(long position, wxRichTextParagraphLayoutBox& fragment)
{
wxRichTextParagraph* firstPara = wxDynamicCast(firstParaNode->GetData(), wxRichTextParagraph);
wxASSERT (firstPara != NULL);
+ // Apply the new paragraph attributes to the existing paragraph
+ wxTextAttrEx attr(para->GetAttributes());
+ wxRichTextApplyStyle(attr, firstPara->GetAttributes());
+ para->SetAttributes(attr);
+
wxRichTextObjectList::compatibility_iterator objectNode = firstPara->GetChildren().GetFirst();
while (objectNode)
{
if (finalPara->GetChildCount() == 0)
{
wxRichTextPlainText* text = new wxRichTextPlainText(wxEmptyString);
-#if !wxRICHTEXT_USE_DYNAMIC_STYLES
- text->SetAttributes(finalPara->GetAttributes());
-#endif
finalPara->AppendChild(text);
}
/// Combines 'style' with 'currentStyle' for the purpose of summarising the attributes of a range of
/// content.
-bool wxRichTextParagraphLayoutBox::CollectStyle(wxTextAttrEx& currentStyle, const wxTextAttrEx& style, long& multipleStyleAttributes)
+bool wxRichTextParagraphLayoutBox::CollectStyle(wxTextAttrEx& currentStyle, const wxTextAttrEx& style, long& multipleStyleAttributes, int& multipleTextEffectAttributes)
{
if (style.HasFont())
{
{
if (currentStyle.HasParagraphSpacingAfter())
{
- if (currentStyle.HasParagraphSpacingAfter() != style.HasParagraphSpacingAfter())
+ if (currentStyle.GetParagraphSpacingAfter() != style.GetParagraphSpacingAfter())
{
// Clash of style - mark as such
multipleStyleAttributes |= wxTEXT_ATTR_PARA_SPACING_AFTER;
{
if (currentStyle.HasParagraphSpacingBefore())
{
- if (currentStyle.HasParagraphSpacingBefore() != style.HasParagraphSpacingBefore())
+ if (currentStyle.GetParagraphSpacingBefore() != style.GetParagraphSpacingBefore())
{
// Clash of style - mark as such
multipleStyleAttributes |= wxTEXT_ATTR_PARA_SPACING_BEFORE;
{
if (currentStyle.HasLineSpacing())
{
- if (currentStyle.HasLineSpacing() != style.HasLineSpacing())
+ if (currentStyle.GetLineSpacing() != style.GetLineSpacing())
{
// Clash of style - mark as such
multipleStyleAttributes |= wxTEXT_ATTR_LINE_SPACING;
{
if (currentStyle.HasCharacterStyleName())
{
- if (currentStyle.HasCharacterStyleName() != style.HasCharacterStyleName())
+ if (currentStyle.GetCharacterStyleName() != style.GetCharacterStyleName())
{
// Clash of style - mark as such
multipleStyleAttributes |= wxTEXT_ATTR_CHARACTER_STYLE_NAME;
{
if (currentStyle.HasParagraphStyleName())
{
- if (currentStyle.HasParagraphStyleName() != style.HasParagraphStyleName())
+ if (currentStyle.GetParagraphStyleName() != style.GetParagraphStyleName())
{
// Clash of style - mark as such
multipleStyleAttributes |= wxTEXT_ATTR_PARAGRAPH_STYLE_NAME;
{
if (currentStyle.HasListStyleName())
{
- if (currentStyle.HasListStyleName() != style.HasListStyleName())
+ if (currentStyle.GetListStyleName() != style.GetListStyleName())
{
// Clash of style - mark as such
multipleStyleAttributes |= wxTEXT_ATTR_LIST_STYLE_NAME;
{
if (currentStyle.HasBulletStyle())
{
- if (currentStyle.HasBulletStyle() != style.HasBulletStyle())
+ if (currentStyle.GetBulletStyle() != style.GetBulletStyle())
{
// Clash of style - mark as such
multipleStyleAttributes |= wxTEXT_ATTR_BULLET_STYLE;
{
if (currentStyle.HasBulletNumber())
{
- if (currentStyle.HasBulletNumber() != style.HasBulletNumber())
+ if (currentStyle.GetBulletNumber() != style.GetBulletNumber())
{
// Clash of style - mark as such
multipleStyleAttributes |= wxTEXT_ATTR_BULLET_NUMBER;
{
if (currentStyle.HasBulletText())
{
- if (currentStyle.HasBulletText() != style.HasBulletText())
+ if (currentStyle.GetBulletText() != style.GetBulletText())
{
// Clash of style - mark as such
multipleStyleAttributes |= wxTEXT_ATTR_BULLET_TEXT;
{
if (currentStyle.HasBulletName())
{
- if (currentStyle.HasBulletName() != style.HasBulletName())
+ if (currentStyle.GetBulletName() != style.GetBulletName())
{
// Clash of style - mark as such
multipleStyleAttributes |= wxTEXT_ATTR_BULLET_NAME;
{
if (currentStyle.HasURL())
{
- if (currentStyle.HasURL() != style.HasURL())
+ if (currentStyle.GetURL() != style.GetURL())
{
// Clash of style - mark as such
multipleStyleAttributes |= wxTEXT_ATTR_URL;
}
}
+ if (style.HasTextEffects() && !wxHasStyle(multipleStyleAttributes, 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.
+
+ int currentRelevantTextEffects = currentStyle.GetTextEffects() & style.GetTextEffectFlags();
+ int newRelevantTextEffects = style.GetTextEffects() & style.GetTextEffectFlags();
+
+ if (currentRelevantTextEffects != newRelevantTextEffects)
+ {
+ // Find the text effects that were different, using XOR
+ int differentEffects = currentRelevantTextEffects ^ newRelevantTextEffects;
+
+ // Clash of style - mark as such
+ multipleTextEffectAttributes |= differentEffects;
+ currentStyle.SetTextEffectFlags(currentStyle.GetTextEffectFlags() & ~differentEffects);
+ }
+ }
+ else
+ {
+ currentStyle.SetTextEffects(style.GetTextEffects());
+ currentStyle.SetTextEffectFlags(style.GetTextEffectFlags());
+ }
+ }
+
+ if (style.HasOutlineLevel() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_OUTLINE_LEVEL))
+ {
+ if (currentStyle.HasOutlineLevel())
+ {
+ if (currentStyle.GetOutlineLevel() != style.GetOutlineLevel())
+ {
+ // Clash of style - mark as such
+ multipleStyleAttributes |= wxTEXT_ATTR_OUTLINE_LEVEL;
+ currentStyle.SetFlags(currentStyle.GetFlags() & ~wxTEXT_ATTR_OUTLINE_LEVEL);
+ }
+ }
+ else
+ currentStyle.SetOutlineLevel(style.GetOutlineLevel());
+ }
+
return true;
}
// The attributes that aren't valid because of multiple styles within the range
long multipleStyleAttributes = 0;
-
+ int multipleTextEffectAttributes = 0;
+
wxRichTextObjectList::compatibility_iterator node = GetChildren().GetFirst();
while (node)
{
{
wxTextAttrEx paraStyle = para->GetCombinedAttributes();
- CollectStyle(style, paraStyle, multipleStyleAttributes);
+ CollectStyle(style, paraStyle, multipleStyleAttributes, multipleTextEffectAttributes);
}
else
{
// First collect paragraph attributes only
wxTextAttrEx paraStyle = para->GetCombinedAttributes();
paraStyle.SetFlags(paraStyle.GetFlags() & wxTEXT_ATTR_PARAGRAPH);
- CollectStyle(style, paraStyle, multipleStyleAttributes);
+ CollectStyle(style, paraStyle, multipleStyleAttributes, multipleTextEffectAttributes);
wxRichTextObjectList::compatibility_iterator childNode = para->GetChildren().GetFirst();
// Now collect character attributes only
childStyle.SetFlags(childStyle.GetFlags() & wxTEXT_ATTR_CHARACTER);
- CollectStyle(style, childStyle, multipleStyleAttributes);
+ CollectStyle(style, childStyle, multipleStyleAttributes, multipleTextEffectAttributes);
}
childNode = childNode->GetNext();
/// Set default style
bool wxRichTextParagraphLayoutBox::SetDefaultStyle(const wxTextAttrEx& style)
{
- // I don't think the default style should be combined with the previous
- // default style.
m_defaultAttributes = style;
-
-#if 0
- // keep the old attributes if the new style doesn't specify them unless the
- // new style is empty - then reset m_defaultStyle (as there is no other way
- // to do it)
- if ( style.IsDefault() )
- m_defaultAttributes = style;
- else
- m_defaultAttributes = wxTextAttrEx::CombineEx(style, m_defaultAttributes, NULL);
-#endif
return true;
}
{
bool withUndo = ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
// bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
+#ifdef __WXDEBUG__
bool specifyLevel = ((flags & wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL) != 0);
+#endif
bool renumber = ((flags & wxRICHTEXT_SETSTYLE_RENUMBER) != 0);
/// position of the paragraph that it had to start looking from.
bool wxRichTextParagraphLayoutBox::FindNextParagraphNumber(wxRichTextParagraph* previousParagraph, wxRichTextAttr& attr) const
{
-#if 0
- wxRichTextObjectList::compatibility_iterator node = m_children.Find(previousParagraph);
-
- if (!node)
- return false;
-#endif
-
if (!previousParagraph->GetAttributes().HasFlag(wxTEXT_ATTR_BULLET_STYLE) || previousParagraph->GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE)
return false;
wxRichTextParagraph::wxRichTextParagraph(wxRichTextObject* parent, wxTextAttrEx* style):
wxRichTextBox(parent)
{
- if (parent && !style)
- SetAttributes(parent->GetAttributes());
if (style)
SetAttributes(*style);
}
-wxRichTextParagraph::wxRichTextParagraph(const wxString& text, wxRichTextObject* parent, wxTextAttrEx* style):
+wxRichTextParagraph::wxRichTextParagraph(const wxString& text, wxRichTextObject* parent, wxTextAttrEx* paraStyle, wxTextAttrEx* charStyle):
wxRichTextBox(parent)
{
- if (parent && !style)
- SetAttributes(parent->GetAttributes());
- if (style)
- SetAttributes(*style);
+ if (paraStyle)
+ SetAttributes(*paraStyle);
- AppendChild(new wxRichTextPlainText(text, this));
+ AppendChild(new wxRichTextPlainText(text, this, charStyle));
}
wxRichTextParagraph::~wxRichTextParagraph()
wxRichTextPlainText::wxRichTextPlainText(const wxString& text, wxRichTextObject* parent, wxTextAttrEx* style):
wxRichTextObject(parent)
{
- if (parent && !style)
- SetAttributes(parent->GetAttributes());
if (style)
SetAttributes(*style);
long len = range.GetLength();
wxString stringChunk = m_text.Mid(range.GetStart() - offset, (size_t) len);
+ if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS))
+ stringChunk.MakeUpper();
int charHeight = dc.GetCharHeight();
dc.DrawRectangle(selRect);
}
dc.DrawText(stringChunk, x, y);
+
+ if (attr.HasTextEffects() && (attr.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH))
+ {
+ wxPen oldPen = dc.GetPen();
+ dc.SetPen(wxPen(attr.GetTextColour(), 1));
+ dc.DrawLine(x, (int) (y+(h/2)+0.5), x+w, (int) (y+(h/2)+0.5));
+ dc.SetPen(oldPen);
+ }
+
x = nextTabPos;
}
}
dc.DrawRectangle(selRect);
}
dc.DrawText(str, x, y);
+
+ if (attr.HasTextEffects() && (attr.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH))
+ {
+ wxPen oldPen = dc.GetPen();
+ dc.SetPen(wxPen(attr.GetTextColour(), 1));
+ dc.DrawLine(x, (int) (y+(h/2)+0.5), x+w, (int) (y+(h/2)+0.5));
+ dc.SetPen(oldPen);
+ }
+
x += w;
}
return true;
if (textAttr.GetFont().Ok())
dc.SetFont(textAttr.GetFont());
+ wxString str = m_text;
+ if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS))
+ str.MakeUpper();
+
wxCoord w, h;
- dc.GetTextExtent(m_text, & w, & h, & m_descent);
+ dc.GetTextExtent(str, & w, & h, & m_descent);
m_size = wxSize(w, dc.GetCharHeight());
return true;
int startPos = range.GetStart() - GetRange().GetStart();
long len = range.GetLength();
wxString stringChunk = m_text.Mid(startPos, (size_t) len);
+
+ if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS))
+ stringChunk.MakeUpper();
+
wxCoord w, h;
int width = 0;
if (stringChunk.Find(wxT('\t')) != wxNOT_FOUND)
{
wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, this, ctrl, false);
+#if wxRICHTEXT_USE_DYNAMIC_STYLES
+ wxTextAttrEx attr(GetDefaultStyle());
+#else
+ wxTextAttrEx attr(GetBasicStyle());
+ wxRichTextApplyStyle(attr, GetDefaultStyle());
+#endif
+
wxTextAttrEx* p = NULL;
wxTextAttrEx paraAttr;
if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
if (!paraAttr.IsDefault())
p = & paraAttr;
}
-
-#if wxRICHTEXT_USE_DYNAMIC_STYLES
- wxTextAttrEx attr(GetDefaultStyle());
-#else
- wxTextAttrEx attr(GetBasicStyle());
- wxRichTextApplyStyle(attr, GetDefaultStyle());
-#endif
+ else
+ p = & attr;
action->GetNewParagraphs() = paragraphs;
p = & paraAttr;
}
-#if wxRICHTEXT_USE_DYNAMIC_STYLES
- wxTextAttrEx attr(GetDefaultStyle());
-#else
- wxTextAttrEx attr(GetBasicStyle());
- wxRichTextApplyStyle(attr, GetDefaultStyle());
-#endif
-
action->GetNewParagraphs().AddParagraphs(text, p);
int length = action->GetNewParagraphs().GetRange().GetLength();
{
wxRichTextAttr numberingAttr;
if (FindNextParagraphNumber(para, numberingAttr))
- wxRichTextApplyStyle(attr, numberingAttr);
+ wxRichTextApplyStyle(attr, (const wxRichTextAttr&) numberingAttr);
}
return attr;
wxRichTextCharacterStyleDefinition* def = GetStyleSheet()->FindCharacterStyle(characterStyle);
if (def)
{
- wxTextAttrEx attr;
- def->GetStyle().CopyTo(attr);
+ wxTextAttrEx attr = def->GetStyle();
return BeginStyle(attr);
}
}
wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(paragraphStyle);
if (def)
{
- wxTextAttrEx attr;
- def->GetStyle().CopyTo(attr);
+ wxTextAttrEx attr = def->GetStyle();
return BeginStyle(attr);
}
}
wxRichTextCharacterStyleDefinition* def = GetStyleSheet()->FindCharacterStyle(characterStyle);
if (def)
{
- def->GetStyle().CopyTo(attr);
+ attr = def->GetStyle();
}
}
attr.SetURL(url);
{
case wxRICHTEXT_INSERT:
{
+ // Store a list of line start character and y positions so we can figure out which area
+ // we need to refresh
+ wxArrayInt optimizationLineCharPositions;
+ 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(GetPosition());
+ 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
+
m_buffer->InsertFragment(GetPosition(), m_newParagraphs);
m_buffer->UpdateRanges();
m_buffer->Invalidate(GetRange());
newCaretPosition --;
newCaretPosition = wxMin(newCaretPosition, (m_buffer->GetRange().GetEnd()-1));
+
- UpdateAppearance(newCaretPosition, true /* send update event */);
+ if (optimizationLineCharPositions.GetCount() > 0)
+ UpdateAppearance(newCaretPosition, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions);
+ else
+ UpdateAppearance(newCaretPosition, true /* send update event */);
break;
}
long newCaretPosition = GetPosition() - 1;
// if (m_newParagraphs.GetPartialParagraph())
// newCaretPosition --;
-
+
UpdateAppearance(newCaretPosition, true /* send update event */);
break;
}
/// Update the control appearance
-void wxRichTextAction::UpdateAppearance(long caretPosition, bool sendUpdateEvent)
+void wxRichTextAction::UpdateAppearance(long caretPosition, bool sendUpdateEvent, wxArrayInt* optimizationLineCharPositions, wxArrayInt* optimizationLineYPositions)
{
if (m_ctrl)
{
{
m_ctrl->LayoutContent();
m_ctrl->PositionCaret();
- m_ctrl->Refresh(false);
+
+#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)
+ {
+ size_t i;
+
+ wxSize clientSize = m_ctrl->GetClientSize();
+ wxPoint firstVisiblePt = m_ctrl->GetFirstVisiblePoint();
+
+ // Start/end positions
+ 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();
+
+ // 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());
+ 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();
+
+ // we want to find the first line that is in the same position
+ // as before. This will mean we're at the end of the changed text.
+
+ if (pt.y > lastY) // going past the end of the window, no more info
+ {
+ node2 = wxRichTextLineList::compatibility_iterator();
+ node = wxRichTextObjectList::compatibility_iterator();
+ }
+ else
+ {
+ if (!foundStart)
+ {
+ firstY = pt.y - firstVisiblePt.y;
+ foundStart = true;
+ }
+
+ // search for this line being at the same position as before
+ for (i = 0; i < optimizationLineCharPositions->GetCount(); i++)
+ {
+ if (((*optimizationLineCharPositions)[i] + positionOffset == range.GetStart()) &&
+ ((*optimizationLineYPositions)[i] == pt.y))
+ {
+ // Stop, we're now the same as we were
+ foundEnd = true;
+ lastY = pt.y - firstVisiblePt.y;
+
+ node2 = wxRichTextLineList::compatibility_iterator();
+ node = wxRichTextObjectList::compatibility_iterator();
+
+ break;
+ }
+ }
+ }
+
+ if (node2)
+ node2 = node2->GetNext();
+ }
+
+ if (node)
+ node = node->GetNext();
+ }
+
+ if (!foundStart)
+ firstY = firstVisiblePt.y;
+ if (!foundEnd)
+ lastY = firstVisiblePt.y + clientSize.y;
+
+ wxRect rect(firstVisiblePt.x, firstY, firstVisiblePt.x + 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 (sendUpdateEvent)
m_ctrl->SendTextUpdatedEvent();
IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage, wxRichTextObject)
-wxRichTextImage::wxRichTextImage(const wxImage& image, wxRichTextObject* parent):
+wxRichTextImage::wxRichTextImage(const wxImage& image, wxRichTextObject* parent, wxTextAttrEx* charStyle):
wxRichTextObject(parent)
{
m_image = image;
+ if (charStyle)
+ SetAttributes(*charStyle);
}
-wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock& imageBlock, wxRichTextObject* parent):
+wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock& imageBlock, wxRichTextObject* parent, wxTextAttrEx* charStyle):
wxRichTextObject(parent)
{
m_imageBlock = imageBlock;
m_imageBlock.Load(m_image);
+ if (charStyle)
+ SetAttributes(*charStyle);
}
/// Load wxImage from the block
attr1.GetFont().GetWeight() == attr2.GetFontWeight() &&
attr1.GetFont().GetFaceName() == attr2.GetFontFaceName() &&
attr1.GetFont().GetUnderlined() == attr2.GetFontUnderlined() &&
+ attr1.GetTextEffects() == attr2.GetTextEffects() &&
+ attr1.GetTextEffectFlags() == attr2.GetTextEffectFlags() &&
attr1.GetAlignment() == attr2.GetAlignment() &&
attr1.GetLeftIndent() == attr2.GetLeftIndent() &&
attr1.GetRightIndent() == attr2.GetRightIndent() &&
attr1.GetBulletText() == attr2.GetBulletText() &&
attr1.GetBulletName() == attr2.GetBulletName() &&
attr1.GetBulletFont() == attr2.GetBulletFont() &&
+ attr1.GetOutlineLevel() == attr2.GetOutlineLevel() &&
attr1.GetCharacterStyleName() == attr2.GetCharacterStyleName() &&
attr1.GetParagraphStyleName() == attr2.GetParagraphStyleName() &&
- attr1.GetListStyleName() == attr2.GetListStyleName());
+ attr1.GetListStyleName() == attr2.GetListStyleName() &&
+ attr1.HasPageBreak() == attr2.HasPageBreak());
}
/// Compare two attribute objects, but take into account the flags
!wxRichTextTabsEq(attr1.GetTabs(), attr2.GetTabs()))
return false;
+ if ((flags & wxTEXT_ATTR_PAGE_BREAK) &&
+ (attr1.HasPageBreak() != attr2.HasPageBreak()))
+ return false;
+
+ if (flags & wxTEXT_ATTR_EFFECTS)
+ {
+ if (attr1.HasTextEffects() != attr2.HasTextEffects())
+ return false;
+ if (!wxRichTextBitlistsEqPartial(attr1.GetTextEffects(), attr2.GetTextEffects(), attr2.GetTextEffectFlags()))
+ return false;
+ }
+
+ if ((flags & wxTEXT_ATTR_OUTLINE_LEVEL) &&
+ (attr1.GetOutlineLevel() != attr2.GetOutlineLevel()))
+ return false;
+
return true;
}
!wxRichTextTabsEq(attr1.GetTabs(), attr2.GetTabs()))
return false;
+ if ((flags & wxTEXT_ATTR_PAGE_BREAK) &&
+ (attr1.HasPageBreak() != attr2.HasPageBreak()))
+ return false;
+
+ if (flags & wxTEXT_ATTR_EFFECTS)
+ {
+ if (attr1.HasTextEffects() != attr2.HasTextEffects())
+ return false;
+ if (!wxRichTextBitlistsEqPartial(attr1.GetTextEffects(), attr2.GetTextEffects(), attr2.GetTextEffectFlags()))
+ return false;
+ }
+
+ if ((flags & wxTEXT_ATTR_OUTLINE_LEVEL) &&
+ (attr1.GetOutlineLevel() != attr2.GetOutlineLevel()))
+ return false;
+
return true;
}
return true;
}
-
/// Apply one style to another
bool wxRichTextApplyStyle(wxTextAttrEx& destStyle, const wxTextAttrEx& style)
{
if (style.HasURL())
destStyle.SetURL(style.GetURL());
+ if (style.HasPageBreak())
+ destStyle.SetPageBreak();
+
+ if (style.HasTextEffects())
+ {
+ int destBits = destStyle.GetTextEffects();
+ int destFlags = destStyle.GetTextEffectFlags();
+
+ int srcBits = style.GetTextEffects();
+ int srcFlags = style.GetTextEffectFlags();
+
+ wxRichTextCombineBitlists(destBits, srcBits, destFlags, srcFlags);
+
+ destStyle.SetTextEffects(destBits);
+ destStyle.SetTextEffectFlags(destFlags);
+ }
+
+ if (style.HasOutlineLevel())
+ destStyle.SetOutlineLevel(style.GetOutlineLevel());
+
return true;
}
bool wxRichTextApplyStyle(wxRichTextAttr& destStyle, const wxTextAttrEx& style)
{
- wxTextAttrEx destStyle2;
- destStyle.CopyTo(destStyle2);
+ wxTextAttrEx destStyle2 = destStyle;
wxRichTextApplyStyle(destStyle2, style);
destStyle = destStyle2;
return true;
}
+bool wxRichTextApplyStyle(wxRichTextAttr& destStyle, const wxRichTextAttr& style, wxRichTextAttr* compareWith)
+{
+ wxTextAttrEx attr(destStyle);
+ wxRichTextApplyStyle(attr, style, compareWith);
+ destStyle = attr;
+ return true;
+}
+
bool wxRichTextApplyStyle(wxTextAttrEx& destStyle, const wxRichTextAttr& style, wxRichTextAttr* compareWith)
{
// Whole font. Avoiding setting individual attributes if possible, since
destStyle.SetURL(style.GetURL());
}
+ if (style.HasPageBreak())
+ {
+ if (!(compareWith && compareWith->HasPageBreak()))
+ destStyle.SetPageBreak();
+ }
+
+ if (style.HasTextEffects())
+ {
+ if (!(compareWith && compareWith->HasTextEffects() && compareWith->GetTextEffects() == style.GetTextEffects()))
+ {
+ int destBits = destStyle.GetTextEffects();
+ int destFlags = destStyle.GetTextEffectFlags();
+
+ int srcBits = style.GetTextEffects();
+ int srcFlags = style.GetTextEffectFlags();
+
+ wxRichTextCombineBitlists(destBits, srcBits, destFlags, srcFlags);
+
+ destStyle.SetTextEffects(destBits);
+ destStyle.SetTextEffectFlags(destFlags);
+ }
+ }
+
+ if (style.HasOutlineLevel())
+ {
+ if (!(compareWith && compareWith->HasOutlineLevel() && compareWith->GetOutlineLevel() == style.GetOutlineLevel()))
+ destStyle.SetOutlineLevel(style.GetOutlineLevel());
+ }
+
+ return true;
+}
+
+/// Combine two bitlists, specifying the bits of interest with separate flags.
+bool wxRichTextCombineBitlists(int& valueA, int valueB, int& flagsA, int flagsB)
+{
+ // We want to apply B's bits to A, taking into account each's flags which indicate which bits
+ // are to be taken into account. A zero in B's bits should reset that bit in A but only if B's flags
+ // indicate it.
+
+ // First, reset the 0 bits from B. We make a mask so we're only dealing with B's zero
+ // bits at this point, ignoring any 1 bits in B or 0 bits in B that are not relevant.
+ int valueA2 = ~(~valueB & flagsB) & valueA;
+
+ // Now combine the 1 bits.
+ int valueA3 = (valueB & flagsB) | valueA2;
+
+ valueA = valueA3;
+ flagsA = (flagsA | flagsB);
+
+ return true;
+}
+
+/// Compare two bitlists
+bool wxRichTextBitlistsEqPartial(int valueA, int valueB, int flags)
+{
+ int relevantBitsA = valueA & flags;
+ int relevantBitsB = valueB & flags;
+ return (relevantBitsA != relevantBitsB);
+}
+
+/// Split into paragraph and character styles
+bool wxRichTextSplitParaCharStyles(const wxTextAttrEx& style, wxTextAttrEx& parStyle, wxTextAttrEx& charStyle)
+{
+ wxTextAttrEx defaultCharStyle1(style);
+ wxTextAttrEx defaultParaStyle1(style);
+ defaultCharStyle1.SetFlags(defaultCharStyle1.GetFlags()&wxTEXT_ATTR_CHARACTER);
+ defaultParaStyle1.SetFlags(defaultParaStyle1.GetFlags()&wxTEXT_ATTR_PARAGRAPH);
+
+ wxRichTextApplyStyle(charStyle, defaultCharStyle1);
+ wxRichTextApplyStyle(parStyle, defaultParaStyle1);
+
return true;
}
m_paragraphSpacingBefore = 0;
m_lineSpacing = 0;
m_bulletStyle = wxTEXT_ATTR_BULLET_STYLE_NONE;
+ m_textEffects = wxTEXT_ATTR_EFFECT_NONE;
+ m_textEffectFlags = wxTEXT_ATTR_EFFECT_NONE;
+ m_outlineLevel = 0;
m_bulletNumber = 0;
}
m_fontWeight = attr.m_fontWeight;
m_fontUnderlined = attr.m_fontUnderlined;
m_fontFaceName = attr.m_fontFaceName;
+ m_textEffects = attr.m_textEffects;
+ m_textEffectFlags = attr.m_textEffectFlags;
m_paragraphSpacingAfter = attr.m_paragraphSpacingAfter;
m_paragraphSpacingBefore = attr.m_paragraphSpacingBefore;
m_bulletText = attr.m_bulletText;
m_bulletFont = attr.m_bulletFont;
m_bulletName = attr.m_bulletName;
+ m_outlineLevel = attr.m_outlineLevel;
m_urlTarget = attr.m_urlTarget;
}
// operators
void wxRichTextAttr::operator= (const wxTextAttrEx& attr)
{
+ m_flags = attr.GetFlags();
+
m_colText = attr.GetTextColour();
m_colBack = attr.GetBackgroundColour();
m_textAlignment = attr.GetAlignment();
m_leftSubIndent = attr.GetLeftSubIndent();
m_rightIndent = attr.GetRightIndent();
m_tabs = attr.GetTabs();
- m_flags = attr.GetFlags();
+ m_textEffects = attr.GetTextEffects();
+ m_textEffectFlags = attr.GetTextEffectFlags();
m_paragraphSpacingAfter = attr.GetParagraphSpacingAfter();
m_paragraphSpacingBefore = attr.GetParagraphSpacingBefore();
m_bulletText = attr.GetBulletText();
m_bulletName = attr.GetBulletName();
m_bulletFont = attr.GetBulletFont();
+ m_outlineLevel = attr.GetOutlineLevel();
m_urlTarget = attr.GetURL();
wxRichTextAttr::operator wxTextAttrEx () const
{
wxTextAttrEx attr;
- CopyTo(attr);
+ attr.SetTextColour(GetTextColour());
+ attr.SetBackgroundColour(GetBackgroundColour());
+ attr.SetAlignment(GetAlignment());
+ attr.SetTabs(GetTabs());
+ attr.SetLeftIndent(GetLeftIndent(), GetLeftSubIndent());
+ attr.SetRightIndent(GetRightIndent());
+ attr.SetFont(CreateFont());
+
+ attr.SetParagraphSpacingAfter(m_paragraphSpacingAfter);
+ attr.SetParagraphSpacingBefore(m_paragraphSpacingBefore);
+ attr.SetLineSpacing(m_lineSpacing);
+ attr.SetBulletStyle(m_bulletStyle);
+ attr.SetBulletNumber(m_bulletNumber);
+ attr.SetBulletText(m_bulletText);
+ attr.SetBulletName(m_bulletName);
+ attr.SetBulletFont(m_bulletFont);
+ attr.SetCharacterStyleName(m_characterStyleName);
+ attr.SetParagraphStyleName(m_paragraphStyleName);
+ attr.SetListStyleName(m_listStyleName);
+ attr.SetTextEffects(m_textEffects);
+ attr.SetTextEffectFlags(m_textEffectFlags);
+ attr.SetOutlineLevel(m_outlineLevel);
+
+ attr.SetURL(m_urlTarget);
+
+ attr.SetFlags(GetFlags()); // Important: set after SetFont and others, since they set flags
return attr;
}
GetBulletFont() == attr.GetBulletFont() &&
GetBulletName() == attr.GetBulletName() &&
- m_fontSize == attr.m_fontSize &&
- m_fontStyle == attr.m_fontStyle &&
- m_fontWeight == attr.m_fontWeight &&
- m_fontUnderlined == attr.m_fontUnderlined &&
- m_fontFaceName == attr.m_fontFaceName &&
-
- m_urlTarget == attr.m_urlTarget;
-}
-
-// Copy to a wxTextAttr
-void wxRichTextAttr::CopyTo(wxTextAttrEx& attr) const
-{
- attr.SetTextColour(GetTextColour());
- attr.SetBackgroundColour(GetBackgroundColour());
- attr.SetAlignment(GetAlignment());
- attr.SetTabs(GetTabs());
- attr.SetLeftIndent(GetLeftIndent(), GetLeftSubIndent());
- attr.SetRightIndent(GetRightIndent());
- attr.SetFont(CreateFont());
+ GetTextEffects() == attr.GetTextEffects() &&
+ GetTextEffectFlags() == attr.GetTextEffectFlags() &&
- attr.SetParagraphSpacingAfter(m_paragraphSpacingAfter);
- attr.SetParagraphSpacingBefore(m_paragraphSpacingBefore);
- attr.SetLineSpacing(m_lineSpacing);
- attr.SetBulletStyle(m_bulletStyle);
- attr.SetBulletNumber(m_bulletNumber);
- attr.SetBulletText(m_bulletText);
- attr.SetBulletName(m_bulletName);
- attr.SetBulletFont(m_bulletFont);
- attr.SetCharacterStyleName(m_characterStyleName);
- attr.SetParagraphStyleName(m_paragraphStyleName);
- attr.SetListStyleName(m_listStyleName);
-
- attr.SetURL(m_urlTarget);
+ GetOutlineLevel() == attr.GetOutlineLevel() &&
- attr.SetFlags(GetFlags()); // Important: set after SetFont and others, since they set flags
+ GetFontSize() == attr.GetFontSize() &&
+ GetFontStyle() == attr.GetFontStyle() &&
+ GetFontWeight() == attr.GetFontWeight() &&
+ GetFontUnderlined() == attr.GetFontUnderlined() &&
+ GetFontFaceName() == attr.GetFontFaceName() &&
+
+ GetURL() == attr.GetURL();
}
// Create font from font attributes.
if (attr.HasURL())
newAttr.SetURL(attr.GetURL());
+ if (attr.HasPageBreak())
+ newAttr.SetPageBreak();
+
+ if (attr.HasTextEffects())
+ {
+ newAttr.SetTextEffects(attr.GetTextEffects());
+ newAttr.SetTextEffectFlags(attr.GetTextEffectFlags());
+ }
+
+ if (attr.HasOutlineLevel())
+ newAttr.SetOutlineLevel(attr.GetOutlineLevel());
+
return newAttr;
}
* wxTextAttrEx is an extended version of wxTextAttr with more paragraph attributes.
*/
-wxTextAttrEx::wxTextAttrEx(const wxTextAttrEx& attr)
+wxTextAttrEx::wxTextAttrEx(const wxTextAttrEx& attr): wxTextAttr()
{
Copy(attr);
}
m_paragraphSpacingBefore = 0;
m_lineSpacing = 0;
m_bulletStyle = wxTEXT_ATTR_BULLET_STYLE_NONE;
+ m_textEffects = wxTEXT_ATTR_EFFECT_NONE;
+ m_textEffectFlags = wxTEXT_ATTR_EFFECT_NONE;
m_bulletNumber = 0;
+ m_outlineLevel = 0;
}
// Copy
m_bulletFont = attr.m_bulletFont;
m_bulletName = attr.m_bulletName;
m_urlTarget = attr.m_urlTarget;
+ m_textEffects = attr.m_textEffects;
+ m_textEffectFlags = attr.m_textEffectFlags;
+ m_outlineLevel = attr.m_outlineLevel;
}
// Assignment from a wxTextAttrEx object
bool wxTextAttrEx::operator== (const wxTextAttrEx& attr) const
{
return (
+ GetFlags() == attr.GetFlags() &&
GetTextColour() == attr.GetTextColour() &&
GetBackgroundColour() == attr.GetBackgroundColour() &&
GetFont() == attr.GetFont() &&
+ GetTextEffects() == attr.GetTextEffects() &&
+ GetTextEffectFlags() == attr.GetTextEffectFlags() &&
GetAlignment() == attr.GetAlignment() &&
GetLeftIndent() == attr.GetLeftIndent() &&
GetRightIndent() == attr.GetRightIndent() &&
GetCharacterStyleName() == attr.GetCharacterStyleName() &&
GetParagraphStyleName() == attr.GetParagraphStyleName() &&
GetListStyleName() == attr.GetListStyleName() &&
+ GetOutlineLevel() == attr.GetOutlineLevel() &&
GetURL() == attr.GetURL());
}
if (attr.HasURL())
newAttr.SetURL(attr.GetURL());
+ if (attr.HasTextEffects())
+ {
+ newAttr.SetTextEffects(attr.GetTextEffects());
+ newAttr.SetTextEffectFlags(attr.GetTextEffectFlags());
+ }
+
+ if (attr.HasOutlineLevel())
+ newAttr.SetOutlineLevel(attr.GetOutlineLevel());
+
return newAttr;
}
}
}
+ buffer->ResetAndClearCommands();
buffer->Clear();
buffer->AddParagraphs(str);
buffer->UpdateRanges();
return true;
-
}
bool wxRichTextPlainTextHandler::DoSaveFile(wxRichTextBuffer *buffer, wxOutputStream& stream)