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);
}
/// 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);
{
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;
{
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.GetBulletFont() == attr2.GetBulletFont() &&
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;
+
return true;
}
!wxRichTextTabsEq(attr1.GetTabs(), attr2.GetTabs()))
return false;
+ if ((flags & wxTEXT_ATTR_PAGE_BREAK) &&
+ (attr1.HasPageBreak() != attr2.HasPageBreak()))
+ return false;
+
return true;
}
if (style.HasURL())
destStyle.SetURL(style.GetURL());
+ if (style.HasPageBreak())
+ destStyle.SetPageBreak();
+
return true;
}
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();
+ }
+
+ return true;
+}
+
+/// 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;
}
if (attr.HasURL())
newAttr.SetURL(attr.GetURL());
+ if (attr.HasPageBreak())
+ newAttr.SetPageBreak();
+
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);
}