// Author: Julian Smart
// Modified by:
// Created: 2005-09-30
-// RCS-ID: $Id$
// Copyright: (c) Julian Smart
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
#include "wx/richtext/richtextstyles.h"
#include "wx/richtext/richtextimagedlg.h"
#include "wx/richtext/richtextsizepage.h"
+#include "wx/richtext/richtextxml.h"
#include "wx/listimpl.cpp"
#include "wx/arrimpl.cpp"
// Helpers for efficiency
inline void wxCheckSetFont(wxDC& dc, const wxFont& font)
{
- // JACS: did I do this some time ago when testing? Should we re-enable it?
-#if 0
- const wxFont& font1 = dc.GetFont();
- if (font1.IsOk() && font.IsOk())
- {
- if (font1.GetPointSize() == font.GetPointSize() &&
- font1.GetFamily() == font.GetFamily() &&
- font1.GetStyle() == font.GetStyle() &&
- font1.GetWeight() == font.GetWeight() &&
- font1.GetUnderlined() == font.GetUnderlined() &&
- font1.GetFamily() == font.GetFamily() &&
- font1.GetFaceName() == font.GetFaceName())
- return;
- }
-#endif
dc.SetFont(font);
}
{
if (invalidRange != wxRICHTEXT_NONE)
{
- SetCachedSize(wxDefaultSize);
+ // If this is a floating object, size may not be recalculated
+ // after floats have been collected in an early stage of Layout.
+ // So avoid resetting the cache for floating objects during layout.
+ if (!IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode())
+ SetCachedSize(wxDefaultSize);
SetMaxSize(wxDefaultSize);
SetMinSize(wxDefaultSize);
}
// Unscale
double scale = 1.0;
if (GetBuffer())
- scale = GetBuffer()->GetScale();
+ scale = GetBuffer()->GetScale() / GetBuffer()->GetDimensionScale();
int p = ConvertTenthsMMToPixels(dc.GetPPI().x, units, scale);
return p;
// Draw the borders and background for the given rectangle and attributes.
// Width and height are taken to be the outer margin size, not the content.
-bool wxRichTextObject::DrawBoxAttributes(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& attr, const wxRect& boxRect, int flags)
+bool wxRichTextObject::DrawBoxAttributes(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& attr, const wxRect& boxRect, int flags, wxRichTextObject* obj)
{
// Assume boxRect is the area around the content
wxRect marginRect = boxRect;
if (flags & wxRICHTEXT_DRAW_GUIDELINES)
{
- wxRichTextAttr editBorderAttr = attr;
+ wxRichTextAttr editBorderAttr;
// TODO: make guideline colour configurable
editBorderAttr.GetTextBoxAttr().GetBorder().SetColour(*wxLIGHT_GREY);
editBorderAttr.GetTextBoxAttr().GetBorder().SetWidth(1, wxTEXT_ATTR_UNITS_PIXELS);
editBorderAttr.GetTextBoxAttr().GetBorder().SetStyle(wxTEXT_BOX_ATTR_BORDER_SOLID);
+ if (obj)
+ {
+ wxRichTextCell* cell = wxDynamicCast(obj, wxRichTextCell);
+ if (cell)
+ {
+ // This ensures that thin lines drawn by adjacent cells (left and above)
+ // don't get overwritten by the guidelines.
+ editBorderAttr.GetTextBoxAttr().GetBorder().GetLeft().Reset();
+ editBorderAttr.GetTextBoxAttr().GetBorder().GetTop().Reset();
+ }
+ }
+
DrawBorder(dc, buffer, editBorderAttr.GetTextBoxAttr().GetBorder(), borderRect, flags);
}
wxBrush brush(col);
dc.SetPen(pen);
dc.SetBrush(brush);
- dc.DrawRectangle(rect.x + rect.width - borderRight, rect.y, borderRight, rect.height);
+ dc.DrawRectangle(rect.x + rect.width - borderRight, rect.y, borderRight, rect.height + 1);
}
}
if (attr.GetBottom().IsValid() && attr.GetBottom().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
{
borderBottom = converter.GetPixels(attr.GetBottom().GetWidth());
- wxColour col(attr.GetTop().GetColour());
+ wxColour col(attr.GetBottom().GetColour());
// If pen width is > 1, resorts to a solid rectangle.
if (borderBottom == 1)
wxBrush brush(col);
dc.SetPen(pen);
dc.SetBrush(brush);
- dc.DrawRectangle(rect.x, rect.y + rect.height - borderBottom, rect.width, borderBottom);
+ dc.DrawRectangle(rect.x, rect.y + rect.height - borderBottom + 1, rect.width, borderBottom);
}
}
wxRichTextBuffer* wxRichTextObject::GetBuffer() const
{
const wxRichTextObject* obj = this;
- while (obj && !obj->IsKindOf(CLASSINFO(wxRichTextBuffer)))
+ while (obj && !wxDynamicCast(obj, wxRichTextBuffer))
obj = obj->GetParent();
return wxDynamicCast(obj, wxRichTextBuffer);
}
// If a paragraph, align the whole paragraph.
// Problem with this: if we're limited by a floating object, a line may be centered
// w.r.t. the smaller resulting box rather than the actual available width.
- if (attr.HasAlignment() && !GetContainer()->GetFloatCollector()->HasFloats()) // FIXME: aligning whole paragraph not compatible with floating objects
+ // FIXME: aligning whole paragraph not compatible with floating objects
+ if (attr.HasAlignment() && (!wxRichTextBuffer::GetFloatingLayoutMode() || (GetContainer()->GetFloatCollector() && !GetContainer()->GetFloatCollector()->HasFloats())))
{
// centering, right-justification
if (attr.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE)
}
/// 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();
while (node)
{
wxRichTextCompositeObject* composite = wxDynamicCast(child, wxRichTextCompositeObject);
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 (child->CanMerge(nextChild) && child->Merge(nextChild))
+ if (node->GetNext())
{
- nextChild->Dereference();
- m_children.Erase(node->GetNext());
-
- // Don't set node -- we'll see if we can merge again with the next
- // child.
+ wxRichTextObject* nextChild = node->GetNext()->GetData();
+ if (child->CanMerge(nextChild, context) && child->Merge(nextChild, context))
+ {
+ nextChild->Dereference();
+ m_children.Erase(node->GetNext());
+ }
+ else
+ node = node->GetNext();
}
else
node = node->GetNext();
}
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();
+
+ // 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
node = node->GetNext();
/// Get/set the object size for the given range. Returns false if the range
/// is invalid for this object.
-bool wxRichTextCompositeObject::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* partialExtents) const
+bool wxRichTextCompositeObject::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, const wxPoint& position, const wxSize& parentSize, wxArrayInt* partialExtents) const
{
if (!range.IsWithin(GetRange()))
return false;
if (!child->GetRange().IsOutside(range))
{
// Floating objects have a zero size within the paragraph.
- if (child->IsFloating())
+ if (child->IsFloating() && wxRichTextBuffer::GetFloatingLayoutMode())
{
if (partialExtents)
{
sz.x += childSize.x;
descent = wxMax(descent, childDescent);
}
- else if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y), p))
+ else if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y), parentSize, p))
{
sz.y = wxMax(sz.y, childSize.y);
sz.x += childSize.x;
}
else if (child->IsTopLevel())
{
- if (invalidRange == wxRICHTEXT_NONE)
- child->Invalidate(wxRICHTEXT_NONE);
+ if (wxRichTextBuffer::GetFloatingLayoutMode() && child->IsFloating() && GetBuffer()->GetFloatCollector() && GetBuffer()->GetFloatCollector()->HasFloat(child))
+ {
+ // Don't invalidate subhierarchy if we've already been laid out
+ }
else
- child->Invalidate(wxRICHTEXT_ALL); // All children must be invalidated if within parent range
+ {
+ if (invalidRange == wxRICHTEXT_NONE)
+ child->Invalidate(wxRICHTEXT_NONE);
+ else
+ child->Invalidate(wxRICHTEXT_ALL); // All children must be invalidated if within parent range
+ }
}
else
child->Invalidate(invalidRange);
m_invalidRange = wxRICHTEXT_ALL;
- SetMargins(4);
m_partialParagraph = false;
m_floatCollector = NULL;
}
return wxRICHTEXT_HITTEST_NONE;
int ret = wxRICHTEXT_HITTEST_NONE;
- if (m_floatCollector && (flags & wxRICHTEXT_HITTEST_NO_FLOATING_OBJECTS) == 0)
+ if (wxRichTextBuffer::GetFloatingLayoutMode() && m_floatCollector && (flags & wxRICHTEXT_HITTEST_NO_FLOATING_OBJECTS) == 0)
ret = m_floatCollector->HitTest(dc, context, pt, textPosition, obj, flags);
if (ret == wxRICHTEXT_HITTEST_NONE)
/// Draw the floating objects
void wxRichTextParagraphLayoutBox::DrawFloats(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
{
- if (m_floatCollector)
+ if (wxRichTextBuffer::GetFloatingLayoutMode() && m_floatCollector)
m_floatCollector->Draw(dc, context, range, selection, rect, descent, style);
}
context.ApplyVirtualAttributes(attr, this);
int flags = style;
- if (selection.IsValid() && GetParentContainer() != this && selection.WithinSelection(GetRange().GetStart(), GetParentContainer()))
+ if (selection.IsValid() && GetParentContainer() != this && selection.GetContainer() == this && selection.WithinSelection(GetRange().GetStart(), GetParentContainer()))
flags |= wxRICHTEXT_DRAW_SELECTED;
// Don't draw guidelines if at top level
int theseFlags = flags;
if (!GetParent())
theseFlags &= ~wxRICHTEXT_DRAW_GUIDELINES;
- DrawBoxAttributes(dc, GetBuffer(), attr, thisRect, theseFlags);
+ DrawBoxAttributes(dc, GetBuffer(), attr, thisRect, theseFlags, this);
- DrawFloats(dc, context, range, selection, rect, descent, style);
+ if (wxRichTextBuffer::GetFloatingLayoutMode())
+ DrawFloats(dc, context, range, selection, rect, descent, style);
+
wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
while (node)
{
availableSpace = GetAvailableContentArea(dc, context, rect);
}
+ // Fix the width if we're at the top level
+ if (!GetParent())
+ attr.GetTextBoxAttr().GetWidth().SetValue(rect.GetWidth(), wxTEXT_ATTR_UNITS_PIXELS);
+
int leftMargin, rightMargin, topMargin, bottomMargin;
wxRichTextObject::GetTotalMargin(dc, GetBuffer(), attr, leftMargin, rightMargin,
topMargin, bottomMargin);
// Gather information about only those floating objects that will not be formatted,
// after which floats will be gathered per-paragraph during layout.
- UpdateFloatingObjects(availableSpace, node ? node->GetData() : (wxRichTextObject*) NULL);
+ if (wxRichTextBuffer::GetFloatingLayoutMode())
+ UpdateFloatingObjects(availableSpace, node ? node->GetData() : (wxRichTextObject*) NULL);
// A way to force speedy rest-of-buffer layout (the 'else' below)
bool forceQuickLayout = false;
else
maxHeight = 0; // topMargin + bottomMargin;
+ // Check the bottom edge of any floating object
+ if (wxRichTextBuffer::GetFloatingLayoutMode() && GetFloatCollector() && GetFloatCollector()->HasFloats())
+ {
+ int bottom = GetFloatCollector()->GetLastRectBottom();
+ if (bottom > maxHeight)
+ maxHeight = bottom;
+ }
+
if (attr.GetTextBoxAttr().GetSize().GetWidth().IsValid())
{
wxRect r = AdjustAvailableSpace(dc, GetBuffer(), wxRichTextAttr() /* not used */, attr, parentRect, parentRect);
maxWidth = wxMax(maxWidth, w);
maxMaxWidth = wxMax(maxMaxWidth, w);
}
+ else
+ {
+ // TODO: Make sure the layout box's position reflects
+ // the position of the children, but without
+ // breaking layout of a box within a paragraph.
+ }
// TODO: (also in para layout) should set the
// object's size to an absolute one if specified,
}
/// Get/set the size for the given range.
-bool wxRichTextParagraphLayoutBox::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* WXUNUSED(partialExtents)) const
+bool wxRichTextParagraphLayoutBox::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, const wxPoint& position, const wxSize& parentSize, wxArrayInt* WXUNUSED(partialExtents)) const
{
wxSize sz;
wxSize childSize;
int childDescent = 0;
- child->GetRangeSize(rangeToFind, childSize, childDescent, dc, context, flags, position);
+ child->GetRangeSize(rangeToFind, childSize, childDescent, dc, context, flags, position, parentSize);
descent = wxMax(childDescent, descent);
wxRichTextAttr* cStyle = & defaultCharStyle;
wxRichTextParagraph* para = new wxRichTextParagraph(text, this, pStyle, cStyle);
+ para->GetAttributes().GetTextBoxAttr().Reset();
AppendChild(para);
size_t len = text.length();
wxString line;
wxRichTextParagraph* para = new wxRichTextParagraph(wxEmptyString, this, pStyle, cStyle);
+ para->GetAttributes().GetTextBoxAttr().Reset();
AppendChild(para);
plainText->SetText(line);
para = new wxRichTextParagraph(wxEmptyString, this, pStyle, cStyle);
+ para->GetAttributes().GetTextBoxAttr().Reset();
AppendChild(para);
wxRichTextAttr* cStyle = & defaultCharStyle;
wxRichTextParagraph* para = new wxRichTextParagraph(this, pStyle);
+ para->GetAttributes().GetTextBoxAttr().Reset();
AppendChild(para);
para->AppendChild(new wxRichTextImage(image, this, cStyle));
wxRichTextRemoveStyle(child->GetAttributes(), style);
}
else if (resetExistingStyle)
+ {
+ // Preserve the URL as it's not really a formatting style but a property of the object
+ wxString url;
+ if (child->GetAttributes().HasURL() && !characterAttributes.HasURL())
+ url = child->GetAttributes().GetURL();
+
child->GetAttributes() = characterAttributes;
+
+ if (!url.IsEmpty())
+ child->GetAttributes().SetURL(url);
+ }
else
{
if (applyMinimal)
{
// Start with the base style
style = GetAttributes();
+ style.GetTextBoxAttr().Reset();
// Apply the paragraph style
wxRichTextApplyStyle(style, obj->GetAttributes());
{
style = wxRichTextAttr();
- wxRichTextAttr clashingAttr;
+ wxRichTextAttr clashingAttrPara, clashingAttrChar;
wxRichTextAttr absentAttrPara, absentAttrChar;
wxRichTextObjectList::compatibility_iterator node = GetChildren().GetFirst();
{
wxRichTextAttr paraStyle = para->GetCombinedAttributes(true /* use box attributes */);
- CollectStyle(style, paraStyle, clashingAttr, absentAttrPara);
+ CollectStyle(style, paraStyle, clashingAttrPara, absentAttrPara);
}
else
{
// First collect paragraph attributes only
wxRichTextAttr paraStyle = para->GetCombinedAttributes();
paraStyle.SetFlags(paraStyle.GetFlags() & wxTEXT_ATTR_PARAGRAPH);
- CollectStyle(style, paraStyle, clashingAttr, absentAttrPara);
+ CollectStyle(style, paraStyle, clashingAttrPara, absentAttrPara);
wxRichTextObjectList::compatibility_iterator childNode = para->GetChildren().GetFirst();
// Now collect character attributes only
childStyle.SetFlags(childStyle.GetFlags() & wxTEXT_ATTR_CHARACTER);
- CollectStyle(style, childStyle, clashingAttr, absentAttrChar);
+ CollectStyle(style, childStyle, clashingAttrChar, absentAttrChar);
}
childNode = childNode->GetNext();
if (childRange.GetLength() == 0 && GetRange().GetLength() == 1)
childRange.SetEnd(childRange.GetEnd()+1);
- if (!childRange.IsOutside(range) && child->IsKindOf(CLASSINFO(wxRichTextPlainText)))
+ if (!childRange.IsOutside(range) && wxDynamicCast(child, wxRichTextPlainText))
{
foundCount ++;
wxRichTextAttr textAttr = para->GetCombinedAttributes(child->GetAttributes());
- if (wxTextAttrEqPartial(textAttr, style))
+ if (textAttr.EqPartial(style, false /* strong test - attributes must be valid in both objects */))
matchingCount ++;
}
wxRichTextApplyStyle(textAttr, para->GetAttributes());
foundCount ++;
- if (wxTextAttrEqPartial(textAttr, style))
+ if (textAttr.EqPartial(style, false /* strong test */))
matchingCount ++;
}
}
buffer->GetRichTextCtrl()->PrepareContent(container);
}
-
/// Set character or paragraph properties
bool wxRichTextParagraphLayoutBox::SetProperties(const wxRichTextRange& range, const wxRichTextProperties& properties, int flags)
{
wxRichTextBuffer* buffer = GetBuffer();
if (buffer && buffer->GetRichTextCtrl())
{
- wxRichTextEvent event(wxEVT_COMMAND_RICHTEXT_BUFFER_RESET, buffer->GetRichTextCtrl()->GetId());
+ wxRichTextEvent event(wxEVT_RICHTEXT_BUFFER_RESET, buffer->GetRichTextCtrl()->GetId());
event.SetEventObject(buffer->GetRichTextCtrl());
event.SetContainer(this);
wxRichTextParagraph* para1 = GetParagraphAtPosition(range.GetStart());
if (para1)
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;
}
wxRichTextApplyStyle(newPara->GetAttributes(), listStyle);
// Now we need to do numbering
- if (renumber)
+ // Preserve the existing list item continuation bullet style, if any
+ if (para->GetAttributes().HasBulletStyle() && (para->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION))
+ newPara->GetAttributes().SetBulletStyle(newPara->GetAttributes().GetBulletStyle()|wxTEXT_ATTR_BULLET_STYLE_CONTINUATION);
+ else
{
- newPara->GetAttributes().SetBulletNumber(n);
- }
+ if (renumber)
+ {
+ newPara->GetAttributes().SetBulletNumber(n);
+ }
- n ++;
+ n ++;
+ }
}
else if (!newPara->GetAttributes().GetListStyleName().IsEmpty())
{
newPara->GetAttributes().SetListStyleName(wxEmptyString);
newPara->GetAttributes().SetLeftIndent(0, 0);
newPara->GetAttributes().SetBulletText(wxEmptyString);
+ newPara->GetAttributes().SetBulletStyle(0);
// Eliminate the main list-related attributes
newPara->GetAttributes().SetFlags(newPara->GetAttributes().GetFlags() & ~wxTEXT_ATTR_LEFT_INDENT & ~wxTEXT_ATTR_BULLET_STYLE & ~wxTEXT_ATTR_BULLET_NUMBER & ~wxTEXT_ATTR_BULLET_TEXT & wxTEXT_ATTR_LIST_STYLE_NAME);
wxRichTextAttr listStyle(defToUse->GetCombinedStyleForLevel(thisLevel, styleSheet));
wxRichTextApplyStyle(newPara->GetAttributes(), listStyle);
+ // Preserve the existing list item continuation bullet style, if any
+ if (para->GetAttributes().HasBulletStyle() && (para->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION))
+ newPara->GetAttributes().SetBulletStyle(newPara->GetAttributes().GetBulletStyle()|wxTEXT_ATTR_BULLET_STYLE_CONTINUATION);
+
// OK, we've (re)applied the style, now let's get the numbering right.
if (currentLevel == -1)
}
else
{
- levels[currentLevel] ++;
+ if (!(para->GetAttributes().HasBulletStyle() && (para->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION)))
+ levels[currentLevel] ++;
}
newPara->GetAttributes().SetBulletNumber(levels[currentLevel]);
/// position of the paragraph that it had to start looking from.
bool wxRichTextParagraphLayoutBox::FindNextParagraphNumber(wxRichTextParagraph* previousParagraph, wxRichTextAttr& attr) const
{
- if (!previousParagraph->GetAttributes().HasFlag(wxTEXT_ATTR_BULLET_STYLE) || previousParagraph->GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE)
+ // TODO: add GetNextChild/GetPreviousChild to composite
+ // Search for a paragraph that isn't a continuation paragraph (no bullet)
+ while (previousParagraph && previousParagraph->GetAttributes().HasBulletStyle() && previousParagraph->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION)
+ {
+ wxRichTextObjectList::compatibility_iterator node = ((wxRichTextCompositeObject*) previousParagraph->GetParent())->GetChildren().Find(previousParagraph);
+ if (node)
+ {
+ node = node->GetPrevious();
+ if (node)
+ previousParagraph = wxDynamicCast(node->GetData(), wxRichTextParagraph);
+ else
+ previousParagraph = NULL;
+ }
+ else
+ previousParagraph = NULL;
+ }
+
+ if (!previousParagraph || !previousParagraph->GetAttributes().HasFlag(wxTEXT_ATTR_BULLET_STYLE) || previousParagraph->GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE)
return false;
wxRichTextBuffer* buffer = GetBuffer();
DrawBoxAttributes(dc, GetBuffer(), attr, paraRect);
// Draw the bullet, if any
- if (attr.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE)
+ if ((attr.GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE) == 0 && (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION) == 0)
{
if (attr.GetLeftSubIndent() != 0)
{
{
wxRichTextObject* child = node2->GetData();
- if (!child->IsFloating() && child->GetRange().GetLength() > 0 && !child->GetRange().IsOutside(lineRange) && !lineRange.IsOutside(range))
+ if ((!child->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && child->GetRange().GetLength() > 0 && !child->GetRange().IsOutside(lineRange) && !lineRange.IsOutside(range))
{
// Draw this part of the line at the correct position
wxRichTextRange objectRange(child->GetRange());
// Deal with floating objects firstly before the normal layout
wxRichTextBuffer* buffer = GetBuffer();
wxASSERT(buffer);
+
wxRichTextFloatCollector* collector = GetContainer()->GetFloatCollector();
- wxASSERT(collector);
- LayoutFloat(dc, context, rect, style, collector);
+
+ if (wxRichTextBuffer::GetFloatingLayoutMode())
+ {
+ wxASSERT(collector != NULL);
+ if (collector)
+ LayoutFloat(dc, context, rect, parentRect, style, collector);
+ }
wxRichTextAttr attr = GetCombinedAttributes();
context.ApplyVirtualAttributes(attr, this);
int lineSpacing = 0;
// Let's assume line spacing of 10 is normal, 15 is 1.5, 20 is 2, etc.
- if (attr.HasLineSpacing() && attr.GetLineSpacing() > 0 && attr.GetFont().IsOk())
+ if (attr.HasLineSpacing() && attr.GetLineSpacing() > 0 && attr.HasFont())
{
- wxCheckSetFont(dc, attr.GetFont());
- lineSpacing = (int) (double(dc.GetCharHeight()) * (double(attr.GetLineSpacing())/10.0 - 1.0));
+ wxFont font(buffer->GetFontTable().FindFont(attr));
+ if (font.IsOk())
+ {
+ wxCheckSetFont(dc, font);
+ lineSpacing = (int) (double(dc.GetCharHeight()) * (double(attr.GetLineSpacing())/10.0 - 1.0));
+ }
}
// Start position for each line relative to the paragraph
int paraDescent = 0;
// This calculates the partial text extents
- GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, rect.GetPosition(), & partialExtents);
+ GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, rect.GetPosition(), parentRect.GetSize(), & partialExtents);
#else
node = m_children.GetFirst();
while (node)
node = node->GetNext();
}
-
#endif
// Split up lines
// If floating, ignore. We already laid out floats.
// Also ignore if empty object, except if we haven't got any
// size yet.
- if (child->IsFloating() || !child->IsShown() ||
- (child->GetRange().GetLength() == 0 && maxHeight > spaceBeforePara)
+ if ((child->IsFloating() && wxRichTextBuffer::GetFloatingLayoutMode())
+ || !child->IsShown() || (child->GetRange().GetLength() == 0 && maxHeight > spaceBeforePara)
)
{
node = node->GetNext();
partialExtents.Clear();
// Recalculate the partial text extents since the child object changed size
- GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, wxPoint(0,0), & partialExtents);
+ GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, wxPoint(0,0), parentRect.GetSize(), & partialExtents);
}
}
{
#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
// Get height only, then the width using the partial extents
- GetRangeSize(wxRichTextRange(lastEndPos+1, lastPosToUse), childSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_HEIGHT_ONLY);
+ GetRangeSize(wxRichTextRange(lastEndPos+1, lastPosToUse), childSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_HEIGHT_ONLY, wxPoint(0,0), parentRect.GetSize());
childSize.x = wxRichTextGetRangeWidth(*this, wxRichTextRange(lastEndPos+1, lastPosToUse), partialExtents);
#else
- GetRangeSize(wxRichTextRange(lastEndPos+1, lastPosToUse), childSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED, rect.GetPosition());
+ GetRangeSize(wxRichTextRange(lastEndPos+1, lastPosToUse), childSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED, rect.GetPosition(), parentRect.GetSize());
#endif
}
wxRect oldAvailableRect = availableRect;
// Available width depends on the floating objects and the line height.
- // Note: the floating objects may be placed vertically along the two side of
+ // Note: the floating objects may be placed vertically along the two sides of
// buffer, so we may have different available line widths with different
// [startY, endY]. So, we can't determine how wide the available
// space is until we know the exact line height.
- lineDescent = wxMax(childDescent, maxDescent);
- lineAscent = wxMax(childSize.y-childDescent, maxAscent);
- lineHeight = lineDescent + lineAscent;
- wxRect floatAvailableRect = collector->GetAvailableRect(rect.y + currentPosition.y, rect.y + currentPosition.y + lineHeight);
-
- // Adjust availableRect to the space that is available when taking floating objects into account.
-
- if (floatAvailableRect.x + startOffset > availableRect.x)
+ if (childDescent == 0)
+ {
+ lineHeight = wxMax(lineHeight, childSize.y);
+ lineDescent = maxDescent;
+ lineAscent = maxAscent;
+ }
+ else
{
- int newX = floatAvailableRect.x + startOffset;
- int newW = availableRect.width - (newX - availableRect.x);
- availableRect.x = newX;
- availableRect.width = newW;
+ lineDescent = wxMax(childDescent, maxDescent);
+ lineAscent = wxMax(childSize.y-childDescent, maxAscent);
}
+ lineHeight = wxMax(lineHeight, (lineDescent + lineAscent));
+
+ if (wxRichTextBuffer::GetFloatingLayoutMode() && collector)
+ {
+ wxRect floatAvailableRect = collector->GetAvailableRect(rect.y + currentPosition.y, rect.y + currentPosition.y + lineHeight);
+
+ // Adjust availableRect to the space that is available when taking floating objects into account.
- if (floatAvailableRect.width < availableRect.width)
- availableRect.width = floatAvailableRect.width;
+ if (floatAvailableRect.x + startOffset > availableRect.x)
+ {
+ int newX = floatAvailableRect.x + startOffset;
+ int newW = availableRect.width - (newX - availableRect.x);
+ availableRect.x = newX;
+ availableRect.width = newW;
+ }
+
+ if (floatAvailableRect.width < availableRect.width)
+ availableRect.width = floatAvailableRect.width;
+ }
currentPosition.x = availableRect.x - rect.x;
{
wxSize oldSize = child->GetCachedSize();
- //child->SetCachedSize(wxDefaultSize);
// Lays out the object first with a given amount of space, and then if no width was specified in attr,
// lays out the object again using the minimum size
child->Invalidate(wxRICHTEXT_ALL);
child->LayoutToBestSize(dc, context, buffer,
- attr, child->GetAttributes(), availableRect, parentRect, style);
+ attr, child->GetAttributes(), availableRect, parentRect.GetSize(), style);
childSize = child->GetCachedSize();
childDescent = child->GetDescent();
- //child->SetPosition(availableRect.GetPosition());
if (oldSize != child->GetCachedSize())
{
partialExtents.Clear();
// Recalculate the partial text extents since the child object changed size
- GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, wxPoint(0,0), & partialExtents);
+ GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, wxPoint(0,0), parentRect.GetSize(), & partialExtents);
}
// Go around the loop finding the available rect for the given floating objects
}
while (doLoop);
+ if (child->IsTopLevel())
+ {
+ // We can move it to the correct position at this point
+ // TODO: probably need to add margin
+ child->Move(GetPosition() + wxPoint(currentWidth + (wxMax(leftIndent, leftIndent + leftSubIndent)), currentPosition.y));
+ }
+
// Cases:
// 1) There was a line break BEFORE the natural break
// 2) There was a line break AFTER the natural break
if ((lineBreakInThisObject && (childSize.x + currentWidth <= availableRect.width))
||
(childSize.x + currentWidth > availableRect.width)
+#if 0
||
((childSize.x + currentWidth <= availableRect.width) && !node->GetNext())
-
+#endif
)
{
- if (child->IsTopLevel())
- {
- // We can move it to the correct position at this point
- child->Move(GetPosition() + wxPoint(currentWidth, currentPosition.y));
- }
-
long wrapPosition = 0;
if ((childSize.x + currentWidth <= availableRect.width) && !node->GetNext() && !lineBreakInThisObject)
wrapPosition = child->GetRange().GetEnd();
// Line end position shouldn't be the same as the end, or greater.
if (wrapPosition >= GetRange().GetEnd())
- wrapPosition = GetRange().GetEnd()-1;
+ wrapPosition = wxMax(0, GetRange().GetEnd()-1);
// wxLogDebug(wxT("Split at %ld"), wrapPosition);
wxSize actualSize;
wxRichTextRange actualRange(lastCompletedEndPos+1, wrapPosition);
- /// Use previous descent, not the wrapping descent we just found, since this may be too big
- /// for the fragment we're about to add.
- childDescent = maxDescent;
+ childDescent = 0;
#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
if (!child->IsEmpty())
{
// Get height only, then the width using the partial extents
- GetRangeSize(actualRange, actualSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_HEIGHT_ONLY);
+ GetRangeSize(actualRange, actualSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_HEIGHT_ONLY, wxPoint(0,0), parentRect.GetSize());
actualSize.x = wxRichTextGetRangeWidth(*this, actualRange, partialExtents);
}
else
#endif
- GetRangeSize(actualRange, actualSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED);
+ GetRangeSize(actualRange, actualSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED, wxPoint(0,0), parentRect.GetSize());
currentWidth = actualSize.x;
- maxDescent = wxMax(childDescent, maxDescent);
- maxAscent = wxMax(actualSize.y-childDescent, maxAscent);
- lineHeight = maxDescent + maxAscent;
+
+ // The descent for the whole line at this point, is the correct max descent
+ maxDescent = childDescent;
+ // Maximum ascent
+ maxAscent = actualSize.y-childDescent;
+
+ // lineHeight is given by the height for the whole line, since it will
+ // take into account ascend/descend.
+ lineHeight = actualSize.y;
if (lineHeight == 0 && buffer)
{
lineCount ++;
- // TODO: account for zero-length objects, such as fields
+ // TODO: account for zero-length objects
// wxASSERT(wrapPosition > lastCompletedEndPos);
lastEndPos = wrapPosition;
{
// We still fit, so don't add a line, and keep going
currentWidth += childSize.x;
- maxDescent = wxMax(childDescent, maxDescent);
- maxAscent = wxMax(childSize.y-childDescent, maxAscent);
- lineHeight = maxDescent + maxAscent;
+
+ if (childDescent == 0)
+ {
+ // An object with a zero descend value wants to take up the whole
+ // height regardless of baseline
+ lineHeight = wxMax(lineHeight, childSize.y);
+ }
+ else
+ {
+ maxDescent = wxMax(childDescent, maxDescent);
+ maxAscent = wxMax(childSize.y-childDescent, maxAscent);
+ }
+
+ lineHeight = wxMax(lineHeight, (maxDescent + maxAscent));
maxWidth = wxMax(maxWidth, currentWidth+startOffset);
lastEndPos = child->GetRange().GetEnd();
//wxASSERT(!(lastCompletedEndPos != -1 && lastCompletedEndPos < GetRange().GetEnd()-1));
+ // Add the last line - it's the current pos -> last para pos
+ // Substract -1 because the last position is always the end-paragraph position.
+ if (lastCompletedEndPos <= GetRange().GetEnd()-1)
+ {
+ currentPosition.x = (lineCount == 0 ? startPositionFirstLine : startPositionSubsequentLines);
+
+ wxRichTextLine* line = AllocateLine(lineCount);
+
+ wxRichTextRange actualRange(lastCompletedEndPos+1, GetRange().GetEnd()-1);
+
+ // Set relative range so we won't have to change line ranges when paragraphs are moved
+ line->SetRange(wxRichTextRange(actualRange.GetStart() - GetRange().GetStart(), actualRange.GetEnd() - GetRange().GetStart()));
+
+ line->SetPosition(currentPosition);
+
+ if (lineHeight == 0 && buffer)
+ {
+ wxFont font(buffer->GetFontTable().FindFont(attr));
+ wxCheckSetFont(dc, font);
+ lineHeight = dc.GetCharHeight();
+ }
+
+ if (maxDescent == 0)
+ {
+ int w, h;
+ dc.GetTextExtent(wxT("X"), & w, &h, & maxDescent);
+ }
+
+ line->SetSize(wxSize(currentWidth, lineHeight));
+ line->SetDescent(maxDescent);
+ currentPosition.y += lineHeight;
+ currentPosition.y += lineSpacing;
+ lineCount ++;
+
+ // Apply paragraph styles such as alignment to the wrapped line
+ ApplyParagraphStyle(line, attr, availableRect, dc);
+ }
+
// Remove remaining unused line objects, if any
ClearUnusedLines(lineCount);
// If floating, ignore. We already laid out floats.
// Also ignore if empty object, except if we haven't got any
// size yet.
- if (!child->IsFloating() && child->GetRange().GetLength() != 0 && !child->IsKindOf(CLASSINFO(wxRichTextPlainText)))
+ if ((!child->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && child->GetRange().GetLength() != 0 && !wxDynamicCast(child, wxRichTextPlainText))
{
if (child->GetCachedSize().x > minWidth)
minWidth = child->GetMinSize().x;
SetMinSize(marginRect.GetSize());
}
-
#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
#if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
// Use the text extents to calculate the size of each fragment in each line
return;
wxPoint pos = line->GetPosition();
+ wxPoint originalPos = pos;
wxSize size = line->GetSize();
// centering, right-justification
pos.x = pos.x + rect.GetWidth() - size.x - rightIndent;
line->SetPosition(pos);
}
+
+ if (pos != originalPos)
+ {
+ wxPoint inc = pos - originalPos;
+
+ wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
+
+ while (node)
+ {
+ wxRichTextObject* child = node->GetData();
+ if (child->IsTopLevel() && !child->GetRange().IsOutside(line->GetAbsoluteRange()))
+ child->Move(child->GetPosition() + inc);
+
+ node = node->GetNext();
+ }
+ }
}
/// Insert text at the given position
/// Get/set the object size for the given range. Returns false if the range
/// is invalid for this object.
-bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* partialExtents) const
+bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, const wxPoint& position, const wxSize& parentSize, wxArrayInt* partialExtents) const
{
if (!range.IsWithin(GetRange()))
return false;
if (flags & wxRICHTEXT_UNFORMATTED)
{
// Just use unformatted data, assume no line breaks
- // TODO: take into account line breaks
-
wxSize sz;
wxArrayInt childExtents;
else
p = NULL;
+ int maxDescent = 0;
+ int maxAscent = 0;
+ int maxLineHeight = 0;
+
wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
while (node)
{
-
wxRichTextObject* child = node->GetData();
if (!child->GetRange().IsOutside(range))
{
// Floating objects have a zero size within the paragraph.
- if (child->IsFloating())
+ if (child->IsFloating() && wxRichTextBuffer::GetFloatingLayoutMode())
{
if (partialExtents)
{
wxRichTextRange rangeToUse = range;
rangeToUse.LimitTo(child->GetRange());
-#if 0
- if (child->IsTopLevel())
- rangeToUse = child->GetOwnRange();
-#endif
int childDescent = 0;
- // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we're already cached the size,
+ // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we've already cached the size,
// but it's only going to be used after caching has taken place.
if ((flags & wxRICHTEXT_HEIGHT_ONLY) && child->GetCachedSize().y != 0)
{
childDescent = child->GetDescent();
childSize = child->GetCachedSize();
- sz.y = wxMax(sz.y, childSize.y);
+ if (childDescent == 0)
+ {
+ maxLineHeight = wxMax(maxLineHeight, childSize.y);
+ }
+ else
+ {
+ maxDescent = wxMax(maxDescent, childDescent);
+ maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
+ }
+
+ maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
+
+ sz.y = wxMax(sz.y, maxLineHeight);
sz.x += childSize.x;
- descent = wxMax(descent, childDescent);
+ descent = maxDescent;
}
else if (child->IsTopLevel())
{
childDescent = child->GetDescent();
childSize = child->GetCachedSize();
- sz.y = wxMax(sz.y, childSize.y);
+ if (childDescent == 0)
+ {
+ maxLineHeight = wxMax(maxLineHeight, childSize.y);
+ }
+ else
+ {
+ maxDescent = wxMax(maxDescent, childDescent);
+ maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
+ }
+
+ maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
+
+ sz.y = wxMax(sz.y, maxLineHeight);
sz.x += childSize.x;
- descent = wxMax(descent, childDescent);
+ descent = maxDescent;
+
+ // FIXME: this won't change the original values.
+ // Should we be calling GetRangeSize above instead of using cached values?
+#if 0
if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange()))
{
child->SetCachedSize(childSize);
child->SetDescent(childDescent);
}
+#endif
if (partialExtents)
{
partialExtents->Add(childSize.x + lastSize);
}
}
- else if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y), p))
+ else if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y), parentSize, p))
{
- sz.y = wxMax(sz.y, childSize.y);
+ if (childDescent == 0)
+ {
+ maxLineHeight = wxMax(maxLineHeight, childSize.y);
+ }
+ else
+ {
+ maxDescent = wxMax(maxDescent, childDescent);
+ maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
+ }
+
+ maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
+
+ sz.y = wxMax(sz.y, maxLineHeight);
sz.x += childSize.x;
- descent = wxMax(descent, childDescent);
+ descent = maxDescent;
if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange()))
{
wxRichTextRange lineRange = line->GetAbsoluteRange();
if (!lineRange.IsOutside(range))
{
- wxSize lineSize;
+ int maxDescent = 0;
+ int maxAscent = 0;
+ int maxLineHeight = 0;
+ int maxLineWidth = 0;
wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
while (node2)
{
wxRichTextObject* child = node2->GetData();
- if (!child->IsFloating() && !child->GetRange().IsOutside(lineRange))
+ if ((!child->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && !child->GetRange().IsOutside(lineRange))
{
wxRichTextRange rangeToUse = lineRange;
rangeToUse.LimitTo(child->GetRange());
wxSize childSize;
int childDescent = 0;
- if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y)))
+ if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y), parentSize))
{
- lineSize.y = wxMax(lineSize.y, childSize.y);
- lineSize.x += childSize.x;
+ if (childDescent == 0)
+ {
+ // Assume that if descent is zero, this child can occupy the full line height
+ // and does not need space for the line's maximum descent. So we influence
+ // the overall max line height only.
+ maxLineHeight = wxMax(maxLineHeight, childSize.y);
+ }
+ else
+ {
+ maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
+ maxDescent = wxMax(maxAscent, childDescent);
+ }
+ maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
+ maxLineWidth += childSize.x;
}
- descent = wxMax(descent, childDescent);
}
node2 = node2->GetNext();
}
+ descent = wxMax(descent, maxDescent);
+
// Increase size by a line (TODO: paragraph spacing)
- sz.y += lineSize.y;
- sz.x = wxMax(sz.x, lineSize.x);
+ sz.y += maxLineHeight;
+ sz.x = wxMax(sz.x, maxLineWidth);
}
node = node->GetNext();
}
while (objNode)
{
wxRichTextObject* child = objNode->GetData();
- if (child->IsTopLevel() && ((flags & wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS) == 0))
+ // Don't recurse if we have wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS,
+ // and also, if this seems composite but actually is marked as atomic,
+ // don't recurse.
+ if (child->IsTopLevel() && ((flags & wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS) == 0) &&
+ (! (((flags & wxRICHTEXT_HITTEST_HONOUR_ATOMIC) != 0) && child->IsAtomic())))
{
{
int hitTest = child->HitTest(dc, context, pt, textPosition, obj, contextObj);
int paraDescent;
// This calculates the partial text extents
- GetRangeSize(lineRange, paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED, linePos, & partialExtents);
+ GetRangeSize(lineRange, paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED, linePos, wxDefaultSize, & partialExtents);
int lastX = linePos.x;
size_t i;
sm_defaultTabs.Clear();
}
-void wxRichTextParagraph::LayoutFloat(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, int style, wxRichTextFloatCollector* floatCollector)
+void wxRichTextParagraph::LayoutFloat(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style, wxRichTextFloatCollector* floatCollector)
{
wxRichTextObjectList::compatibility_iterator node = GetChildren().GetFirst();
while (node)
wxRichTextObject* anchored = node->GetData();
if (anchored && anchored->IsFloating() && !floatCollector->HasFloat(anchored))
{
+ int x = 0;
+ wxRichTextAttr parentAttr(GetAttributes());
+ context.ApplyVirtualAttributes(parentAttr, this);
+#if 1
+ // 27-09-2012
+ wxRect availableSpace = GetParent()->GetAvailableContentArea(dc, context, rect);
+
+ anchored->LayoutToBestSize(dc, context, GetBuffer(),
+ parentAttr, anchored->GetAttributes(),
+ parentRect, availableSpace,
+ style);
+ wxSize size = anchored->GetCachedSize();
+#else
wxSize size;
- int descent, x = 0;
+ int descent = 0;
anchored->GetRangeSize(anchored->GetRange(), size, descent, dc, context, style);
+#endif
int offsetY = 0;
if (anchored->GetAttributes().GetTextBoxAttr().GetTop().IsValid())
else if (anchored->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_RIGHT)
x = rect.x + rect.width - size.x;
- anchored->SetPosition(wxPoint(x, pos));
+ //anchored->SetPosition(wxPoint(x, pos));
+ anchored->Move(wxPoint(x, pos)); // should move children
anchored->SetCachedSize(size);
floatCollector->CollectFloat(this, anchored);
}
int offset = GetRange().GetStart();
- // Replace line break characters with spaces
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;
str.Replace(toRemove, wxT(" "));
- if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS))
+ if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & (wxTEXT_ATTR_EFFECT_CAPITALS|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS)))
str.MakeUpper();
long len = range.GetLength();
int x, y;
if ( textFont.IsOk() )
{
+ if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SMALL_CAPITALS))
+ {
+ textFont.SetPointSize((int) (textFont.GetPointSize()*0.75));
+ wxCheckSetFont(dc, textFont);
+ charHeight = dc.GetCharHeight();
+ }
+
if ( textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT) )
{
- double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
- textFont.SetPointSize( static_cast<int>(size) );
- x = rect.x;
- y = rect.y;
+ if (textFont.IsUsingSizeInPixels())
+ {
+ double size = static_cast<double>(textFont.GetPixelSize().y) / wxSCRIPT_MUL_FACTOR;
+ textFont.SetPixelSize(wxSize(0, static_cast<int>(size)));
+ x = rect.x;
+ y = rect.y;
+ }
+ else
+ {
+ double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
+ textFont.SetPointSize(static_cast<int>(size));
+ x = rect.x;
+ y = rect.y;
+ }
wxCheckSetFont(dc, textFont);
}
else if ( textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT) )
{
- double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
- textFont.SetPointSize( static_cast<int>(size) );
- x = rect.x;
- int sub_height = static_cast<int>( static_cast<double>(charHeight) / wxSCRIPT_MUL_FACTOR);
- y = rect.y + (rect.height - sub_height + (descent - m_descent));
+ if (textFont.IsUsingSizeInPixels())
+ {
+ double size = static_cast<double>(textFont.GetPixelSize().y) / wxSCRIPT_MUL_FACTOR;
+ textFont.SetPixelSize(wxSize(0, static_cast<int>(size)));
+ x = rect.x;
+ int sub_height = static_cast<int>(static_cast<double>(charHeight) / wxSCRIPT_MUL_FACTOR);
+ y = rect.y + (rect.height - sub_height + (descent - m_descent));
+ }
+ else
+ {
+ double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
+ textFont.SetPointSize(static_cast<int>(size));
+ x = rect.x;
+ int sub_height = static_cast<int>(static_cast<double>(charHeight) / wxSCRIPT_MUL_FACTOR);
+ y = rect.y + (rect.height - sub_height + (descent - m_descent));
+ }
wxCheckSetFont(dc, textFont);
}
else
x += w;
}
- return true;
+ return true;
}
/// Lay the item out
/// Get/set the object size for the given range. Returns false if the range
/// is invalid for this object.
-bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int WXUNUSED(flags), wxPoint position, wxArrayInt* partialExtents) const
+bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int WXUNUSED(flags), const wxPoint& position, const wxSize& WXUNUSED(parentSize), wxArrayInt* partialExtents) const
{
if (!range.IsWithin(GetRange()))
return false;
|| (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT) ) )
{
wxFont textFont = font;
- double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
- textFont.SetPointSize( static_cast<int>(size) );
+ if (textFont.IsUsingSizeInPixels())
+ {
+ double size = static_cast<double>(textFont.GetPixelSize().y) / wxSCRIPT_MUL_FACTOR;
+ textFont.SetPixelSize(wxSize(0, static_cast<int>(size)));
+ }
+ else
+ {
+ double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
+ textFont.SetPointSize(static_cast<int>(size));
+ }
+ wxCheckSetFont(dc, textFont);
+ bScript = true;
+ }
+ else if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SMALL_CAPITALS))
+ {
+ wxFont textFont = font;
+ textFont.SetPointSize((int) (textFont.GetPointSize()*0.75));
wxCheckSetFont(dc, textFont);
bScript = true;
}
long len = range.GetLength();
wxString str(m_text);
+ if (context.HasVirtualText(this))
+ {
+ if (!context.GetVirtualText(this, str) || str.Length() != m_text.Length())
+ str = m_text;
+ }
+
wxString toReplace = wxRichTextLineBreakChar;
str.Replace(toReplace, wxT(" "));
wxString stringChunk = str.Mid(startPos, (size_t) len);
- if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS))
+ if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & (wxTEXT_ATTR_EFFECT_CAPITALS|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS)))
stringChunk.MakeUpper();
wxCoord w, h;
}
/// 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() == CLASSINFO(wxRichTextPlainText) &&
- (m_text.empty() || (wxTextAttrEq(GetAttributes(), object->GetAttributes()) && m_properties == object->GetProperties()));
+ // JACS 2013-01-27
+ 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.
/// 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);
wxASSERT( textObject != NULL );
return false;
}
-/// Dump to output stream for debugging
-void wxRichTextPlainText::Dump(wxTextOutputStream& stream)
+bool wxRichTextPlainText::CanSplit(wxRichTextDrawingContext& context) const
{
- wxRichTextObject::Dump(stream);
- stream << m_text << wxT("\n");
+ // 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);
}
-/// Get the first position from pos that has a line break character.
-long wxRichTextPlainText::GetFirstLineBreakPosition(long pos)
+wxRichTextObject* wxRichTextPlainText::Split(wxRichTextDrawingContext& context)
{
- int i;
- int len = m_text.length();
- int startPos = pos - m_range.GetStart();
- for (i = startPos; i < len; i++)
+ int count = context.GetVirtualSubobjectAttributesCount(this);
+ if (count > 0 && GetParent())
{
- wxChar ch = m_text[i];
- if (ch == wxRichTextLineBreakChar)
+ wxRichTextCompositeObject* parent = wxDynamicCast(GetParent(), wxRichTextCompositeObject);
+ wxRichTextObjectList::compatibility_iterator node = parent->GetChildren().Find(this);
+ if (node)
{
- return i + m_range.GetStart();
- }
- }
- return -1;
-}
-
-/*!
- * wxRichTextBuffer
- * This is a kind of box, used to represent the whole buffer
+ 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
+void wxRichTextPlainText::Dump(wxTextOutputStream& stream)
+{
+ wxRichTextObject::Dump(stream);
+ stream << m_text << wxT("\n");
+}
+
+/// Get the first position from pos that has a line break character.
+long wxRichTextPlainText::GetFirstLineBreakPosition(long pos)
+{
+ int i;
+ int len = m_text.length();
+ int startPos = pos - m_range.GetStart();
+ for (i = startPos; i < len; i++)
+ {
+ wxChar ch = m_text[i];
+ if (ch == wxRichTextLineBreakChar)
+ {
+ return i + m_range.GetStart();
+ }
+ }
+ return -1;
+}
+
+/*!
+ * wxRichTextBuffer
+ * This is a kind of box, used to represent the whole buffer
*/
IMPLEMENT_DYNAMIC_CLASS(wxRichTextBuffer, wxRichTextParagraphLayoutBox)
-wxList wxRichTextBuffer::sm_handlers;
-wxList wxRichTextBuffer::sm_drawingHandlers;
-wxRichTextRenderer* wxRichTextBuffer::sm_renderer = NULL;
-int wxRichTextBuffer::sm_bulletRightMargin = 20;
-float wxRichTextBuffer::sm_bulletProportion = (float) 0.3;
+wxList wxRichTextBuffer::sm_handlers;
+wxList wxRichTextBuffer::sm_drawingHandlers;
+wxRichTextFieldTypeHashMap wxRichTextBuffer::sm_fieldTypes;
+wxRichTextRenderer* wxRichTextBuffer::sm_renderer = NULL;
+int wxRichTextBuffer::sm_bulletRightMargin = 20;
+float wxRichTextBuffer::sm_bulletProportion = (float) 0.3;
+bool wxRichTextBuffer::sm_floatingLayoutMode = true;
/// Initialisation
void wxRichTextBuffer::Init()
m_suppressUndo = 0;
m_handlerFlags = 0;
m_scale = 1.0;
+ m_dimensionScale = 1.0;
+ m_fontScale = 1.0;
+ SetMargins(4);
}
/// Initialisation
m_batchedCommand = NULL;
m_suppressUndo = obj.m_suppressUndo;
m_invalidRange = obj.m_invalidRange;
+ m_dimensionScale = obj.m_dimensionScale;
+ m_fontScale = obj.m_fontScale;
}
/// Push style sheet to top of stack
/// Submit command to insert the given text
bool wxRichTextBuffer::InsertTextWithUndo(long pos, const wxString& text, wxRichTextCtrl* ctrl, int flags)
{
- return ctrl->GetFocusObject()->InsertTextWithUndo(this, pos, text, ctrl, flags);
+ if (ctrl)
+ return ctrl->GetFocusObject()->InsertTextWithUndo(this, pos, text, ctrl, flags);
+ else
+ return wxRichTextParagraphLayoutBox::InsertTextWithUndo(this, pos, text, ctrl, flags);
}
/// Submit command to insert the given text
}
wxRichTextAttr attr(buffer->GetDefaultStyle());
+ // Don't include box attributes such as margins
+ attr.GetTextBoxAttr().Reset();
wxRichTextParagraph* newPara = new wxRichTextParagraph(wxEmptyString, this, & attr);
action->GetNewParagraphs().AppendChild(newPara);
action->SetPosition(pos);
- // Use the default character style
// Use the default character style
if (!buffer->GetDefaultStyle().IsDefault() && newPara->GetChildren().GetFirst())
{
// Check whether the default style merely reflects the paragraph/basic style,
// in which case don't apply it.
wxRichTextAttr defaultStyle(buffer->GetDefaultStyle());
+ defaultStyle.GetTextBoxAttr().Reset();
wxRichTextAttr toApply;
if (para)
{
wxRichTextAttr attr(buffer->GetDefaultStyle());
+ // Don't include box attributes such as margins
+ attr.GetTextBoxAttr().Reset();
+
wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
if (p)
newPara->SetAttributes(*p);
wxRichTextAttr attr(buffer->GetDefaultStyle());
+ // Don't include box attributes such as margins
+ attr.GetTextBoxAttr().Reset();
+
wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
if (p)
newPara->SetAttributes(*p);
return obj;
}
+wxRichTextField* wxRichTextParagraphLayoutBox::InsertFieldWithUndo(wxRichTextBuffer* buffer, long pos, const wxString& fieldType,
+ const wxRichTextProperties& properties,
+ wxRichTextCtrl* ctrl, int flags,
+ const wxRichTextAttr& textAttr)
+{
+ wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Field"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
+
+ wxRichTextAttr* p = NULL;
+ wxRichTextAttr paraAttr;
+ if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
+ {
+ paraAttr = GetStyleForNewParagraph(buffer, pos);
+ if (!paraAttr.IsDefault())
+ p = & paraAttr;
+ }
+
+ wxRichTextAttr attr(buffer->GetDefaultStyle());
+
+ // Don't include box attributes such as margins
+ attr.GetTextBoxAttr().Reset();
+
+ wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
+ if (p)
+ newPara->SetAttributes(*p);
+
+ wxRichTextField* fieldObject = new wxRichTextField();
+ fieldObject->wxRichTextObject::SetProperties(properties);
+ fieldObject->SetFieldType(fieldType);
+ fieldObject->SetAttributes(textAttr);
+ newPara->AppendChild(fieldObject);
+ action->GetNewParagraphs().AppendChild(newPara);
+ action->GetNewParagraphs().UpdateRanges();
+ action->GetNewParagraphs().SetPartialParagraph(true);
+ action->SetPosition(pos);
+
+ // Set the range we'll need to delete in Undo
+ action->SetRange(wxRichTextRange(pos, pos));
+
+ buffer->SubmitAction(action);
+
+ wxRichTextField* obj = wxDynamicCast(GetLeafObjectAtPosition(pos), wxRichTextField);
+ return obj;
+}
+
+bool wxRichTextParagraphLayoutBox::SetObjectPropertiesWithUndo(wxRichTextObject& obj, const wxRichTextProperties& properties, wxRichTextObject* objToSet)
+{
+ wxRichTextBuffer* buffer = GetBuffer();
+ wxCHECK_MSG(buffer, false, wxT("Invalid buffer"));
+ wxRichTextCtrl* rtc = buffer->GetRichTextCtrl();
+ wxCHECK_MSG(rtc, false, wxT("Invalid rtc"));
+
+ wxRichTextAction* action = NULL;
+ wxRichTextObject* clone = NULL;
+
+ // The object on which to set properties will usually be 'obj', but use objToSet if it's valid.
+ // This is necessary e.g. on setting a wxRichTextCell's properties, when obj will be the parent table
+ if (objToSet == NULL)
+ objToSet = &obj;
+
+ if (rtc->SuppressingUndo())
+ objToSet->SetProperties(properties);
+ else
+ {
+ clone = obj.Clone();
+ objToSet->SetProperties(properties);
+
+ // The 'true' parameter in the next line says "Ignore first time"; otherwise the objects are prematurely switched
+ action = new wxRichTextAction(NULL, _("Change Properties"), wxRICHTEXT_CHANGE_OBJECT, buffer, obj.GetParentContainer(), rtc, true);
+ action->SetOldAndNewObjects(& obj, clone);
+ action->SetPosition(obj.GetRange().GetStart());
+ action->SetRange(obj.GetRange());
+ buffer->SubmitAction(action);
+ }
+
+ return true;
+}
+
/// Get the style that is appropriate for a new paragraph at this position.
/// If the previous paragraph has a paragraph style name, look up the next-paragraph
/// style.
if (BatchingUndo() && m_batchedCommand && !SuppressingUndo())
{
- wxRichTextCommand* cmd = new wxRichTextCommand(action->GetName());
- cmd->AddAction(action);
- cmd->Do();
- cmd->GetActions().Clear();
- delete cmd;
+ if (!action->GetIgnoreFirstTime())
+ {
+ wxRichTextCommand* cmd = new wxRichTextCommand(action->GetName());
+ cmd->AddAction(action);
+ cmd->Do();
+ cmd->GetActions().Clear();
+ delete cmd;
+ }
m_batchedCommand->AddAction(action);
}
cmd->AddAction(action);
// Only store it if we're not suppressing undo.
- return GetCommandProcessor()->Submit(cmd, !SuppressingUndo());
+ if (!action->GetIgnoreFirstTime())
+ {
+ return GetCommandProcessor()->Submit(cmd, !SuppressingUndo());
+ }
+ else if (!SuppressingUndo())
+ {
+ GetCommandProcessor()->Store(cmd); // Just store it, without Do()ing anything
+ }
}
return true;
bool wxRichTextBuffer::BeginStyle(const wxRichTextAttr& style)
{
wxRichTextAttr newStyle(GetDefaultStyle());
+ newStyle.GetTextBoxAttr().Reset();
// Save the old default style
- m_attributeStack.Append((wxObject*) new wxRichTextAttr(GetDefaultStyle()));
+ m_attributeStack.Append((wxObject*) new wxRichTextAttr(newStyle));
wxRichTextApplyStyle(newStyle, style);
newStyle.SetFlags(style.GetFlags()|newStyle.GetFlags());
return wildcard;
}
+#if wxUSE_FFILE && wxUSE_STREAMS
/// Load a file
bool wxRichTextBuffer::LoadFile(const wxString& filename, wxRichTextFileType type)
{
else
return false;
}
+#endif // wxUSE_FFILE && wxUSE_STREAMS
+#if wxUSE_STREAMS
/// Load from a stream
bool wxRichTextBuffer::LoadFile(wxInputStream& stream, wxRichTextFileType type)
{
else
return false;
}
+#endif // wxUSE_STREAMS
/// Copy the range to the clipboard
bool wxRichTextBuffer::CopyToClipboard(const wxRichTextRange& range)
if (GetRichTextCtrl())
winid = GetRichTextCtrl()->GetId();
- wxRichTextEvent event(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACING, winid);
+ wxRichTextEvent event(wxEVT_RICHTEXT_STYLESHEET_REPLACING, winid);
event.SetEventObject(GetRichTextCtrl());
event.SetContainer(GetRichTextCtrl()->GetFocusObject());
event.SetOldStyleSheet(oldSheet);
SetStyleSheet(sheet);
- event.SetEventType(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACED);
+ event.SetEventType(wxEVT_RICHTEXT_STYLESHEET_REPLACED);
event.SetOldStyleSheet(NULL);
event.Allow();
}
}
+void wxRichTextBuffer::SetFontScale(double fontScale)
+{
+ m_fontScale = fontScale;
+ m_fontTable.SetFontScale(fontScale);
+}
+
+void wxRichTextBuffer::SetDimensionScale(double dimScale)
+{
+ m_dimensionScale = dimScale;
+}
+
bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph* paragraph, wxDC& dc, const wxRichTextAttr& bulletAttr, const wxRect& rect)
{
if (bulletAttr.GetTextColour().IsOk())
if ((attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL) && !attr.GetBulletFont().IsEmpty() && attr.HasFont())
{
wxRichTextAttr fontAttr;
- fontAttr.SetFontSize(attr.GetFontSize());
+ if (attr.HasFontPixelSize())
+ fontAttr.SetFontPixelSize(attr.GetFontSize());
+ else
+ fontAttr.SetFontPointSize(attr.GetFontSize());
fontAttr.SetFontStyle(attr.GetFontStyle());
fontAttr.SetFontWeight(attr.GetFontWeight());
fontAttr.SetFontUnderlined(attr.GetFontUnderlined());
return false;
}
+/*!
+ * wxRichTextField
+ */
+
+IMPLEMENT_DYNAMIC_CLASS(wxRichTextField, wxRichTextParagraphLayoutBox)
+
+wxRichTextField::wxRichTextField(const wxString& fieldType, wxRichTextObject* parent):
+ wxRichTextParagraphLayoutBox(parent)
+{
+ SetFieldType(fieldType);
+}
+
+/// Draw the item
+bool wxRichTextField::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
+{
+ if (!IsShown())
+ return true;
+
+ wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
+ if (fieldType && fieldType->Draw(this, dc, context, range, selection, rect, descent, style))
+ return true;
+
+ // Fallback; but don't draw guidelines.
+ style &= ~wxRICHTEXT_DRAW_GUIDELINES;
+ return wxRichTextParagraphLayoutBox::Draw(dc, context, range, selection, rect, descent, style);
+}
+
+bool wxRichTextField::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style)
+{
+ wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
+ if (fieldType && fieldType->Layout(this, dc, context, rect, parentRect, style))
+ return true;
+
+ // Fallback
+ return wxRichTextParagraphLayoutBox::Layout(dc, context, rect, parentRect, style);
+}
+
+bool wxRichTextField::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, const wxPoint& position, const wxSize& parentSize, wxArrayInt* partialExtents) const
+{
+ wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
+ if (fieldType)
+ return fieldType->GetRangeSize((wxRichTextField*) this, range, size, descent, dc, context, flags, position, parentSize, partialExtents);
+
+ return wxRichTextParagraphLayoutBox::GetRangeSize(range, size, descent, dc, context, flags, position, parentSize, partialExtents);
+}
+
+/// Calculate range
+void wxRichTextField::CalculateRange(long start, long& end)
+{
+ if (IsTopLevel())
+ wxRichTextParagraphLayoutBox::CalculateRange(start, end);
+ else
+ wxRichTextObject::CalculateRange(start, end);
+}
+
+/// Copy
+void wxRichTextField::Copy(const wxRichTextField& obj)
+{
+ wxRichTextParagraphLayoutBox::Copy(obj);
+
+ UpdateField(GetBuffer());
+}
+
+// Edit properties via a GUI
+bool wxRichTextField::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
+{
+ wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
+ if (fieldType)
+ return fieldType->EditProperties(this, parent, buffer);
+
+ return false;
+}
+
+bool wxRichTextField::CanEditProperties() const
+{
+ wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
+ if (fieldType)
+ return fieldType->CanEditProperties((wxRichTextField*) this);
+
+ return false;
+}
+
+wxString wxRichTextField::GetPropertiesMenuLabel() const
+{
+ wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
+ if (fieldType)
+ return fieldType->GetPropertiesMenuLabel((wxRichTextField*) this);
+
+ return wxEmptyString;
+}
+
+bool wxRichTextField::UpdateField(wxRichTextBuffer* buffer)
+{
+ wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
+ if (fieldType)
+ return fieldType->UpdateField(buffer, (wxRichTextField*) this);
+
+ return false;
+}
+
+bool wxRichTextField::IsTopLevel() const
+{
+ wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
+ if (fieldType)
+ return fieldType->IsTopLevel((wxRichTextField*) this);
+
+ return true;
+}
+
+IMPLEMENT_CLASS(wxRichTextFieldType, wxObject)
+
+IMPLEMENT_CLASS(wxRichTextFieldTypeStandard, wxRichTextFieldType)
+
+wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString& name, const wxString& label, int displayStyle)
+{
+ Init();
+
+ SetName(name);
+ SetLabel(label);
+ SetDisplayStyle(displayStyle);
+}
+
+wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString& name, const wxBitmap& bitmap, int displayStyle)
+{
+ Init();
+
+ SetName(name);
+ SetBitmap(bitmap);
+ SetDisplayStyle(displayStyle);
+}
+
+void wxRichTextFieldTypeStandard::Init()
+{
+ m_displayStyle = wxRICHTEXT_FIELD_STYLE_RECTANGLE;
+ m_font = wxFont(6, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
+ m_textColour = *wxWHITE;
+ m_borderColour = *wxBLACK;
+ m_backgroundColour = *wxBLACK;
+ m_verticalPadding = 1;
+ m_horizontalPadding = 3;
+ m_horizontalMargin = 2;
+ m_verticalMargin = 0;
+}
+
+void wxRichTextFieldTypeStandard::Copy(const wxRichTextFieldTypeStandard& field)
+{
+ wxRichTextFieldType::Copy(field);
+
+ m_label = field.m_label;
+ m_displayStyle = field.m_displayStyle;
+ m_font = field.m_font;
+ m_textColour = field.m_textColour;
+ m_borderColour = field.m_borderColour;
+ m_backgroundColour = field.m_backgroundColour;
+ m_verticalPadding = field.m_verticalPadding;
+ m_horizontalPadding = field.m_horizontalPadding;
+ m_horizontalMargin = field.m_horizontalMargin;
+ m_bitmap = field.m_bitmap;
+}
+
+bool wxRichTextFieldTypeStandard::Draw(wxRichTextField* obj, wxDC& dc, wxRichTextDrawingContext& WXUNUSED(context), const wxRichTextRange& WXUNUSED(range), const wxRichTextSelection& selection, const wxRect& rect, int descent, int WXUNUSED(style))
+{
+ if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_COMPOSITE)
+ return false; // USe default composite drawing
+ else // if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_RECTANGLE || m_displayStyle == wxRICHTEXT_FIELD_STYLE_NOBORDER)
+ {
+ int borderSize = 1;
+
+ wxPen borderPen(m_borderColour, 1, wxSOLID);
+ wxBrush backgroundBrush(m_backgroundColour);
+ wxColour textColour(m_textColour);
+
+ if (selection.WithinSelection(obj->GetRange().GetStart(), obj))
+ {
+ wxColour highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
+ wxColour highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
+
+ borderPen = wxPen(highlightTextColour, 1, wxSOLID);
+ backgroundBrush = wxBrush(highlightColour);
+
+ wxCheckSetBrush(dc, backgroundBrush);
+ wxCheckSetPen(dc, wxPen(highlightColour, 1, wxSOLID));
+ dc.DrawRectangle(rect);
+ }
+
+ if (m_displayStyle != wxRICHTEXT_FIELD_STYLE_NO_BORDER)
+ borderSize = 0;
+
+ // objectRect is the area where the content is drawn, after margins around it have been taken into account
+ wxRect objectRect = wxRect(wxPoint(rect.x + m_horizontalMargin, rect.y + wxMax(0, rect.height - descent - obj->GetCachedSize().y)),
+ wxSize(obj->GetCachedSize().x - 2*m_horizontalMargin - borderSize, obj->GetCachedSize().y));
+
+ // clientArea is where the text is actually written
+ wxRect clientArea = objectRect;
+
+ if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_RECTANGLE)
+ {
+ dc.SetPen(borderPen);
+ dc.SetBrush(backgroundBrush);
+ dc.DrawRoundedRectangle(objectRect, 4.0);
+ }
+ else if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_START_TAG)
+ {
+ int arrowLength = objectRect.height/2;
+ clientArea.width -= (arrowLength - m_horizontalPadding);
+
+ wxPoint pts[5];
+ pts[0].x = objectRect.x; pts[0].y = objectRect.y;
+ pts[1].x = objectRect.x + objectRect.width - arrowLength; pts[1].y = objectRect.y;
+ pts[2].x = objectRect.x + objectRect.width; pts[2].y = objectRect.y + (objectRect.height/2);
+ pts[3].x = objectRect.x + objectRect.width - arrowLength; pts[3].y = objectRect.y + objectRect.height;
+ pts[4].x = objectRect.x; pts[4].y = objectRect.y + objectRect.height;
+ dc.SetPen(borderPen);
+ dc.SetBrush(backgroundBrush);
+ dc.DrawPolygon(5, pts);
+ }
+ else if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_END_TAG)
+ {
+ int arrowLength = objectRect.height/2;
+ clientArea.width -= (arrowLength - m_horizontalPadding);
+ clientArea.x += (arrowLength - m_horizontalPadding);
+
+ wxPoint pts[5];
+ pts[0].x = objectRect.x + objectRect.width; pts[0].y = objectRect.y;
+ pts[1].x = objectRect.x + arrowLength; pts[1].y = objectRect.y;
+ pts[2].x = objectRect.x; pts[2].y = objectRect.y + (objectRect.height/2);
+ pts[3].x = objectRect.x + arrowLength; pts[3].y = objectRect.y + objectRect.height;
+ pts[4].x = objectRect.x + objectRect.width; pts[4].y = objectRect.y + objectRect.height;
+ dc.SetPen(borderPen);
+ dc.SetBrush(backgroundBrush);
+ dc.DrawPolygon(5, pts);
+ }
+
+ if (m_bitmap.IsOk())
+ {
+ int x = clientArea.x + (clientArea.width - m_bitmap.GetWidth())/2;
+ int y = clientArea.y + m_verticalPadding;
+ dc.DrawBitmap(m_bitmap, x, y, true);
+
+ if (selection.WithinSelection(obj->GetRange().GetStart(), obj))
+ {
+ wxCheckSetBrush(dc, *wxBLACK_BRUSH);
+ wxCheckSetPen(dc, *wxBLACK_PEN);
+ dc.SetLogicalFunction(wxINVERT);
+ dc.DrawRectangle(wxRect(x, y, m_bitmap.GetWidth(), m_bitmap.GetHeight()));
+ dc.SetLogicalFunction(wxCOPY);
+ }
+ }
+ else
+ {
+ wxString label(m_label);
+ if (label.IsEmpty())
+ label = wxT("??");
+ int w, h, maxDescent;
+ dc.SetFont(m_font);
+ dc.GetTextExtent(m_label, & w, &h, & maxDescent);
+ dc.SetTextForeground(textColour);
+
+ int x = clientArea.x + (clientArea.width - w)/2;
+ int y = clientArea.y + (clientArea.height - (h - maxDescent))/2;
+ dc.DrawText(m_label, x, y);
+ }
+ }
+
+ return true;
+}
+
+bool wxRichTextFieldTypeStandard::Layout(wxRichTextField* obj, wxDC& dc, wxRichTextDrawingContext& context, const wxRect& WXUNUSED(rect), const wxRect& WXUNUSED(parentRect), int style)
+{
+ if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_COMPOSITE)
+ return false; // USe default composite layout
+
+ wxSize size = GetSize(obj, dc, context, style);
+ obj->SetCachedSize(size);
+ obj->SetMinSize(size);
+ obj->SetMaxSize(size);
+ return true;
+}
+
+bool wxRichTextFieldTypeStandard::GetRangeSize(wxRichTextField* obj, const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, const wxPoint& position, const wxSize& parentSize, wxArrayInt* partialExtents) const
+{
+ if (IsTopLevel(obj))
+ return obj->wxRichTextParagraphLayoutBox::GetRangeSize(range, size, descent, dc, context, flags, position, parentSize);
+ else
+ {
+ wxSize sz = GetSize(obj, dc, context, 0);
+ if (partialExtents)
+ {
+ int lastSize;
+ if (partialExtents->GetCount() > 0)
+ lastSize = (*partialExtents)[partialExtents->GetCount()-1];
+ else
+ lastSize = 0;
+ partialExtents->Add(lastSize + sz.x);
+ }
+ size = sz;
+ return true;
+ }
+}
+
+wxSize wxRichTextFieldTypeStandard::GetSize(wxRichTextField* WXUNUSED(obj), wxDC& dc, wxRichTextDrawingContext& WXUNUSED(context), int WXUNUSED(style)) const
+{
+ int borderSize = 1;
+ int w = 0, h = 0, maxDescent = 0;
+
+ wxSize sz;
+ if (m_bitmap.IsOk())
+ {
+ w = m_bitmap.GetWidth();
+ h = m_bitmap.GetHeight();
+
+ sz = wxSize(w + m_horizontalMargin*2, h + m_verticalMargin*2);
+ }
+ else
+ {
+ wxString label(m_label);
+ if (label.IsEmpty())
+ label = wxT("??");
+ dc.SetFont(m_font);
+ dc.GetTextExtent(label, & w, &h, & maxDescent);
+
+ sz = wxSize(w + m_horizontalPadding*2 + m_horizontalMargin*2, h + m_verticalPadding *2 + m_verticalMargin*2);
+ }
+
+ if (m_displayStyle != wxRICHTEXT_FIELD_STYLE_NO_BORDER)
+ {
+ sz.x += borderSize*2;
+ sz.y += borderSize*2;
+ }
+
+ if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_START_TAG || m_displayStyle == wxRICHTEXT_FIELD_STYLE_END_TAG)
+ {
+ // Add space for the arrow
+ sz.x += (sz.y/2 - m_horizontalPadding);
+ }
+
+ return sz;
+}
+
IMPLEMENT_DYNAMIC_CLASS(wxRichTextCell, wxRichTextBox)
wxRichTextCell::wxRichTextCell(wxRichTextObject* parent):
return wxRichTextBox::Draw(dc, context, range, selection, rect, descent, style);
}
+int wxRichTextCell::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
+{
+ int ret = wxRichTextParagraphLayoutBox::HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
+ if (ret != wxRICHTEXT_HITTEST_NONE)
+ {
+ return ret;
+ }
+ else
+ {
+ textPosition = m_ownRange.GetEnd()-1;
+ *obj = this;
+ *contextObj = this;
+ return wxRICHTEXT_HITTEST_AFTER|wxRICHTEXT_HITTEST_OUTSIDE;
+ }
+}
+
/// Copy
void wxRichTextCell::Copy(const wxRichTextCell& obj)
{
else
caption = _("Cell Properties");
+ // We don't want position and floating controls for a cell.
+ wxRichTextSizePage::ShowPositionControls(false);
+ wxRichTextSizePage::ShowFloatingControls(false);
+ wxRichTextSizePage::ShowAlignmentControls(true);
+
wxRichTextObjectPropertiesDialog cellDlg(this, wxGetTopLevelParent(parent), wxID_ANY, caption);
cellDlg.SetAttributes(attr);
- wxRichTextSizePage* sizePage = wxDynamicCast(cellDlg.FindPage(CLASSINFO(wxRichTextSizePage)), wxRichTextSizePage);
- if (sizePage)
+ bool ok = (cellDlg.ShowModal() == wxID_OK);
+
+ wxRichTextSizePage::ShowPositionControls(true);
+ wxRichTextSizePage::ShowFloatingControls(true);
+
+ if (ok)
+ {
+ if (multipleCells)
+ {
+ const wxRichTextSelection& sel = buffer->GetRichTextCtrl()->GetSelection();
+ // Apply the style; we interpret indeterminate attributes as 'don't touch this attribute'
+ // since it may represent clashing attributes across multiple objects.
+ table->SetCellStyle(sel, attr);
+ }
+ else
+ // For a single object, indeterminate attributes set by the user should be reflected in the
+ // actual object style, so pass the wxRICHTEXT_SETSTYLE_RESET flag to assign
+ // the style directly instead of applying (which ignores indeterminate attributes,
+ // leaving them as they were).
+ cellDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
+ return true;
+ }
+ else
+ return false;
+}
+
+// The next 2 methods return span values. Note that the default is 1, not 0
+int wxRichTextCell::GetColspan() const
+{
+ int span = 1;
+ if (GetProperties().HasProperty(wxT("colspan")))
+ {
+ span = GetProperties().GetPropertyLong(wxT("colspan"));
+ }
+
+ return span;
+}
+
+int wxRichTextCell::GetRowspan() const
+{
+ int span = 1;
+ if (GetProperties().HasProperty(wxT("rowspan")))
+ {
+ span = GetProperties().GetPropertyLong(wxT("rowspan"));
+ }
+
+ return span;
+}
+
+WX_DEFINE_OBJARRAY(wxRichTextObjectPtrArrayArray)
+
+IMPLEMENT_DYNAMIC_CLASS(wxRichTextTable, wxRichTextBox)
+
+wxRichTextTable::wxRichTextTable(wxRichTextObject* parent): wxRichTextBox(parent)
+{
+ m_rowCount = 0;
+ m_colCount = 0;
+}
+
+// Draws the object.
+bool wxRichTextTable::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
+{
+ wxRichTextBox::Draw(dc, context, range, selection, rect, descent, style);
+
+ int colCount = GetColumnCount();
+ int rowCount = GetRowCount();
+ int col, row;
+ for (col = 0; col < colCount; col++)
+ {
+ for (row = 0; row < rowCount; row++)
+ {
+ if (row == 0 || row == (rowCount-1) || col == 0 || col == (colCount-1))
+ {
+ wxRichTextCell* cell = GetCell(row, col);
+ if (cell && cell->IsShown() && !cell->GetRange().IsOutside(range))
+ {
+ wxRect childRect(cell->GetPosition(), cell->GetCachedSize());
+ wxRichTextAttr attr(cell->GetAttributes());
+ if (row != 0)
+ attr.GetTextBoxAttr().GetBorder().GetTop().Reset();
+ if (row != (rowCount-1))
+ attr.GetTextBoxAttr().GetBorder().GetBottom().Reset();
+ if (col != 0)
+ attr.GetTextBoxAttr().GetBorder().GetLeft().Reset();
+ if (col != (colCount-1))
+ attr.GetTextBoxAttr().GetBorder().GetRight().Reset();
+
+ if (attr.GetTextBoxAttr().GetBorder().IsValid())
+ {
+ wxRect boxRect(cell->GetPosition(), cell->GetCachedSize());
+ wxRect marginRect = boxRect;
+ wxRect contentRect, borderRect, paddingRect, outlineRect;
+
+ cell->GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
+ cell->DrawBorder(dc, GetBuffer(), attr.GetTextBoxAttr().GetBorder(), borderRect);
+ }
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+ // Helper function for Layout() that clears the space needed by a cell with rowspan > 1
+int GetRowspanDisplacement(const wxRichTextTable* table, int row, int col, int paddingX, const wxArrayInt& colWidths)
+{
+ // If one or more cells above-left of this one has rowspan > 1, the affected cells below it
+ // will have been hidden and have width 0. As a result they are ignored by the layout algorithm,
+ // and all cells to their right are effectively shifted left. As a result there's no hole for
+ // the spanning cell to fill.
+ // So search back along the current row for hidden cells. However there's also the annoying issue of a
+ // rowspanning cell that also has colspam. So we can't rely on the rowspanning cell being directly above
+ // the first hidden one we come to. We also can't rely on a cell being hidden only by one type of span;
+ // there's nothing to stop a cell being hidden by colspan, and then again hidden from above by rowspan.
+ // The answer is to look above each hidden cell in turn, which I think covers all bases.
+ int deltaX = 0;
+ for (int prevcol = 0; prevcol < col; ++prevcol)
+ {
+ if (!table->GetCell(row, prevcol)->IsShown())
+ {
+ // We've found a hidden cell. If it's hidden because of colspan to its left, it's
+ // already been taken into account; but not if there's a rowspanning cell above
+ for (int prevrow = row-1; prevrow >= 0; --prevrow)
+ {
+ wxRichTextCell* cell = table->GetCell(prevrow, prevcol);
+ if (cell && cell->IsShown())
+ {
+ int rowSpan = cell->GetRowspan();
+ if (rowSpan > 1 && rowSpan > (row-prevrow))
+ {
+ // There is a rowspanning cell above above the hidden one, so we need
+ // to right-shift the index cell by this column's width. Furthermore,
+ // if the cell also colspans, we need to shift by all affected columns
+ for (int colSpan = 0; colSpan < cell->GetColspan(); ++colSpan)
+ deltaX += (colWidths[prevcol+colSpan] + paddingX);
+ break;
+ }
+ }
+ }
+ }
+ }
+ return deltaX;
+}
+
+ // Helper function for Layout() that expands any cell with rowspan > 1
+void ExpandCellsWithRowspan(const wxRichTextTable* table, int paddingY, int& bottomY, wxDC& dc, wxRichTextDrawingContext& context, const wxRect& availableSpace, int style)
+{
+ // This is called when the table's cell layout is otherwise complete.
+ // For any cell with rowspan > 1, expand downwards into the row(s) below.
+
+ // Start by finding the current 'y' of the top of each row, plus the bottom of the available area for cells.
+ // Deduce this from the top of a visible cell in the row below. (If none are visible, the row will be invisible anyway and can be ignored.)
+ const int rowCount = table->GetRowCount();
+ const int colCount = table->GetColumnCount();
+ wxArrayInt rowTops;
+ rowTops.Add(0, rowCount+1);
+ int row;
+ for (row = 0; row < rowCount; ++row)
{
- // We don't want position and floating controls for a cell.
- sizePage->ShowPositionControls(false);
- sizePage->ShowFloatingControls(false);
+ for (int column = 0; column < colCount; ++column)
+ {
+ wxRichTextCell* cell = table->GetCell(row, column);
+ if (cell && cell->IsShown())
+ {
+ rowTops[row] = cell->GetPosition().y;
+ break;
+ }
+ }
}
+ rowTops[rowCount] = bottomY + paddingY; // The table bottom, which was passed to us
+
+ bool needsRelay = false;
- if (cellDlg.ShowModal() == wxID_OK)
+ for (row = 0; row < rowCount-1; ++row) // -1 as the bottom row can't rowspan
{
- if (multipleCells)
+ for (int col = 0; col < colCount; ++col)
{
- const wxRichTextSelection& sel = buffer->GetRichTextCtrl()->GetSelection();
- // Apply the style; we interpret indeterminate attributes as 'don't touch this attribute'
- // since it may represent clashing attributes across multiple objects.
- table->SetCellStyle(sel, attr);
+ wxRichTextCell* cell = table->GetCell(row, col);
+ if (cell && cell->IsShown())
+ {
+ int span = cell->GetRowspan();
+ if (span > 1)
+ {
+ span = wxMin(span, rowCount-row); // Don't try to span below the table!
+ if (span < 2)
+ continue;
+
+ int availableHeight = rowTops[row+span] - rowTops[row] - paddingY;
+ wxSize newSize = wxSize(cell->GetCachedSize().GetWidth(), availableHeight);
+ wxRect availableCellSpace = wxRect(cell->GetPosition(), newSize);
+ cell->Invalidate(wxRICHTEXT_ALL);
+ cell->Layout(dc, context, availableCellSpace, availableSpace, style);
+ // Ensure there's room in the span to display its contents, else it'll overwrite lower rows
+ int overhang = cell->GetCachedSize().GetHeight() - availableHeight;
+ cell->SetCachedSize(newSize);
+
+ if (overhang > 0)
+ {
+ // There are 3 things to get right:
+ // 1) The easiest is the rows below the span: they need to be downshifted by the overhang, and so does the table bottom itself
+ // 2) The rows within the span, including the one holding this cell, need to be deepened by their share of the overhang
+ // e.g. if rowspan == 3, each row should increase in depth by 1/3rd of the overhang.
+ // 3) The cell with the rowspan shouldn't be touched in 2); its height will be set to the whole span later.
+ int deltaY = overhang / span;
+ int spare = overhang % span;
+
+ // Each row in the span needs to by deepened by its share of the overhang (give the first row any spare).
+ // This is achieved by increasing the value stored in the following row's rowTops
+ for (int spannedRows = 0; spannedRows < span; ++spannedRows)
+ {
+ rowTops[row+spannedRows+1] += ((deltaY * (spannedRows+1)) + (spannedRows == 0 ? spare:0));
+ }
+
+ // Any rows below the span need shifting down
+ for (int rowsBelow = row + span+1; rowsBelow <= rowCount; ++rowsBelow)
+ {
+ rowTops[rowsBelow] += overhang;
+ }
+
+ needsRelay = true;
+ }
+ }
+ }
}
- else
- // For a single object, indeterminate attributes set by the user should be reflected in the
- // actual object style, so pass the wxRICHTEXT_SETSTYLE_RESET flag to assign
- // the style directly instead of applying (which ignores indeterminate attributes,
- // leaving them as they were).
- cellDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
- return true;
}
- else
- return false;
-}
-
-WX_DEFINE_OBJARRAY(wxRichTextObjectPtrArrayArray)
+
+ if (!needsRelay)
+ return;
-IMPLEMENT_DYNAMIC_CLASS(wxRichTextTable, wxRichTextBox)
+ // There were overflowing rowspanning cells, so layout yet again to make the increased row depths show
+ for (row = 0; row < rowCount; ++row)
+ {
+ for (int col = 0; col < colCount; ++col)
+ {
+ wxRichTextCell* cell = table->GetCell(row, col);
+ if (cell && cell->IsShown())
+ {
+ wxPoint position(cell->GetPosition().x, rowTops[row]);
-wxRichTextTable::wxRichTextTable(wxRichTextObject* parent): wxRichTextBox(parent)
-{
- m_rowCount = 0;
- m_colCount = 0;
-}
+ // GetRowspan() will usually return 1, but may be greater
+ wxSize size(cell->GetCachedSize().GetWidth(), rowTops[row + cell->GetRowspan()] - rowTops[row] - paddingY);
-// Draws the object.
-bool wxRichTextTable::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
-{
- return wxRichTextBox::Draw(dc, context, range, selection, rect, descent, style);
+ wxRect availableCellSpace = wxRect(position, size);
+ cell->Invalidate(wxRICHTEXT_ALL);
+ cell->Layout(dc, context, availableCellSpace, availableSpace, style);
+ cell->SetCachedSize(size);
+ }
+ }
+
+ bottomY = rowTops[rowCount] - paddingY;
+ }
}
-WX_DECLARE_OBJARRAY(wxRect, wxRichTextRectArray);
-WX_DEFINE_OBJARRAY(wxRichTextRectArray);
-
// Lays the object out. rect is the space available for layout. Often it will
// be the specified overall space for this object, if trying to constrain
// layout to a particular size, or it could be the total space available in the
{
SetPosition(rect.GetPosition());
- // TODO: the meaty bit. Calculate sizes of all cells and rows. Try to use
+ // The meaty bit. Calculate sizes of all cells and rows. Try to use
// minimum size if within alloted size, then divide up remaining size
// between rows/cols.
wxRichTextAttr attr(GetAttributes());
context.ApplyVirtualAttributes(attr, this);
+ bool tableHasPercentWidth = (attr.GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE);
// If we have no fixed table size, and assuming we're not pushed for
// space, then we don't have to try to stretch the table to fit the contents.
- bool stretchToFitTableWidth = false;
-
+ bool stretchToFitTableWidth = tableHasPercentWidth;
+
int tableWidth = rect.width;
- if (attr.GetTextBoxAttr().GetWidth().IsValid())
+ if (attr.GetTextBoxAttr().GetWidth().IsValid() && !tableHasPercentWidth)
{
tableWidth = converter.GetPixels(attr.GetTextBoxAttr().GetWidth());
wxArrayInt absoluteColWidths;
absoluteColWidths.Add(0, m_colCount);
- // wxArrayInt absoluteColWidthsSpanning(m_colCount);
+
wxArrayInt percentageColWidths;
percentageColWidths.Add(0, m_colCount);
// wxArrayInt percentageColWidthsSpanning(m_colCount);
{
for (i = 0; i < m_colCount; i++)
{
- wxRichTextBox* cell = GetCell(j, i);
- int colSpan = 1, rowSpan = 1;
- if (cell->GetProperties().HasProperty(wxT("colspan")))
- colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
- if (cell->GetProperties().HasProperty(wxT("rowspan")))
- rowSpan = cell->GetProperties().GetPropertyLong(wxT("rowspan"));
+ wxRichTextCell* cell = GetCell(j, i);
+ int colSpan = cell->GetColspan();
+ int rowSpan = cell->GetRowspan();
if (colSpan > 1 || rowSpan > 1)
{
rectArray.Add(wxRect(i, j, colSpan, rowSpan));
{
for (i = 0; i < m_colCount; i++)
{
- wxRichTextBox* cell = GetCell(j, i);
+ wxRichTextCell* cell = GetCell(j, i);
if (rectArray.GetCount() == 0)
{
cell->Show(true);
}
else
{
- int colSpan = 1, rowSpan = 1;
- if (cell->GetProperties().HasProperty(wxT("colspan")))
- colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
- if (cell->GetProperties().HasProperty(wxT("rowspan")))
- rowSpan = cell->GetProperties().GetPropertyLong(wxT("rowspan"));
+ int colSpan = cell->GetColspan();
+ int rowSpan = cell->GetRowspan();
+
if (colSpan > 1 || rowSpan > 1)
{
// Assume all spanning cells are shown
}
}
- // TODO: find the first spanned cell in each row that spans the most columns and doesn't
+ // Find the first spanned cell in each row that spans the most columns and doesn't
// overlap with a spanned cell starting at a previous column position.
// This means we need to keep an array of rects so we can check. However
// it does also mean that some spans simply may not be taken into account
for (i = 0; i < m_colCount; i++)
{
- wxRichTextBox* cell = GetCell(j, i);
+ wxRichTextCell* cell = GetCell(j, i);
if (cell->IsShown())
{
- int colSpan = 1;
- if (cell->GetProperties().HasProperty(wxT("colspan")))
- colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
+ int colSpan = cell->GetColspan();
// Lay out cell to find min/max widths
cell->Invalidate(wxRICHTEXT_ALL);
for (i = 0; i < m_colCount; i++)
{
- wxRichTextBox* cell = GetCell(j, i);
+ wxRichTextCell* cell = GetCell(j, i);
if (cell->IsShown())
{
- int colSpan = 1;
- if (cell->GetProperties().HasProperty(wxT("colspan")))
- colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
-
+ int colSpan = cell->GetColspan();
if (colSpan > 1)
{
int spans = wxMin(colSpan, m_colCount - i);
int stretchColCount = 0;
for (i = 0; i < m_colCount; i++)
{
- // TODO: we need to take into account min widths.
// Subtract min width from width left, then
// add the colShare to the min width
if (colWidths[i] > 0) // absolute or proportional width has been specified
if (colWidths[i] > 0) // absolute or proportional width has been specified
{
- int colSpan = 1;
- if (cell->GetProperties().HasProperty(wxT("colspan")))
- colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
-
+ int colSpan = cell->GetColspan();
wxRect availableCellSpace;
- // TODO: take into acount spans
+ // Take into account spans
if (colSpan > 1)
{
// Calculate the size of this spanning cell from its constituent columns
- int xx = x;
+ int xx = 0;
int spans = wxMin(colSpan, m_colCount - i);
- for (k = i; k < spans; k++)
+ for (k = i; k < (i+spans); k++)
{
if (k != i)
xx += paddingX;
// Store actual width so we can force cell to be the appropriate width on the final loop
actualWidths[i] = availableCellSpace.GetWidth();
+ // We now need to shift right by the width of any rowspanning cells above-left of us
+ int deltaX = GetRowspanDisplacement(this, j, i, paddingX, colWidths);
+ availableCellSpace.SetX(availableCellSpace.GetX() + deltaX);
+
// Lay out cell
cell->Invalidate(wxRICHTEXT_ALL);
cell->Layout(dc, context, availableCellSpace, availableSpace, style);
// TODO: use GetCachedSize().x to compute 'natural' size
x += (availableCellSpace.GetWidth() + paddingX);
- if (cell->GetCachedSize().y > maxCellHeight)
+ if ((cell->GetCachedSize().y > maxCellHeight) && (cell->GetRowspan() < 2))
maxCellHeight = cell->GetCachedSize().y;
}
}
if (j < (m_rowCount-1))
y += paddingY;
}
+
+ // Finally we need to expand any cell with rowspan > 1. We couldn't earlier; lower rows' heights weren't known
+ ExpandCellsWithRowspan(this, paddingY, y, dc, context, availableSpace, style);
// We need to add back the margins etc.
{
}
// Gets the range size.
-bool wxRichTextTable::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* partialExtents) const
+bool wxRichTextTable::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, const wxPoint& position, const wxSize& parentSize, wxArrayInt* partialExtents) const
{
- return wxRichTextBox::GetRangeSize(range, size, descent, dc, context, flags, position, partialExtents);
+ return wxRichTextBox::GetRangeSize(range, size, descent, dc, context, flags, position, parentSize, partialExtents);
}
// Deletes content in the given range.
{
m_cells.Clear();
DeleteChildren();
+ m_rowCount = 0;
+ m_colCount = 0;
}
bool wxRichTextTable::CreateTable(int rows, int cols)
{
ClearTable();
+ wxRichTextAttr cellattr;
+ cellattr.SetTextColour(GetBasicStyle().GetTextColour());
+
m_rowCount = rows;
m_colCount = cols;
for (j = 0; j < cols; j++)
{
wxRichTextCell* cell = new wxRichTextCell;
+ cell->GetAttributes() = cellattr;
+
AppendChild(cell);
cell->AddParagraph(wxEmptyString);
return true;
}
+wxPosition wxRichTextTable::GetFocusedCell() const
+{
+ wxPosition position(-1, -1);
+ const wxRichTextObject* focus = GetBuffer()->GetRichTextCtrl()->GetFocusObject();
+
+ for (int row = 0; row < GetRowCount(); ++row)
+ {
+ for (int col = 0; col < GetColumnCount(); ++col)
+ {
+ if (GetCell(row, col) == focus)
+ {
+ position.SetRow(row);
+ position.SetCol(col);
+ return position;
+ }
+ }
+ }
+
+ return position;
+}
+
+int wxRichTextTable::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
+{
+ for (int row = 0; row < GetRowCount(); ++row)
+ {
+ for (int col = 0; col < GetColumnCount(); ++col)
+ {
+ wxRichTextCell* cell = GetCell(row, col);
+ if (cell->wxRichTextObject::HitTest(dc, context, pt, textPosition, obj, contextObj, flags) != wxRICHTEXT_HITTEST_NONE)
+ {
+ return cell->HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
+ }
+ }
+ }
+
+ return wxRICHTEXT_HITTEST_NONE;
+}
+
bool wxRichTextTable::DeleteRows(int startRow, int noRows)
{
- wxASSERT((startRow + noRows) < m_rowCount);
- if ((startRow + noRows) >= m_rowCount)
+ wxASSERT((startRow + noRows) <= m_rowCount);
+ if ((startRow + noRows) > m_rowCount)
return false;
+ wxCHECK_MSG(noRows != m_rowCount, false, "Trying to delete all the cells in a table");
+
+ wxRichTextBuffer* buffer = GetBuffer();
+ wxRichTextCtrl* rtc = buffer->GetRichTextCtrl();
+
+ wxRichTextAction* action = NULL;
+ wxRichTextTable* clone = NULL;
+ if (!rtc->SuppressingUndo())
+ {
+ // Create a clone containing the current state of the table. It will be used to Undo the action
+ clone = wxStaticCast(this->Clone(), wxRichTextTable);
+ clone->SetParent(GetParent());
+ action = new wxRichTextAction(NULL, _("Delete Row"), wxRICHTEXT_CHANGE_OBJECT, buffer, this, rtc);
+ action->SetObject(this);
+ action->SetPosition(GetRange().GetStart());
+ }
+
int i, j;
for (i = startRow; i < (startRow+noRows); i++)
{
m_rowCount = m_rowCount - noRows;
+ if (!rtc->SuppressingUndo())
+ {
+ buffer->SubmitAction(action);
+ // Finally store the original-state clone; doing so earlier would cause various failures
+ action->StoreObject(clone);
+ }
+
return true;
}
bool wxRichTextTable::DeleteColumns(int startCol, int noCols)
{
- wxASSERT((startCol + noCols) < m_colCount);
- if ((startCol + noCols) >= m_colCount)
+ wxASSERT((startCol + noCols) <= m_colCount);
+ if ((startCol + noCols) > m_colCount)
return false;
+ wxCHECK_MSG(noCols != m_colCount, false, "Trying to delete all the cells in a table");
+
+ wxRichTextBuffer* buffer = GetBuffer();
+ wxRichTextCtrl* rtc = buffer->GetRichTextCtrl();
+
+ wxRichTextAction* action = NULL;
+ wxRichTextTable* clone = NULL;
+ if (!rtc->SuppressingUndo())
+ {
+ // Create a clone containing the current state of the table. It will be used to Undo the action
+ clone = wxStaticCast(this->Clone(), wxRichTextTable);
+ clone->SetParent(GetParent());
+ action = new wxRichTextAction(NULL, _("Delete Column"), wxRICHTEXT_CHANGE_OBJECT, buffer, this, rtc);
+ action->SetObject(this);
+ action->SetPosition(GetRange().GetStart());
+ }
+
bool deleteRows = (noCols == m_colCount);
int i, j;
for (i = 0; i < m_rowCount; i++)
{
wxRichTextObjectPtrArray& colArray = m_cells[deleteRows ? 0 : i];
- for (j = startCol; j < (startCol+noCols); j++)
+ for (j = 0; j < noCols; j++)
{
- wxRichTextObject* cell = colArray[j];
+ wxRichTextObject* cell = colArray[startCol];
RemoveChild(cell, true);
+ colArray.RemoveAt(startCol);
}
if (deleteRows)
m_rowCount = 0;
m_colCount = m_colCount - noCols;
+ if (!rtc->SuppressingUndo())
+ {
+ buffer->SubmitAction(action);
+ // Finally store the original-state clone; doing so earlier would cause various failures
+ action->StoreObject(clone);
+ }
+
return true;
}
if (startRow > m_rowCount)
return false;
+ wxRichTextBuffer* buffer = GetBuffer();
+ wxRichTextAction* action = NULL;
+ wxRichTextTable* clone = NULL;
+
+ if (!buffer->GetRichTextCtrl()->SuppressingUndo())
+ {
+ // Create a clone containing the current state of the table. It will be used to Undo the action
+ clone = wxStaticCast(this->Clone(), wxRichTextTable);
+ clone->SetParent(GetParent());
+ action = new wxRichTextAction(NULL, _("Add Row"), wxRICHTEXT_CHANGE_OBJECT, buffer, this, buffer->GetRichTextCtrl());
+ action->SetObject(this);
+ action->SetPosition(GetRange().GetStart());
+ }
+
+ wxRichTextAttr cellattr = attr;
+ if (!cellattr.GetTextColour().IsOk())
+ cellattr.SetTextColour(buffer->GetBasicStyle().GetTextColour());
+
int i, j;
for (i = 0; i < noRows; i++)
{
for (j = 0; j < m_colCount; j++)
{
wxRichTextCell* cell = new wxRichTextCell;
- cell->GetAttributes() = attr;
+ cell->GetAttributes() = cellattr;
AppendChild(cell);
+ cell->AddParagraph(wxEmptyString);
colArray.Add(cell);
}
}
m_rowCount = m_rowCount + noRows;
+
+ if (!buffer->GetRichTextCtrl()->SuppressingUndo())
+ {
+ buffer->SubmitAction(action);
+ // Finally store the original-state clone; doing so earlier would cause various failures
+ action->StoreObject(clone);
+ }
+
return true;
}
if (startCol > m_colCount)
return false;
+ wxRichTextBuffer* buffer = GetBuffer();
+ wxRichTextAction* action = NULL;
+ wxRichTextTable* clone = NULL;
+
+ if (!buffer->GetRichTextCtrl()->SuppressingUndo())
+ {
+ // Create a clone containing the current state of the table. It will be used to Undo the action
+ clone = wxStaticCast(this->Clone(), wxRichTextTable);
+ clone->SetParent(GetParent());
+ action = new wxRichTextAction(NULL, _("Add Column"), wxRICHTEXT_CHANGE_OBJECT, buffer, this, buffer->GetRichTextCtrl());
+ action->SetObject(this);
+ action->SetPosition(GetRange().GetStart());
+ }
+
+ wxRichTextAttr cellattr = attr;
+ if (!cellattr.GetTextColour().IsOk())
+ cellattr.SetTextColour(buffer->GetBasicStyle().GetTextColour());
+
int i, j;
for (i = 0; i < m_rowCount; i++)
{
for (j = 0; j < noCols; j++)
{
wxRichTextCell* cell = new wxRichTextCell;
- cell->GetAttributes() = attr;
+ cell->GetAttributes() = cellattr;
AppendChild(cell);
+ cell->AddParagraph(wxEmptyString);
if (startCol == m_colCount)
colArray.Add(cell);
m_colCount = m_colCount + noCols;
+ if (!buffer->GetRichTextCtrl()->SuppressingUndo())
+ {
+ buffer->SubmitAction(action);
+ // Finally store the original-state clone; doing so earlier would cause various failures
+ action->StoreObject(clone);
+ }
+
return true;
}
return false;
}
+bool wxRichTextTableBlock::ComputeBlockForSelection(wxRichTextTable* table, wxRichTextCtrl* ctrl, bool requireCellSelection)
+{
+ if (!ctrl)
+ return false;
+
+ ColStart() = 0;
+ ColEnd() = table->GetColumnCount()-1;
+ RowStart() = 0;
+ RowEnd() = table->GetRowCount()-1;
+
+ wxRichTextSelection selection = ctrl->GetSelection();
+ if (selection.IsValid() && selection.GetContainer() == table)
+ {
+ // Start with an invalid block and increase.
+ wxRichTextTableBlock selBlock(-1, -1, -1, -1);
+ wxRichTextRangeArray ranges = selection.GetRanges();
+ int row, col;
+ for (row = 0; row < table->GetRowCount(); row++)
+ {
+ for (col = 0; col < table->GetColumnCount(); col++)
+ {
+ if (selection.WithinSelection(table->GetCell(row, col)->GetRange().GetStart()))
+ {
+ if (selBlock.ColStart() == -1)
+ selBlock.ColStart() = col;
+ if (selBlock.ColEnd() == -1)
+ selBlock.ColEnd() = col;
+ if (col < selBlock.ColStart())
+ selBlock.ColStart() = col;
+ if (col > selBlock.ColEnd())
+ selBlock.ColEnd() = col;
+
+ if (selBlock.RowStart() == -1)
+ selBlock.RowStart() = row;
+ if (selBlock.RowEnd() == -1)
+ selBlock.RowEnd() = row;
+ if (row < selBlock.RowStart())
+ selBlock.RowStart() = row;
+ if (row > selBlock.RowEnd())
+ selBlock.RowEnd() = row;
+ }
+ }
+ }
+
+ if (selBlock.RowStart() != -1 && selBlock.RowEnd() != -1 && selBlock.ColStart() != -1 && selBlock.ColEnd() != -1)
+ (*this) = selBlock;
+ }
+ else
+ {
+ // See if a whole cell's contents is selected, in which case we can treat the cell as selected.
+ // wxRTC lacks the ability to select a single cell.
+ wxRichTextCell* cell = wxDynamicCast(ctrl->GetFocusObject(), wxRichTextCell);
+ if (cell && (!requireCellSelection || (ctrl->HasSelection() && ctrl->GetSelectionRange() == cell->GetOwnRange())))
+ {
+ int row, col;
+ if (table->GetCellRowColumnPosition(cell->GetRange().GetStart(), row, col))
+ {
+ RowStart() = row;
+ RowEnd() = row;
+ ColStart() = col;
+ ColEnd() = col;
+ }
+ }
+ }
+
+ return true;
+}
+
+// Does this block represent the whole table?
+bool wxRichTextTableBlock::IsWholeTable(wxRichTextTable* table) const
+{
+ return (ColStart() == 0 && RowStart() == 0 && ColEnd() == (table->GetColumnCount()-1) && RowEnd() == (table->GetRowCount()-1));
+}
+
+// Returns the cell focused in the table, if any
+wxRichTextCell* wxRichTextTableBlock::GetFocusedCell(wxRichTextCtrl* ctrl)
+{
+ if (!ctrl)
+ return NULL;
+
+ wxRichTextCell* cell = wxDynamicCast(ctrl->GetFocusObject(), wxRichTextCell);
+ return cell;
+}
+
/*
* Module to initialise and clean up handlers
*/
wxRichTextBuffer::SetRenderer(new wxRichTextStdRenderer);
wxRichTextBuffer::InitStandardHandlers();
wxRichTextParagraph::InitDefaultTabs();
+
+ wxRichTextXMLHandler::RegisterNodeName(wxT("text"), wxT("wxRichTextPlainText"));
+ wxRichTextXMLHandler::RegisterNodeName(wxT("symbol"), wxT("wxRichTextPlainText"));
+ wxRichTextXMLHandler::RegisterNodeName(wxT("image"), wxT("wxRichTextImage"));
+ wxRichTextXMLHandler::RegisterNodeName(wxT("paragraph"), wxT("wxRichTextParagraph"));
+ wxRichTextXMLHandler::RegisterNodeName(wxT("paragraphlayout"), wxT("wxRichTextParagraphLayoutBox"));
+ wxRichTextXMLHandler::RegisterNodeName(wxT("textbox"), wxT("wxRichTextBox"));
+ wxRichTextXMLHandler::RegisterNodeName(wxT("cell"), wxT("wxRichTextCell"));
+ wxRichTextXMLHandler::RegisterNodeName(wxT("table"), wxT("wxRichTextTable"));
+ wxRichTextXMLHandler::RegisterNodeName(wxT("field"), wxT("wxRichTextField"));
+
return true;
}
void OnExit()
{
wxRichTextBuffer::CleanUpHandlers();
wxRichTextBuffer::CleanUpDrawingHandlers();
+ wxRichTextBuffer::CleanUpFieldTypes();
+ wxRichTextXMLHandler::ClearNodeToClassMap();
wxRichTextDecimalToRoman(-1);
wxRichTextParagraph::ClearDefaultTabs();
wxRichTextCtrl::ClearAvailableFontNames();
void wxRichTextModuleInit()
{
wxModule* module = new wxRichTextModule;
- module->Init();
wxModule::RegisterModule(module);
+ wxModule::InitializeModules();
}
// first, but of course this means we'll be doing it twice.
if (!m_buffer->IsDirty() && m_ctrl) // can only do optimisation if the buffer is already laid out correctly
{
- wxSize clientSize = m_ctrl->GetClientSize();
- wxPoint firstVisiblePt = m_ctrl->GetFirstVisiblePoint();
+ wxSize clientSize = m_ctrl->GetUnscaledSize(m_ctrl->GetClientSize());
+ wxPoint firstVisiblePt = m_ctrl->GetUnscaledPoint(m_ctrl->GetFirstVisiblePoint());
int lastY = firstVisiblePt.y + clientSize.y;
wxRichTextParagraph* para = container->GetParagraphAtPosition(GetRange().GetStart());
UpdateAppearance(newCaretPosition, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions, true /* do */);
wxRichTextEvent cmdEvent(
- wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED,
+ wxEVT_RICHTEXT_CONTENT_INSERTED,
m_ctrl ? m_ctrl->GetId() : -1);
cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
cmdEvent.SetRange(GetRange());
UpdateAppearance(caretPos, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions, true /* do */);
wxRichTextEvent cmdEvent(
- wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED,
+ wxEVT_RICHTEXT_CONTENT_DELETED,
m_ctrl ? m_ctrl->GetId() : -1);
cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
cmdEvent.SetRange(GetRange());
{
ApplyParagraphs(GetNewParagraphs());
- // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
- // Layout() would stop prematurely at the top level.
- container->InvalidateHierarchy(GetRange());
+ // Invalidate the whole buffer if there were floating objects
+ if (wxRichTextBuffer::GetFloatingLayoutMode() && container->GetFloatingObjectCount() > 0)
+ m_buffer->InvalidateHierarchy(wxRICHTEXT_ALL);
+ else
+ {
+ // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
+ // Layout() would stop prematurely at the top level.
+ container->InvalidateHierarchy(GetRange());
+ }
UpdateAppearance(GetPosition());
wxRichTextEvent cmdEvent(
- m_cmdId == wxRICHTEXT_CHANGE_STYLE ? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED : wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED,
+ m_cmdId == wxRICHTEXT_CHANGE_STYLE ? wxEVT_RICHTEXT_STYLE_CHANGED : wxEVT_RICHTEXT_PROPERTIES_CHANGED,
m_ctrl ? m_ctrl->GetId() : -1);
cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
cmdEvent.SetRange(GetRange());
// InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
// Layout() would stop prematurely at the top level.
- container->InvalidateHierarchy(GetRange());
+ // Invalidate the whole buffer if there were floating objects
+ if (wxRichTextBuffer::GetFloatingLayoutMode() && container->GetFloatingObjectCount() > 0)
+ m_buffer->InvalidateHierarchy(wxRICHTEXT_ALL);
+ else
+ container->InvalidateHierarchy(GetRange());
UpdateAppearance(GetPosition());
wxRichTextEvent cmdEvent(
- wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED,
+ wxEVT_RICHTEXT_STYLE_CHANGED,
m_ctrl ? m_ctrl->GetId() : -1);
cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
cmdEvent.SetRange(GetRange());
case wxRICHTEXT_CHANGE_OBJECT:
{
wxRichTextObject* obj = m_objectAddress.GetObject(m_buffer);
- // wxRichTextObject* obj = container->GetChildAtPosition(GetRange().GetStart());
- if (obj && m_object)
+ if (obj && m_object && m_ctrl)
{
- wxRichTextObjectList::compatibility_iterator node = container->GetChildren().Find(obj);
+ // The plan is to swap the current object with the stored, previous-state, clone
+ // We can't get 'node' from the containing buffer (as it doesn't directly store objects)
+ // so use the parent paragraph
+ wxRichTextParagraph* para = wxDynamicCast(obj->GetParent(), wxRichTextParagraph);
+ wxCHECK_MSG(para, false, "Invalid parent paragraph");
+
+ // The stored object, m_object, may have a stale parent paragraph. This would cause
+ // a crash during layout, so use obj's parent para, which should be the correct one.
+ // (An alternative would be to return the parent too from m_objectAddress.GetObject(),
+ // or to set obj's parent there before returning)
+ m_object->SetParent(para);
+
+ wxRichTextObjectList::compatibility_iterator node = para->GetChildren().Find(obj);
if (node)
{
wxRichTextObject* obj = node->GetData();
}
}
+ // We can't rely on the current focus-object remaining valid, if it's e.g. a table's cell.
+ // And we can't cope with this in the calling code: a user may later click in the cell
+ // before deciding to Undo() or Redo(). So play safe and set focus to the buffer.
+ if (m_ctrl)
+ m_ctrl->SetFocusObject(m_buffer, false);
+
// InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
// Layout() would stop prematurely at the top level.
- container->InvalidateHierarchy(GetRange());
+ // Invalidate the whole buffer if there were floating objects
+ if (wxRichTextBuffer::GetFloatingLayoutMode() && container->GetFloatingObjectCount() > 0)
+ m_buffer->InvalidateHierarchy(wxRICHTEXT_ALL);
+ else
+ container->InvalidateHierarchy(GetRange());
- UpdateAppearance(GetPosition());
+ UpdateAppearance(GetPosition(), true);
// TODO: send new kind of modification event
container->DeleteRange(GetRange());
container->UpdateRanges();
+
// InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
// Layout() would stop prematurely at the top level.
container->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
UpdateAppearance(newCaretPosition, true, /* send update event */ & optimizationLineCharPositions, & optimizationLineYPositions, false /* undo */);
wxRichTextEvent cmdEvent(
- wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED,
+ wxEVT_RICHTEXT_CONTENT_DELETED,
m_ctrl ? m_ctrl->GetId() : -1);
cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
cmdEvent.SetRange(GetRange());
container->InsertFragment(GetRange().GetStart(), m_oldParagraphs);
container->UpdateRanges();
+
// InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
// Layout() would stop prematurely at the top level.
container->InvalidateHierarchy(GetRange());
UpdateAppearance(GetPosition(), true, /* send update event */ & optimizationLineCharPositions, & optimizationLineYPositions, false /* undo */);
wxRichTextEvent cmdEvent(
- wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED,
+ wxEVT_RICHTEXT_CONTENT_INSERTED,
m_ctrl ? m_ctrl->GetId() : -1);
cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
cmdEvent.SetRange(GetRange());
UpdateAppearance(GetPosition());
wxRichTextEvent cmdEvent(
- m_cmdId == wxRICHTEXT_CHANGE_STYLE ? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED : wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED,
+ m_cmdId == wxRICHTEXT_CHANGE_STYLE ? wxEVT_RICHTEXT_STYLE_CHANGED : wxEVT_RICHTEXT_PROPERTIES_CHANGED,
m_ctrl ? m_ctrl->GetId() : -1);
cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
cmdEvent.SetRange(GetRange());
// Refresh everything if there were floating objects or the container changed size
// (we can't yet optimize in these cases, since more complex interaction with other content occurs)
- if (container->GetFloatingObjectCount() > 0 || (container->GetParent() && containerRect != container->GetRect()))
+ if ((wxRichTextBuffer::GetFloatingLayoutMode() && container->GetFloatingObjectCount() > 0) || (container->GetParent() && containerRect != container->GetRect()))
{
m_ctrl->Refresh(false);
}
{
size_t i;
- wxSize clientSize = m_ctrl->GetClientSize();
- wxPoint firstVisiblePt = m_ctrl->GetFirstVisiblePoint();
+ wxSize clientSize = m_ctrl->GetUnscaledSize(m_ctrl->GetClientSize());
+ wxPoint firstVisiblePt = m_ctrl->GetUnscaledPoint(m_ctrl->GetFirstVisiblePoint());
// Start/end positions
int firstY = 0;
// 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.
+ long pos = GetRange().GetStart();
- wxRichTextParagraph* para = container->GetParagraphAtPosition(GetPosition());
+ wxRichTextParagraph* para = container->GetParagraphAtPosition(pos, false /* is not caret pos */);
// Since we support floating layout, we should redraw the whole para instead of just
// the first line touching the invalid range.
if (para)
{
- firstY = para->GetPosition().y;
+ // In case something was drawn above the paragraph,
+ // such as a line break, allow a little extra.
+ firstY = para->GetPosition().y - 4;
}
wxRichTextObjectList::compatibility_iterator node = container->GetChildren().Find(para);
// Stop, we're now the same as we were
foundEnd = true;
- lastY = pt.y;
+ lastY = pt.y + line->GetSize().y;
node2 = wxRichTextLineList::compatibility_iterator();
node = wxRichTextObjectList::compatibility_iterator();
lastY = firstVisiblePt.y + clientSize.y;
// Convert to device coordinates
- wxRect rect(m_ctrl->GetPhysicalPoint(wxPoint(firstVisiblePt.x, firstY)), wxSize(clientSize.x, lastY - firstY));
+ wxRect rect(m_ctrl->GetPhysicalPoint(m_ctrl->GetScaledPoint(wxPoint(firstVisiblePt.x, firstY))), m_ctrl->GetScaledSize(wxSize(clientSize.x, lastY - firstY)));
m_ctrl->RefreshRect(rect);
}
else
wxRichTextImage::wxRichTextImage(const wxImage& image, wxRichTextObject* parent, wxRichTextAttr* charStyle):
wxRichTextObject(parent)
{
+ Init();
m_imageBlock.MakeImageBlockDefaultQuality(image, wxBITMAP_TYPE_PNG);
if (charStyle)
SetAttributes(*charStyle);
}
-wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock& imageBlock, wxRichTextObject* parent, wxRichTextAttr* charStyle):
- wxRichTextObject(parent)
-{
- m_imageBlock = imageBlock;
- if (charStyle)
- SetAttributes(*charStyle);
-}
+wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock& imageBlock, wxRichTextObject* parent, wxRichTextAttr* charStyle):
+ wxRichTextObject(parent)
+{
+ Init();
+ m_imageBlock = imageBlock;
+ if (charStyle)
+ SetAttributes(*charStyle);
+}
+
+wxRichTextImage::~wxRichTextImage()
+{
+}
+
+void wxRichTextImage::Init()
+{
+ m_originalImageSize = wxSize(-1, -1);
+}
+
+/// Create a cached image at the required size
+bool wxRichTextImage::LoadImageCache(wxDC& dc, bool resetCache, const wxSize& parentSize)
+{
+ if (!m_imageBlock.IsOk())
+ return false;
+
+ // If we have an original image size, use that to compute the cached bitmap size
+ // instead of loading the image each time. This way we can avoid loading
+ // the image so long as the new cached bitmap size hasn't changed.
+
+ wxImage image;
+ if (resetCache || m_originalImageSize.GetWidth() <= 0 || m_originalImageSize.GetHeight() <= 0)
+ {
+ m_imageCache = wxNullBitmap;
+
+ m_imageBlock.Load(image);
+ if (!image.IsOk())
+ return false;
+
+ m_originalImageSize = wxSize(image.GetWidth(), image.GetHeight());
+ }
+
+ int width = m_originalImageSize.GetWidth();
+ int height = m_originalImageSize.GetHeight();
+
+ int parentWidth = 0;
+ int parentHeight = 0;
+
+ int maxWidth = -1;
+ int maxHeight = -1;
+
+ wxSize sz = parentSize;
+ if (sz == wxDefaultSize)
+ {
+ if (GetParent() && GetParent()->GetParent())
+ sz = GetParent()->GetParent()->GetCachedSize();
+ }
+
+ if (sz != wxDefaultSize)
+ {
+ wxRichTextBuffer* buffer = GetBuffer();
+ if (buffer)
+ {
+ // Find the actual space available when margin is taken into account
+ wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
+ marginRect = wxRect(0, 0, sz.x, sz.y);
+ if (GetParent() && GetParent()->GetParent())
+ {
+ buffer->GetBoxRects(dc, buffer, GetParent()->GetParent()->GetAttributes(), marginRect, borderRect, contentRect, paddingRect, outlineRect);
+ sz = contentRect.GetSize();
+ }
+
+ // Use a minimum size to stop images becoming very small
+ parentWidth = wxMax(100, sz.GetWidth());
+ parentHeight = wxMax(100, sz.GetHeight());
+
+ if (buffer->GetRichTextCtrl())
+ // Start with a maximum width of the control size, even if not specified by the content,
+ // to minimize the amount of picture overlapping the right-hand side
+ maxWidth = parentWidth;
+ }
+ }
+
+ if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetWidth().GetValue() > 0)
+ {
+ if (parentWidth > 0 && GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
+ width = (int) ((GetAttributes().GetTextBoxAttr().GetWidth().GetValue() * parentWidth)/100.0);
+ else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
+ width = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetWidth().GetValue());
+ else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
+ width = GetAttributes().GetTextBoxAttr().GetWidth().GetValue();
+ }
+
+ // Limit to max width
+
+ if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() > 0)
+ {
+ int mw = -1;
+
+ if (parentWidth > 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
+ mw = (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() * parentWidth)/100.0);
+ else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
+ mw = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue());
+ else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
+ mw = GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue();
+
+ // If we already have a smaller max width due to the constraints of the control size,
+ // don't use the larger max width.
+ if (mw != -1 && ((maxWidth == -1) || (mw < maxWidth)))
+ maxWidth = mw;
+ }
+
+ if (maxWidth > 0 && width > maxWidth)
+ width = maxWidth;
+
+ // Preserve the aspect ratio
+ if (width != m_originalImageSize.GetWidth())
+ height = (int) (float(m_originalImageSize.GetHeight()) * (float(width)/float(m_originalImageSize.GetWidth())));
+
+ if (GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetHeight().GetValue() > 0)
+ {
+ if (parentHeight > 0 && GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
+ height = (int) ((GetAttributes().GetTextBoxAttr().GetHeight().GetValue() * parentHeight)/100.0);
+ else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
+ height = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetHeight().GetValue());
+ else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
+ height = GetAttributes().GetTextBoxAttr().GetHeight().GetValue();
+
+ // Preserve the aspect ratio
+ if (height != m_originalImageSize.GetHeight())
+ width = (int) (float(m_originalImageSize.GetWidth()) * (float(height)/float(m_originalImageSize.GetHeight())));
+ }
+
+ // Limit to max height
-/// Create a cached image at the required size
-bool wxRichTextImage::LoadImageCache(wxDC& dc, bool resetCache)
-{
- if (resetCache || !m_imageCache.IsOk() /* || m_imageCache.GetWidth() != size.x || m_imageCache.GetHeight() != size.y */)
+ if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() > 0)
{
- if (!m_imageBlock.IsOk())
- return false;
+ if (parentHeight > 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
+ maxHeight = (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() * parentHeight)/100.0);
+ else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
+ maxHeight = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue());
+ else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
+ maxHeight = GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue();
+ }
- wxImage image;
- m_imageBlock.Load(image);
- if (!image.IsOk())
- return false;
+ if (maxHeight > 0 && height > maxHeight)
+ {
+ height = maxHeight;
- int width = image.GetWidth();
- int height = image.GetHeight();
+ // Preserve the aspect ratio
+ if (height != m_originalImageSize.GetHeight())
+ width = (int) (float(m_originalImageSize.GetWidth()) * (float(height)/float(m_originalImageSize.GetHeight())));
+ }
- if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetWidth().GetValue() > 0)
- {
- if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
- width = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetWidth().GetValue());
- else
- width = GetAttributes().GetTextBoxAttr().GetWidth().GetValue();
- }
- if (GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetHeight().GetValue() > 0)
+ // Prevent the use of zero size
+ width = wxMax(1, width);
+ height = wxMax(1, height);
+
+ if (m_imageCache.IsOk() && m_imageCache.GetWidth() == width && m_imageCache.GetHeight() == height)
+ {
+ // Do nothing, we didn't need to change the image cache
+ }
+ else
+ {
+ if (!image.IsOk())
{
- if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
- height = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetHeight().GetValue());
- else
- height = GetAttributes().GetTextBoxAttr().GetHeight().GetValue();
+ m_imageBlock.Load(image);
+ if (!image.IsOk())
+ return false;
}
if (image.GetWidth() == width && image.GetHeight() == height)
}
/// Draw the item
-bool wxRichTextImage::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int WXUNUSED(descent), int WXUNUSED(style))
+bool wxRichTextImage::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& WXUNUSED(range), const wxRichTextSelection& selection, const wxRect& rect, int WXUNUSED(descent), int WXUNUSED(style))
{
if (!IsShown())
return true;
DrawBoxAttributes(dc, GetBuffer(), attr, wxRect(rect.GetPosition(), GetCachedSize()));
-#if 0
- int y = rect.y + (rect.height - m_imageCache.GetHeight());
-
- dc.DrawBitmap(m_imageCache, rect.x, y, true);
-#endif
-
wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
marginRect = rect; // outer rectangle, will calculate contentRect
dc.DrawBitmap(m_imageCache, contentRect.x, contentRect.y, true);
- if (selection.WithinSelection(range.GetStart(), this))
+ if (selection.WithinSelection(GetRange().GetStart(), this))
{
wxCheckSetBrush(dc, *wxBLACK_BRUSH);
wxCheckSetPen(dc, *wxBLACK_PEN);
/// Get/set the object size for the given range. Returns false if the range
/// is invalid for this object.
-bool wxRichTextImage::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& WXUNUSED(descent), wxDC& dc, wxRichTextDrawingContext& context, int WXUNUSED(flags), wxPoint WXUNUSED(position), wxArrayInt* partialExtents) const
+bool wxRichTextImage::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& WXUNUSED(descent), wxDC& dc, wxRichTextDrawingContext& context, int WXUNUSED(flags), const wxPoint& WXUNUSED(position), const wxSize& parentSize, wxArrayInt* partialExtents) const
{
if (!range.IsWithin(GetRange()))
return false;
- if (!((wxRichTextImage*)this)->LoadImageCache(dc))
+ if (!((wxRichTextImage*)this)->LoadImageCache(dc, false, parentSize))
{
size.x = 0; size.y = 0;
if (partialExtents)
wxRichTextObject::Copy(obj);
m_imageBlock = obj.m_imageBlock;
+ m_originalImageSize = obj.m_originalImageSize;
}
/// Edit properties via a GUI
return (attr1 == attr2);
}
-// Partial equality test taking flags into account
-bool wxTextAttrEqPartial(const wxRichTextAttr& attr1, const wxRichTextAttr& attr2)
-{
- return attr1.EqPartial(attr2);
-}
-
/// Compare tabs
bool wxRichTextTabsEq(const wxArrayInt& tabs1, const wxArrayInt& tabs2)
{
* The data object for a wxRichTextBuffer
*/
-const wxChar *wxRichTextBufferDataObject::ms_richTextBufferFormatId = wxT("wxShape");
+const wxChar *wxRichTextBufferDataObject::ms_richTextBufferFormatId = wxT("wxRichText");
wxRichTextBufferDataObject::wxRichTextBufferDataObject(wxRichTextBuffer* richTextBuffer)
{
public:
wxRichTextFontTableData() {}
- wxFont FindFont(const wxRichTextAttr& fontSpec);
+ wxFont FindFont(const wxRichTextAttr& fontSpec, double fontScale);
wxRichTextFontTableHashMap m_hashMap;
};
-wxFont wxRichTextFontTableData::FindFont(const wxRichTextAttr& fontSpec)
+wxFont wxRichTextFontTableData::FindFont(const wxRichTextAttr& fontSpec, double fontScale)
{
wxString facename(fontSpec.GetFontFaceName());
- wxString spec(wxString::Format(wxT("%d-%d-%d-%d-%s-%d"), fontSpec.GetFontSize(), fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), (int) fontSpec.GetFontUnderlined(), facename.c_str(), (int) fontSpec.GetFontEncoding()));
- wxRichTextFontTableHashMap::iterator entry = m_hashMap.find(spec);
+ int fontSize = fontSpec.GetFontSize();
+ if (fontScale != 1.0)
+ fontSize = (int) ((double(fontSize) * fontScale) + 0.5);
+
+ wxString units;
+ if (fontSpec.HasFontPixelSize() && !fontSpec.HasFontPointSize())
+ units = wxT("px");
+ else
+ units = wxT("pt");
+ wxString spec = wxString::Format(wxT("%d-%s-%d-%d-%d-%d-%s-%d"),
+ fontSize, units.c_str(), fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), (int) fontSpec.GetFontUnderlined(), (int) fontSpec.GetFontStrikethrough(),
+ facename.c_str(), (int) fontSpec.GetFontEncoding());
+
+ wxRichTextFontTableHashMap::iterator entry = m_hashMap.find(spec);
if ( entry == m_hashMap.end() )
{
- wxFont font(fontSpec.GetFontSize(), wxDEFAULT, fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), fontSpec.GetFontUnderlined(), facename.c_str());
- m_hashMap[spec] = font;
- return font;
+ if (fontSpec.HasFontPixelSize() && !fontSpec.HasFontPointSize())
+ {
+ wxFont font(wxSize(0, fontSize), wxFONTFAMILY_DEFAULT, fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), fontSpec.GetFontUnderlined(), facename);
+ if (fontSpec.HasFontStrikethrough() && fontSpec.GetFontStrikethrough())
+ font.SetStrikethrough(true);
+ m_hashMap[spec] = font;
+ return font;
+ }
+ else
+ {
+ wxFont font(fontSize, wxFONTFAMILY_DEFAULT, fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), fontSpec.GetFontUnderlined(), facename.c_str());
+ if (fontSpec.HasFontStrikethrough() && fontSpec.GetFontStrikethrough())
+ font.SetStrikethrough(true);
+
+ m_hashMap[spec] = font;
+ return font;
+ }
}
else
{
wxRichTextFontTable::wxRichTextFontTable()
{
m_refData = new wxRichTextFontTableData;
+ m_fontScale = 1.0;
}
wxRichTextFontTable::wxRichTextFontTable(const wxRichTextFontTable& table)
void wxRichTextFontTable::operator= (const wxRichTextFontTable& table)
{
Ref(table);
+ m_fontScale = table.m_fontScale;
}
wxFont wxRichTextFontTable::FindFont(const wxRichTextAttr& fontSpec)
{
wxRichTextFontTableData* data = (wxRichTextFontTableData*) m_refData;
if (data)
- return data->FindFont(fontSpec);
+ return data->FindFont(fontSpec, m_fontScale);
else
return wxFont();
}
data->m_hashMap.clear();
}
-// wxTextBoxAttr
+void wxRichTextFontTable::SetFontScale(double fontScale)
+{
+ if (fontScale != m_fontScale)
+ Clear();
+ m_fontScale = fontScale;
+}
+// wxTextBoxAttr
void wxTextBoxAttr::Reset()
{
}
// Partial equality test
-bool wxTextBoxAttr::EqPartial(const wxTextBoxAttr& attr) const
+bool wxTextBoxAttr::EqPartial(const wxTextBoxAttr& attr, bool weakTest) const
{
+ if (!weakTest &&
+ ((!HasFloatMode() && attr.HasFloatMode()) ||
+ (!HasClearMode() && attr.HasClearMode()) ||
+ (!HasCollapseBorders() && attr.HasCollapseBorders()) ||
+ (!HasVerticalAlignment() && attr.HasVerticalAlignment()) ||
+ (!HasBoxStyleName() && attr.HasBoxStyleName())))
+ {
+ return false;
+ }
if (attr.HasFloatMode() && HasFloatMode() && (GetFloatMode() != attr.GetFloatMode()))
return false;
// Position
- if (!m_position.EqPartial(attr.m_position))
+ if (!m_position.EqPartial(attr.m_position, weakTest))
return false;
// Size
- if (!m_size.EqPartial(attr.m_size))
+ if (!m_size.EqPartial(attr.m_size, weakTest))
return false;
- if (!m_minSize.EqPartial(attr.m_minSize))
+ if (!m_minSize.EqPartial(attr.m_minSize, weakTest))
return false;
- if (!m_maxSize.EqPartial(attr.m_maxSize))
+ if (!m_maxSize.EqPartial(attr.m_maxSize, weakTest))
return false;
// Margins
- if (!m_margins.EqPartial(attr.m_margins))
+ if (!m_margins.EqPartial(attr.m_margins, weakTest))
return false;
// Padding
- if (!m_padding.EqPartial(attr.m_padding))
+ if (!m_padding.EqPartial(attr.m_padding, weakTest))
return false;
// Border
- if (!GetBorder().EqPartial(attr.GetBorder()))
+ if (!GetBorder().EqPartial(attr.GetBorder(), weakTest))
return false;
// Outline
- if (!GetOutline().EqPartial(attr.GetOutline()))
+ if (!GetOutline().EqPartial(attr.GetOutline(), weakTest))
return false;
return true;
return (m_textBoxAttr == attr.m_textBoxAttr);
}
-// Partial equality test taking comparison object into account
-bool wxRichTextAttr::EqPartial(const wxRichTextAttr& attr) const
+// Partial equality test
+bool wxRichTextAttr::EqPartial(const wxRichTextAttr& attr, bool weakTest) const
{
- if (!(wxTextAttr::EqPartial(attr)))
+ if (!(wxTextAttr::EqPartial(attr, weakTest)))
return false;
- return m_textBoxAttr.EqPartial(attr.m_textBoxAttr);
+ return m_textBoxAttr.EqPartial(attr.m_textBoxAttr, weakTest);
}
// Merges the given attributes. If compareWith
}
// Partial equality test
-bool wxTextAttrBorder::EqPartial(const wxTextAttrBorder& border) const
+bool wxTextAttrBorder::EqPartial(const wxTextAttrBorder& border, bool weakTest) const
{
- if (border.HasStyle() && !HasStyle() && (border.GetStyle() != GetStyle()))
+ if (!weakTest &&
+ ((!HasStyle() && border.HasStyle()) ||
+ (!HasColour() && border.HasColour()) ||
+ (!HasWidth() && border.HasWidth())))
+ {
+ return false;
+ }
+
+ if (border.HasStyle() && HasStyle() && (border.GetStyle() != GetStyle()))
return false;
- if (border.HasColour() && !HasColour() && (border.GetColourLong() != GetColourLong()))
+ if (border.HasColour() && HasColour() && (border.GetColourLong() != GetColourLong()))
return false;
- if (border.HasWidth() && !HasWidth() && !(border.GetWidth() == GetWidth()))
+ if (border.HasWidth() && HasWidth() && !(border.GetWidth() == GetWidth()))
return false;
return true;
}
// Partial equality test
-bool wxTextAttrBorders::EqPartial(const wxTextAttrBorders& borders) const
+bool wxTextAttrBorders::EqPartial(const wxTextAttrBorders& borders, bool weakTest) const
{
- return m_left.EqPartial(borders.m_left) && m_right.EqPartial(borders.m_right) &&
- m_top.EqPartial(borders.m_top) && m_bottom.EqPartial(borders.m_bottom);
+ return m_left.EqPartial(borders.m_left, weakTest) && m_right.EqPartial(borders.m_right, weakTest) &&
+ m_top.EqPartial(borders.m_top, weakTest) && m_bottom.EqPartial(borders.m_bottom, weakTest);
}
// Apply border to 'this', but not if the same as compareWith
}
// Partial equality test
-bool wxTextAttrDimension::EqPartial(const wxTextAttrDimension& dim) const
+bool wxTextAttrDimension::EqPartial(const wxTextAttrDimension& dim, bool weakTest) const
{
+ if (!weakTest && !IsValid() && dim.IsValid())
+ return false;
+
if (dim.IsValid() && IsValid() && !((*this) == dim))
return false;
else
}
// Partial equality test
-bool wxTextAttrDimensions::EqPartial(const wxTextAttrDimensions& dims) const
+bool wxTextAttrDimensions::EqPartial(const wxTextAttrDimensions& dims, bool weakTest) const
{
- if (!m_left.EqPartial(dims.m_left))
+ if (!m_left.EqPartial(dims.m_left, weakTest))
return false;
- if (!m_right.EqPartial(dims.m_right))
+ if (!m_right.EqPartial(dims.m_right, weakTest))
return false;
- if (!m_top.EqPartial(dims.m_top))
+ if (!m_top.EqPartial(dims.m_top, weakTest))
return false;
- if (!m_bottom.EqPartial(dims.m_bottom))
+ if (!m_bottom.EqPartial(dims.m_bottom, weakTest))
return false;
return true;
}
// Partial equality test
-bool wxTextAttrSize::EqPartial(const wxTextAttrSize& size) const
+bool wxTextAttrSize::EqPartial(const wxTextAttrSize& size, bool weakTest) const
{
- if (!m_width.EqPartial(size.m_width))
+ if (!m_width.EqPartial(size.m_width, weakTest))
return false;
- if (!m_height.EqPartial(size.m_height))
+ if (!m_height.EqPartial(size.m_height, weakTest))
return false;
return true;
long forbiddenFlags = clashingAttr.GetFlags()|absentAttr.GetFlags();
- if (attr.HasFont())
+ // If different font size units are being used, this is a clash.
+ if (((attr.GetFlags() & wxTEXT_ATTR_FONT_SIZE) | (currentStyle.GetFlags() & wxTEXT_ATTR_FONT_SIZE)) == wxTEXT_ATTR_FONT_SIZE)
{
- if (attr.HasFontSize() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_SIZE))
+ currentStyle.SetFontSize(0);
+ currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_SIZE);
+ clashingAttr.AddFlag(wxTEXT_ATTR_FONT_SIZE);
+ }
+ else
+ {
+ if (attr.HasFontPointSize() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_POINT_SIZE))
{
- if (currentStyle.HasFontSize())
+ if (currentStyle.HasFontPointSize())
{
if (currentStyle.GetFontSize() != attr.GetFontSize())
{
// Clash of attr - mark as such
- clashingAttr.AddFlag(wxTEXT_ATTR_FONT_SIZE);
- currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_SIZE);
+ clashingAttr.AddFlag(wxTEXT_ATTR_FONT_POINT_SIZE);
+ currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_POINT_SIZE);
}
}
else
currentStyle.SetFontSize(attr.GetFontSize());
}
+ else if (!attr.HasFontPointSize() && currentStyle.HasFontPointSize())
+ {
+ clashingAttr.AddFlag(wxTEXT_ATTR_FONT_POINT_SIZE);
+ currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_POINT_SIZE);
+ }
- if (attr.HasFontItalic() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_ITALIC))
+ if (attr.HasFontPixelSize() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_PIXEL_SIZE))
{
- if (currentStyle.HasFontItalic())
+ if (currentStyle.HasFontPixelSize())
{
- if (currentStyle.GetFontStyle() != attr.GetFontStyle())
+ if (currentStyle.GetFontSize() != attr.GetFontSize())
{
// Clash of attr - mark as such
- clashingAttr.AddFlag(wxTEXT_ATTR_FONT_ITALIC);
- currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC);
+ clashingAttr.AddFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE);
+ currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE);
}
}
else
- currentStyle.SetFontStyle(attr.GetFontStyle());
+ currentStyle.SetFontPixelSize(attr.GetFontSize());
+ }
+ else if (!attr.HasFontPixelSize() && currentStyle.HasFontPixelSize())
+ {
+ clashingAttr.AddFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE);
+ currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE);
}
+ }
- if (attr.HasFontFamily() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_FAMILY))
+ if (attr.HasFontItalic() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_ITALIC))
+ {
+ if (currentStyle.HasFontItalic())
{
- if (currentStyle.HasFontFamily())
+ if (currentStyle.GetFontStyle() != attr.GetFontStyle())
{
- if (currentStyle.GetFontFamily() != attr.GetFontFamily())
- {
- // Clash of attr - mark as such
- clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FAMILY);
- currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY);
- }
+ // Clash of attr - mark as such
+ clashingAttr.AddFlag(wxTEXT_ATTR_FONT_ITALIC);
+ currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC);
+ }
+ }
+ else
+ currentStyle.SetFontStyle(attr.GetFontStyle());
+ }
+ else if (!attr.HasFontItalic() && currentStyle.HasFontItalic())
+ {
+ clashingAttr.AddFlag(wxTEXT_ATTR_FONT_ITALIC);
+ currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC);
+ }
+
+ if (attr.HasFontFamily() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_FAMILY))
+ {
+ if (currentStyle.HasFontFamily())
+ {
+ if (currentStyle.GetFontFamily() != attr.GetFontFamily())
+ {
+ // Clash of attr - mark as such
+ clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FAMILY);
+ currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY);
}
- else
- currentStyle.SetFontFamily(attr.GetFontFamily());
}
+ else
+ currentStyle.SetFontFamily(attr.GetFontFamily());
+ }
+ else if (!attr.HasFontFamily() && currentStyle.HasFontFamily())
+ {
+ clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FAMILY);
+ currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY);
+ }
- if (attr.HasFontWeight() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_WEIGHT))
+ if (attr.HasFontWeight() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_WEIGHT))
+ {
+ if (currentStyle.HasFontWeight())
{
- if (currentStyle.HasFontWeight())
+ if (currentStyle.GetFontWeight() != attr.GetFontWeight())
{
- if (currentStyle.GetFontWeight() != attr.GetFontWeight())
- {
- // Clash of attr - mark as such
- clashingAttr.AddFlag(wxTEXT_ATTR_FONT_WEIGHT);
- currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT);
- }
+ // Clash of attr - mark as such
+ clashingAttr.AddFlag(wxTEXT_ATTR_FONT_WEIGHT);
+ currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT);
}
- else
- currentStyle.SetFontWeight(attr.GetFontWeight());
}
+ else
+ currentStyle.SetFontWeight(attr.GetFontWeight());
+ }
+ else if (!attr.HasFontWeight() && currentStyle.HasFontWeight())
+ {
+ clashingAttr.AddFlag(wxTEXT_ATTR_FONT_WEIGHT);
+ currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT);
+ }
- if (attr.HasFontFaceName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_FACE))
+ if (attr.HasFontFaceName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_FACE))
+ {
+ if (currentStyle.HasFontFaceName())
{
- if (currentStyle.HasFontFaceName())
+ wxString faceName1(currentStyle.GetFontFaceName());
+ wxString faceName2(attr.GetFontFaceName());
+
+ if (faceName1 != faceName2)
{
- wxString faceName1(currentStyle.GetFontFaceName());
- wxString faceName2(attr.GetFontFaceName());
+ // Clash of attr - mark as such
+ clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FACE);
+ currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FACE);
+ }
+ }
+ else
+ currentStyle.SetFontFaceName(attr.GetFontFaceName());
+ }
+ else if (!attr.HasFontFaceName() && currentStyle.HasFontFaceName())
+ {
+ clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FACE);
+ currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FACE);
+ }
- if (faceName1 != faceName2)
- {
- // Clash of attr - mark as such
- clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FACE);
- currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FACE);
- }
+ if (attr.HasFontUnderlined() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_UNDERLINE))
+ {
+ if (currentStyle.HasFontUnderlined())
+ {
+ if (currentStyle.GetFontUnderlined() != attr.GetFontUnderlined())
+ {
+ // Clash of attr - mark as such
+ clashingAttr.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE);
+ currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE);
}
- else
- currentStyle.SetFontFaceName(attr.GetFontFaceName());
}
+ else
+ currentStyle.SetFontUnderlined(attr.GetFontUnderlined());
+ }
+ else if (!attr.HasFontUnderlined() && currentStyle.HasFontUnderlined())
+ {
+ clashingAttr.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE);
+ currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE);
+ }
- if (attr.HasFontUnderlined() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_UNDERLINE))
+ if (attr.HasFontStrikethrough() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_STRIKETHROUGH))
+ {
+ if (currentStyle.HasFontStrikethrough())
{
- if (currentStyle.HasFontUnderlined())
+ if (currentStyle.GetFontStrikethrough() != attr.GetFontStrikethrough())
{
- if (currentStyle.GetFontUnderlined() != attr.GetFontUnderlined())
- {
- // Clash of attr - mark as such
- clashingAttr.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE);
- currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE);
- }
+ // Clash of attr - mark as such
+ clashingAttr.AddFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH);
+ currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH);
}
- else
- currentStyle.SetFontUnderlined(attr.GetFontUnderlined());
}
+ else
+ currentStyle.SetFontStrikethrough(attr.GetFontStrikethrough());
+ }
+ else if (!attr.HasFontStrikethrough() && currentStyle.HasFontStrikethrough())
+ {
+ clashingAttr.AddFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH);
+ currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH);
}
if (attr.HasTextColour() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_TEXT_COLOUR))
else
currentStyle.SetTextColour(attr.GetTextColour());
}
+ else if (!attr.HasTextColour() && currentStyle.HasTextColour())
+ {
+ clashingAttr.AddFlag(wxTEXT_ATTR_TEXT_COLOUR);
+ currentStyle.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR);
+ }
if (attr.HasBackgroundColour() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BACKGROUND_COLOUR))
{
else
currentStyle.SetBackgroundColour(attr.GetBackgroundColour());
}
+ else if (!attr.HasBackgroundColour() && currentStyle.HasBackgroundColour())
+ {
+ clashingAttr.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
+ currentStyle.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
+ }
if (attr.HasAlignment() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_ALIGNMENT))
{
else
currentStyle.SetAlignment(attr.GetAlignment());
}
+ else if (!attr.HasAlignment() && currentStyle.HasAlignment())
+ {
+ clashingAttr.AddFlag(wxTEXT_ATTR_ALIGNMENT);
+ currentStyle.RemoveFlag(wxTEXT_ATTR_ALIGNMENT);
+ }
if (attr.HasTabs() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_TABS))
{
else
currentStyle.SetTabs(attr.GetTabs());
}
+ else if (!attr.HasTabs() && currentStyle.HasTabs())
+ {
+ clashingAttr.AddFlag(wxTEXT_ATTR_TABS);
+ currentStyle.RemoveFlag(wxTEXT_ATTR_TABS);
+ }
if (attr.HasLeftIndent() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LEFT_INDENT))
{
else
currentStyle.SetLeftIndent(attr.GetLeftIndent(), attr.GetLeftSubIndent());
}
+ else if (!attr.HasLeftIndent() && currentStyle.HasLeftIndent())
+ {
+ clashingAttr.AddFlag(wxTEXT_ATTR_LEFT_INDENT);
+ currentStyle.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT);
+ }
if (attr.HasRightIndent() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_RIGHT_INDENT))
{
else
currentStyle.SetRightIndent(attr.GetRightIndent());
}
+ else if (!attr.HasRightIndent() && currentStyle.HasRightIndent())
+ {
+ clashingAttr.AddFlag(wxTEXT_ATTR_RIGHT_INDENT);
+ currentStyle.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT);
+ }
if (attr.HasParagraphSpacingAfter() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARA_SPACING_AFTER))
{
else
currentStyle.SetParagraphSpacingAfter(attr.GetParagraphSpacingAfter());
}
+ else if (!attr.HasParagraphSpacingAfter() && currentStyle.HasParagraphSpacingAfter())
+ {
+ clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
+ currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
+ }
if (attr.HasParagraphSpacingBefore() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARA_SPACING_BEFORE))
{
else
currentStyle.SetParagraphSpacingBefore(attr.GetParagraphSpacingBefore());
}
+ else if (!attr.HasParagraphSpacingBefore() && currentStyle.HasParagraphSpacingBefore())
+ {
+ clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
+ currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
+ }
if (attr.HasLineSpacing() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LINE_SPACING))
{
else
currentStyle.SetLineSpacing(attr.GetLineSpacing());
}
+ else if (!attr.HasLineSpacing() && currentStyle.HasLineSpacing())
+ {
+ clashingAttr.AddFlag(wxTEXT_ATTR_LINE_SPACING);
+ currentStyle.RemoveFlag(wxTEXT_ATTR_LINE_SPACING);
+ }
if (attr.HasCharacterStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_CHARACTER_STYLE_NAME))
{
else
currentStyle.SetCharacterStyleName(attr.GetCharacterStyleName());
}
+ else if (!attr.HasCharacterStyleName() && currentStyle.HasCharacterStyleName())
+ {
+ clashingAttr.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
+ currentStyle.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
+ }
if (attr.HasParagraphStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARAGRAPH_STYLE_NAME))
{
else
currentStyle.SetParagraphStyleName(attr.GetParagraphStyleName());
}
+ else if (!attr.HasParagraphStyleName() && currentStyle.HasParagraphStyleName())
+ {
+ clashingAttr.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
+ currentStyle.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
+ }
if (attr.HasListStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LIST_STYLE_NAME))
{
else
currentStyle.SetListStyleName(attr.GetListStyleName());
}
+ else if (!attr.HasListStyleName() && currentStyle.HasListStyleName())
+ {
+ clashingAttr.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
+ currentStyle.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
+ }
if (attr.HasBulletStyle() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_STYLE))
{
else
currentStyle.SetBulletStyle(attr.GetBulletStyle());
}
+ else if (!attr.HasBulletStyle() && currentStyle.HasBulletStyle())
+ {
+ clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_STYLE);
+ currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE);
+ }
if (attr.HasBulletNumber() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_NUMBER))
{
else
currentStyle.SetBulletNumber(attr.GetBulletNumber());
}
+ else if (!attr.HasBulletNumber() && currentStyle.HasBulletNumber())
+ {
+ clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NUMBER);
+ currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER);
+ }
if (attr.HasBulletText() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_TEXT))
{
currentStyle.SetBulletFont(attr.GetBulletFont());
}
}
+ else if (!attr.HasBulletText() && currentStyle.HasBulletText())
+ {
+ clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_TEXT);
+ currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT);
+ }
if (attr.HasBulletName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_NAME))
{
currentStyle.SetBulletName(attr.GetBulletName());
}
}
+ else if (!attr.HasBulletName() && currentStyle.HasBulletName())
+ {
+ clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NAME);
+ currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NAME);
+ }
if (attr.HasURL() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_URL))
{
currentStyle.SetURL(attr.GetURL());
}
}
+ else if (!attr.HasURL() && currentStyle.HasURL())
+ {
+ clashingAttr.AddFlag(wxTEXT_ATTR_URL);
+ currentStyle.RemoveFlag(wxTEXT_ATTR_URL);
+ }
if (attr.HasTextEffects() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_EFFECTS))
{
if (currentStyle.GetTextEffectFlags() == 0)
currentStyle.RemoveFlag(wxTEXT_ATTR_EFFECTS);
}
+ else if (!attr.HasTextEffects() && currentStyle.HasTextEffects())
+ {
+ clashingAttr.AddFlag(wxTEXT_ATTR_EFFECTS);
+ currentStyle.RemoveFlag(wxTEXT_ATTR_EFFECTS);
+ }
if (attr.HasOutlineLevel() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_OUTLINE_LEVEL))
{
else
currentStyle.SetOutlineLevel(attr.GetOutlineLevel());
}
+ else if (!attr.HasOutlineLevel() && currentStyle.HasOutlineLevel())
+ {
+ clashingAttr.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
+ currentStyle.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
+ }
}
WX_DEFINE_OBJARRAY(wxRichTextVariantArray);
+WX_DEFINE_OBJARRAY(wxRichTextAttrArray);
+WX_DEFINE_OBJARRAY(wxRichTextRectArray);
IMPLEMENT_DYNAMIC_CLASS(wxRichTextProperties, wxObject)
IMPLEMENT_CLASS(wxRichTextDrawingHandler, 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
{
+ if (!GetVirtualAttributesEnabled())
+ return false;
+
wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
while (node)
{
wxRichTextAttr wxRichTextDrawingContext::GetVirtualAttributes(wxRichTextObject* obj) const
{
wxRichTextAttr attr;
+ if (!GetVirtualAttributesEnabled())
+ return attr;
+
// We apply all handlers, so we can may combine several different attributes
wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
while (node)
bool wxRichTextDrawingContext::ApplyVirtualAttributes(wxRichTextAttr& attr, wxRichTextObject* obj) const
{
+ if (!GetVirtualAttributesEnabled())
+ return false;
+
if (HasVirtualAttributes(obj))
{
wxRichTextAttr a(GetVirtualAttributes(obj));
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
void wxRichTextBuffer::AddDrawingHandler(wxRichTextDrawingHandler *handler)
{
sm_drawingHandlers.Clear();
}
+void wxRichTextBuffer::AddFieldType(wxRichTextFieldType *fieldType)
+{
+ sm_fieldTypes[fieldType->GetName()] = fieldType;
+}
+
+bool wxRichTextBuffer::RemoveFieldType(const wxString& name)
+{
+ wxRichTextFieldTypeHashMap::iterator it = sm_fieldTypes.find(name);
+ if (it == sm_fieldTypes.end())
+ return false;
+ else
+ {
+ wxRichTextFieldType* fieldType = it->second;
+ sm_fieldTypes.erase(it);
+ delete fieldType;
+ return true;
+ }
+}
+
+wxRichTextFieldType *wxRichTextBuffer::FindFieldType(const wxString& name)
+{
+ wxRichTextFieldTypeHashMap::iterator it = sm_fieldTypes.find(name);
+ if (it == sm_fieldTypes.end())
+ return NULL;
+ else
+ return it->second;
+}
+
+void wxRichTextBuffer::CleanUpFieldTypes()
+{
+ wxRichTextFieldTypeHashMap::iterator it;
+ for( it = sm_fieldTypes.begin(); it != sm_fieldTypes.end(); ++it )
+ {
+ wxRichTextFieldType* fieldType = it->second;
+ delete fieldType;
+ }
+
+ sm_fieldTypes.clear();
+}
+
#endif
// wxUSE_RICHTEXT