diff options
author | Vladimir Glazounov <vg@openoffice.org> | 2008-08-19 22:25:02 +0000 |
---|---|---|
committer | Vladimir Glazounov <vg@openoffice.org> | 2008-08-19 22:25:02 +0000 |
commit | 741509393dbe476215ae20abcd5dc38e7bd4e813 (patch) | |
tree | bfde03976d8fbf7cd6736463de4bd828ca96a332 /svx/source/svdraw/svdotextdecomposition.cxx | |
parent | bef38fc0d2d070bb52a5b933b3b0f3e333cb0117 (diff) |
INTEGRATION: CWS aw033 (1.1.2); FILE ADDED
2008/08/19 16:46:30 cl 1.1.2.26: fixed license files
2008/05/27 14:50:03 aw 1.1.2.25: #i39532# changes DEV300 m12 resync corrections
2008/04/04 06:07:51 aw 1.1.2.24: #i39532# added primitive support for tables
2008/03/18 07:15:24 aw 1.1.2.23: #i39532# changes after resync
2008/02/07 14:14:56 aw 1.1.2.22: stable version from working GIT repository for unxlngi6 build
2007/11/20 15:46:45 aw 1.1.2.21: #i39532# adaption to TextLayouter
2007/10/10 14:19:59 aw 1.1.2.20: changes after resync
2007/10/02 16:57:52 aw 1.1.2.19: #i39532# RTL and BiDi support
2007/10/01 09:19:45 aw 1.1.2.18: #i39532# continued Strnig-index-len reworks for text primitives
2007/09/27 16:05:52 aw 1.1.2.17: #i39532# bullet extensions
2007/09/26 11:38:43 aw 1.1.2.16: #i73860# text decomposition extrended
2007/09/20 09:53:03 aw 1.1.2.15: #i39532# added Waveline support and tiled polygon
2007/08/13 15:34:34 aw 1.1.2.14: #i39532# changes after resync
2007/08/08 15:29:29 aw 1.1.2.13: #i39532# text decomposition needed spell checking knowledge to not show last decomposition
2007/08/03 10:47:48 aw 1.1.2.12: #i39532# restructured MetaFile comment reation to use TextHierarchy primitives
2007/08/02 11:46:43 aw 1.1.2.11: #i39532# added support for Metafile comment actions over MetaFile VCLprimitive renderer
2007/04/20 08:12:13 hdu 1.1.2.10: #i73860# allow fontcolor!=textlinecolor case for text primitives
2007/02/21 11:26:10 hdu 1.1.2.9: outline-flag is a font attribute, not a portion attribute
2007/01/23 11:12:37 aw 1.1.2.8: changes after resync
2006/12/11 17:40:59 aw 1.1.2.7: #i39532# changes after resync
2006/10/19 11:03:51 aw 1.1.2.6: #i39532# primitive
2006/08/09 17:20:06 aw 1.1.2.5: #i39532#
2006/07/16 21:50:14 thb 1.1.2.4: #i65883# Added drawinglayer to the dependency list; gcc4.x fixes - moving out temporaries into explicit local variables
2006/06/02 14:17:53 aw 1.1.2.3: #i39532# adaptions to new primitives, error corrections
2006/05/12 12:46:30 aw 1.1.2.2: code changes for primitive support
2005/10/28 11:39:55 aw 1.1.2.1: #i39532#
Diffstat (limited to 'svx/source/svdraw/svdotextdecomposition.cxx')
-rw-r--r-- | svx/source/svdraw/svdotextdecomposition.cxx | 1137 |
1 files changed, 1137 insertions, 0 deletions
diff --git a/svx/source/svdraw/svdotextdecomposition.cxx b/svx/source/svdraw/svdotextdecomposition.cxx new file mode 100644 index 000000000000..8ebdb320e4de --- /dev/null +++ b/svx/source/svdraw/svdotextdecomposition.cxx @@ -0,0 +1,1137 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: svdotextdecomposition.cxx,v $ + * + * $Revision: 1.2 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_svx.hxx" + +#include <svx/svdotext.hxx> +#include <svx/svdoutl.hxx> +#include <basegfx/vector/b2dvector.hxx> +#include <svx/sdr/primitive2d/sdrtextprimitive2d.hxx> +#include <drawinglayer/primitive2d/textprimitive2d.hxx> +#include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx> +#include <basegfx/range/b2drange.hxx> +#include <editstat.hxx> +#include <vcl/salbtype.hxx> +#include <svx/sdtfchim.hxx> +#include <svtools/itemset.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <drawinglayer/animation/animationtiming.hxx> +#include <basegfx/color/bcolor.hxx> +#include <vcl/svapp.hxx> +#include <svx/eeitemid.hxx> +#include <svx/escpitem.hxx> +#include <svx/svxenum.hxx> +#include <svx/flditem.hxx> +#include <drawinglayer/primitive2d/texthierarchyprimitive2d.hxx> +#include <vcl/metaact.hxx> +#include <drawinglayer/primitive2d/wrongspellprimitive2d.hxx> +#include <drawinglayer/primitive2d/graphicprimitive2d.hxx> +#include <drawinglayer/primitive2d/textlayoutdevice.hxx> +#include <unoapi.hxx> +#include <drawinglayer/geometry/viewinformation2d.hxx> +#include <svx/outlobj.hxx> + +////////////////////////////////////////////////////////////////////////////// +// helpers + +namespace +{ + drawinglayer::primitive2d::Primitive2DSequence impConvertVectorToPrimitive2DSequence(const std::vector< drawinglayer::primitive2d::BasePrimitive2D* >& rPrimitiveVector) + { + const sal_Int32 nCount(rPrimitiveVector.size()); + drawinglayer::primitive2d::Primitive2DSequence aRetval(nCount); + + for(sal_Int32 a(0L); a < nCount; a++) + { + aRetval[a] = drawinglayer::primitive2d::Primitive2DReference(rPrimitiveVector[a]); + } + + return aRetval; + } + + class impTextBreakupHandler + { + private: + std::vector< drawinglayer::primitive2d::BasePrimitive2D* > maTextPortionPrimitives; + std::vector< drawinglayer::primitive2d::BasePrimitive2D* > maLinePrimitives; + std::vector< drawinglayer::primitive2d::BasePrimitive2D* > maParagraphPrimitives; + + SdrOutliner& mrOutliner; + basegfx::B2DHomMatrix maNewTransformA; + basegfx::B2DHomMatrix maNewTransformB; + + DECL_LINK(decomposeContourTextPrimitive, DrawPortionInfo* ); + DECL_LINK(decomposeBlockTextPrimitive, DrawPortionInfo* ); + DECL_LINK(decomposeStretchTextPrimitive, DrawPortionInfo* ); + + DECL_LINK(decomposeContourBulletPrimitive, DrawBulletInfo* ); + DECL_LINK(decomposeBlockBulletPrimitive, DrawBulletInfo* ); + DECL_LINK(decomposeStretchBulletPrimitive, DrawBulletInfo* ); + + bool impIsUnderlineAbove(const Font& rFont) const; + void impCreateTextPortionPrimitive(const DrawPortionInfo& rInfo); + drawinglayer::primitive2d::BasePrimitive2D* impCheckFieldPrimitive(drawinglayer::primitive2d::BasePrimitive2D* pPrimitive, const DrawPortionInfo& rInfo) const; + void impFlushTextPortionPrimitivesToLinePrimitives(); + void impFlushLinePrimitivesToParagraphPrimitives(); + void impHandleDrawPortionInfo(const DrawPortionInfo& rInfo); + void impHandleDrawBulletInfo(const DrawBulletInfo& rInfo); + + public: + impTextBreakupHandler(SdrOutliner& rOutliner) + : mrOutliner(rOutliner) + { + } + + void decomposeContourTextPrimitive(const basegfx::B2DHomMatrix& rNewTransformA, const basegfx::B2DHomMatrix& rNewTransformB) + { + maNewTransformA = rNewTransformA; + maNewTransformB = rNewTransformB; + mrOutliner.SetDrawPortionHdl(LINK(this, impTextBreakupHandler, decomposeContourTextPrimitive)); + mrOutliner.SetDrawBulletHdl(LINK(this, impTextBreakupHandler, decomposeContourBulletPrimitive)); + mrOutliner.StripPortions(); + mrOutliner.SetDrawPortionHdl(Link()); + mrOutliner.SetDrawBulletHdl(Link()); + } + + void decomposeBlockTextPrimitive(const basegfx::B2DHomMatrix& rNewTransformA, const basegfx::B2DHomMatrix& rNewTransformB) + { + maNewTransformA = rNewTransformA; + maNewTransformB = rNewTransformB; + mrOutliner.SetDrawPortionHdl(LINK(this, impTextBreakupHandler, decomposeBlockTextPrimitive)); + mrOutliner.SetDrawBulletHdl(LINK(this, impTextBreakupHandler, decomposeBlockBulletPrimitive)); + mrOutliner.StripPortions(); + mrOutliner.SetDrawPortionHdl(Link()); + mrOutliner.SetDrawBulletHdl(Link()); + } + + void decomposeStretchTextPrimitive(const basegfx::B2DHomMatrix& rNewTransformA, const basegfx::B2DHomMatrix& rNewTransformB) + { + maNewTransformA = rNewTransformA; + maNewTransformB = rNewTransformB; + mrOutliner.SetDrawPortionHdl(LINK(this, impTextBreakupHandler, decomposeStretchTextPrimitive)); + mrOutliner.SetDrawBulletHdl(LINK(this, impTextBreakupHandler, decomposeStretchBulletPrimitive)); + mrOutliner.StripPortions(); + mrOutliner.SetDrawPortionHdl(Link()); + mrOutliner.SetDrawBulletHdl(Link()); + } + + drawinglayer::primitive2d::Primitive2DSequence getPrimitive2DSequence(); + }; + + bool impTextBreakupHandler::impIsUnderlineAbove(const Font& rFont) const + { + if(!rFont.IsVertical()) + { + return false; + } + + if((LANGUAGE_JAPANESE == rFont.GetLanguage()) || (LANGUAGE_JAPANESE == rFont.GetCJKContextLanguage())) + { + // the underline is right for Japanese only + return true; + } + + return false; + } + + void impTextBreakupHandler::impCreateTextPortionPrimitive(const DrawPortionInfo& rInfo) + { + if(rInfo.mrText.Len() && rInfo.mnTextLen) + { + basegfx::B2DVector aSize; + drawinglayer::primitive2d::FontAttributes aFontAttributes(drawinglayer::primitive2d::getFontAttributesFromVclFont( + aSize, + rInfo.mrFont, + rInfo.IsRTL(), + false)); + basegfx::B2DHomMatrix aNewTransform; + + // add font scale to new transform + aNewTransform.scale(aSize.getX(), aSize.getY()); + + // look for proportional font scaling, evtl scale accordingly + if(100 != rInfo.mrFont.GetPropr()) + { + const double fFactor(rInfo.mrFont.GetPropr() / 100.0); + aNewTransform.scale(fFactor, fFactor); + } + + // apply font rotate + if(rInfo.mrFont.GetOrientation()) + { + aNewTransform.rotate(-rInfo.mrFont.GetOrientation() * F_PI1800); + } + + // look for escapement, evtl translate accordingly + if(rInfo.mrFont.GetEscapement()) + { + sal_Int16 nEsc(rInfo.mrFont.GetEscapement()); + + if(DFLT_ESC_AUTO_SUPER == nEsc) + { + nEsc = 33; + } + else if(DFLT_ESC_AUTO_SUB == nEsc) + { + nEsc = -20; + } + + if(nEsc > 100) + { + nEsc = 100; + } + else if(nEsc < -100) + { + nEsc = -100; + } + + const double fEscapement(nEsc / -100.0); + aNewTransform.translate(0.0, fEscapement * aSize.getY()); + } + + // apply transformA + aNewTransform *= maNewTransformA; + + // apply local offset + aNewTransform.translate(rInfo.mrStartPos.X(), rInfo.mrStartPos.Y()); + + // also apply embedding object's transform + aNewTransform *= maNewTransformB; + + // prepare DXArray content. To make it independent from font size (and such from + // the text transformation), scale it to unit coordinates + ::std::vector< double > aDXArray; + + if(rInfo.mpDXArray && rInfo.mnTextLen) + { + const double fScaleFactor(basegfx::fTools::equalZero(aSize.getX()) ? 1.0 : 1.0 / aSize.getX()); + aDXArray.reserve(rInfo.mnTextLen); + + for(xub_StrLen a(0); a < rInfo.mnTextLen; a++) + { + aDXArray.push_back((double)rInfo.mpDXArray[a] * fScaleFactor); + } + } + + // prepare underline data + drawinglayer::primitive2d::FontUnderline eFontUnderline(drawinglayer::primitive2d::FONT_UNDERLINE_NONE); + + switch(rInfo.mrFont.GetUnderline()) + { + case UNDERLINE_SINGLE : eFontUnderline = drawinglayer::primitive2d::FONT_UNDERLINE_SINGLE; break; + case UNDERLINE_DOUBLE : eFontUnderline = drawinglayer::primitive2d::FONT_UNDERLINE_DOUBLE; break; + case UNDERLINE_DOTTED : eFontUnderline = drawinglayer::primitive2d::FONT_UNDERLINE_DOTTED; break; + case UNDERLINE_DASH : eFontUnderline = drawinglayer::primitive2d::FONT_UNDERLINE_DASH; break; + case UNDERLINE_LONGDASH : eFontUnderline = drawinglayer::primitive2d::FONT_UNDERLINE_LONGDASH; break; + case UNDERLINE_DASHDOT : eFontUnderline = drawinglayer::primitive2d::FONT_UNDERLINE_DASHDOT; break; + case UNDERLINE_DASHDOTDOT : eFontUnderline = drawinglayer::primitive2d::FONT_UNDERLINE_DASHDOTDOT; break; + case UNDERLINE_SMALLWAVE : eFontUnderline = drawinglayer::primitive2d::FONT_UNDERLINE_SMALLWAVE; break; + case UNDERLINE_WAVE : eFontUnderline = drawinglayer::primitive2d::FONT_UNDERLINE_WAVE; break; + case UNDERLINE_DOUBLEWAVE : eFontUnderline = drawinglayer::primitive2d::FONT_UNDERLINE_DOUBLEWAVE; break; + case UNDERLINE_BOLD : eFontUnderline = drawinglayer::primitive2d::FONT_UNDERLINE_BOLD; break; + case UNDERLINE_BOLDDOTTED : eFontUnderline = drawinglayer::primitive2d::FONT_UNDERLINE_BOLDDOTTED; break; + case UNDERLINE_BOLDDASH : eFontUnderline = drawinglayer::primitive2d::FONT_UNDERLINE_BOLDDASH; break; + case UNDERLINE_BOLDLONGDASH : eFontUnderline = drawinglayer::primitive2d::FONT_UNDERLINE_BOLDLONGDASH; break; + case UNDERLINE_BOLDDASHDOT : eFontUnderline = drawinglayer::primitive2d::FONT_UNDERLINE_BOLDDASHDOT; break; + case UNDERLINE_BOLDDASHDOTDOT : eFontUnderline = drawinglayer::primitive2d::FONT_UNDERLINE_BOLDDASHDOTDOT; break; + case UNDERLINE_BOLDWAVE : eFontUnderline = drawinglayer::primitive2d::FONT_UNDERLINE_BOLDWAVE; break; + default : break; // FontUnderline_FORCE_EQUAL_SIZE, UNDERLINE_DONTKNOW, UNDERLINE_NONE + } + + const bool bUnderlineAbove(drawinglayer::primitive2d::FONT_UNDERLINE_NONE != eFontUnderline && impIsUnderlineAbove(rInfo.mrFont)); + + // prepare strikeout data + drawinglayer::primitive2d::FontStrikeout eFontStrikeout(drawinglayer::primitive2d::FONT_STRIKEOUT_NONE); + + switch(rInfo.mrFont.GetStrikeout()) + { + case STRIKEOUT_SINGLE: eFontStrikeout = drawinglayer::primitive2d::FONT_STRIKEOUT_SINGLE; break; + case STRIKEOUT_DOUBLE: eFontStrikeout = drawinglayer::primitive2d::FONT_STRIKEOUT_DOUBLE; break; + case STRIKEOUT_BOLD: eFontStrikeout = drawinglayer::primitive2d::FONT_STRIKEOUT_BOLD; break; + case STRIKEOUT_SLASH: eFontStrikeout = drawinglayer::primitive2d::FONT_STRIKEOUT_SLASH; break; + case STRIKEOUT_X: eFontStrikeout = drawinglayer::primitive2d::FONT_STRIKEOUT_X; break; + default : break; // FontStrikeout_FORCE_EQUAL_SIZE, STRIKEOUT_NONE, STRIKEOUT_DONTKNOW + } + + // prepare wordLineMode (for underline and strikeout) + // NOT for bullet texts. It is set (this may be an error by itself), but needs to be suppressed to hinder e.g. '1)' + // to be splitted which would not look like the original + const bool bWordLineMode(rInfo.mrFont.IsWordLineMode() && !rInfo.mbEndOfBullet); + + // prepare emphasis mark data + drawinglayer::primitive2d::FontEmphasisMark eFontEmphasisMark(drawinglayer::primitive2d::FONT_EMPHASISMARK_NONE); + + switch(rInfo.mrFont.GetEmphasisMark() & EMPHASISMARK_STYLE) + { + case EMPHASISMARK_DOT : eFontEmphasisMark = drawinglayer::primitive2d::FONT_EMPHASISMARK_DOT; break; + case EMPHASISMARK_CIRCLE : eFontEmphasisMark = drawinglayer::primitive2d::FONT_EMPHASISMARK_CIRCLE; break; + case EMPHASISMARK_DISC : eFontEmphasisMark = drawinglayer::primitive2d::FONT_EMPHASISMARK_DISC; break; + case EMPHASISMARK_ACCENT : eFontEmphasisMark = drawinglayer::primitive2d::FONT_EMPHASISMARK_ACCENT; break; + } + + const bool bEmphasisMarkAbove(rInfo.mrFont.GetEmphasisMark() & EMPHASISMARK_POS_ABOVE); + const bool bEmphasisMarkBelow(rInfo.mrFont.GetEmphasisMark() & EMPHASISMARK_POS_BELOW); + + // prepare font relief data + drawinglayer::primitive2d::FontRelief eFontRelief(drawinglayer::primitive2d::FONT_RELIEF_NONE); + + switch(rInfo.mrFont.GetRelief()) + { + case RELIEF_EMBOSSED : eFontRelief = drawinglayer::primitive2d::FONT_RELIEF_EMBOSSED; break; + case RELIEF_ENGRAVED : eFontRelief = drawinglayer::primitive2d::FONT_RELIEF_ENGRAVED; break; + default : break; // RELIEF_NONE, FontRelief_FORCE_EQUAL_SIZE + } + + // prepare shadow/outline data + const bool bShadow(rInfo.mrFont.IsShadow()); + + // create complex text primitive and append + const Color aFontColor(rInfo.mrFont.GetColor()); + const basegfx::BColor aBFontColor(aFontColor.getBColor()); + + // get line color. If it's on automatic (0xffffffff) use FontColor instead + const Color aLineColor(rInfo.maTextLineColor); + const basegfx::BColor aBLineColor((0xffffffff == aLineColor.GetColor()) ? aBFontColor : aLineColor.getBColor()); + + drawinglayer::primitive2d::BasePrimitive2D* pNewPrimitive = new drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D( + + // attributes for TextSimplePortionPrimitive2D + aNewTransform, + rInfo.mrText, + rInfo.mnTextStart, + rInfo.mnTextLen, + aDXArray, + aFontAttributes, + rInfo.mpLocale ? *rInfo.mpLocale : ::com::sun::star::lang::Locale(), + aBFontColor, + + // attributes for TextDecoratedPortionPrimitive2D + aBLineColor, + eFontUnderline, + bUnderlineAbove, + eFontStrikeout, + bWordLineMode, + eFontEmphasisMark, + bEmphasisMarkAbove, + bEmphasisMarkBelow, + eFontRelief, + bShadow); + + if(rInfo.mbEndOfBullet) + { + // embed in TextHierarchyBulletPrimitive2D + const drawinglayer::primitive2d::Primitive2DReference aNewReference(pNewPrimitive); + const drawinglayer::primitive2d::Primitive2DSequence aNewSequence(&aNewReference, 1); + pNewPrimitive = new drawinglayer::primitive2d::TextHierarchyBulletPrimitive2D(aNewSequence); + } + + if(rInfo.mpFieldData) + { + pNewPrimitive = impCheckFieldPrimitive(pNewPrimitive, rInfo); + } + + maTextPortionPrimitives.push_back(pNewPrimitive); + + // support for WrongSpellVector. Create WrongSpellPrimitives as needed + if(rInfo.mpWrongSpellVector && !aDXArray.empty()) + { + const sal_uInt32 nSize(rInfo.mpWrongSpellVector->size()); + const sal_uInt32 nDXCount(aDXArray.size()); + const basegfx::BColor aSpellColor(1.0, 0.0, 0.0); // red, hard coded + + for(sal_uInt32 a(0); a < nSize; a++) + { + const EEngineData::WrongSpellClass& rCandidate = (*rInfo.mpWrongSpellVector)[a]; + + if(rCandidate.nStart >= rInfo.mnTextStart && rCandidate.nEnd >= rInfo.mnTextStart && rCandidate.nEnd > rCandidate.nStart) + { + const sal_uInt32 nStart(rCandidate.nStart - rInfo.mnTextStart); + const sal_uInt32 nEnd(rCandidate.nEnd - rInfo.mnTextStart); + double fStart(0.0); + double fEnd(0.0); + + if(nStart > 0 && nStart - 1 < nDXCount) + { + fStart = aDXArray[nStart - 1]; + } + + if(nEnd > 0 && nEnd - 1 < nDXCount) + { + fEnd = aDXArray[nEnd - 1]; + } + + if(!basegfx::fTools::equal(fStart, fEnd)) + { + maTextPortionPrimitives.push_back(new drawinglayer::primitive2d::WrongSpellPrimitive2D( + aNewTransform, + fStart, + fEnd, + aSpellColor)); + } + } + } + } + } + } + + drawinglayer::primitive2d::BasePrimitive2D* impTextBreakupHandler::impCheckFieldPrimitive(drawinglayer::primitive2d::BasePrimitive2D* pPrimitive, const DrawPortionInfo& rInfo) const + { + if(rInfo.mpFieldData) + { + // Support for FIELD_SEQ_BEGIN, FIELD_SEQ_END. If used, create a TextHierarchyFieldPrimitive2D + // which holds the field type and evtl. the URL + const SvxURLField* pURLField = dynamic_cast< const SvxURLField* >(rInfo.mpFieldData); + const SvxPageField* pPageField = dynamic_cast< const SvxPageField* >(rInfo.mpFieldData); + + // embed current primitive to a sequence + drawinglayer::primitive2d::Primitive2DSequence aSequence; + + if(pPrimitive) + { + aSequence.realloc(1); + aSequence[0] = drawinglayer::primitive2d::Primitive2DReference(pPrimitive); + } + + if(pURLField) + { + pPrimitive = new drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(aSequence, drawinglayer::primitive2d::FIELD_TYPE_URL, pURLField->GetURL()); + } + else if(pPageField) + { + pPrimitive = new drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(aSequence, drawinglayer::primitive2d::FIELD_TYPE_PAGE, String()); + } + else + { + pPrimitive = new drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(aSequence, drawinglayer::primitive2d::FIELD_TYPE_COMMON, String()); + } + } + + return pPrimitive; + } + + void impTextBreakupHandler::impFlushTextPortionPrimitivesToLinePrimitives() + { + // only create a line primitive when we had content; there is no need for + // empty line primitives (contrary to paragraphs, see below). + if(maTextPortionPrimitives.size()) + { + drawinglayer::primitive2d::Primitive2DSequence aLineSequence(impConvertVectorToPrimitive2DSequence(maTextPortionPrimitives)); + maTextPortionPrimitives.clear(); + maLinePrimitives.push_back(new drawinglayer::primitive2d::TextHierarchyLinePrimitive2D(aLineSequence)); + } + } + + void impTextBreakupHandler::impFlushLinePrimitivesToParagraphPrimitives() + { + // ALWAYS create a paragraph primitive, even when no content was added. This is done to + // have the correct paragraph count even with empty paragraphs. Those paragraphs will + // have an empty sub-PrimitiveSequence. + drawinglayer::primitive2d::Primitive2DSequence aParagraphSequence(impConvertVectorToPrimitive2DSequence(maLinePrimitives)); + maLinePrimitives.clear(); + maParagraphPrimitives.push_back(new drawinglayer::primitive2d::TextHierarchyParagraphPrimitive2D(aParagraphSequence)); + } + + void impTextBreakupHandler::impHandleDrawPortionInfo(const DrawPortionInfo& rInfo) + { + impCreateTextPortionPrimitive(rInfo); + + if(rInfo.mbEndOfLine || rInfo.mbEndOfParagraph) + { + impFlushTextPortionPrimitivesToLinePrimitives(); + } + + if(rInfo.mbEndOfParagraph) + { + impFlushLinePrimitivesToParagraphPrimitives(); + } + } + + void impTextBreakupHandler::impHandleDrawBulletInfo(const DrawBulletInfo& rInfo) + { + basegfx::B2DHomMatrix aNewTransform; + + // add size to new transform + aNewTransform.scale(rInfo.maBulletSize.getWidth(), rInfo.maBulletSize.getHeight()); + + // apply transformA + aNewTransform *= maNewTransformA; + + // apply local offset + aNewTransform.translate(rInfo.maBulletPosition.X(), rInfo.maBulletPosition.Y()); + + // also apply embedding object's transform + aNewTransform *= maNewTransformB; + + // prepare empty GraphicAttr + const GraphicAttr aGraphicAttr; + + // create GraphicPrimitive2D + const drawinglayer::primitive2d::Primitive2DReference aNewReference(new drawinglayer::primitive2d::GraphicPrimitive2D( + aNewTransform, + rInfo.maBulletGraphicObject, + aGraphicAttr)); + + // embed in TextHierarchyBulletPrimitive2D + const drawinglayer::primitive2d::Primitive2DSequence aNewSequence(&aNewReference, 1); + drawinglayer::primitive2d::BasePrimitive2D* pNewPrimitive = new drawinglayer::primitive2d::TextHierarchyBulletPrimitive2D(aNewSequence); + + // add to output + maTextPortionPrimitives.push_back(pNewPrimitive); + } + + IMPL_LINK(impTextBreakupHandler, decomposeContourTextPrimitive, DrawPortionInfo*, pInfo) + { + if(pInfo) + { + impHandleDrawPortionInfo(*pInfo); + } + + return 0; + } + + IMPL_LINK(impTextBreakupHandler, decomposeBlockTextPrimitive, DrawPortionInfo*, pInfo) + { + if(pInfo) + { + impHandleDrawPortionInfo(*pInfo); + } + + return 0; + } + + IMPL_LINK(impTextBreakupHandler, decomposeStretchTextPrimitive, DrawPortionInfo*, pInfo) + { + if(pInfo) + { + impHandleDrawPortionInfo(*pInfo); + } + + return 0; + } + + IMPL_LINK(impTextBreakupHandler, decomposeContourBulletPrimitive, DrawBulletInfo*, pInfo) + { + if(pInfo) + { + impHandleDrawBulletInfo(*pInfo); + } + + return 0; + } + + IMPL_LINK(impTextBreakupHandler, decomposeBlockBulletPrimitive, DrawBulletInfo*, pInfo) + { + if(pInfo) + { + impHandleDrawBulletInfo(*pInfo); + } + + return 0; + } + + IMPL_LINK(impTextBreakupHandler, decomposeStretchBulletPrimitive, DrawBulletInfo*, pInfo) + { + if(pInfo) + { + impHandleDrawBulletInfo(*pInfo); + } + + return 0; + } + + drawinglayer::primitive2d::Primitive2DSequence impTextBreakupHandler::getPrimitive2DSequence() + { + if(maTextPortionPrimitives.size()) + { + // collect non-closed lines + impFlushTextPortionPrimitivesToLinePrimitives(); + } + + if(maLinePrimitives.size()) + { + // collect non-closed paragraphs + impFlushLinePrimitivesToParagraphPrimitives(); + } + + return impConvertVectorToPrimitive2DSequence(maParagraphPrimitives); + } +} // end of anonymous namespace + +////////////////////////////////////////////////////////////////////////////// +// primitive decompositions + +bool SdrTextObj::impCheckSpellCheckForDecomposeTextPrimitive() const +{ + SdrOutliner& rOutliner = ImpGetDrawOutliner(); + return (rOutliner.GetControlWord() & EE_CNTRL_NOREDLINES); +} + +bool SdrTextObj::impDecomposeContourTextPrimitive( + drawinglayer::primitive2d::Primitive2DSequence& rTarget, + const drawinglayer::primitive2d::SdrContourTextPrimitive2D& rSdrContourTextPrimitive, + const drawinglayer::geometry::ViewInformation2D& aViewInformation) const +{ + // decompose matrix to have position and size of text + basegfx::B2DVector aScale, aTranslate; + double fRotate, fShearX; + rSdrContourTextPrimitive.getObjectTransform().decompose(aScale, aTranslate, fRotate, fShearX); + + // prepare contour polygon, force to non-mirrored for layouting + basegfx::B2DPolyPolygon aPolyPolygon(rSdrContourTextPrimitive.getUnitPolyPolygon()); + basegfx::B2DHomMatrix aTransform; + aTransform.scale(fabs(aScale.getX()), fabs(aScale.getY())); + aPolyPolygon.transform(aTransform); + + // prepare outliner + SdrOutliner& rOutliner = ImpGetDrawOutliner(); + const Size aNullSize; + rOutliner.SetPaperSize(aNullSize); + rOutliner.SetPolygon(aPolyPolygon); + rOutliner.SetUpdateMode(true); + rOutliner.SetText(*rSdrContourTextPrimitive.getSdrText().GetOutlinerParaObject()); + + // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition + rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage())); + + // prepare matrices to apply to newly created primitives + basegfx::B2DHomMatrix aNewTransformA; + basegfx::B2DHomMatrix aNewTransformB; + + // mirroring. We are now in the polygon sizes. When mirroring in X and Y, + // move the null point which was top left to bottom right. + const bool bMirrorX(basegfx::fTools::less(aScale.getX(), 0.0)); + const bool bMirrorY(basegfx::fTools::less(aScale.getY(), 0.0)); + aNewTransformB.scale(bMirrorX ? -1.0 : 1.0, bMirrorY ? -1.0 : 1.0); + + // in-between the translations of the single primitives will take place. Afterwards, + // the object's transformations need to be applied + aNewTransformB.shearX(fShearX); + aNewTransformB.rotate(fRotate); + aNewTransformB.translate(aTranslate.getX(), aTranslate.getY()); + + // now break up text primitives. + impTextBreakupHandler aConverter(rOutliner); + aConverter.decomposeContourTextPrimitive(aNewTransformA, aNewTransformB); + + // cleanup outliner + rOutliner.Clear(); + rOutliner.setVisualizedPage(0); + + rTarget = aConverter.getPrimitive2DSequence(); + return (rOutliner.GetControlWord() & EE_CNTRL_NOREDLINES); +} + +bool SdrTextObj::impDecomposeBlockTextPrimitive( + drawinglayer::primitive2d::Primitive2DSequence& rTarget, + const drawinglayer::primitive2d::SdrBlockTextPrimitive2D& rSdrBlockTextPrimitive, + const drawinglayer::geometry::ViewInformation2D& aViewInformation) const +{ + // decompose matrix to have position and size of text + basegfx::B2DVector aScale, aTranslate; + double fRotate, fShearX; + rSdrBlockTextPrimitive.getTextRangeTransform().decompose(aScale, aTranslate, fRotate, fShearX); + + // use B2DRange aAnchorTextRange for calculations + basegfx::B2DRange aAnchorTextRange(aTranslate); + aAnchorTextRange.expand(aTranslate + aScale); + + // prepare outliner + const bool bIsCell(rSdrBlockTextPrimitive.getCellText()); + const SfxItemSet& rTextItemSet = rSdrBlockTextPrimitive.getSdrText().GetItemSet(); + SdrOutliner& rOutliner = ImpGetDrawOutliner(); + SdrTextVertAdjust eVAdj = GetTextVerticalAdjust(rTextItemSet); + SdrTextHorzAdjust eHAdj = GetTextHorizontalAdjust(rTextItemSet); + const sal_uInt32 nOriginalControlWord(rOutliner.GetControlWord()); + const Size aNullSize; + + // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition + rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage())); + + rOutliner.SetFixedCellHeight(((const SdrTextFixedCellHeightItem&)rTextItemSet.Get(SDRATTR_TEXT_USEFIXEDCELLHEIGHT)).GetValue()); + rOutliner.SetControlWord(nOriginalControlWord|EE_CNTRL_AUTOPAGESIZE); + rOutliner.SetMinAutoPaperSize(aNullSize); + rOutliner.SetMaxAutoPaperSize(Size(1000000,1000000)); + + // add one to rage sizes to get back to the old Rectangle and outliner measurements + const sal_uInt32 nAnchorTextWidth(FRound(aAnchorTextRange.getWidth() + 1L)); + const sal_uInt32 nAnchorTextHeight(FRound(aAnchorTextRange.getHeight() + 1L)); + const OutlinerParaObject* pOutlinerParaObject = rSdrBlockTextPrimitive.getSdrText().GetOutlinerParaObject(); + OSL_ENSURE(pOutlinerParaObject, "impDecomposeBlockTextPrimitive used with no OutlinerParaObject (!)"); + const bool bVerticalWritintg(pOutlinerParaObject->IsVertical()); + const Size aAnchorTextSize(Size(nAnchorTextWidth, nAnchorTextHeight)); + + if(bIsCell) + { + // cell text is formated neither like a text object nor like a object + // text, so use a special setup here + rOutliner.SetMinAutoPaperSize(aNullSize); + rOutliner.SetMaxAutoPaperSize(aAnchorTextSize); + rOutliner.SetPaperSize(aAnchorTextSize); + rOutliner.SetMinAutoPaperSize(Size(nAnchorTextWidth, 0)); + rOutliner.SetUpdateMode(TRUE); + rOutliner.SetText(*pOutlinerParaObject); + rOutliner.SetUpdateMode(TRUE); + rOutliner.SetControlWord(nOriginalControlWord); + } + else + { + if(IsTextFrame() && !rSdrBlockTextPrimitive.getUnlimitedPage()) + { + rOutliner.SetMaxAutoPaperSize(aAnchorTextSize); + } + + if(SDRTEXTHORZADJUST_BLOCK == eHAdj && !bVerticalWritintg) + { + rOutliner.SetMinAutoPaperSize(Size(nAnchorTextWidth, 0)); + } + + if(SDRTEXTVERTADJUST_BLOCK == eVAdj && bVerticalWritintg) + { + rOutliner.SetMinAutoPaperSize(Size(0, nAnchorTextHeight)); + } + + rOutliner.SetPaperSize(aNullSize); + rOutliner.SetUpdateMode(true); + rOutliner.SetText(*pOutlinerParaObject); + rOutliner.SetControlWord(nOriginalControlWord); + } + + // now get back the layouted text size from outliner + const Size aOutlinerTextSiz(rOutliner.GetPaperSize()); + const basegfx::B2DVector aOutlinerScale(aOutlinerTextSiz.Width(), aOutlinerTextSiz.Height()); + basegfx::B2DVector aAdjustTranslate(0.0, 0.0); + + // For draw objects containing text correct hor/ver alignment if text is bigger + // than the object itself. Without that correction, the text would always be + // formatted to the left edge (or top edge when vertical) of the draw object. + if(!IsTextFrame() && !bIsCell) + { + if(aAnchorTextRange.getWidth() < aOutlinerScale.getX() && !bVerticalWritintg) + { + // Horizontal case here. Correct only if eHAdj == SDRTEXTHORZADJUST_BLOCK, + // else the alignment is wanted. + if(SDRTEXTHORZADJUST_BLOCK == eHAdj) + { + eHAdj = SDRTEXTHORZADJUST_CENTER; + } + } + + if(aAnchorTextRange.getHeight() < aOutlinerScale.getY() && bVerticalWritintg) + { + // Vertical case here. Correct only if eHAdj == SDRTEXTVERTADJUST_BLOCK, + // else the alignment is wanted. + if(SDRTEXTVERTADJUST_BLOCK == eVAdj) + { + eVAdj = SDRTEXTVERTADJUST_CENTER; + } + } + } + + // correct horizontal translation using the now known text size + if(SDRTEXTHORZADJUST_CENTER == eHAdj || SDRTEXTHORZADJUST_RIGHT == eHAdj) + { + const double fFree(aAnchorTextRange.getWidth() - aOutlinerScale.getX()); + + if(SDRTEXTHORZADJUST_CENTER == eHAdj) + { + aAdjustTranslate.setX(fFree / 2.0); + } + + if(SDRTEXTHORZADJUST_RIGHT == eHAdj) + { + aAdjustTranslate.setX(fFree); + } + } + + // correct vertical translation using the now known text size + if(SDRTEXTVERTADJUST_CENTER == eVAdj || SDRTEXTVERTADJUST_BOTTOM == eVAdj) + { + const double fFree(aAnchorTextRange.getHeight() - aOutlinerScale.getY()); + + if(SDRTEXTVERTADJUST_CENTER == eVAdj) + { + aAdjustTranslate.setY(fFree / 2.0); + } + + if(SDRTEXTVERTADJUST_BOTTOM == eVAdj) + { + aAdjustTranslate.setY(fFree); + } + } + + // prepare matrices to apply to newly created primitives. aNewTransformA + // will get coordinates in aOutlinerScale size and positive in X, Y. + basegfx::B2DHomMatrix aNewTransformA; + basegfx::B2DHomMatrix aNewTransformB; + + // translate relative to given primitive to get same rotation and shear + // as the master shape we are working on. For vertical, use the top-right + // corner + const double fStartInX(bVerticalWritintg ? aAdjustTranslate.getX() + aOutlinerScale.getX() : aAdjustTranslate.getX()); + aNewTransformA.translate(fStartInX, aAdjustTranslate.getY()); + + // mirroring. We are now in aAnchorTextRange sizes. When mirroring in X and Y, + // move the null point which was top left to bottom right. + const bool bMirrorX(basegfx::fTools::less(aScale.getX(), 0.0)); + const bool bMirrorY(basegfx::fTools::less(aScale.getY(), 0.0)); + aNewTransformB.scale(bMirrorX ? -1.0 : 1.0, bMirrorY ? -1.0 : 1.0); + + // in-between the translations of the single primitives will take place. Afterwards, + // the object's transformations need to be applied + aNewTransformB.shearX(fShearX); + aNewTransformB.rotate(fRotate); + aNewTransformB.translate(aTranslate.getX(), aTranslate.getY()); + + // now break up text primitives. + impTextBreakupHandler aConverter(rOutliner); + aConverter.decomposeBlockTextPrimitive(aNewTransformA, aNewTransformB); + + // cleanup outliner + rOutliner.Clear(); + rOutliner.setVisualizedPage(0); + + rTarget = aConverter.getPrimitive2DSequence(); + return (rOutliner.GetControlWord() & EE_CNTRL_NOREDLINES); +} + +bool SdrTextObj::impDecomposeStretchTextPrimitive( + drawinglayer::primitive2d::Primitive2DSequence& rTarget, + const drawinglayer::primitive2d::SdrStretchTextPrimitive2D& rSdrStretchTextPrimitive, + const drawinglayer::geometry::ViewInformation2D& aViewInformation) const +{ + // decompose matrix to have position and size of text + basegfx::B2DVector aScale, aTranslate; + double fRotate, fShearX; + rSdrStretchTextPrimitive.getTextRangeTransform().decompose(aScale, aTranslate, fRotate, fShearX); + + // use non-mirrored B2DRange aAnchorTextRange for calculations + basegfx::B2DRange aAnchorTextRange(aTranslate); + aAnchorTextRange.expand(aTranslate + aScale); + + // prepare outliner + SdrOutliner& rOutliner = ImpGetDrawOutliner(); + const sal_uInt32 nOriginalControlWord(rOutliner.GetControlWord()); + const SfxItemSet& rTextItemSet = rSdrStretchTextPrimitive.getSdrText().GetItemSet(); + const Size aNullSize; + + rOutliner.SetControlWord(nOriginalControlWord|EE_CNTRL_STRETCHING|EE_CNTRL_AUTOPAGESIZE); + rOutliner.SetFixedCellHeight(((const SdrTextFixedCellHeightItem&)rTextItemSet.Get(SDRATTR_TEXT_USEFIXEDCELLHEIGHT)).GetValue()); + rOutliner.SetMinAutoPaperSize(aNullSize); + rOutliner.SetMaxAutoPaperSize(Size(1000000,1000000)); + rOutliner.SetPaperSize(aNullSize); + rOutliner.SetUpdateMode(true); + rOutliner.SetText(*rSdrStretchTextPrimitive.getSdrText().GetOutlinerParaObject()); + + // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition + rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage())); + + // now get back the layouted text size from outliner + const Size aOutlinerTextSiz(rOutliner.CalcTextSize()); + const basegfx::B2DVector aOutlinerScale( + basegfx::fTools::equalZero(aOutlinerTextSiz.Width()) ? 1.0 : aOutlinerTextSiz.Width(), + basegfx::fTools::equalZero(aOutlinerTextSiz.Height()) ? 1.0 : aOutlinerTextSiz.Height()); + + // prepare matrices to apply to newly created primitives + basegfx::B2DHomMatrix aNewTransformA; + basegfx::B2DHomMatrix aNewTransformB; + + // calculate global char stretching scale parameters. Use non-mirrored sizes + // to layout without mirroring + const double fScaleX(fabs(aScale.getX()) / aOutlinerScale.getX()); + const double fScaleY(fabs(aScale.getY()) / aOutlinerScale.getY()); + rOutliner.SetGlobalCharStretching((sal_Int16)FRound(fScaleX * 100.0), (sal_Int16)FRound(fScaleY * 100.0)); + + // mirroring. We are now in aAnchorTextRange sizes. When mirroring in X and Y, + // move the null point which was top left to bottom right. + const bool bMirrorX(basegfx::fTools::less(aScale.getX(), 0.0)); + const bool bMirrorY(basegfx::fTools::less(aScale.getY(), 0.0)); + aNewTransformB.scale(bMirrorX ? -1.0 : 1.0, bMirrorY ? -1.0 : 1.0); + + // in-between the translations of the single primitives will take place. Afterwards, + // the object's transformations need to be applied + aNewTransformB.shearX(fShearX); + aNewTransformB.rotate(fRotate); + aNewTransformB.translate(aTranslate.getX(), aTranslate.getY()); + + // now break up text primitives. + impTextBreakupHandler aConverter(rOutliner); + aConverter.decomposeStretchTextPrimitive(aNewTransformA, aNewTransformB); + + // cleanup outliner + rOutliner.SetControlWord(nOriginalControlWord); + rOutliner.Clear(); + rOutliner.setVisualizedPage(0); + + rTarget = aConverter.getPrimitive2DSequence(); + return (rOutliner.GetControlWord() & EE_CNTRL_NOREDLINES); +} + +////////////////////////////////////////////////////////////////////////////// +// timing generators +#define ENDLESS_LOOP (0xffffffff) +#define ENDLESS_TIME ((double)0xffffffff) +#define PIXEL_DPI (96.0) + +void SdrTextObj::impGetBlinkTextTiming(drawinglayer::animation::AnimationEntryList& rAnimList) const +{ + if(SDRTEXTANI_BLINK == GetTextAniKind()) + { + // get values + const SfxItemSet& rSet = GetObjectItemSet(); + const sal_uInt32 nRepeat((sal_uInt32)((SdrTextAniCountItem&)rSet.Get(SDRATTR_TEXT_ANICOUNT)).GetValue()); + bool bVisisbleWhenStopped(((SdrTextAniStopInsideItem&)rSet.Get(SDRATTR_TEXT_ANISTOPINSIDE)).GetValue()); + double fDelay((double)((SdrTextAniDelayItem&)rSet.Get(SDRATTR_TEXT_ANIDELAY)).GetValue()); + + if(0.0 == fDelay) + { + // use default + fDelay = 250.0; + } + + // prepare loop and add + drawinglayer::animation::AnimationEntryLoop aLoop(nRepeat ? nRepeat : ENDLESS_LOOP); + drawinglayer::animation::AnimationEntryFixed aStart(fDelay, 0.0); + aLoop.append(aStart); + drawinglayer::animation::AnimationEntryFixed aEnd(fDelay, 1.0); + aLoop.append(aEnd); + rAnimList.append(aLoop); + + // add stopped state if loop is not endless + if(0L != nRepeat) + { + drawinglayer::animation::AnimationEntryFixed aStop(ENDLESS_TIME, bVisisbleWhenStopped ? 0.0 : 1.0); + rAnimList.append(aStop); + } + } +} + +void impCreateScrollTiming(const SfxItemSet& rSet, drawinglayer::animation::AnimationEntryList& rAnimList, bool bForward, double fTimeFullPath, double fFrequency) +{ + bool bVisisbleWhenStopped(((SdrTextAniStopInsideItem&)rSet.Get(SDRATTR_TEXT_ANISTOPINSIDE)).GetValue()); + bool bVisisbleWhenStarted(((SdrTextAniStartInsideItem&)rSet.Get(SDRATTR_TEXT_ANISTOPINSIDE )).GetValue()); + const sal_uInt32 nRepeat(((SdrTextAniCountItem&)rSet.Get(SDRATTR_TEXT_ANICOUNT)).GetValue()); + + if(bVisisbleWhenStarted) + { + // move from center to outside + drawinglayer::animation::AnimationEntryLinear aInOut(fTimeFullPath * 0.5, fFrequency, 0.5, bForward ? 1.0 : 0.0); + rAnimList.append(aInOut); + } + + // loop. In loop, move through + if(nRepeat || 0L == nRepeat) + { + drawinglayer::animation::AnimationEntryLoop aLoop(nRepeat ? nRepeat : ENDLESS_LOOP); + drawinglayer::animation::AnimationEntryLinear aThrough(fTimeFullPath, fFrequency, bForward ? 0.0 : 1.0, bForward ? 1.0 : 0.0); + aLoop.append(aThrough); + rAnimList.append(aLoop); + } + + if(0L != nRepeat && bVisisbleWhenStopped) + { + // move from outside to center + drawinglayer::animation::AnimationEntryLinear aOutIn(fTimeFullPath * 0.5, fFrequency, bForward ? 0.0 : 1.0, 0.5); + rAnimList.append(aOutIn); + + // add timing for staying at the end + drawinglayer::animation::AnimationEntryFixed aEnd(ENDLESS_TIME, 0.5); + rAnimList.append(aEnd); + } +} + +void impCreateAlternateTiming(const SfxItemSet& rSet, drawinglayer::animation::AnimationEntryList& rAnimList, double fRelativeTextLength, bool bForward, double fTimeFullPath, double fFrequency) +{ + if(basegfx::fTools::more(fRelativeTextLength, 0.5)) + { + // this is the case when fTextLength > fFrameLength, text is bigger than animation frame. + // In that case, correct direction + bForward = !bForward; + } + + const double fStartPosition(bForward ? fRelativeTextLength : 1.0 - fRelativeTextLength); + const double fEndPosition(bForward ? 1.0 - fRelativeTextLength : fRelativeTextLength); + bool bVisisbleWhenStopped(((SdrTextAniStopInsideItem&)rSet.Get(SDRATTR_TEXT_ANISTOPINSIDE)).GetValue()); + bool bVisisbleWhenStarted(((SdrTextAniStartInsideItem&)rSet.Get(SDRATTR_TEXT_ANISTOPINSIDE )).GetValue()); + const sal_uInt32 nRepeat(((SdrTextAniCountItem&)rSet.Get(SDRATTR_TEXT_ANICOUNT)).GetValue()); + + if(!bVisisbleWhenStarted) + { + // move from outside to center + drawinglayer::animation::AnimationEntryLinear aOutIn(fTimeFullPath * 0.5, fFrequency, bForward ? 0.0 : 1.0, 0.5); + rAnimList.append(aOutIn); + } + + // loop. In loop, move out and in again. fInnerMovePath may be negative when text is bigger then frame, + // so use absolute value + const double fInnerMovePath(fabs(1.0 - (fRelativeTextLength * 2.0))); + const double fTimeForInnerPath(fTimeFullPath * fInnerMovePath); + const double fHalfInnerPath(fTimeForInnerPath * 0.5); + const sal_uInt32 nDoubleRepeat(nRepeat / 2L); + + if(nDoubleRepeat || 0L == nRepeat) + { + // double forth and back loop + drawinglayer::animation::AnimationEntryLoop aLoop(nDoubleRepeat ? nDoubleRepeat : ENDLESS_LOOP); + drawinglayer::animation::AnimationEntryLinear aTime0(fHalfInnerPath, fFrequency, 0.5, fEndPosition); + aLoop.append(aTime0); + drawinglayer::animation::AnimationEntryLinear aTime1(fTimeForInnerPath, fFrequency, fEndPosition, fStartPosition); + aLoop.append(aTime1); + drawinglayer::animation::AnimationEntryLinear aTime2(fHalfInnerPath, fFrequency, fStartPosition, 0.5); + aLoop.append(aTime2); + rAnimList.append(aLoop); + } + + if(nRepeat % 2L) + { + // repeat is uneven, so we need one more forth and back to center + drawinglayer::animation::AnimationEntryLinear aTime0(fHalfInnerPath, fFrequency, 0.5, fEndPosition); + rAnimList.append(aTime0); + drawinglayer::animation::AnimationEntryLinear aTime1(fHalfInnerPath, fFrequency, fEndPosition, 0.5); + rAnimList.append(aTime1); + } + + if(0L != nRepeat) + { + if(bVisisbleWhenStopped) + { + // add timing for staying at the end + drawinglayer::animation::AnimationEntryFixed aEnd(ENDLESS_TIME, 0.5); + rAnimList.append(aEnd); + } + else + { + // move from center to outside + drawinglayer::animation::AnimationEntryLinear aInOut(fTimeFullPath * 0.5, fFrequency, 0.5, bForward ? 1.0 : 0.0); + rAnimList.append(aInOut); + } + } +} + +void impCreateSlideTiming(const SfxItemSet& rSet, drawinglayer::animation::AnimationEntryList& rAnimList, bool bForward, double fTimeFullPath, double fFrequency) +{ + // move in from outside, start outside + const double fStartPosition(bForward ? 0.0 : 1.0); + const sal_uInt32 nRepeat(((SdrTextAniCountItem&)rSet.Get(SDRATTR_TEXT_ANICOUNT)).GetValue()); + + // move from outside to center + drawinglayer::animation::AnimationEntryLinear aOutIn(fTimeFullPath * 0.5, fFrequency, fStartPosition, 0.5); + rAnimList.append(aOutIn); + + // loop. In loop, move out and in again + if(nRepeat > 1L || 0L == nRepeat) + { + drawinglayer::animation::AnimationEntryLoop aLoop(nRepeat ? nRepeat - 1L : ENDLESS_LOOP); + drawinglayer::animation::AnimationEntryLinear aTime0(fTimeFullPath * 0.5, fFrequency, 0.5, fStartPosition); + aLoop.append(aTime0); + drawinglayer::animation::AnimationEntryLinear aTime1(fTimeFullPath * 0.5, fFrequency, fStartPosition, 0.5); + aLoop.append(aTime1); + rAnimList.append(aLoop); + } + + // always visible when stopped, so add timing for staying at the end when not endless + if(0L != nRepeat) + { + drawinglayer::animation::AnimationEntryFixed aEnd(ENDLESS_TIME, 0.5); + rAnimList.append(aEnd); + } +} + +void SdrTextObj::impGetScrollTextTiming(drawinglayer::animation::AnimationEntryList& rAnimList, double fFrameLength, double fTextLength) const +{ + const SdrTextAniKind eAniKind(GetTextAniKind()); + + if(SDRTEXTANI_SCROLL == eAniKind || SDRTEXTANI_ALTERNATE == eAniKind || SDRTEXTANI_SLIDE == eAniKind) + { + // get data. Goal is to calculate fTimeFullPath which is the time needed to + // move animation from (0.0) to (1.0) state + const SfxItemSet& rSet = GetObjectItemSet(); + double fAnimationDelay((double)((SdrTextAniDelayItem&)rSet.Get(SDRATTR_TEXT_ANIDELAY)).GetValue()); + double fSingleStepWidth((double)((SdrTextAniAmountItem&)rSet.Get(SDRATTR_TEXT_ANIAMOUNT)).GetValue()); + const SdrTextAniDirection eDirection(GetTextAniDirection()); + const bool bForward(SDRTEXTANI_RIGHT == eDirection || SDRTEXTANI_DOWN == eDirection); + + if(basegfx::fTools::equalZero(fAnimationDelay)) + { + // default to 1/20 second + fAnimationDelay = 50.0; + } + + if(basegfx::fTools::less(fSingleStepWidth, 0.0)) + { + // data is in pixels, convert to logic. Imply PIXEL_DPI dpi. + // It makes no sense to keep the view-transformation centered + // definitions, so get rid of them here. + fSingleStepWidth = (-fSingleStepWidth * (2540.0 / PIXEL_DPI)); + } + + if(basegfx::fTools::equalZero(fSingleStepWidth)) + { + // default to 1 milimeter + fSingleStepWidth = 100.0; + } + + // use the length of the full animation path and the number of steps + // to get the full path time + const double fFullPathLength(fFrameLength + fTextLength); + const double fNumberOfSteps(fFullPathLength / fSingleStepWidth); + double fTimeFullPath(fNumberOfSteps * fAnimationDelay); + + if(fTimeFullPath < fAnimationDelay) + { + fTimeFullPath = fAnimationDelay; + } + + switch(eAniKind) + { + case SDRTEXTANI_SCROLL : + { + impCreateScrollTiming(rSet, rAnimList, bForward, fTimeFullPath, fAnimationDelay); + break; + } + case SDRTEXTANI_ALTERNATE : + { + double fRelativeTextLength(fTextLength / (fFrameLength + fTextLength)); + impCreateAlternateTiming(rSet, rAnimList, fRelativeTextLength, bForward, fTimeFullPath, fAnimationDelay); + break; + } + case SDRTEXTANI_SLIDE : + { + impCreateSlideTiming(rSet, rAnimList, bForward, fTimeFullPath, fAnimationDelay); + break; + } + default : break; // SDRTEXTANI_NONE, SDRTEXTANI_BLINK + } + } +} + +// eof |