Added support for sub-object virtual attributes (temporary attributes for characters within objects)

and also virtual text that can replace the actual text.


git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@73454 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Julian Smart
2013-02-04 12:52:14 +00:00
parent 236cff7334
commit f7667b84a6
8 changed files with 675 additions and 34 deletions

View File

@@ -125,6 +125,7 @@ enum wxRichTextFileType
class WXDLLIMPEXP_FWD_RICHTEXT wxRichTextCtrl; class WXDLLIMPEXP_FWD_RICHTEXT wxRichTextCtrl;
class WXDLLIMPEXP_FWD_RICHTEXT wxRichTextObject; class WXDLLIMPEXP_FWD_RICHTEXT wxRichTextObject;
class WXDLLIMPEXP_FWD_RICHTEXT wxRichTextImage; class WXDLLIMPEXP_FWD_RICHTEXT wxRichTextImage;
class WXDLLIMPEXP_FWD_RICHTEXT wxRichTextPlainText;
class WXDLLIMPEXP_FWD_RICHTEXT wxRichTextCacheObject; class WXDLLIMPEXP_FWD_RICHTEXT wxRichTextCacheObject;
class WXDLLIMPEXP_FWD_RICHTEXT wxRichTextObjectList; class WXDLLIMPEXP_FWD_RICHTEXT wxRichTextObjectList;
class WXDLLIMPEXP_FWD_RICHTEXT wxRichTextLine; class WXDLLIMPEXP_FWD_RICHTEXT wxRichTextLine;
@@ -1543,9 +1544,16 @@ public:
*/ */
void SetTextBoxAttr(const wxTextBoxAttr& attr) { m_textBoxAttr = attr; } void SetTextBoxAttr(const wxTextBoxAttr& attr) { m_textBoxAttr = attr; }
/**
Returns @true if no attributes are set.
*/
bool IsDefault() const { return (GetFlags() == 0) && m_textBoxAttr.IsDefault(); }
wxTextBoxAttr m_textBoxAttr; wxTextBoxAttr m_textBoxAttr;
}; };
WX_DECLARE_USER_EXPORTED_OBJARRAY(wxRichTextAttr, wxRichTextAttrArray, WXDLLIMPEXP_RICHTEXT);
WX_DECLARE_USER_EXPORTED_OBJARRAY(wxVariant, wxRichTextVariantArray, WXDLLIMPEXP_RICHTEXT); WX_DECLARE_USER_EXPORTED_OBJARRAY(wxVariant, wxRichTextVariantArray, WXDLLIMPEXP_RICHTEXT);
/** /**
@@ -2106,9 +2114,9 @@ public:
Pass the buffer to the context so the context can retrieve information Pass the buffer to the context so the context can retrieve information
such as virtual attributes. such as virtual attributes.
*/ */
wxRichTextDrawingContext(wxRichTextBuffer* buffer) { Init(); m_buffer = buffer; } wxRichTextDrawingContext(wxRichTextBuffer* buffer);
void Init() { m_buffer = NULL; } void Init() { m_buffer = NULL; m_enableVirtualAttributes = true; }
/** /**
Does this object have virtual attributes? Does this object have virtual attributes?
@@ -2129,7 +2137,45 @@ public:
*/ */
bool ApplyVirtualAttributes(wxRichTextAttr& attr, wxRichTextObject* obj) const; bool ApplyVirtualAttributes(wxRichTextAttr& attr, wxRichTextObject* obj) const;
/**
Gets the count for mixed virtual attributes for individual positions within the object.
For example, individual characters within a text object may require special highlighting.
*/
int GetVirtualSubobjectAttributesCount(wxRichTextObject* obj) const;
/**
Gets the mixed virtual attributes for individual positions within the object.
For example, individual characters within a text object may require special highlighting.
The function is passed the count returned by GetVirtualSubobjectAttributesCount.
*/
int GetVirtualSubobjectAttributes(wxRichTextObject* obj, wxArrayInt& positions, wxRichTextAttrArray& attributes) const;
/**
Do we have virtual text for this object? Virtual text allows an application
to replace characters in an object for editing and display purposes, for example
for highlighting special characters.
*/
bool HasVirtualText(const wxRichTextPlainText* obj) const;
/**
Gets the virtual text for this object.
*/
bool GetVirtualText(const wxRichTextPlainText* obj, wxString& text) const;
/**
Enables virtual attribute processing.
*/
void EnableVirtualAttributes(bool b) { m_enableVirtualAttributes = b; }
/**
Returns @true if virtual attribute processing is enabled.
*/
bool GetVirtualAttributesEnabled() const { return m_enableVirtualAttributes; }
wxRichTextBuffer* m_buffer; wxRichTextBuffer* m_buffer;
bool m_enableVirtualAttributes;
}; };
/** /**
@@ -2252,13 +2298,26 @@ public:
/** /**
Returns @true if this object can merge itself with the given one. Returns @true if this object can merge itself with the given one.
*/ */
virtual bool CanMerge(wxRichTextObject* WXUNUSED(object)) const { return false; } virtual bool CanMerge(wxRichTextObject* WXUNUSED(object), wxRichTextDrawingContext& WXUNUSED(context)) const { return false; }
/** /**
Returns @true if this object merged itself with the given one. Returns @true if this object merged itself with the given one.
The calling code will then delete the given object. The calling code will then delete the given object.
*/ */
virtual bool Merge(wxRichTextObject* WXUNUSED(object)) { return false; } virtual bool Merge(wxRichTextObject* WXUNUSED(object), wxRichTextDrawingContext& WXUNUSED(context)) { return false; }
/**
JACS
Returns @true if this object can potentially be split, by virtue of having
different virtual attributes for individual sub-objects.
*/
virtual bool CanSplit(wxRichTextDrawingContext& WXUNUSED(context)) const { return false; }
/**
Returns the final object in the split objects if this object was split due to differences between sub-object virtual attributes.
Returns itself if it was not split.
*/
virtual wxRichTextObject* Split(wxRichTextDrawingContext& WXUNUSED(context)) { return this; }
/** /**
Dump object data to the given output stream for debugging. Dump object data to the given output stream for debugging.
@@ -2811,7 +2870,7 @@ public:
/** /**
Recursively merges all pieces that can be merged. Recursively merges all pieces that can be merged.
*/ */
bool Defragment(const wxRichTextRange& range = wxRICHTEXT_ALL); bool Defragment(wxRichTextDrawingContext& context, const wxRichTextRange& range = wxRICHTEXT_ALL);
/** /**
Moves the object recursively, by adding the offset from old to new. Moves the object recursively, by adding the offset from old to new.
@@ -4295,12 +4354,16 @@ public:
virtual bool IsEmpty() const { return m_text.empty(); } virtual bool IsEmpty() const { return m_text.empty(); }
virtual bool CanMerge(wxRichTextObject* object) const; virtual bool CanMerge(wxRichTextObject* object, wxRichTextDrawingContext& context) const;
virtual bool Merge(wxRichTextObject* object); virtual bool Merge(wxRichTextObject* object, wxRichTextDrawingContext& context);
virtual void Dump(wxTextOutputStream& stream); virtual void Dump(wxTextOutputStream& stream);
virtual bool CanSplit(wxRichTextDrawingContext& context) const;
virtual wxRichTextObject* Split(wxRichTextDrawingContext& context);
/** /**
Get the first position from pos that has a line break character. Get the first position from pos that has a line break character.
*/ */
@@ -6254,6 +6317,31 @@ public:
*/ */
virtual bool GetVirtualAttributes(wxRichTextAttr& attr, wxRichTextObject* obj) const = 0; virtual bool GetVirtualAttributes(wxRichTextAttr& attr, wxRichTextObject* obj) const = 0;
/**
Gets the count for mixed virtual attributes for individual positions within the object.
For example, individual characters within a text object may require special highlighting.
*/
virtual int GetVirtualSubobjectAttributesCount(wxRichTextObject* obj) const = 0;
/**
Gets the mixed virtual attributes for individual positions within the object.
For example, individual characters within a text object may require special highlighting.
Returns the number of virtual attributes found.
*/
virtual int GetVirtualSubobjectAttributes(wxRichTextObject* obj, wxArrayInt& positions, wxRichTextAttrArray& attributes) const = 0;
/**
Do we have virtual text for this object? Virtual text allows an application
to replace characters in an object for editing and display purposes, for example
for highlighting special characters.
*/
virtual bool HasVirtualText(const wxRichTextPlainText* obj) const = 0;
/**
Gets the virtual text for this object.
*/
virtual bool GetVirtualText(const wxRichTextPlainText* obj, wxString& text) const = 0;
/** /**
Sets the name of the handler. Sets the name of the handler.
*/ */

View File

@@ -1760,6 +1760,18 @@ public:
*/ */
wxRect GetScaledRect(const wxRect& rect) const; wxRect GetScaledRect(const wxRect& rect) const;
/**
Returns @true if this control can use virtual attributes and virtual text.
The default is @false.
*/
bool GetVirtualAttributesEnabled() const { return m_useVirtualAttributes; }
/**
Pass @true to let the control use virtual attributes.
The default is @false.
*/
void EnableVirtualAttributes(bool b) { m_useVirtualAttributes = b; }
// Command handlers // Command handlers
/** /**
@@ -2254,6 +2266,9 @@ protected:
/// Are we editable? /// Are we editable?
bool m_editable; bool m_editable;
/// Can we use virtual attributes and virtual text?
bool m_useVirtualAttributes;
/// Is the vertical scrollbar enabled? /// Is the vertical scrollbar enabled?
bool m_verticalScrollbarEnabled; bool m_verticalScrollbarEnabled;

View File

@@ -1424,9 +1424,16 @@ public:
*/ */
void SetTextBoxAttr(const wxTextBoxAttr& attr) { m_textBoxAttr = attr; } void SetTextBoxAttr(const wxTextBoxAttr& attr) { m_textBoxAttr = attr; }
/**
Returns @true if no attributes are set.
*/
bool IsDefault() const { return (GetFlags() == 0) && m_textBoxAttr.IsDefault(); }
wxTextBoxAttr m_textBoxAttr; wxTextBoxAttr m_textBoxAttr;
}; };
WX_DECLARE_USER_EXPORTED_OBJARRAY(wxRichTextAttr, wxRichTextAttrArray, WXDLLIMPEXP_RICHTEXT);
WX_DECLARE_USER_EXPORTED_OBJARRAY(wxVariant, wxRichTextVariantArray, WXDLLIMPEXP_RICHTEXT); WX_DECLARE_USER_EXPORTED_OBJARRAY(wxVariant, wxRichTextVariantArray, WXDLLIMPEXP_RICHTEXT);
/** /**
@@ -2010,6 +2017,43 @@ public:
*/ */
bool ApplyVirtualAttributes(wxRichTextAttr& attr, wxRichTextObject* obj) const; bool ApplyVirtualAttributes(wxRichTextAttr& attr, wxRichTextObject* obj) const;
/**
Gets the count for mixed virtual attributes for individual positions within the object.
For example, individual characters within a text object may require special highlighting.
*/
int GetVirtualSubobjectAttributesCount(wxRichTextObject* obj) const;
/**
Gets the mixed virtual attributes for individual positions within the object.
For example, individual characters within a text object may require special highlighting.
The function is passed the count returned by GetVirtualSubobjectAttributesCount.
*/
int GetVirtualSubobjectAttributes(wxRichTextObject* obj, wxArrayInt& positions, wxRichTextAttrArray& attributes) const;
/**
Do we have virtual text for this object? Virtual text allows an application
to replace characters in an object for editing and display purposes, for example
for highlighting special characters.
*/
bool HasVirtualText(const wxRichTextPlainText* obj) const;
/**
Gets the virtual text for this object.
*/
bool GetVirtualText(const wxRichTextPlainText* obj, wxString& text) const;
/**
Enables virtual attribute processing.
*/
void EnableVirtualAttributes(bool b);
/**
Returns @true if virtual attribute processing is enabled.
*/
bool GetVirtualAttributesEnabled() const;
wxRichTextBuffer* m_buffer; wxRichTextBuffer* m_buffer;
}; };
@@ -2129,13 +2173,25 @@ public:
/** /**
Returns @true if this object can merge itself with the given one. Returns @true if this object can merge itself with the given one.
*/ */
virtual bool CanMerge(wxRichTextObject* WXUNUSED(object)) const { return false; } virtual bool CanMerge(wxRichTextObject* object, wxRichTextDrawingContext& context) const { return false; }
/** /**
Returns @true if this object merged itself with the given one. Returns @true if this object merged itself with the given one.
The calling code will then delete the given object. The calling code will then delete the given object.
*/ */
virtual bool Merge(wxRichTextObject* WXUNUSED(object)) { return false; } virtual bool Merge(wxRichTextObject* object, wxRichTextDrawingContext& context) { return false; }
/**
Returns @true if this object can potentially be split, by virtue of having
different virtual attributes for individual sub-objects.
*/
virtual bool CanSplit(wxRichTextDrawingContext& context) const;
/**
Returns the final object in the split objects if this object was split due to differences between sub-object virtual attributes.
Returns itself if it was not split.
*/
virtual wxRichTextObject* Split(wxRichTextDrawingContext& context);
/** /**
Dump object data to the given output stream for debugging. Dump object data to the given output stream for debugging.
@@ -2688,7 +2744,7 @@ public:
/** /**
Recursively merges all pieces that can be merged. Recursively merges all pieces that can be merged.
*/ */
bool Defragment(const wxRichTextRange& range = wxRICHTEXT_ALL); bool Defragment(wxRichTextDrawingContext& context, const wxRichTextRange& range = wxRICHTEXT_ALL);
/** /**
Moves the object recursively, by adding the offset from old to new. Moves the object recursively, by adding the offset from old to new.
@@ -4169,9 +4225,13 @@ public:
virtual bool IsEmpty() const { return m_text.empty(); } virtual bool IsEmpty() const { return m_text.empty(); }
virtual bool CanMerge(wxRichTextObject* object) const; virtual bool CanMerge(wxRichTextObject* object, wxRichTextDrawingContext& context) const;
virtual bool Merge(wxRichTextObject* object); virtual bool Merge(wxRichTextObject* object, wxRichTextDrawingContext& context);
virtual bool CanSplit(wxRichTextDrawingContext& context) const;
virtual wxRichTextObject* Split(wxRichTextDrawingContext& context);
virtual void Dump(wxTextOutputStream& stream); virtual void Dump(wxTextOutputStream& stream);
@@ -6112,6 +6172,31 @@ public:
*/ */
virtual bool GetVirtualAttributes(wxRichTextAttr& attr, wxRichTextObject* obj) const = 0; virtual bool GetVirtualAttributes(wxRichTextAttr& attr, wxRichTextObject* obj) const = 0;
/**
Gets the count for mixed virtual attributes for individual positions within the object.
For example, individual characters within a text object may require special highlighting.
*/
virtual int GetVirtualSubobjectAttributesCount(wxRichTextObject* obj) const = 0;
/**
Gets the mixed virtual attributes for individual positions within the object.
For example, individual characters within a text object may require special highlighting.
Returns the number of virtual attributes found.
*/
virtual int GetVirtualSubobjectAttributes(wxRichTextObject* obj, wxArrayInt& positions, wxRichTextAttrArray& attributes) const = 0;
/**
Do we have virtual text for this object? Virtual text allows an application
to replace characters in an object for editing and display purposes, for example
for highlighting special characters.
*/
virtual bool HasVirtualText(const wxRichTextPlainText* obj) const = 0;
/**
Gets the virtual text for this object.
*/
virtual bool GetVirtualText(const wxRichTextPlainText* obj, wxString& text) const = 0;
/** /**
Sets the name of the handler. Sets the name of the handler.
*/ */

View File

@@ -1725,6 +1725,18 @@ public:
*/ */
wxRect GetScaledRect(const wxRect& rect) const; wxRect GetScaledRect(const wxRect& rect) const;
/**
Returns @true if this control can use virtual attributes and virtual text.
The default is @false.
*/
bool GetVirtualAttributesEnabled() const;
/**
Pass @true to let the control use virtual attributes.
The default is @false.
*/
void EnableVirtualAttributes(bool b);
// Command handlers // Command handlers
/** /**

View File

@@ -1181,7 +1181,8 @@ void MyFrame::WriteInitialText()
cellAttr.GetTextBoxAttr().GetWidth().SetValue(200, wxTEXT_ATTR_UNITS_PIXELS); cellAttr.GetTextBoxAttr().GetWidth().SetValue(200, wxTEXT_ATTR_UNITS_PIXELS);
cellAttr.GetTextBoxAttr().GetHeight().SetValue(150, wxTEXT_ATTR_UNITS_PIXELS); cellAttr.GetTextBoxAttr().GetHeight().SetValue(150, wxTEXT_ATTR_UNITS_PIXELS);
wxRichTextTable* table = r.WriteTable(3, 2, attr, cellAttr); //wxRichTextTable* table = r.WriteTable(3, 2, attr, cellAttr);
wxRichTextTable* table = r.WriteTable(24, 2, attr, cellAttr);
int i, j; int i, j;
for (j = 0; j < table->GetRowCount(); j++) for (j = 0; j < table->GetRowCount(); j++)
{ {
@@ -2080,6 +2081,31 @@ public:
*/ */
virtual bool GetVirtualAttributes(wxRichTextAttr& attr, wxRichTextObject* obj) const; virtual bool GetVirtualAttributes(wxRichTextAttr& attr, wxRichTextObject* obj) const;
/**
Gets the count for mixed virtual attributes for individual positions within the object.
For example, individual characters within a text object may require special highlighting.
*/
virtual int GetVirtualSubobjectAttributesCount(wxRichTextObject* WXUNUSED(obj)) const { return 0; }
/**
Gets the mixed virtual attributes for individual positions within the object.
For example, individual characters within a text object may require special highlighting.
Returns the number of virtual attributes found.
*/
virtual int GetVirtualSubobjectAttributes(wxRichTextObject* WXUNUSED(obj), wxArrayInt& WXUNUSED(positions), wxRichTextAttrArray& WXUNUSED(attributes)) const { return 0; }
/**
Do we have virtual text for this object? Virtual text allows an application
to replace characters in an object for editing and display purposes, for example
for highlighting special characters.
*/
virtual bool HasVirtualText(const wxRichTextPlainText* WXUNUSED(obj)) const { return false; }
/**
Gets the virtual text for this object.
*/
virtual bool GetVirtualText(const wxRichTextPlainText* WXUNUSED(obj), wxString& WXUNUSED(text)) const { return false; }
wxColour m_lockBackgroundColour; wxColour m_lockBackgroundColour;
}; };

View File

@@ -1400,7 +1400,7 @@ wxRichTextObject* wxRichTextCompositeObject::GetChildAtPosition(long pos) const
} }
/// Recursively merge all pieces that can be merged. /// Recursively merge all pieces that can be merged.
bool wxRichTextCompositeObject::Defragment(const wxRichTextRange& range) bool wxRichTextCompositeObject::Defragment(wxRichTextDrawingContext& context, const wxRichTextRange& range)
{ {
wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst(); wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
while (node) while (node)
@@ -1410,24 +1410,85 @@ bool wxRichTextCompositeObject::Defragment(const wxRichTextRange& range)
{ {
wxRichTextCompositeObject* composite = wxDynamicCast(child, wxRichTextCompositeObject); wxRichTextCompositeObject* composite = wxDynamicCast(child, wxRichTextCompositeObject);
if (composite) if (composite)
composite->Defragment(); composite->Defragment(context);
if (node->GetNext()) // Optimization: if there are no virtual attributes, we won't need to
// to split objects in order to paint individually attributed chunks.
// So only merge in this case.
if (!context.GetVirtualAttributesEnabled())
{ {
wxRichTextObject* nextChild = node->GetNext()->GetData(); if (node->GetNext())
if (child->CanMerge(nextChild) && child->Merge(nextChild))
{ {
nextChild->Dereference(); wxRichTextObject* nextChild = node->GetNext()->GetData();
m_children.Erase(node->GetNext()); if (child->CanMerge(nextChild, context) && child->Merge(nextChild, context))
{
// Don't set node -- we'll see if we can merge again with the next nextChild->Dereference();
// child. m_children.Erase(node->GetNext());
}
else
node = node->GetNext();
} }
else else
node = node->GetNext(); node = node->GetNext();
} }
else else
node = node->GetNext(); {
// If we might have virtual attributes, we first see if we have to split
// objects so that they may be painted with potential virtual attributes,
// since text objects can only draw or measure with a single attributes object
// at a time.
wxRichTextObject* childAfterSplit = child;
if (child->CanSplit(context))
{
childAfterSplit = child->Split(context);
node = m_children.Find(childAfterSplit);
}
if (node->GetNext())
{
wxRichTextObject* nextChild = node->GetNext()->GetData();
wxRichTextObjectList::compatibility_iterator nextNode = node->GetNext();
// First split child and nextChild so we have smaller fragments to merge.
// Then Merge only has to test per-object virtual attributes
// because for an object with all the same sub-object attributes,
// then any general virtual attributes should be merged with sub-objects by
// the implementation.
wxRichTextObject* nextChildAfterSplit = nextChild;
if (nextChildAfterSplit->CanSplit(context))
nextChildAfterSplit = nextChild->Split(context);
bool splitNextChild = nextChild != nextChildAfterSplit;
// See if we can merge this new fragment with (perhaps the first part of) the next object.
// Note that we use nextChild because if we had split nextChild, the first object always
// remains (and further parts are appended). However we must use childAfterSplit since
// it's the last part of a possibly split child.
if (childAfterSplit->CanMerge(nextChild, context) && childAfterSplit->Merge(nextChild, context))
{
nextChild->Dereference();
m_children.Erase(node->GetNext());
// Don't set node -- we'll see if we can merge again with the next
// child. UNLESS we split this or the next child, in which case we know we have to
// move on to the end of the next child.
if (splitNextChild)
node = m_children.Find(nextChildAfterSplit);
}
else
{
if (splitNextChild)
node = m_children.Find(nextChildAfterSplit); // start from the last object in the split
else
node = node->GetNext();
}
}
else
node = node->GetNext();
}
} }
else else
node = node->GetNext(); node = node->GetNext();
@@ -3795,8 +3856,21 @@ wxRichTextRange wxRichTextParagraphLayoutBox::GetInvalidRange(bool wholeParagrap
wxRichTextParagraph* para1 = GetParagraphAtPosition(range.GetStart()); wxRichTextParagraph* para1 = GetParagraphAtPosition(range.GetStart());
if (para1) if (para1)
range.SetStart(para1->GetRange().GetStart()); range.SetStart(para1->GetRange().GetStart());
// floating layout make all child should be relayout
range.SetEnd(GetOwnRange().GetEnd()); // FIXME: be more intelligent about this. Check if we have floating objects
// before the end of the range. But it's not clear how we can in general
// tell where it's safe to stop laying out.
// Anyway, this code is central to efficiency when laying in floating mode.
if (!wxRichTextBuffer::GetFloatingLayoutMode())
{
wxRichTextParagraph* para2 = GetParagraphAtPosition(range.GetEnd());
if (para2)
range.SetEnd(para2->GetRange().GetEnd());
}
else
// Floating layout means that all children should be laid out,
// because we can't tell how the whole buffer will be affected.
range.SetEnd(GetOwnRange().GetEnd());
} }
return range; return range;
} }
@@ -6313,8 +6387,14 @@ bool wxRichTextPlainText::Draw(wxDC& dc, wxRichTextDrawingContext& context, cons
int offset = GetRange().GetStart(); int offset = GetRange().GetStart();
// Replace line break characters with spaces
wxString str = m_text; wxString str = m_text;
if (context.HasVirtualText(this))
{
if (!context.GetVirtualText(this, str) || str.Length() != m_text.Length())
str = m_text;
}
// Replace line break characters with spaces
wxString toRemove = wxRichTextLineBreakChar; wxString toRemove = wxRichTextLineBreakChar;
str.Replace(toRemove, wxT(" ")); str.Replace(toRemove, wxT(" "));
if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & (wxTEXT_ATTR_EFFECT_CAPITALS|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS))) if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & (wxTEXT_ATTR_EFFECT_CAPITALS|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS)))
@@ -6696,6 +6776,12 @@ bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange& range, wxSize& siz
long len = range.GetLength(); long len = range.GetLength();
wxString str(m_text); wxString str(m_text);
if (context.HasVirtualText(this))
{
if (!context.GetVirtualText(this, str) || str.Length() != m_text.Length())
str = m_text;
}
wxString toReplace = wxRichTextLineBreakChar; wxString toReplace = wxRichTextLineBreakChar;
str.Replace(toReplace, wxT(" ")); str.Replace(toReplace, wxT(" "));
@@ -6902,15 +6988,44 @@ wxString wxRichTextPlainText::GetTextForRange(const wxRichTextRange& range) cons
} }
/// Returns true if this object can merge itself with the given one. /// Returns true if this object can merge itself with the given one.
bool wxRichTextPlainText::CanMerge(wxRichTextObject* object) const bool wxRichTextPlainText::CanMerge(wxRichTextObject* object, wxRichTextDrawingContext& context) const
{ {
return object->GetClassInfo() == wxCLASSINFO(wxRichTextPlainText) && // JACS 2013-01-27
(m_text.empty() || (wxTextAttrEq(GetAttributes(), object->GetAttributes()) && m_properties == object->GetProperties())); if (!context.GetVirtualAttributesEnabled())
{
return object->GetClassInfo() == wxCLASSINFO(wxRichTextPlainText) &&
(m_text.empty() || (wxTextAttrEq(GetAttributes(), object->GetAttributes()) && m_properties == object->GetProperties()));
}
else
{
wxRichTextPlainText* otherObj = wxDynamicCast(object, wxRichTextPlainText);
if (!otherObj || m_text.empty())
return false;
if (!wxTextAttrEq(GetAttributes(), object->GetAttributes()) || !(m_properties == object->GetProperties()))
return false;
// Check if differing virtual attributes makes it impossible to merge
// these strings.
bool hasVirtualAttr1 = context.HasVirtualAttributes((wxRichTextObject*) this);
bool hasVirtualAttr2 = context.HasVirtualAttributes((wxRichTextObject*) object);
if (!hasVirtualAttr1 && !hasVirtualAttr2)
return true;
else if (hasVirtualAttr1 != hasVirtualAttr2)
return false;
else
{
wxRichTextAttr virtualAttr1 = context.GetVirtualAttributes((wxRichTextObject*) this);
wxRichTextAttr virtualAttr2 = context.GetVirtualAttributes((wxRichTextObject*) object);
return virtualAttr1 == virtualAttr2;
}
}
} }
/// Returns true if this object merged itself with the given one. /// Returns true if this object merged itself with the given one.
/// The calling code will then delete the given object. /// The calling code will then delete the given object.
bool wxRichTextPlainText::Merge(wxRichTextObject* object) bool wxRichTextPlainText::Merge(wxRichTextObject* object, wxRichTextDrawingContext& WXUNUSED(context))
{ {
wxRichTextPlainText* textObject = wxDynamicCast(object, wxRichTextPlainText); wxRichTextPlainText* textObject = wxDynamicCast(object, wxRichTextPlainText);
wxASSERT( textObject != NULL ); wxASSERT( textObject != NULL );
@@ -6925,6 +7040,214 @@ bool wxRichTextPlainText::Merge(wxRichTextObject* object)
return false; return false;
} }
bool wxRichTextPlainText::CanSplit(wxRichTextDrawingContext& context) const
{
// If this object has any virtual attributes at all, whether for the whole object
// or individual ones, we should try splitting it by calling Split.
// Must be more than one character in order to be able to split.
return m_text.Length() > 1 && context.HasVirtualAttributes((wxRichTextObject*) this);
}
wxRichTextObject* wxRichTextPlainText::Split(wxRichTextDrawingContext& context)
{
int count = context.GetVirtualSubobjectAttributesCount(this);
if (count > 0 && GetParent())
{
wxRichTextCompositeObject* parent = wxDynamicCast(GetParent(), wxRichTextCompositeObject);
wxRichTextObjectList::compatibility_iterator node = parent->GetChildren().Find(this);
if (node)
{
const wxRichTextAttr emptyAttr;
wxRichTextObjectList::compatibility_iterator next = node->GetNext();
wxArrayInt positions;
wxRichTextAttrArray attributes;
if (context.GetVirtualSubobjectAttributes(this, positions, attributes) && positions.GetCount() > 0)
{
wxASSERT(positions.GetCount() == attributes.GetCount());
// We will gather up runs of text with the same virtual attributes
int len = m_text.Length();
int i = 0;
// runStart and runEnd represent the accumulated run with a consistent attribute
// that hasn't yet been appended
int runStart = -1;
int runEnd = -1;
wxRichTextAttr currentAttr;
wxString text = m_text;
wxRichTextPlainText* lastPlainText = this;
for (i = 0; i < (int) positions.GetCount(); i++)
{
int pos = positions[i];
wxASSERT(pos >= 0 && pos < len);
if (pos >= 0 && pos < len)
{
const wxRichTextAttr& attr = attributes[i];
if (pos == 0)
{
runStart = 0;
currentAttr = attr;
}
// Check if there was a gap from the last known attribute and this.
// In that case, we need to do something with the span of non-attributed text.
else if ((pos-1) > runEnd)
{
if (runEnd == -1)
{
// We hadn't processed anything previously, so the previous run is from the text start
// to just before this position. The current attribute remains empty.
runStart = 0;
runEnd = pos-1;
}
else
{
// If the previous attribute matches the gap's attribute (i.e., no attributes)
// then just extend the run.
if (currentAttr.IsDefault())
{
runEnd = pos-1;
}
else
{
// We need to add an object, or reuse the existing one.
if (runStart == 0)
{
lastPlainText = this;
SetText(text.Mid(runStart, runEnd - runStart + 1));
}
else
{
wxRichTextPlainText* obj = new wxRichTextPlainText;
lastPlainText = obj;
obj->SetAttributes(GetAttributes());
obj->SetProperties(GetProperties());
obj->SetParent(parent);
obj->SetText(text.Mid(runStart, runEnd - runStart + 1));
if (next)
parent->GetChildren().Insert(next, obj);
else
parent->GetChildren().Append(obj);
}
runStart = runEnd+1;
runEnd = pos-1;
currentAttr = emptyAttr;
}
}
}
wxASSERT(runEnd == pos-1);
// Now we only have to deal with the previous run
if (currentAttr == attr)
{
// If we still have the same attributes, then we
// simply increase the run size.
runEnd = pos;
}
else
{
if (runEnd >= 0)
{
// We need to add an object, or reuse the existing one.
if (runStart == 0)
{
lastPlainText = this;
SetText(text.Mid(runStart, runEnd - runStart + 1));
}
else
{
wxRichTextPlainText* obj = new wxRichTextPlainText;
lastPlainText = obj;
obj->SetAttributes(GetAttributes());
obj->SetProperties(GetProperties());
obj->SetParent(parent);
obj->SetText(text.Mid(runStart, runEnd - runStart + 1));
if (next)
parent->GetChildren().Insert(next, obj);
else
parent->GetChildren().Append(obj);
}
}
runStart = pos;
runEnd = pos;
currentAttr = attr;
}
}
}
// We may still have a run to add, and possibly a no-attribute text fragment after that.
// If the whole string was already a single attribute (the run covers the whole string), don't split.
if ((runStart != -1) && !(runStart == 0 && runEnd == (len-1)))
{
// If the current attribute is empty, merge the run with the next fragment
// which by definition (because it's not specified) has empty attributes.
if (currentAttr.IsDefault())
runEnd = (len-1);
if (runEnd < (len-1))
{
// We need to add an object, or reuse the existing one.
if (runStart == 0)
{
lastPlainText = this;
SetText(text.Mid(runStart, runEnd - runStart + 1));
}
else
{
wxRichTextPlainText* obj = new wxRichTextPlainText;
lastPlainText = obj;
obj->SetAttributes(GetAttributes());
obj->SetProperties(GetProperties());
obj->SetParent(parent);
obj->SetText(text.Mid(runStart, runEnd - runStart + 1));
if (next)
parent->GetChildren().Insert(next, obj);
else
parent->GetChildren().Append(obj);
}
runStart = runEnd+1;
runEnd = (len-1);
}
// Now the last, non-attributed fragment at the end, if any
if ((runStart < len) && !(runStart == 0 && runEnd == (len-1)))
{
wxASSERT(runStart != 0);
wxRichTextPlainText* obj = new wxRichTextPlainText;
obj->SetAttributes(GetAttributes());
obj->SetProperties(GetProperties());
obj->SetParent(parent);
obj->SetText(text.Mid(runStart, runEnd - runStart + 1));
if (next)
parent->GetChildren().Insert(next, obj);
else
parent->GetChildren().Append(obj);
lastPlainText = obj;
}
}
return lastPlainText;
}
}
}
return this;
}
/// Dump to output stream for debugging /// Dump to output stream for debugging
void wxRichTextPlainText::Dump(wxTextOutputStream& stream) void wxRichTextPlainText::Dump(wxTextOutputStream& stream)
{ {
@@ -13098,6 +13421,9 @@ void wxTextAttrCollectCommonAttributes(wxTextAttr& currentStyle, const wxTextAtt
WX_DEFINE_OBJARRAY(wxRichTextVariantArray); WX_DEFINE_OBJARRAY(wxRichTextVariantArray);
// JACS 2013-01-27
WX_DEFINE_OBJARRAY(wxRichTextAttrArray);
IMPLEMENT_DYNAMIC_CLASS(wxRichTextProperties, wxObject) IMPLEMENT_DYNAMIC_CLASS(wxRichTextProperties, wxObject)
bool wxRichTextProperties::operator==(const wxRichTextProperties& props) const bool wxRichTextProperties::operator==(const wxRichTextProperties& props) const
@@ -13411,8 +13737,19 @@ bool wxRichTextSelection::WithinSelection(const wxRichTextRange& range, const wx
IMPLEMENT_CLASS(wxRichTextDrawingHandler, wxObject) IMPLEMENT_CLASS(wxRichTextDrawingHandler, wxObject)
IMPLEMENT_CLASS(wxRichTextDrawingContext, wxObject) IMPLEMENT_CLASS(wxRichTextDrawingContext, wxObject)
wxRichTextDrawingContext::wxRichTextDrawingContext(wxRichTextBuffer* buffer)
{
Init();
m_buffer = buffer;
if (m_buffer && m_buffer->GetRichTextCtrl())
EnableVirtualAttributes(m_buffer->GetRichTextCtrl()->GetVirtualAttributesEnabled());
}
bool wxRichTextDrawingContext::HasVirtualAttributes(wxRichTextObject* obj) const bool wxRichTextDrawingContext::HasVirtualAttributes(wxRichTextObject* obj) const
{ {
if (!GetVirtualAttributesEnabled())
return false;
wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst(); wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
while (node) while (node)
{ {
@@ -13428,6 +13765,9 @@ bool wxRichTextDrawingContext::HasVirtualAttributes(wxRichTextObject* obj) const
wxRichTextAttr wxRichTextDrawingContext::GetVirtualAttributes(wxRichTextObject* obj) const wxRichTextAttr wxRichTextDrawingContext::GetVirtualAttributes(wxRichTextObject* obj) const
{ {
wxRichTextAttr attr; wxRichTextAttr attr;
if (!GetVirtualAttributesEnabled())
return attr;
// We apply all handlers, so we can may combine several different attributes // We apply all handlers, so we can may combine several different attributes
wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst(); wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
while (node) while (node)
@@ -13447,6 +13787,9 @@ wxRichTextAttr wxRichTextDrawingContext::GetVirtualAttributes(wxRichTextObject*
bool wxRichTextDrawingContext::ApplyVirtualAttributes(wxRichTextAttr& attr, wxRichTextObject* obj) const bool wxRichTextDrawingContext::ApplyVirtualAttributes(wxRichTextAttr& attr, wxRichTextObject* obj) const
{ {
if (!GetVirtualAttributesEnabled())
return false;
if (HasVirtualAttributes(obj)) if (HasVirtualAttributes(obj))
{ {
wxRichTextAttr a(GetVirtualAttributes(obj)); wxRichTextAttr a(GetVirtualAttributes(obj));
@@ -13457,6 +13800,75 @@ bool wxRichTextDrawingContext::ApplyVirtualAttributes(wxRichTextAttr& attr, wxRi
return false; return false;
} }
int wxRichTextDrawingContext::GetVirtualSubobjectAttributesCount(wxRichTextObject* obj) const
{
if (!GetVirtualAttributesEnabled())
return 0;
wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
while (node)
{
wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
int count = handler->GetVirtualSubobjectAttributesCount(obj);
if (count > 0)
return count;
node = node->GetNext();
}
return 0;
}
int wxRichTextDrawingContext::GetVirtualSubobjectAttributes(wxRichTextObject* obj, wxArrayInt& positions, wxRichTextAttrArray& attributes) const
{
if (!GetVirtualAttributesEnabled())
return 0;
wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
while (node)
{
wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
if (handler->GetVirtualSubobjectAttributes(obj, positions, attributes))
return positions.GetCount();
node = node->GetNext();
}
return 0;
}
bool wxRichTextDrawingContext::HasVirtualText(const wxRichTextPlainText* obj) const
{
if (!GetVirtualAttributesEnabled())
return false;
wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
while (node)
{
wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
if (handler->HasVirtualText(obj))
return true;
node = node->GetNext();
}
return false;
}
bool wxRichTextDrawingContext::GetVirtualText(const wxRichTextPlainText* obj, wxString& text) const
{
if (!GetVirtualAttributesEnabled())
return false;
wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
while (node)
{
wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
if (handler->GetVirtualText(obj, text))
return true;
node = node->GetNext();
}
return false;
}
/// Adds a handler to the end /// Adds a handler to the end
void wxRichTextBuffer::AddDrawingHandler(wxRichTextDrawingHandler *handler) void wxRichTextBuffer::AddDrawingHandler(wxRichTextDrawingHandler *handler)
{ {

View File

@@ -355,6 +355,7 @@ void wxRichTextCtrl::Init()
m_selectionAnchorObject = NULL; m_selectionAnchorObject = NULL;
m_selectionState = wxRichTextCtrlSelectionState_Normal; m_selectionState = wxRichTextCtrlSelectionState_Normal;
m_editable = true; m_editable = true;
m_useVirtualAttributes = false;
m_verticalScrollbarEnabled = true; m_verticalScrollbarEnabled = true;
m_caretAtLineStart = false; m_caretAtLineStart = false;
m_dragging = false; m_dragging = false;
@@ -2941,7 +2942,8 @@ void wxRichTextCtrl::DoWriteText(const wxString& value, int flags)
wxString valueUnix = wxTextFile::Translate(value, wxTextFileType_Unix); wxString valueUnix = wxTextFile::Translate(value, wxTextFileType_Unix);
GetFocusObject()->InsertTextWithUndo(& GetBuffer(), m_caretPosition+1, valueUnix, this, wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE); GetFocusObject()->InsertTextWithUndo(& GetBuffer(), m_caretPosition+1, valueUnix, this, wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE);
GetBuffer().Defragment(); wxRichTextDrawingContext context(& GetBuffer());
GetBuffer().Defragment(context);
if ( flags & SetValue_SendEvent ) if ( flags & SetValue_SendEvent )
wxTextCtrl::SendTextUpdatedEvent(this); wxTextCtrl::SendTextUpdatedEvent(this);
@@ -3890,7 +3892,7 @@ bool wxRichTextCtrl::LayoutContent(bool onlyVisibleRect)
dc.SetFont(GetFont()); dc.SetFont(GetFont());
wxRichTextDrawingContext context(& GetBuffer()); wxRichTextDrawingContext context(& GetBuffer());
GetBuffer().Defragment(); GetBuffer().Defragment(context);
GetBuffer().UpdateRanges(); // If items were deleted, ranges need recalculation GetBuffer().UpdateRanges(); // If items were deleted, ranges need recalculation
GetBuffer().Layout(dc, context, availableSpace, availableSpace, flags); GetBuffer().Layout(dc, context, availableSpace, availableSpace, flags);
GetBuffer().Invalidate(wxRICHTEXT_NONE); GetBuffer().Invalidate(wxRICHTEXT_NONE);

View File

@@ -75,7 +75,8 @@ bool wxRichTextHTMLHandler::DoSaveFile(wxRichTextBuffer *buffer, wxOutputStream&
ClearTemporaryImageLocations(); ClearTemporaryImageLocations();
buffer->Defragment(); wxRichTextDrawingContext context(buffer);
buffer->Defragment(context);
#if wxUSE_UNICODE #if wxUSE_UNICODE
wxCSConv* customEncoding = NULL; wxCSConv* customEncoding = NULL;