X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/aa8f57f491ec10fe1e74413a668698a80d271e45..64ea838d8f4d1853b7d850db93ee565e901d099a:/src/richtext/richtextbuffer.cpp diff --git a/src/richtext/richtextbuffer.cpp b/src/richtext/richtextbuffer.cpp index 5881f6f45c..dbeab138df 100644 --- a/src/richtext/richtextbuffer.cpp +++ b/src/richtext/richtextbuffer.cpp @@ -42,6 +42,7 @@ #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" @@ -464,21 +465,6 @@ int wxRichTextFloatCollector::HitTest(wxDC& dc, wxRichTextDrawingContext& contex // 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); } @@ -613,7 +599,11 @@ void wxRichTextObject::Invalidate(const wxRichTextRange& invalidRange) { 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()) + SetCachedSize(wxDefaultSize); SetMaxSize(wxDefaultSize); SetMinSize(wxDefaultSize); } @@ -625,7 +615,7 @@ int wxRichTextObject::ConvertTenthsMMToPixels(wxDC& dc, int units) const // 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; @@ -1018,7 +1008,7 @@ void wxRichTextObject::Dump(wxTextOutputStream& stream) 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); } @@ -1653,7 +1643,6 @@ void wxRichTextParagraphLayoutBox::Init() m_invalidRange = wxRICHTEXT_ALL; - SetMargins(4); m_partialParagraph = false; m_floatCollector = NULL; } @@ -1866,6 +1855,10 @@ bool wxRichTextParagraphLayoutBox::Layout(wxDC& dc, wxRichTextDrawingContext& co 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); @@ -2026,6 +2019,14 @@ bool wxRichTextParagraphLayoutBox::Layout(wxDC& dc, wxRichTextDrawingContext& co else maxHeight = 0; // topMargin + bottomMargin; + // Check the bottom edge of any floating object + if (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); @@ -2036,6 +2037,12 @@ bool wxRichTextParagraphLayoutBox::Layout(wxDC& dc, wxRichTextDrawingContext& co 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, @@ -2356,6 +2363,7 @@ wxRichTextRange wxRichTextParagraphLayoutBox::AddParagraph(const wxString& text, wxRichTextAttr* cStyle = & defaultCharStyle; wxRichTextParagraph* para = new wxRichTextParagraph(text, this, pStyle, cStyle); + para->GetAttributes().GetTextBoxAttr().Reset(); AppendChild(para); @@ -2397,6 +2405,7 @@ wxRichTextRange wxRichTextParagraphLayoutBox::AddParagraphs(const wxString& text size_t len = text.length(); wxString line; wxRichTextParagraph* para = new wxRichTextParagraph(wxEmptyString, this, pStyle, cStyle); + para->GetAttributes().GetTextBoxAttr().Reset(); AppendChild(para); @@ -2414,6 +2423,7 @@ wxRichTextRange wxRichTextParagraphLayoutBox::AddParagraphs(const wxString& text plainText->SetText(line); para = new wxRichTextParagraph(wxEmptyString, this, pStyle, cStyle); + para->GetAttributes().GetTextBoxAttr().Reset(); AppendChild(para); @@ -2463,6 +2473,7 @@ wxRichTextRange wxRichTextParagraphLayoutBox::AddImage(const wxImage& image, wxR wxRichTextAttr* cStyle = & defaultCharStyle; wxRichTextParagraph* para = new wxRichTextParagraph(this, pStyle); + para->GetAttributes().GetTextBoxAttr().Reset(); AppendChild(para); para->AppendChild(new wxRichTextImage(image, this, cStyle)); @@ -3326,6 +3337,7 @@ bool wxRichTextParagraphLayoutBox::DoGetStyle(long position, wxRichTextAttr& sty { // Start with the base style style = GetAttributes(); + style.GetTextBoxAttr().Reset(); // Apply the paragraph style wxRichTextApplyStyle(style, obj->GetAttributes()); @@ -3466,12 +3478,12 @@ bool wxRichTextParagraphLayoutBox::HasCharacterAttributes(const wxRichTextRange& 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 ++; } @@ -3514,7 +3526,7 @@ bool wxRichTextParagraphLayoutBox::HasParagraphAttributes(const wxRichTextRange& wxRichTextApplyStyle(textAttr, para->GetAttributes()); foundCount ++; - if (wxTextAttrEqPartial(textAttr, style)) + if (textAttr.EqPartial(style, false /* strong test */)) matchingCount ++; } } @@ -3531,7 +3543,6 @@ void wxRichTextParagraphLayoutBox::PrepareContent(wxRichTextParagraphLayoutBox& buffer->GetRichTextCtrl()->PrepareContent(container); } - /// Set character or paragraph properties bool wxRichTextParagraphLayoutBox::SetProperties(const wxRichTextRange& range, const wxRichTextProperties& properties, int flags) { @@ -3974,12 +3985,19 @@ bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange& range, wx 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); - } + // Now we need to do numbering + if (renumber) + { + newPara->GetAttributes().SetBulletNumber(n); + } - n ++; + n ++; + } } else if (!newPara->GetAttributes().GetListStyleName().IsEmpty()) { @@ -4152,6 +4170,10 @@ bool wxRichTextParagraphLayoutBox::DoNumberList(const wxRichTextRange& range, co 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) @@ -4185,7 +4207,8 @@ bool wxRichTextParagraphLayoutBox::DoNumberList(const wxRichTextRange& range, co } else { - levels[currentLevel] ++; + if (!(para->GetAttributes().HasBulletStyle() && (para->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION))) + levels[currentLevel] ++; } newPara->GetAttributes().SetBulletNumber(levels[currentLevel]); @@ -4264,6 +4287,22 @@ bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy, const wxRichTextRa /// position of the paragraph that it had to start looking from. bool wxRichTextParagraphLayoutBox::FindNextParagraphNumber(wxRichTextParagraph* previousParagraph, wxRichTextAttr& attr) const { + // 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->GetAttributes().HasFlag(wxTEXT_ATTR_BULLET_STYLE) || previousParagraph->GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE) return false; @@ -4363,7 +4402,7 @@ bool wxRichTextParagraph::Draw(wxDC& dc, wxRichTextDrawingContext& context, cons 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) { @@ -4600,7 +4639,6 @@ bool wxRichTextParagraph::Layout(wxDC& dc, wxRichTextDrawingContext& context, co node = node->GetNext(); } - #endif // Split up lines @@ -4713,13 +4751,22 @@ bool wxRichTextParagraph::Layout(wxDC& dc, wxRichTextDrawingContext& context, co 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; + if (childDescent == 0) + { + lineHeight = wxMax(lineHeight, childSize.y); + lineDescent = maxDescent; + lineAscent = maxAscent; + } + else + { + lineDescent = wxMax(childDescent, maxDescent); + lineAscent = wxMax(childSize.y-childDescent, maxAscent); + } + lineHeight = wxMax(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. @@ -4743,7 +4790,6 @@ bool wxRichTextParagraph::Layout(wxDC& dc, wxRichTextDrawingContext& context, co { 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); @@ -4751,7 +4797,6 @@ bool wxRichTextParagraph::Layout(wxDC& dc, wxRichTextDrawingContext& context, co attr, child->GetAttributes(), availableRect, parentRect, style); childSize = child->GetCachedSize(); childDescent = child->GetDescent(); - //child->SetPosition(availableRect.GetPosition()); if (oldSize != child->GetCachedSize()) { @@ -4771,6 +4816,13 @@ bool wxRichTextParagraph::Layout(wxDC& dc, wxRichTextDrawingContext& context, co } 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 @@ -4785,12 +4837,6 @@ bool wxRichTextParagraph::Layout(wxDC& dc, wxRichTextDrawingContext& context, co ) { - 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(); @@ -4813,7 +4859,7 @@ bool wxRichTextParagraph::Layout(wxDC& dc, wxRichTextDrawingContext& context, co // 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); @@ -4821,9 +4867,7 @@ bool wxRichTextParagraph::Layout(wxDC& dc, wxRichTextDrawingContext& context, co 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()) @@ -4837,9 +4881,15 @@ bool wxRichTextParagraph::Layout(wxDC& dc, wxRichTextDrawingContext& context, co GetRangeSize(actualRange, actualSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED); 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) { @@ -4875,7 +4925,7 @@ bool wxRichTextParagraph::Layout(wxDC& dc, wxRichTextDrawingContext& context, co lineCount ++; - // TODO: account for zero-length objects, such as fields + // TODO: account for zero-length objects // wxASSERT(wrapPosition > lastCompletedEndPos); lastEndPos = wrapPosition; @@ -4902,9 +4952,20 @@ bool wxRichTextParagraph::Layout(wxDC& dc, wxRichTextDrawingContext& context, co { // 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(); @@ -4949,7 +5010,7 @@ bool wxRichTextParagraph::Layout(wxDC& dc, wxRichTextDrawingContext& context, co // 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() && child->GetRange().GetLength() != 0 && !wxDynamicCast(child, wxRichTextPlainText)) { if (child->GetCachedSize().x > minWidth) minWidth = child->GetMinSize().x; @@ -4963,7 +5024,6 @@ bool wxRichTextParagraph::Layout(wxDC& dc, wxRichTextDrawingContext& context, co 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 @@ -5017,6 +5077,7 @@ void wxRichTextParagraph::ApplyParagraphStyle(wxRichTextLine* line, const wxRich return; wxPoint pos = line->GetPosition(); + wxPoint originalPos = pos; wxSize size = line->GetSize(); // centering, right-justification @@ -5032,6 +5093,22 @@ void wxRichTextParagraph::ApplyParagraphStyle(wxRichTextLine* line, const wxRich 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 @@ -5125,8 +5202,6 @@ bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange& range, wxSize& siz if (flags & wxRICHTEXT_UNFORMATTED) { // Just use unformatted data, assume no line breaks - // TODO: take into account line breaks - wxSize sz; wxArrayInt childExtents; @@ -5136,10 +5211,13 @@ bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange& range, wxSize& siz 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)) { @@ -5163,36 +5241,61 @@ bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange& range, wxSize& siz 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) { @@ -5207,9 +5310,21 @@ bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange& range, wxSize& siz } else if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y), 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())) { @@ -5261,7 +5376,10 @@ bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange& range, wxSize& siz 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) @@ -5279,18 +5397,31 @@ bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange& range, wxSize& siz int childDescent = 0; if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y))) { - 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(); } @@ -5419,7 +5550,11 @@ int wxRichTextParagraph::HitTest(wxDC& dc, wxRichTextDrawingContext& context, co 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); @@ -6145,7 +6280,7 @@ bool wxRichTextPlainText::Draw(wxDC& dc, wxRichTextDrawingContext& context, cons wxString str = m_text; 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(); @@ -6161,21 +6296,49 @@ bool wxRichTextPlainText::Draw(wxDC& dc, wxRichTextDrawingContext& context, cons 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(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR; - textFont.SetPointSize( static_cast(size) ); - x = rect.x; - y = rect.y; + if (textFont.IsUsingSizeInPixels()) + { + double size = static_cast(textFont.GetPixelSize().y) / wxSCRIPT_MUL_FACTOR; + textFont.SetPixelSize(wxSize(0, static_cast(size))); + x = rect.x; + y = rect.y; + } + else + { + double size = static_cast(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR; + textFont.SetPointSize(static_cast(size)); + x = rect.x; + y = rect.y; + } wxCheckSetFont(dc, textFont); } else if ( textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT) ) { - double size = static_cast(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR; - textFont.SetPointSize( static_cast(size) ); - x = rect.x; - int sub_height = static_cast( static_cast(charHeight) / wxSCRIPT_MUL_FACTOR); - y = rect.y + (rect.height - sub_height + (descent - m_descent)); + if (textFont.IsUsingSizeInPixels()) + { + double size = static_cast(textFont.GetPixelSize().y) / wxSCRIPT_MUL_FACTOR; + textFont.SetPixelSize(wxSize(0, static_cast(size))); + x = rect.x; + int sub_height = static_cast(static_cast(charHeight) / wxSCRIPT_MUL_FACTOR); + y = rect.y + (rect.height - sub_height + (descent - m_descent)); + } + else + { + double size = static_cast(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR; + textFont.SetPointSize(static_cast(size)); + x = rect.x; + int sub_height = static_cast(static_cast(charHeight) / wxSCRIPT_MUL_FACTOR); + y = rect.y + (rect.height - sub_height + (descent - m_descent)); + } wxCheckSetFont(dc, textFont); } else @@ -6414,8 +6577,8 @@ bool wxRichTextPlainText::DrawTabbedString(wxDC& dc, const wxRichTextAttr& attr, x += w; } - return true; + return true; } /// Lay the item out @@ -6465,8 +6628,23 @@ bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange& range, wxSize& siz || (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT) ) ) { wxFont textFont = font; - double size = static_cast(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR; - textFont.SetPointSize( static_cast(size) ); + if (textFont.IsUsingSizeInPixels()) + { + double size = static_cast(textFont.GetPixelSize().y) / wxSCRIPT_MUL_FACTOR; + textFont.SetPixelSize(wxSize(0, static_cast(size))); + } + else + { + double size = static_cast(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR; + textFont.SetPointSize(static_cast(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; } @@ -6486,7 +6664,7 @@ bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange& range, wxSize& siz 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; @@ -6689,7 +6867,7 @@ wxString wxRichTextPlainText::GetTextForRange(const wxRichTextRange& range) cons /// Returns true if this object can merge itself with the given one. bool wxRichTextPlainText::CanMerge(wxRichTextObject* object) const { - return object->GetClassInfo() == CLASSINFO(wxRichTextPlainText) && + return object->GetClassInfo() == wxCLASSINFO(wxRichTextPlainText) && (m_text.empty() || (wxTextAttrEq(GetAttributes(), object->GetAttributes()) && m_properties == object->GetProperties())); } @@ -6741,11 +6919,12 @@ long wxRichTextPlainText::GetFirstLineBreakPosition(long pos) 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; /// Initialisation void wxRichTextBuffer::Init() @@ -6758,6 +6937,9 @@ void wxRichTextBuffer::Init() m_suppressUndo = 0; m_handlerFlags = 0; m_scale = 1.0; + m_dimensionScale = 1.0; + m_fontScale = 1.0; + SetMargins(4); } /// Initialisation @@ -6792,6 +6974,8 @@ void wxRichTextBuffer::Copy(const wxRichTextBuffer& obj) 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 @@ -6912,6 +7096,8 @@ bool wxRichTextParagraphLayoutBox::InsertNewlineWithUndo(wxRichTextBuffer* buffe } 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); @@ -6939,13 +7125,13 @@ bool wxRichTextParagraphLayoutBox::InsertNewlineWithUndo(wxRichTextBuffer* buffe 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) { @@ -6995,6 +7181,9 @@ bool wxRichTextParagraphLayoutBox::InsertImageWithUndo(wxRichTextBuffer* buffer, 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); @@ -7039,6 +7228,9 @@ wxRichTextObject* wxRichTextParagraphLayoutBox::InsertObjectWithUndo(wxRichTextB 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); @@ -7060,6 +7252,50 @@ wxRichTextObject* wxRichTextParagraphLayoutBox::InsertObjectWithUndo(wxRichTextB 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; +} + /// 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. @@ -7256,9 +7492,10 @@ bool wxRichTextBuffer::EndSuppressUndo() 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()); @@ -8006,6 +8243,17 @@ int wxRichTextBuffer::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const } } +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()) @@ -8094,7 +8342,10 @@ bool wxRichTextStdRenderer::DrawTextBullet(wxRichTextParagraph* paragraph, wxDC& 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()); @@ -8205,6 +8456,345 @@ bool wxRichTextBox::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer) 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, wxPoint position, wxArrayInt* partialExtents) const +{ + wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType()); + if (fieldType) + return fieldType->GetRangeSize((wxRichTextField*) this, range, size, descent, dc, context, flags, position, partialExtents); + + return wxRichTextParagraphLayoutBox::GetRangeSize(range, size, descent, dc, context, flags, position, 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, wxPoint position, wxArrayInt* partialExtents) const +{ + if (IsTopLevel(obj)) + return obj->wxRichTextParagraphLayoutBox::GetRangeSize(range, size, descent, dc, context, flags, position); + 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): @@ -8269,7 +8859,7 @@ bool wxRichTextCell::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer) wxRichTextObjectPropertiesDialog cellDlg(this, wxGetTopLevelParent(parent), wxID_ANY, caption); cellDlg.SetAttributes(attr); - wxRichTextSizePage* sizePage = wxDynamicCast(cellDlg.FindPage(CLASSINFO(wxRichTextSizePage)), wxRichTextSizePage); + wxRichTextSizePage* sizePage = wxDynamicCast(cellDlg.FindPage(wxCLASSINFO(wxRichTextSizePage)), wxRichTextSizePage); if (sizePage) { // We don't want position and floating controls for a cell. @@ -8391,7 +8981,7 @@ bool wxRichTextTable::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxArrayInt absoluteColWidths; absoluteColWidths.Add(0, m_colCount); - // wxArrayInt absoluteColWidthsSpanning(m_colCount); + wxArrayInt percentageColWidths; percentageColWidths.Add(0, m_colCount); // wxArrayInt percentageColWidthsSpanning(m_colCount); @@ -9385,12 +9975,25 @@ public: 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(); @@ -9521,8 +10124,8 @@ void wxRichTextAction::CalculateRefreshOptimizations(wxArrayInt& optimizationLin // 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()); @@ -9758,6 +10361,7 @@ bool wxRichTextAction::Undo() 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())); @@ -9789,6 +10393,7 @@ bool wxRichTextAction::Undo() 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()); @@ -9874,8 +10479,8 @@ void wxRichTextAction::UpdateAppearance(long caretPosition, bool sendUpdateEvent { 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; @@ -9970,7 +10575,7 @@ void wxRichTextAction::UpdateAppearance(long caretPosition, bool sendUpdateEvent 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 @@ -10057,6 +10662,7 @@ IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage, wxRichTextObject) wxRichTextImage::wxRichTextImage(const wxImage& image, wxRichTextObject* parent, wxRichTextAttr* charStyle): wxRichTextObject(parent) { + Init(); m_imageBlock.MakeImageBlockDefaultQuality(image, wxBITMAP_TYPE_PNG); if (charStyle) SetAttributes(*charStyle); @@ -10065,40 +10671,155 @@ wxRichTextImage::wxRichTextImage(const wxImage& image, wxRichTextObject* parent, wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock& imageBlock, wxRichTextObject* parent, wxRichTextAttr* charStyle): wxRichTextObject(parent) { + Init(); m_imageBlock = imageBlock; if (charStyle) SetAttributes(*charStyle); } +void wxRichTextImage::Init() +{ + m_originalImageSize = wxSize(-1, -1); +} + /// 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 (!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 == wxSize(-1, -1)) { - if (!m_imageBlock.IsOk()) - return false; + m_imageCache = wxNullBitmap; - wxImage image; m_imageBlock.Load(image); if (!image.IsOk()) return false; - int width = image.GetWidth(); - int height = image.GetHeight(); + 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; - if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetWidth().GetValue() > 0) + wxRichTextBuffer* buffer = GetBuffer(); + if (buffer) + { + wxSize sz; + if (buffer->GetRichTextCtrl()) { - if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM) - width = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetWidth().GetValue()); - else - width = GetAttributes().GetTextBoxAttr().GetWidth().GetValue(); + // Subtract borders + sz = buffer->GetRichTextCtrl()->GetClientSize(); + + wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect; + marginRect = wxRect(0, 0, sz.x, sz.y); + buffer->GetBoxRects(dc, buffer, buffer->GetAttributes(), marginRect, borderRect, contentRect, paddingRect, outlineRect); + + sz = contentRect.GetSize(); + + // 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 = sz.x; } - if (GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetHeight().GetValue() > 0) + else + sz = buffer->GetCachedSize(); + parentWidth = sz.GetWidth(); + parentHeight = sz.GetHeight(); + } + + 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 + + if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() > 0) + { + 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(); + } + + if (maxHeight > 0 && height > maxHeight) + { + height = maxHeight; + + // Preserve the aspect ratio + if (height != m_originalImageSize.GetHeight()) + width = (int) (float(m_originalImageSize.GetWidth()) * (float(height)/float(m_originalImageSize.GetHeight()))); + } + + 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) @@ -10125,7 +10846,7 @@ bool wxRichTextImage::LoadImageCache(wxDC& dc, bool resetCache) } /// 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; @@ -10140,12 +10861,6 @@ bool wxRichTextImage::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wx 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 @@ -10153,7 +10868,7 @@ bool wxRichTextImage::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wx 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); @@ -10243,6 +10958,7 @@ void wxRichTextImage::Copy(const wxRichTextImage& obj) wxRichTextObject::Copy(obj); m_imageBlock = obj.m_imageBlock; + m_originalImageSize = obj.m_originalImageSize; } /// Edit properties via a GUI @@ -10273,12 +10989,6 @@ bool wxTextAttrEq(const wxRichTextAttr& attr1, const wxRichTextAttr& attr2) 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) { @@ -10893,22 +11603,48 @@ class wxRichTextFontTableData: public wxObjectRefData 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 { @@ -10921,6 +11657,7 @@ IMPLEMENT_DYNAMIC_CLASS(wxRichTextFontTable, wxObject) wxRichTextFontTable::wxRichTextFontTable() { m_refData = new wxRichTextFontTableData; + m_fontScale = 1.0; } wxRichTextFontTable::wxRichTextFontTable(const wxRichTextFontTable& table) @@ -10942,13 +11679,14 @@ bool wxRichTextFontTable::operator == (const wxRichTextFontTable& table) const 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(); } @@ -10960,8 +11698,14 @@ void wxRichTextFontTable::Clear() data->m_hashMap.clear(); } -// wxTextBoxAttr +void wxRichTextFontTable::SetFontScale(double fontScale) +{ + if (fontScale != m_fontScale) + Clear(); + m_fontScale = fontScale; +} +// wxTextBoxAttr void wxTextBoxAttr::Reset() { @@ -11010,8 +11754,17 @@ bool wxTextBoxAttr::operator== (const wxTextBoxAttr& attr) const } // 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; @@ -11029,36 +11782,36 @@ bool wxTextBoxAttr::EqPartial(const wxTextBoxAttr& attr) const // 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; @@ -11283,13 +12036,13 @@ bool wxRichTextAttr::operator==(const wxRichTextAttr& attr) const 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 @@ -11320,15 +12073,23 @@ void wxRichTextAttr::CollectCommonAttributes(const wxRichTextAttr& attr, wxRichT } // 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.HasColour() && !HasColour() && (border.GetColourLong() != GetColourLong())) + if (border.HasStyle() && HasStyle() && (border.GetStyle() != GetStyle())) return false; - if (border.HasWidth() && !HasWidth() && !(border.GetWidth() == GetWidth())) + if (border.HasColour() && HasColour() && (border.GetColourLong() != GetColourLong())) + return false; + + if (border.HasWidth() && HasWidth() && !(border.GetWidth() == GetWidth())) return false; return true; @@ -11415,10 +12176,10 @@ void wxTextAttrBorder::CollectCommonAttributes(const wxTextAttrBorder& attr, wxT } // 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 @@ -11487,8 +12248,11 @@ void wxTextAttrBorders::SetWidth(const wxTextAttrDimension& width) } // 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 @@ -11585,18 +12349,18 @@ int wxTextAttrDimensionConverter::GetTenthsMM(const wxTextAttrDimension& dim) co } // 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; @@ -11639,12 +12403,12 @@ void wxTextAttrDimensions::CollectCommonAttributes(const wxTextAttrDimensions& a } // 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; @@ -11689,19 +12453,44 @@ void wxTextAttrCollectCommonAttributes(wxTextAttr& currentStyle, const wxTextAtt if (attr.HasFont()) { - if (attr.HasFontSize() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_SIZE)) + // 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 (currentStyle.HasFontSize()) + 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.GetFontSize() != attr.GetFontSize()) + if (currentStyle.HasFontPointSize()) { - // Clash of attr - mark as such - clashingAttr.AddFlag(wxTEXT_ATTR_FONT_SIZE); - currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_SIZE); + if (currentStyle.GetFontSize() != attr.GetFontSize()) + { + // Clash of attr - mark as such + clashingAttr.AddFlag(wxTEXT_ATTR_FONT_POINT_SIZE); + currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_POINT_SIZE); + } } + else + currentStyle.SetFontSize(attr.GetFontSize()); + } + + if (attr.HasFontPixelSize() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_PIXEL_SIZE)) + { + if (currentStyle.HasFontPixelSize()) + { + if (currentStyle.GetFontSize() != attr.GetFontSize()) + { + // Clash of attr - mark as such + clashingAttr.AddFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE); + currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE); + } + } + else + currentStyle.SetFontPixelSize(attr.GetFontSize()); } - else - currentStyle.SetFontSize(attr.GetFontSize()); } if (attr.HasFontItalic() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_ITALIC)) @@ -11781,6 +12570,21 @@ void wxTextAttrCollectCommonAttributes(wxTextAttr& currentStyle, const wxTextAtt else currentStyle.SetFontUnderlined(attr.GetFontUnderlined()); } + + if (attr.HasFontStrikethrough() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_STRIKETHROUGH)) + { + if (currentStyle.HasFontStrikethrough()) + { + if (currentStyle.GetFontStrikethrough() != attr.GetFontStrikethrough()) + { + // Clash of attr - mark as such + clashingAttr.AddFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH); + currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH); + } + } + else + currentStyle.SetFontStrikethrough(attr.GetFontStrikethrough()); + } } if (attr.HasTextColour() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_TEXT_COLOUR)) @@ -12514,5 +13318,45 @@ void wxRichTextBuffer::CleanUpDrawingHandlers() 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