--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/textrendering/textformatting/tagma/TMINTERP.CPP Tue Feb 02 02:02:46 2010 +0200
@@ -0,0 +1,3282 @@
+/*
+* Copyright (c) 1999-2009 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:
+* TAGMA's bytecode interpreter classes. These convert between document positions and x-y coordinates,
+* draw the text, find the bytecode for some text, and supply information about lines.
+*
+*/
+
+
+#include "TMSTD.H"
+#include "TMINTERP.H"
+#include "TmHighlightSource.h"
+#include <s32mem.h>
+#include <frmtlay.h>
+
+TTmInterpreterParam::TTmInterpreterParam(const CTmTextLayout& aTextLayout):
+ iByteCode(&aTextLayout.Code()),
+ iCodeStart(0),
+ iCodeEnd(aTextLayout.Code().Size()),
+ iTextStart(aTextLayout.StartChar()),
+ iWidth(aTextLayout.LayoutWidth()),
+ iDrawingInterpFlags(aTextLayout.GetDrawingInterpFlags())
+ {
+ }
+
+TTmInterpreterParam::TTmInterpreterParam(const CTmCode& aByteCode):
+ iByteCode(&aByteCode),
+ iCodeStart(0),
+ iCodeEnd(aByteCode.Size()),
+ iTextStart(0),
+ iWidth(0),
+ iDrawingInterpFlags(0)
+ {
+ }
+
+TTmInterpreter::TTmInterpreter(const TTmInterpreterParam& aParam,const TPoint* aTopLeft):
+ iReader(*aParam.iByteCode,aParam.iCodeStart,aParam.iCodeEnd),
+ iTextPos(aParam.iTextStart),
+ iOp(0),
+ iOpMod(0),
+ iOpStartCode(0),
+ iOpEndCode(0),
+ iOpSubscript(0),
+ iVisualEndOfLineIsAmbiguous(ETrue),
+ iVisualStartOfLineIsAmbiguous(ETrue),
+ iInlineTextFormat(0),
+ iLabelType(MTmSource::ENoLabel),
+ iBdState(NULL)
+ {
+ iOpStartChar = iOpEndChar = iTextPos;
+ iTextLayoutWidth = aParam.iWidth;
+ if (aTopLeft)
+ iTextLayoutTopLeft = *aTopLeft;
+ iNextLineTop = iTextLayoutTopLeft.iY;
+ iNextLineStartChar = iTextPos;
+ iLineInfo.iLineNumber = -1;
+ iLineInfo.iParNumber = -1;
+ iLineInfo.iLineInPar = -1;
+ iLineInfo.iFlags = 0;
+ if (0 == aParam.iTextStart)
+ {
+ iLineInfo.iFlags = TTmLineInfo::ELineEndsInForcedLineBreak;
+ iVisualEndOfLineIsAmbiguous = EFalse;
+ }
+ }
+
+/**
+ * Returns ETrue if the document position is within the current line.
+ * @internalComponent
+ */
+TBool TTmInterpreter::PosIsInLine(const TTmDocPos& aDocPos) const
+ {
+ if (aDocPos.iPos < iOpStartChar || iOpEndChar < aDocPos.iPos)
+ return EFalse;
+ if (iOpStartChar < aDocPos.iPos && aDocPos.iPos < iOpEndChar)
+ return ETrue;
+ if (aDocPos.iPos == iOpStartChar)
+ return aDocPos.iLeadingEdge || !iVisualStartOfLineIsAmbiguous;
+ // aDocPos.iPos == iOpEndChar
+ return !aDocPos.iLeadingEdge && iVisualEndOfLineIsAmbiguous;
+ }
+
+/**
+ * Read an operator EOpLine and its arguments from the bytecode, updating the
+ * member variables accordingly.
+ * @param aChars Number of characters in the line.
+ * @return Number of bytes in the byte code for the line.
+ * @internalComponent
+ */
+TInt TTmInterpreter::ReadOpLine(TInt aChars)
+ {
+ iVisualStartOfLineIsAmbiguous = iVisualEndOfLineIsAmbiguous;
+ iLineInfo.iLineNumber++;
+ iLineInfo.iLineInPar++;
+ iLineInfo.iFlags = 0;
+ if (iOpMod & EModParStart)
+ {
+ iVisualStartOfLineIsAmbiguous = EFalse;
+ iLineInfo.iParNumber++;
+ iLineInfo.iLineInPar = 0;
+ iLineInfo.iParTop = iNextLineTop;
+ iLineInfo.iFlags |= TTmLineInfo::EParStart;
+ }
+ if (iOpMod & EModParEnd)
+ iLineInfo.iFlags |= TTmLineInfo::EParEnd;
+ TInt flags = 0;
+ if (iOpMod & EModLineFlags)
+ flags = iReader.ReadNumber(); // <F> (flags)
+ if (flags & ELineFlagLineEndsInForcedLineBreak)
+ iLineInfo.iFlags |= TTmLineInfo::ELineEndsInForcedLineBreak;
+ iVisualEndOfLineIsAmbiguous = flags & ELineFlagVisualEndOfLineIsAmbiguous;
+ if (iOpMod & EModRightToLeft)
+ iLineInfo.iFlags |= TTmLineInfo::EParRightToLeft;
+
+ TInt extra_bytes = iReader.ReadNumber(); // <B> (bytecode size)
+
+ // Set line information; read height <H> and inner rect <I> and make band-relative
+ iLineInfo.iStart = iOpStartChar = iNextLineStartChar;
+ iLineInfo.iEnd = iOpEndChar = iNextLineStartChar = iOpStartChar + aChars;
+ iLineInfo.iOuterRect.iTl.iX = iTextLayoutTopLeft.iX;
+ iLineInfo.iOuterRect.iBr.iX = iTextLayoutTopLeft.iX + iTextLayoutWidth;
+ iLineInfo.iOuterRect.iTl.iY = iNextLineTop;
+ iNextLineTop += iReader.ReadNumber(); // <H> (height)
+ iLineInfo.iOuterRect.iBr.iY = iNextLineTop;
+ iLineInfo.iInnerRect = iReader.ReadRect(); // <I> (inner rectangle)
+ iLineInfo.iInnerRect.Move(iLineInfo.iOuterRect.iTl);
+ iLineInfo.iBaseline = iLineInfo.iInnerRect.iTl.iY + iReader.ReadNumber(); // <A> (ascent)
+
+ // Set the start and end pen positions
+ iOpStartPen = iLineInfo.iInnerRect.iTl;
+ iOpStartPen.iY = iLineInfo.iBaseline;
+ iOpEndPen = iLineInfo.iInnerRect.iBr;
+ iOpEndPen.iY = iLineInfo.iBaseline;
+
+ // Read the bidirectional state immediately if it is present and is wanted (iBdState is non-null)
+ //first reset the bidirectional state pointer - if the state is not present in the
+ //bytecode that means that it's the default state
+ if(iBdState)
+ {
+ iBdState->Reset();
+ }
+ while (iBdState && iReader.CodePos() < iReader.CodeEndPos()
+ && iReader.PeekByte() == EOpParam)
+ {
+ TInt end_code = iReader.CodePos() + extra_bytes;
+ iReader.ReadByte(); // skip EOpParam
+ TUint8 id = iReader.ReadByte();
+ TInt bytes = iReader.ReadNumber();
+ if (id == EParamBdState)
+ {
+ RBufReadStream stream(*iReader.Code().Buffer(),iReader.CodePos());
+ TRAPD(error,iBdState->InternalizeL(stream));
+ if (error)
+ Panic(ECorrupt);
+ stream.Close();
+ }
+ Skip(iReader.CodePos() + bytes,iOpStartChar);
+ extra_bytes = end_code - iReader.CodePos();
+ }
+
+ // Set the interpreter text and pen positions.
+ iPen = iOpStartPen;
+ iTextPos = iOpStartChar;
+
+ return extra_bytes;
+ }
+
+/** Read an operator and its arguments from the bytecode, updating the member
+variables accordingly. Note that the current position in the byte code is
+not necessarily brought up to the next operator.
+@pre The current position in the byte code is at an operator.
+@return EFalse if there was insufficient byte code to read.
+@internalComponent */
+TBool TTmInterpreter::Next()
+ {
+ if (iReader.CodePos() >= iReader.CodeEndPos())
+ return FALSE;
+
+ // Initialise start character, pen position and code position.
+ iOpStartChar = iTextPos;
+ iOpStartPen = iPen;
+ iOpStartCode = iReader.CodePos();
+
+ // Unpack the operator.
+ iOp = iOpMod = iReader.ReadByte();
+ iOp &= KMaskOpcode; // top three bits
+ iOpMod &= KMaskModifiers; // low five bits
+
+ TInt chars = 1;
+ iOpSubscript = 0;
+ if (iOp == EOpLine)
+ {
+ chars = iReader.ReadNumber();
+ iLineContextChar = iReader.ReadNumber(); // Read the context for this line
+ }
+
+ else
+ {
+ // Read optional arguments <P>, <N> and <Y>
+ if (iOpMod & EModPos)
+ iOpStartChar += iReader.ReadNumber();
+ if (iOpMod & EModCount)
+ chars = iReader.ReadNumber();
+ if (iOpMod & EModSubscript)
+ iOpSubscript = iReader.ReadNumber();
+ }
+ iOpEndChar = iOpStartChar + chars;
+ iOpEndPen = iOpStartPen;
+
+ TInt extra_bytes = 0;
+ switch (iOp)
+ {
+ case EOpLine:
+ extra_bytes = ReadOpLine(chars);
+ break;
+
+ case EOpText:
+ case EOpSpecialChar:
+ {
+ TInt deltaX = iReader.ReadNumber();
+ if (0 <= deltaX)
+ iOpEndPen.iX += deltaX;
+ else
+ iOpStartPen.iX += deltaX;
+ }
+ iContextChar = iReader.ReadNumber(); // Read the context for this chunk.
+ iTextPos = iOpEndChar;
+ iPen.iX = iOpEndPen.iX;
+ break;
+
+ case EOpInlineText:
+ iOpEndPen.iX += iReader.ReadNumber();
+ iInlineTextFormat = iReader.ReadNumber();
+ {
+ iInlineText.SetLength(chars + 2);
+ iInlineText[0] = 0xFFFF;
+ for (int i = 0; i < chars; i++)
+ iInlineText[i+1] = (TText)iReader.ReadNumber();
+ iInlineText[chars+1] = 0xFFFF;
+ }
+ iOpEndChar = iOpStartChar;
+ iTextPos = iOpEndChar;
+ iPen.iX = iOpEndPen.iX;
+ break;
+
+ case EOpRule:
+ iBounds = iReader.ReadRect();
+ iBounds.Move(iOpStartPen);
+ iRuleColour = TRgb(iReader.ReadNumber());
+ break;
+
+ case EOpDoc:
+ extra_bytes = iReader.ReadNumber(); // <B> (bytecode size)
+ iBounds = iReader.ReadRect(); // <R> (bounds)
+ iBounds.Move(iOpStartPen);
+ iOpEndPen.iX = iBounds.iBr.iX;
+ break;
+
+ case EOpLabel:
+ extra_bytes = iReader.ReadNumber(); // <B> (bytecode size)
+ iBounds = iReader.ReadRect(); // <R> (bounds)
+ iLabelType = (MTmSource::TLabelType)iReader.ReadNumber(); // <L> (label type)
+ break;
+
+ case EOpParam:
+ iParamId = iReader.ReadByte();
+ iOpEndChar = iOpStartChar; // parameters consume no text
+ extra_bytes = iReader.ReadNumber(); // <B> (bytecode size)
+ break;
+
+ default:
+ Panic(ECorrupt);
+ break;
+ }
+
+ iOpEndCode = iReader.CodePos() + extra_bytes;
+
+ return TRUE;
+ }
+
+/**
+ * Sets the position within the byte code.
+ * @param aCodePos The position within the byte code to go to.
+ * @param aTextPos The document position that corresponds to aCodePos.
+ * @internalComponent
+ */
+void TTmInterpreter::Skip(TInt aCodePos,TInt aTextPos)
+ {
+ iReader.SetCodePos(aCodePos);
+ iTextPos = aTextPos;
+ }
+
+/** Returns the picture subscript value */
+TInt TTmInterpreter::Subscript()
+ {
+ return iOpSubscript;
+ }
+
+/**
+@internalComponent
+*/
+RTmGeneralInterpreter::RTmGeneralInterpreter(MTmSource& aSource,
+ const TTmInterpreterParam& aParam, const TPoint* aTopLeft)
+ : TTmInterpreter(aParam,aTopLeft),
+ iTextCache(aSource,aSource.InterpretDevice())
+ {
+ iCopyFit = &aSource.InterpretDevice() != &aSource.FormatDevice();
+ iTmTextDrawExt = reinterpret_cast <MTmTextDrawExt*> (Source().GetExtendedInterface(KTmTextDrawExtId));
+ if(!iTmTextDrawExt)
+ {
+ iTmTextDrawExt = &iTmTextDrawExtDefault;
+ }
+ __ASSERT_ALWAYS(iTmTextDrawExt != NULL, Panic(ENotImplemented));
+ }
+
+/**
+ * Get source text starting at aPos and not more than (aEndChar - aPos)
+ * characters.
+ * @param aPos The start position for the text to be returned.
+ * @param aEndChar The maximum position to be returned plus one.
+ * @param aText Returns a view of the text.
+ * @param aFormat
+ * If non-null, returns the format of this text. All the text returned is
+ * in this format.
+ * @param aFont If aFont is not null, on return contains a pointer to an opened CTmTextFontCache*
+ The caller must call Close on aFont when finished with the font.
+ * @internalComponent
+ */
+void RTmGeneralInterpreter::GetText(TInt aPos,TInt aEndChar,TPtrC& aText,TTmCharFormat* aFormat,CTmTextFontCache** aFont)
+ {
+ iTextCache.GetText(aPos,aEndChar,aText,aFormat,aFont);
+ }
+
+/**
+ * Gets the inline text inserted by the formatter in the current position.
+ * @param aText Returns the inline text. Valid until the position is moved.
+ * @param aFormat If non-null, returns the format of the inline text.
+ * @param aFont If aFont is not null, on return contains a pointer to an opened CTmTextFontCache*
+ The caller must call Close on aFont when finished with the font.
+ * @internalComponent
+ */
+void RTmGeneralInterpreter::GetInlineText(TPtrC& aText,TTmCharFormat* aFormat,CTmTextFontCache** aFont)
+ {
+ TPtrC p;
+ TInt inlineTextFormatPos = InlineTextFormat() + LineInfo().iStart;
+ //InlineTextFormat(): the offset position in current line
+ //LineInfo().iStart : start position of current line
+ //inlineTextFormatPos : position in the whole document
+
+ iTextCache.GetText(inlineTextFormatPos,KMaxTInt,p,aFormat,aFont);
+ aText.Set(InlineText());
+ }
+
+/**
+ * Returns the number of lines in the band.
+ * @return Number of lines formatted.
+ * @internalComponent
+ */
+TInt TTmInterpreter::Lines()
+ {
+ while (Next())
+ {
+ __ASSERT_ALWAYS(Op() == EOpLine, Panic(ECorrupt));
+ Skip();
+ }
+ return iLineInfo.iLineNumber + 1;
+ }
+
+/**
+ * Returns the number of paragraphs in the band.
+ * @return Number of paragraphs formatted.
+ * @internalComponent
+ */
+TInt TTmInterpreter::Paragraphs()
+ {
+ while (Next())
+ {
+ __ASSERT_ALWAYS(Op() == EOpLine, Panic(ECorrupt));
+ Skip();
+ }
+ return iLineInfo.iParNumber + 1;
+ }
+
+/**
+ * Iterates to the line containing the document position if any. Any position
+ * from CTmTextLayout::StartChar() to CTmTextLayout::EndChar() inclusive can be
+ * found.
+ * @param aDocPos The document position to be found.
+ * @return ETrue if it was found.
+ * @internalComponent
+ */
+TBool TTmInterpreter::DocPosToLine(const TTmDocPosSpec& aDocPos)
+ {
+ /*
+ Convert to a document position. Treat positions specified using directionality as
+ trailing, then correct to the following line if necessary.
+ */
+ TTmDocPos doc_pos(aDocPos.iPos,aDocPos.iType == TTmDocPosSpec::ELeading);
+
+ if (!Next())
+ return EFalse;
+ __ASSERT_ALWAYS(Op() == EOpLine, Panic(ECorrupt));
+ while (!PosIsInLine(doc_pos))
+ {
+ Skip();
+ if (!Next())
+ return EFalse;
+ __ASSERT_ALWAYS(Op() == EOpLine, Panic(ECorrupt));
+ }
+
+ /*
+ If the position is at the end of the line and was sought by direction it might not be in the line;
+ if not it will be in the next line if any. Read operators and check for the position until it is
+ found or we get to the next line.
+ */
+ if (aDocPos.iPos == EndChar()
+ && aDocPos.iType != TTmDocPosSpec::ETrailing
+ && aDocPos.iType != TTmDocPosSpec::ELeading
+ && !AtEndOfTextLayout())
+ {
+ TBool rightToLeft = (aDocPos.iType == TTmDocPosSpec::ERightToLeft);
+ while (Next() && Op() != EOpLine)
+ {
+ TBool sameDirectionality = rightToLeft?
+ RightToLeft() : !RightToLeft();
+ if (aDocPos.iPos == EndChar() && sameDirectionality)
+ return ETrue;
+ }
+ }
+
+ return ETrue;
+ }
+
+/**
+ * Iterates to the line with the specified line number if any.
+ * @param aLineNumber The line number to find.
+ * @return ETrue if the line was found (i.e. if it is formatted).
+ * @internalComponent
+ */
+TBool TTmInterpreter::LineNumberToLine(TInt aLineNumber)
+ {
+ while (Next())
+ {
+ __ASSERT_ALWAYS(Op() == EOpLine, Panic(ECorrupt));
+ if (iLineInfo.iLineNumber == aLineNumber)
+ return TRUE;
+ else
+ Skip();
+ }
+ return EFalse;
+ }
+
+/**
+ * Iterates to the specified line of the specified paragraph. This function
+ * will always return ETrue if the paragraph is formatted. If the line number
+ * is out of range it will be camped to the correct range; thus if aLineInPar
+ * is KMaxTInt the last line of the paragraph is found.
+ * @param aParNumber Paragraph number to be found.
+ * @param aLineInPar Line number within paragraph to be found.
+ * @return
+ * ETrue if the operation is successful. This will be the case if the
+ * applicable line is formatted.
+ * @internalComponent
+ */
+TBool TTmInterpreter::ParNumberToLine(TInt aParNumber,TInt aLineInPar)
+ {
+ while (Next())
+ {
+ __ASSERT_ALWAYS(Op() == EOpLine, Panic(ECorrupt));
+ if (iLineInfo.iParNumber == aParNumber)
+ {
+ if (iLineInfo.iLineInPar >= aLineInPar || ParEnd())
+ return TRUE;
+ }
+ Skip();
+ }
+ return EFalse;
+ }
+
+/**
+ * Iterates to the line containing the specified Y position number if any.
+ * @param aYPos The Y position to find.
+ * @return ETrue if the line was found (i.e. if it is formatted).
+ * @internalComponent
+ */
+TBool TTmInterpreter::YPosToLine(TInt aYPos)
+ {
+ while (Next())
+ {
+ __ASSERT_ALWAYS(Op() == EOpLine, Panic(ECorrupt));
+ if (iLineInfo.iOuterRect.iTl.iY <= aYPos && iLineInfo.iOuterRect.iBr.iY > aYPos)
+ return TRUE;
+ else
+ Skip();
+ }
+ return EFalse;
+ }
+
+/**
+ * Iterates to the width of the widest line in the specified vertical range
+ * that is formatted, returning its width.
+ * @param aTop Line number of the top line that is to be considered.
+ * @param aBottom
+ * Line number of the first line below aTop that should not be considered.
+ * @return
+ * The width found, or 0 if the entire range is not formatted.
+ * @internalComponent
+ */
+TInt TTmInterpreter::WidthOfWidestLine(TInt aTop,TInt aBottom)
+ {
+ int width = 0;
+ while (Next())
+ {
+ __ASSERT_ALWAYS(Op() == EOpLine, Panic(ECorrupt));
+ // Quit if below the range.
+ if (iLineInfo.iOuterRect.iTl.iY >= aBottom)
+ break;
+ // If the line overlaps the range check its width.
+ if (iLineInfo.iOuterRect.iBr.iY > aTop)
+ {
+ int cur_width = iLineInfo.iInnerRect.iBr.iX - iLineInfo.iInnerRect.iTl.iX;
+ if (cur_width > width)
+ width = cur_width;
+ }
+ Skip();
+ }
+ return width;
+ }
+
+/**
+Finds the leftmost left bound and the rightmost right bound within the
+specified y co-ordinate range
+@param aTopY
+ The top of the range to check.
+@param aBottomY
+ The bottom of the range to check.
+@param aLeft
+ The left most X-coordinate found. KMaxTInt if there was nothing in the
+ range at all.
+@param aRight
+ The right most X-coordinate found. KMinTInt if there was nothing in the
+ range at all.
+@internalComponent
+*/
+void TTmInterpreter::HorizontalExtremes(TInt &aLeft, TInt &aRight,
+ TInt aTopY, TInt aBottomY)
+ {
+ aLeft = KMaxTInt;
+ aRight = KMinTInt;
+ if (!Next())
+ return;
+ while (iLineInfo.iOuterRect.iBr.iY < aTopY)
+ {
+ __ASSERT_ALWAYS(Op() == EOpLine, Panic(ECorrupt));
+ Skip();
+ if (!Next())
+ return;
+ }
+ while (iLineInfo.iOuterRect.iTl.iY <= aBottomY)
+ {
+ __ASSERT_ALWAYS(Op() == EOpLine, Panic(ECorrupt));
+ if (iLineInfo.iInnerRect.iTl.iX < aLeft)
+ aLeft = iLineInfo.iInnerRect.iTl.iX;
+ if (aRight < iLineInfo.iInnerRect.iBr.iX)
+ aRight = iLineInfo.iInnerRect.iBr.iX;
+ Skip();
+ if (!Next())
+ return;
+ }
+ }
+
+/**
+ * Finds the smallest rectangle that needs to be redrawn after reformatting
+ * some text.
+ * @param aNewCode Code representing the formatting of the changed text.
+ * @param aStartChar The first character that has changed.
+ * @param aOldLength The length of the text that was changed.
+ * @param aNewLength The new length of the changed text.
+ * @param aRedrawRect The rectangle that needs redrawing.
+ * @internalComponent
+ */
+void TTmInterpreter::CalculateRedrawRect(TTmInterpreter& aNewCode,TInt aStartChar,TInt aOldLength,TInt aNewLength,
+ TRect& aRedrawRect)
+ {
+ /*
+ Strip leading identical lines before the reformatted range and set aRedrawRect to the bounds of
+ the first line to be redrawn.
+ */
+ TBool have_old_line = Next();
+ TBool have_new_line = aNewCode.Next();
+ while (have_old_line && have_new_line)
+ {
+ aRedrawRect = LineInfo().iOuterRect;
+ if (EndChar() > aStartChar ||
+ StartChar() != aNewCode.StartChar() || EndChar() != aNewCode.EndChar())
+ break;
+ Skip();
+ have_old_line = Next();
+ aNewCode.Skip();
+ have_new_line = aNewCode.Next();
+ }
+
+ // Move after the reformatted range.
+ while (have_old_line && StartChar() < aStartChar + aOldLength)
+ {
+ Skip();
+ have_old_line = Next();
+ }
+ while (have_new_line && aNewCode.StartChar() < aStartChar + aNewLength)
+ {
+ aRedrawRect.iBr.iY = aNewCode.LineInfo().iOuterRect.iBr.iY;
+ aNewCode.Skip();
+ have_new_line = aNewCode.Next();
+ }
+
+ // Compare lines after the reformatted range.
+ int length_change = aNewLength - aOldLength;
+ while (have_old_line && have_new_line)
+ {
+ if (StartChar() + length_change == aNewCode.StartChar() && EndChar() + length_change == aNewCode.EndChar())
+ {
+ /*
+ These lines are identical so don't need to be redrawn, so remove the new line's rectangle from
+ the redraw rectangle, and stop comparing, because subsequent lines will all be identical.
+ */
+ aRedrawRect.iBr.iY = aNewCode.LineInfo().iOuterRect.iTl.iY;
+ break;
+ }
+
+ // Add the current new line to the redraw rectangle.
+ aRedrawRect.iBr.iY = aNewCode.LineInfo().iOuterRect.iBr.iY;
+
+ // Attempt to synchronise.
+ if (EndChar() + length_change < aNewCode.EndChar())
+ {
+ Skip();
+ have_old_line = Next();
+ }
+ else
+ {
+ aNewCode.Skip();
+ have_new_line = aNewCode.Next();
+ }
+ }
+
+ // If the old text has run out, extend the redraw rect to cover any remaining text.
+ while (have_new_line && !have_old_line)
+ {
+ aRedrawRect.iBr.iY = aNewCode.LineInfo().iOuterRect.iBr.iY;
+ aNewCode.Skip();
+ have_new_line = aNewCode.Next();
+ }
+ }
+
+/**
+Resets so that will return ETrue for AtEnd().
+*/
+void RTmGraphemeInTextChunkIterator::Reset()
+ {
+ iPosition.iPosInText = 0;
+ iLength = 0;
+ }
+
+/**
+Constructs an iterator that will return ETrue for AtEnd().
+*/
+RTmGraphemeInTextChunkIterator::RTmGraphemeInTextChunkIterator()
+ : iFont(NULL)
+ {
+ Reset();
+ }
+
+void RTmGraphemeInTextChunkIterator::Close()
+ {
+ if (iFont)
+ {
+ iFont->Close();
+ iFont = NULL;
+ }
+
+ if (iShapeInfo.IsOpen())
+ iShapeInfo.Close();
+ }
+
+/**
+Begins the iteration.
+@param aByteCodeIterator
+ The interpreter with the information about the formatting being traversed.
+ Must be set to the start of the text chunk to be iterated over. The
+ interpreter is not advanced by this function.
+@param aCache
+ A pointer to a two-byte cache in which to store the results of the
+ iteration.
+*/
+void RTmGraphemeInTextChunkIterator::Begin(
+ RTmGeneralInterpreter& aByteCodeIterator,
+ TTmGraphemeEdgeInfo (*aCache)[2])
+ {
+ if (iShapeInfo.IsOpen())
+ iShapeInfo.Close();
+ iCache = aCache;
+ iReverse = aByteCodeIterator.RightToLeft()?
+ RTmTextCache::EVisualRightToLeft : RTmTextCache::ELeftToRight;
+ iIgnorePosition = -1;
+ if (!aByteCodeIterator.VisualEndOfLineIsAmbiguous())
+ iIgnorePosition = aByteCodeIterator.LineInfo().iEnd;
+
+ __ASSERT_DEBUG(aByteCodeIterator.Op() == TTmInterpreter::EOpText, TmPanic(EInvariant));
+
+ if (iFont)
+ {
+ iFont->Close();
+ iFont = NULL;
+ }
+ iEndChar = aByteCodeIterator.EndChar();
+ iStartChar = aByteCodeIterator.StartChar();
+ iLength = iEndChar - aByteCodeIterator.StartChar();
+ if (KMaxTextChunkSize < iLength)
+ {
+ iLength = KMaxTextChunkSize;
+ iEndChar = iStartChar + iLength;
+ }
+
+ // If a custom interface to draw the text in context exists, then get the Displayed Text within context.
+ if (aByteCodeIterator.TextCache().Source().GetExtendedInterface(KTmCustomExtensionUid))
+ {
+ aByteCodeIterator.TextCache().GetDisplayedText(iStartChar,iEndChar,iReverse,iBuffer,aByteCodeIterator.ContextCharChar(),0,
+ &iFont);
+ }
+ else
+ {
+ aByteCodeIterator.TextCache().GetDisplayedText(iStartChar,iEndChar,iReverse,iBuffer,0,0,&iFont);
+ }
+
+ if (!iFont)
+ {
+ iPosition.iPosInText = 0;
+ iLength = 0;
+ return;
+ }
+
+ // start_char and end_char need to be adjusted to take account of
+ // the extra context supplied by GetDisplayedText.
+ --iStartChar;
+ ++iEndChar;
+ ++iLength;
+ iPosition.iText.Set(iBuffer, iLength + 1);
+ iPosition.iPosInText = 1;
+ iPosition.iPen = aByteCodeIterator.StartPen();
+
+ iStartPenX = iPosition.iPen.iX;
+ iEndPenX = aByteCodeIterator.EndPen().iX;
+ }
+
+/** Find any protruding left side-bearing.
+@return Maximum of aStartOfChunk - left bound of each glyph.
+@internalComponent */
+TInt LeftSideBearing(const TInt aStartOfChunk, const CFont::TPositionParam& aPos)
+ {
+ TInt bearing(aStartOfChunk);
+ TInt glyph(aPos.iOutputGlyphs);
+ while (glyph != 0)
+ {
+ --glyph;
+ TInt left(aPos.iOutput[glyph].iBounds.iTl.iX);
+ if (left < bearing)
+ bearing = left;
+ }
+ return aStartOfChunk - bearing;
+ }
+
+/**
+Advances the iteration to the next character. The aCache array as passed to
+Begin will be overwritten with the results.
+@return
+ The number of characters found; 1 or 2.
+*/
+TInt RTmGraphemeInTextChunkIterator::Next()
+ {
+ __ASSERT_DEBUG(!AtEnd(), TmPanic(EBadArg));
+ TInt previousPositionInBuffer = iPosition.iPosInText;
+ TInt previousPosition = iReverse?
+ iEndChar - iPosition.iPosInText : iStartChar + iPosition.iPosInText;
+ TPoint previousPen = iPosition.iPen;
+
+ //The return value of GetCharacterPosition2() is not used here
+ //form and gdi has had correct behavior when GetCharacterPosition2() return EFalse
+ //And that was done by changing iPosInText inside GetCharacterPosition2()
+ TBool isGlyphGenerated = iFont->Font().GetCharacterPosition2(iPosition, iShapeInfo);
+
+ // Move the pen (cursor position) to the right if the cluster just seen
+ // has a negative side-bearing. This corrects for the fact that
+ // DrawText() pushes text with left side-bearings to the right.
+ iPosition.iPen.iX += LeftSideBearing(iStartPenX, iPosition);
+ TInt numCodePoints = iPosition.iPosInText - previousPositionInBuffer;
+ TInt currentPosition = iReverse?
+ iEndChar - iPosition.iPosInText : iStartChar + iPosition.iPosInText;
+ __ASSERT_DEBUG(currentPosition != previousPosition, TmPanic(EInvariant));
+
+ TInt cachePos = 0;
+ // Report the left hand edge of the character.
+ if (iIgnorePosition != previousPosition)
+ {
+ (*iCache)[0].iPos.iRightToLeft = iReverse;
+ (*iCache)[0].iPos.iDocPos.iLeadingEdge = !iReverse;
+ (*iCache)[0].iPos.iDocPos.iPos = previousPosition;
+ (*iCache)[0].iPos.iEdge = previousPen;
+ (*iCache)[0].iCodePoints = numCodePoints;
+ cachePos = 1;
+ }
+ // Report the right hand edge of the character.
+ if (iIgnorePosition != currentPosition)
+ {
+ (*iCache)[cachePos].iPos.iRightToLeft = iReverse;
+ (*iCache)[cachePos].iPos.iDocPos.iLeadingEdge = iReverse;
+ (*iCache)[cachePos].iPos.iDocPos.iPos = currentPosition;
+ (*iCache)[cachePos].iPos.iEdge = iPosition.iPen;
+ (*iCache)[cachePos].iCodePoints = numCodePoints;
+ if (iPosition.iPosInText == iLength)
+ {
+ // We are probably moving over a chunk, so its right edge
+ // will be the end of the chunk, not where the renderer
+ // thinks it is.
+ if ((*iCache)[cachePos].iPos.iEdge.iX < iEndPenX)
+ (*iCache)[cachePos].iPos.iEdge.iX = iEndPenX;
+ }
+ ++cachePos;
+ }
+ __ASSERT_DEBUG(0 < cachePos, TmPanic(EInvariant));
+ return cachePos;
+ }
+
+/**
+Begins the iteration.
+@param aByteCodeIterator
+ The interpreter with the information about the formatting being traversed.
+ Must be set to the start of the text chunk to be iterated over. The
+ interpreter is not advanced by this function.
+*/
+RTmGraphemeInTextChunkIteratorNice::RTmGraphemeInTextChunkIteratorNice(
+ RTmGeneralInterpreter& aByteCodeIterator)
+ {
+ iBase.Begin(aByteCodeIterator, &iCache);
+ iCurrent = iCache;
+ iEnd = iCache;
+ if (!iBase.AtEnd())
+ iEnd += iBase.Next();
+ }
+
+void RTmGraphemeInTextChunkIteratorNice::Close()
+ {
+ iBase.Close();
+ }
+
+/**
+Advances the iteration to the next edge.
+*/
+void RTmGraphemeInTextChunkIteratorNice::Next()
+ {
+ __ASSERT_DEBUG(!AtEnd(), TmPanic(EBadArg));
+ // Return if we have not finished moving through the cache.
+ if (++iCurrent != iEnd)
+ return;
+ // Return if there are no more characters.
+ if (iBase.AtEnd())
+ return;
+ iCurrent = iCache;
+ iEnd = iCache + iBase.Next();
+ }
+
+/**
+Returns a pointer to the current element.
+@return
+ The current element in the iteration. Will be undefined if AtEnd is
+ returning ETrue.
+*/
+const TTmGraphemeEdgeInfo* RTmGraphemeInTextChunkIteratorNice::Get() const
+ {
+ return iCurrent;
+ }
+
+/**
+Reports whether the iteration has finished.
+@return
+ EFalse if the iteration has not finished. If ETrue is returned, Get will
+ return a defined value.
+*/
+TBool RTmGraphemeInTextChunkIteratorNice::AtEnd() const
+ {
+ return iCurrent == iEnd? ETrue : EFalse;
+ }
+
+/**
+Advances to the position specified. Behaviour is undefined if the position does
+not exist in the chunk. The position is considered to exist if it is within a
+grapheme cluster in the chunk, even if the specific edge does not exist.
+@param aDocPos
+ Position to advance to.
+*/
+void RTmGraphemeInTextChunkIteratorNice::FindEdge(const TTmDocPos& aDocPos)
+ {
+ __ASSERT_DEBUG(!AtEnd(), TmPanic(EBadArg));
+ while (RTmGraphemeEdgeIterator::ETotalMatch !=
+ RTmGraphemeEdgeIterator::DocPosMatches(aDocPos, *Get()))
+ {
+ Next();
+ __ASSERT_DEBUG(!AtEnd(), TmPanic(EBadArg));
+ }
+ }
+
+void TTmVisualDocPos::Reset()
+ {
+ iAmbiguity = ENotFound;
+ }
+
+TTmVisualDocPos::TTmVisualDocPos()
+ {
+ Reset();
+ }
+
+void TTmVisualDocPos::SetAsUnambiguous(const TTmPosInfo2& aPos)
+ {
+ iLeft = iRight = aPos;
+ iAmbiguity = EUnambiguous;
+ }
+
+void TTmVisualDocPos::SetIfAmbiguous(
+ const TTmPosInfo2& aLeft, const TTmPosInfo2& aRight)
+ {
+ if (aLeft.iDocPos.iPos == aRight.iDocPos.iPos
+ && aLeft.iEdge.iX == aRight.iEdge.iX)
+ // These edges do meet.
+ return;
+
+ // There is an ambiguity, but which edges are involved?
+ TBool leftIsRightHandEdge = aLeft.iRightToLeft?
+ aLeft.iDocPos.iLeadingEdge : !aLeft.iDocPos.iLeadingEdge;
+ TBool rightIsLeftHandEdge = aRight.iRightToLeft?
+ !aRight.iDocPos.iLeadingEdge : aRight.iDocPos.iLeadingEdge;
+
+ if (leftIsRightHandEdge)
+ {
+ iLeft = aLeft;
+ if (rightIsLeftHandEdge)
+ {
+ iRight = aRight;
+ iAmbiguity = EAmbiguous;
+ }
+ else
+ {
+ iRight = aLeft;
+ iAmbiguity = ELeftOnly;
+ }
+ }
+ else if (rightIsLeftHandEdge)
+ {
+ iLeft = iRight = aRight;
+ iAmbiguity = ERightOnly;
+ }
+ }
+
+/**
+Return the position that best matches the X co-ordinate specified by aX.
+@param aX X coordinate sought.
+@return
+ 0 if no value was found. A pointer to the result if a value was found.
+*/
+const TTmPosInfo2* TTmVisualDocPos::NearestToX(TInt aX) const
+ {
+ if (iAmbiguity == ENotFound)
+ return 0;
+ TInt leftDistance = iLeft.iEdge.iX < aX?
+ aX - iLeft.iEdge.iX : iLeft.iEdge.iX - aX;
+ TInt rightDistance = iRight.iEdge.iX < aX?
+ aX - iRight.iEdge.iX : iRight.iEdge.iX - aX;
+ if (leftDistance < rightDistance)
+ return &iLeft;
+ else
+ return &iRight;
+ }
+
+/** Restarts the checker for a new line. */
+void TTmAmbiguityChecker::Reset()
+ {
+ iPrev.iEdge.iX = KMinTInt;
+ iPrev.iRightToLeft = EFalse;
+ iPrev.iDocPos.iPos = KMinTInt;
+ iPrev.iDocPos.iLeadingEdge = ETrue;
+ iLastAmbiguousPos.Reset();
+ }
+
+/** Constructs a checker ready to check a line. */
+TTmAmbiguityChecker::TTmAmbiguityChecker()
+ {
+ Reset();
+ }
+
+/**
+Checks an edge for ambiguity against the last edge added.
+@param aEdge The edge to check and store.
+*/
+void TTmAmbiguityChecker::AddEdge(const TTmPosInfo2& aEdge)
+ {
+ iLastAmbiguousPos.SetIfAmbiguous(iPrev, aEdge);
+ iPrev = aEdge;
+ }
+
+/**
+Checks whether the last edge added is ambiguous if there are no more edges in
+the line.
+*/
+void TTmAmbiguityChecker::EndLine()
+ {
+ TTmPosInfo2 dummyEdge;
+ dummyEdge.iEdge.iX = KMaxTInt;
+ dummyEdge.iRightToLeft = EFalse;
+ dummyEdge.iDocPos.iLeadingEdge = EFalse;
+ dummyEdge.iDocPos.iPos = KMaxTInt;
+ AddEdge(dummyEdge);
+ }
+
+/**
+Reports whether the iteration is over.
+@return ETrue if Next may no longer be called as there is no more information.
+*/
+TBool RTmGraphemeInTextChunkIterator::AtEnd() const
+ {
+ return iLength <= iPosition.iPosInText;
+ }
+
+/**
+ Releases all resources
+*/
+void RTmGraphemeEdgeIterator::Close()
+ {
+ iTextChunkIterator.Close();
+ }
+
+/**
+Advances the iteration to the next character edge. Note that this one may be in
+the same place as the last.
+
+Begin must be called before this function can be used.
+@see Begin
+*/
+void RTmGraphemeEdgeIterator::Next()
+ {
+ __ASSERT_DEBUG(!AtEnd(), TmPanic(EBadArg));
+ --iCacheSize;
+ if (0 != iCacheSize)
+ {
+ ++iCachePos;
+ return;
+ }
+
+ iCachePos = 0;
+
+ // more positions within this text chunk?
+ if (!iTextChunkIterator.AtEnd())
+ {
+ iCacheSize = iTextChunkIterator.Next();
+ return;
+ }
+
+ // Get the next position in this line
+ while (iByteCodeIterator->CodePos() < iEndLineCodePos)
+ {
+ // The WHILE loop assures that there is always sufficientByteCode avaliable.
+ TBool haveSufficientByteCode = iByteCodeIterator->Next();
+ __ASSERT_DEBUG(haveSufficientByteCode, TmPanic(EBadArg));
+
+ iByteCodeIterator->Skip();
+ TInt op = iByteCodeIterator->Op();
+ if (op == TTmInterpreter::EOpSpecialChar)
+ {
+ TBool reverse = iByteCodeIterator->RightToLeft();
+ TInt endChar = iByteCodeIterator->EndChar();
+ TInt startChar = iByteCodeIterator->StartChar();
+ TInt actualEndChar = reverse? startChar : endChar; // Based on direction of text
+ TInt actualStartChar = reverse? endChar : startChar; // Based on direction of text
+ iPosInfoCache[0].iPos.iDocPos.iPos = reverse? endChar : startChar;
+ iPosInfoCache[0].iPos.iDocPos.iLeadingEdge = !reverse;
+ iPosInfoCache[0].iPos.iRightToLeft = reverse;
+ iPosInfoCache[0].iPos.iEdge = iByteCodeIterator->StartPen();
+ iPosInfoCache[0].iCodePoints = 1;
+ /** If we have reached the end edge (for LTR text), do not cache the next character in the bytecode.
+ Simply return.*/
+ if (actualEndChar == iIgnorePosition)
+ {
+ iCacheSize = 1;
+ return;
+ }
+ iPosInfoCache[1].iPos.iDocPos.iPos = reverse? startChar : endChar;
+ iPosInfoCache[1].iPos.iDocPos.iLeadingEdge = reverse;
+ iPosInfoCache[1].iPos.iRightToLeft = reverse;
+ iPosInfoCache[1].iPos.iEdge = iByteCodeIterator->EndPen();
+ iPosInfoCache[1].iCodePoints = 1;
+ iCacheSize = 2;
+ /** If we have reached the start edge (for RTL text), do not cache the next character in the bytecode.
+ Simply return.*/
+ if (actualStartChar == iIgnorePosition)
+ {
+ iCacheSize = 1;
+ iCachePos = 1;
+ return;
+ }
+ // Otherwise, simply return with both cache positions set, cache size of 2, and position in cache 0
+ return;
+ }
+ else if (op == TTmInterpreter::EOpText)
+ {
+ iTextChunkIterator.Begin(*iByteCodeIterator, &iPosInfoCache);
+ if (!iTextChunkIterator.AtEnd())
+ {
+ iCacheSize = iTextChunkIterator.Next();
+ return;
+ }
+ }
+ }
+
+ // No more positions within the line.
+ if (!iTrailingEdgeOfUnambiguousLineEndAdded
+ && iByteCodeIterator->TrailingEdgeOfUnambiguousLineEnd(
+ iPosInfoCache[0].iPos, ETrue))
+ {
+ // Got the trailing edge of the previous line's last character.
+ iPosInfoCache[0].iCodePoints = 1;
+ iCacheSize = 1;
+ iTrailingEdgeOfUnambiguousLineEndAdded = ETrue;
+ return;
+ }
+ }
+
+/**
+Gets the current edge, if the iteration is not at an end.
+
+Begin must be called before this function can be used.
+@see Begin
+@see AtEnd
+
+@return
+ The current edge co-ordinates in formatting space, the character
+ index that the position is just before and whether the edge is the trailing
+ edge of the previous character or the leading edge of the next character.
+ Undefined if the iteration is at an end. Call AtEnd to see if this is the
+ case.
+*/
+const TTmGraphemeEdgeInfo& RTmGraphemeEdgeIterator::Get() const
+ {
+ return iPosInfoCache[iCachePos];
+ }
+
+/**
+Gets the current edge, if the iteration is not at an end. A shortcut for
+Get().iPos.
+
+Begin must be called before this function can be used.
+@see Begin
+@see Get
+@see AtEnd
+
+@return
+ The current edge co-ordinates in formatting space, the character index that
+ the position is just before and whether the edge is the trailing edge of
+ the previous character or the leading edge of the next character. Undefined
+ if the iteration is at an end. Call AtEnd to see if this is the case.
+*/
+const TTmPosInfo2& RTmGraphemeEdgeIterator::GetInfo() const
+ {
+ return iPosInfoCache[iCachePos].iPos;
+ }
+
+/**
+Determines if the iteration has finished the line or not.
+@return ETrue if the iteration is at an end.
+*/
+TBool RTmGraphemeEdgeIterator::AtEnd() const
+ {
+ return iCacheSize == 0? ETrue : EFalse;
+ }
+
+/**
+Begins the iteration again from the start of the line. The iterator passed in
+will not be advanced beyond the current line.
+@param aByteCodeIterator
+ Iterator to the formatting which should be set to the start of the
+ formatting to be traversed. It will be advanced during the use of this
+ iterator. It should not be used by any other code while this
+ RTmGraphemeEdgeIterator is being used.
+ */
+void RTmGraphemeEdgeIterator::Begin(
+ RTmGeneralInterpreter& aByteCodeIterator)
+ {
+ /** Set the position of the edge of the line to stop at when iterating through the edges */
+ if (!aByteCodeIterator.VisualEndOfLineIsAmbiguous())
+ iIgnorePosition = aByteCodeIterator.LineInfo().iEnd;
+ iByteCodeIterator = &aByteCodeIterator;
+ iEndLineCodePos = iByteCodeIterator->EndCodePos();
+ iTextChunkIterator.Reset();
+ iCachePos = 0;
+ iCacheSize = 1;
+ // Get the first value if there is one
+ if (iByteCodeIterator->TrailingEdgeOfUnambiguousLineEnd(
+ iPosInfoCache[0].iPos, EFalse))
+ {
+ // Got the trailing edge of the previous line's last character.
+ iPosInfoCache[0].iCodePoints = 1;
+ iTrailingEdgeOfUnambiguousLineEndAdded = ETrue;
+ return;
+ }
+ iTrailingEdgeOfUnambiguousLineEndAdded = EFalse;
+ Next();
+ }
+
+/**
+Finds the nearest edge to the x position specified. The iterator should be
+initialised to the start of the line to be searched.
+@param aX The X position to search for (in formatting coordinates).
+@param aNearest The result.
+*/
+void RTmGraphemeEdgeIterator::FindXPos(TInt aX, TTmVisualDocPos& aNearest)
+ {
+ TTmPosInfo2 nearest;
+ if (!FindEdgeByX(aX, nearest, aNearest))
+ aNearest.Reset();
+ else if (aNearest.Ambiguity() == TTmVisualDocPos::ENotFound)
+ aNearest.SetAsUnambiguous(nearest);
+ }
+
+/**
+Finds the nearest edge to the x position specified. The iterator should be
+initialised to the start of the line to be searched.
+@param aX The position to find (in formatting coordinates).
+@param aNearest
+ The nearest position to aX, if ETrue is returned.
+@param aAmbiguity
+ Any ambiguity visually coincident with aNearest if ETrue is returned.
+@return
+ ETrue if there is any position there at all. If EFalse, aNearest and
+ aAmbiguity are undefined.
+*/
+TBool RTmGraphemeEdgeIterator::FindEdgeByX(TInt aX, TTmPosInfo2& aNearest,
+ TTmVisualDocPos& aAmbiguity)
+ {
+ TInt lastX = KMinTInt;
+ TInt distance = KMaxTInt;
+ TTmAmbiguityChecker checker;
+ TTmPosInfo2 found;
+ // Find the last edge before aX
+ while (!AtEnd() && GetInfo().iEdge.iX <= aX)
+ {
+ found = GetInfo();
+ if (lastX != found.iEdge.iX)
+ {
+ lastX = found.iEdge.iX;
+ distance = aX - lastX;
+ checker.Reset();
+ }
+ checker.AddEdge(found);
+ Next();
+ }
+ // Find the first edge after aX if it is closer.
+ if (!AtEnd() && GetInfo().iEdge.iX - aX < distance)
+ {
+ checker.Reset();
+ found = GetInfo();
+ lastX = found.iEdge.iX;
+ checker.AddEdge(found);
+ Next();
+ while (!AtEnd() && GetInfo().iEdge.iX == lastX)
+ {
+ checker.AddEdge(GetInfo());
+ Next();
+ }
+ }
+ if (AtEnd())
+ checker.EndLine();
+ aAmbiguity = checker.LastAmbiguity();
+ if (lastX == KMinTInt)
+ return EFalse;
+ aNearest = found;
+ return ETrue;
+ }
+
+/**
+Determines if aDocPos matches the grapheme edge defined by aEdgeInfo.
+@param aDocPos The document position specification to match.
+@param aEdgeInfo The grapheme edge to match.
+@return
+ ENoMatch if the document position does not match the grapheme edge.
+ EPositionOnly if the document position edge specification is ERightToLeft
+ or ELeftToRight and the document position matches but not the
+ directionality. ETotalMatch if the document position and edge
+ specifications match.
+*/
+RTmGraphemeEdgeIterator::TGraphemeMatch
+RTmGraphemeEdgeIterator::DocPosMatches(const TTmDocPosSpec& aDocPos,
+ const TTmGraphemeEdgeInfo& aEdgeInfo)
+ {
+ TInt startOfGrapheme = aEdgeInfo.iPos.iDocPos.iPos -
+ (aEdgeInfo.iPos.iDocPos.iLeadingEdge? 0 : aEdgeInfo.iCodePoints - 1);
+ if (startOfGrapheme <= aDocPos.iPos
+ && aDocPos.iPos < startOfGrapheme + aEdgeInfo.iCodePoints)
+ {
+ // Found a grapheme that matches the document position.
+ // Does it match the rest of the spec?
+ switch (aDocPos.iType)
+ {
+ case TTmDocPosSpec::ELeftToRight:
+ return aEdgeInfo.iPos.iRightToLeft?
+ EPositionOnly : ETotalMatch;
+ case TTmDocPosSpec::ERightToLeft:
+ return aEdgeInfo.iPos.iRightToLeft?
+ ETotalMatch : EPositionOnly;
+ case TTmDocPosSpec::ELeading:
+ return aEdgeInfo.iPos.iDocPos.iLeadingEdge?
+ ETotalMatch : ENoMatch;
+ case TTmDocPosSpec::ETrailing:
+ return aEdgeInfo.iPos.iDocPos.iLeadingEdge?
+ ENoMatch : ETotalMatch;
+ default:
+ return ENoMatch;
+ }
+ }
+ return ENoMatch;
+ }
+
+/**
+Finds the edge specified. If a Right-to-Left or Left-to-Right edge is demanded,
+an edge with the wrong directionality will be returned if the right one does
+not exist, but one or two with the wrong one does. The iterator should be
+initialised to the start of the line to be searched.
+@param aDocPos The position to be found.
+@param aInfo The result.
+@return ETrue if a result could be found.
+*/
+TBool RTmGraphemeEdgeIterator::FindEdge(const TTmDocPosSpec& aDocPos,
+ TTmPosInfo2& aInfo)
+ {
+ TInt matches = 0;
+ while (!AtEnd())
+ {
+ TTmGraphemeEdgeInfo info = Get();
+ TInt match = static_cast<TInt>(DocPosMatches(aDocPos, info));
+ if (0 != match)
+ {
+ matches += match;
+ aInfo = info.iPos;
+ if (2 <= matches)
+ return ETrue;
+ }
+ Next();
+ }
+ return matches;
+ }
+
+/**
+Finds the rightmost edge that is to the left of aDocPos in this line.
+@param aDocPos Document position that the result must be to the left of.
+@param aNext Result. Undefined if 0 or 1 is returned.
+@param aNearest Information about aDocPos itself. Undefined if 0 is returned.
+@return
+ 2 if both aNearest and aNext have a result, 1 if only aNearest does and 0
+ if neither were found.
+*/
+RTmGraphemeEdgeIterator::TEdgesFound
+RTmGraphemeEdgeIterator::FindEdgeLeftwards(const TTmDocPosSpec& aDocPos,
+ TTmPosInfo2& aNearest, TTmVisualDocPos& aNext)
+ {
+ TInt matches = 0;
+ TTmPosInfo2 last;
+ last = GetInfo();
+ TTmAmbiguityChecker checker;
+ TTmVisualDocPos lastXVisualPosition;
+ while (!AtEnd() && matches < 2)
+ {
+ if (last.iEdge.iX != GetInfo().iEdge.iX)
+ {
+ lastXVisualPosition = checker.LastAmbiguity();
+ if (lastXVisualPosition.Ambiguity() == TTmVisualDocPos::ENotFound)
+ lastXVisualPosition.SetAsUnambiguous(last);
+ checker.Reset();
+ }
+ last = GetInfo();
+ checker.AddEdge(last);
+ TInt match = static_cast<TInt>(DocPosMatches(aDocPos, Get()));
+ if (match != 0)
+ {
+ aNearest = last;
+ aNext = lastXVisualPosition;
+ matches += match;
+ }
+ Next();
+ }
+ if (matches == 0)
+ return ENone;
+ return (aNext.Ambiguity() == TTmVisualDocPos::ENotFound)?
+ ENearestOnly : ENearestAndNext;
+ }
+
+/**
+Finds the leftmost edge that is to the right of aDocPos in this line.
+@param aDocPos Document position that the result must be to the right of.
+@param aNext Result. Undefined if 0 or 1 is returned.
+@param aNearest Information about aDocPos itself. Undefined if 0 is returned.
+@return
+ 2 if both aNearest and aNext have a result, 1 if only aNearest does and 0
+ if neither were found.
+*/
+RTmGraphemeEdgeIterator::TEdgesFound
+RTmGraphemeEdgeIterator::FindEdgeRightwards(const TTmDocPosSpec& aDocPos,
+ TTmPosInfo2& aNearest, TTmVisualDocPos& aNext)
+ {
+ if (AtEnd())
+ return ENone;
+
+ RTmGraphemeEdgeIterator::TEdgesFound result = ENone;
+ TTmAmbiguityChecker checker;
+ TTmGraphemeEdgeInfo info = Get();
+ TTmPosInfo2 firstOfLastX = info.iPos;
+ TInt lastX = info.iPos.iEdge.iX;
+ TInt nearestPositionX = KMinTInt;
+ TInt nextPositionX = KMinTInt;
+ TInt matches = 0;
+ while (!AtEnd())
+ {
+ info = Get();
+ TInt x = info.iPos.iEdge.iX;
+
+ //Are we moving to a new postion?
+ if (x != lastX)
+ {
+ if (lastX == nearestPositionX)
+ {
+ // Moving from nearest position to next position.
+ // Need to start checking for ambiguity.
+ nextPositionX = x;
+ checker.Reset();
+ }
+ else if (lastX == nextPositionX)
+ {
+ // Moving off next position. Possibly we have finished.
+ aNext = checker.LastAmbiguity();
+ if (aNext.Ambiguity() == TTmVisualDocPos::ENotFound)
+ aNext.SetAsUnambiguous(firstOfLastX);
+ result = ENearestAndNext;
+ if (1 < matches)
+ return result;
+ }
+ lastX = x;
+ firstOfLastX = info.iPos;
+ }
+
+ // If we are in the "next" position (one to the right of the
+ // "nearest" position) we need to check for ambiguity.
+ if (x == nextPositionX)
+ checker.AddEdge(info.iPos);
+
+ // Find out if we are or may be in the "nearest" position.
+ TInt match = static_cast<TInt>(DocPosMatches(aDocPos, info));
+ if (0 != match && matches < 2)
+ {
+ // We are in the nearest position, or are in a half-match
+ // where the position matches but the directionality does not.
+ matches += match;
+ aNearest = info.iPos;
+ result = ENearestOnly;
+ nearestPositionX = x;
+ nextPositionX = KMinTInt;
+ }
+
+ Next();
+ }
+
+ if (lastX == nextPositionX)
+ {
+ // We reached the end of the line at the "next" position,
+ // so we need to finish the ambiguity checking.
+ checker.EndLine();
+ aNext = checker.LastAmbiguity();
+ if (aNext.Ambiguity() == TTmVisualDocPos::ENotFound)
+ aNext.SetAsUnambiguous(firstOfLastX);
+ return ENearestAndNext;
+ }
+
+ return result;
+ }
+
+/**
+Returns the first trailing edge beyond aDocPos not visually coincident with the
+leading edge at aDocPos in this line.
+@param aDocPos
+ The starting document index.
+@return
+ The document position found, or KErrNotFound if none was found in this
+ line.
+*/
+TInt RTmGraphemeEdgeIterator::NextPosition(TInt aDocPos)
+ {
+ TBool currentXIsForbidden = EFalse;
+ TInt currentX = KMinTInt;
+ TInt lastXBestPos = KMaxTInt;
+ TInt currentXBestPos = KMaxTInt;
+ while (!AtEnd())
+ {
+ TInt x = GetInfo().iEdge.iX;
+ const TTmDocPos &docPos = GetInfo().iDocPos;
+ if (x != currentX)
+ {
+ currentX = x;
+ if (!currentXIsForbidden && currentXBestPos < lastXBestPos)
+ lastXBestPos = currentXBestPos;
+ currentXBestPos = KMaxTInt;
+ currentXIsForbidden = EFalse;
+ }
+ if (docPos.iLeadingEdge)
+ {
+ if (docPos.iPos == aDocPos)
+ currentXIsForbidden = ETrue;
+ }
+ else
+ {
+ if (aDocPos < docPos.iPos && docPos.iPos < currentXBestPos)
+ currentXBestPos = docPos.iPos;
+ }
+ Next();
+ }
+ if (!currentXIsForbidden && currentXBestPos < lastXBestPos)
+ lastXBestPos = currentXBestPos;
+ return lastXBestPos == KMaxTInt? KErrNotFound : lastXBestPos;
+ }
+
+/**
+Returns the last leading edge before aDocPos not visually coincident with the
+trailing edge at aDocPos in this line.
+@param aDocPos
+ The starting document index.
+@return
+ The document position found, or KErrNotFound if none was found in this
+ line.
+*/
+TInt RTmGraphemeEdgeIterator::PreviousPosition(TInt aDocPos)
+ {
+ TBool currentXIsForbidden = EFalse;
+ TInt currentX = KMinTInt;
+ TInt lastXBestPos = KMinTInt;
+ TInt currentXBestPos = KMinTInt;
+ while (!AtEnd())
+ {
+ TInt x = GetInfo().iEdge.iX;
+ const TTmDocPos &docPos = GetInfo().iDocPos;
+ if (x != currentX)
+ {
+ currentX = x;
+ if (!currentXIsForbidden && lastXBestPos < currentXBestPos)
+ lastXBestPos = currentXBestPos;
+ currentXBestPos = KMinTInt;
+ currentXIsForbidden = EFalse;
+ }
+ if (!docPos.iLeadingEdge)
+ {
+ if (docPos.iPos == aDocPos)
+ currentXIsForbidden = ETrue;
+ }
+ else
+ {
+ if (currentXBestPos < docPos.iPos && docPos.iPos < aDocPos)
+ currentXBestPos = docPos.iPos;
+ }
+ Next();
+ }
+ if (!currentXIsForbidden && lastXBestPos < currentXBestPos)
+ lastXBestPos = currentXBestPos;
+ return lastXBestPos == KMinTInt? KErrNotFound : lastXBestPos;
+ }
+
+/**
+ * Gets a line of text as displayed, after bidirectional formatting, conversion
+ * of invisible characters to visible forms, and conversion of glyphs to
+ * context-dependent forms.
+ * @param aLineNumber Line to be retrieved.
+ * @param aText Buffer for output.
+ * @param aNeeded
+ * Length of buffer required for the output. This will be greater than
+ * aText.MaxLength() if aText was not big enough.
+ * @internalComponent
+ */
+TBool RTmGeneralInterpreter::GetDisplayedTextL(TInt aLineNumber,TDes& aText,TUint aContextChar,TInt& aNeeded)
+ {
+ aText.SetLength(0);
+ aNeeded = 0;
+ TBool found = LineNumberToLine(aLineNumber);
+ if (!found)
+ return FALSE;
+
+ int i = 0;
+ while (Next() && Op() != EOpLine)
+ switch (Op())
+ {
+ case EOpText:
+ case EOpSpecialChar:
+ {
+ TText buffer[KMaxTextChunkSize + 2];
+ CTmTextFontCache* font = 0;
+ RTmTextCache::TDisplayedTextDirectionality directionality =
+ RightToLeft()?
+ RTmTextCache::EVisualRightToLeft
+ : RTmTextCache::ELeftToRight;
+ User::LeaveIfError(iTextCache.GetDisplayedText(StartChar(), EndChar(),
+ directionality, buffer,aContextChar, 0, &font));
+ __ASSERT_DEBUG(font, TmPanic(EInvariant));
+ CFont::TPositionParam p;
+ int length = EndChar() - StartChar() + 1;
+ p.iText.Set(buffer, length + 1);
+ p.iPosInText = 1;
+ TInt textMaxLength = aText.MaxLength();
+ RShapeInfo shapeInfo;
+ while (p.iPosInText < length)
+ {
+ if(!font->Font().GetCharacterPosition2(p, shapeInfo))
+ {
+ //The iPosInText changed after calling the GetCharacterPosition2()
+ continue;
+ }
+
+ aNeeded += p.iOutputGlyphs;
+ for (i = 0; i < p.iOutputGlyphs; i++)
+ {
+ TInt code = p.iOutput[i].iCode;
+ if (code <= 0xFFFF)
+ {
+ // non-surrogate character
+ if (aText.Length() < textMaxLength)
+ aText.Append(code);
+ }
+ else if (code <= 0x10FFFF)
+ {
+ // surrogate pair
+ ++aNeeded;
+ if (aText.Length() < textMaxLength)
+ aText.Append(0xD7C0 + (code >> 10));
+ if (aText.Length() < textMaxLength)
+ aText.Append(0xDC00 | (code & 0x3FF));
+ }
+ else
+ {
+ // Glyph code; cannot be specified as Unicode.
+ // Instead add 0xFFFD the Unicode Replacement
+ // Character
+ if (aText.Length() < textMaxLength)
+ aText.Append(0xFFFD);
+ }
+ }
+ }
+ font->Close();
+ if (shapeInfo.IsOpen())
+ shapeInfo.Close();
+ }
+ break;
+
+ case EOpInlineText:
+ for (i = 0; i < InlineText().Length(); i++)
+ {
+ aNeeded++;
+ if (aText.Length() < aText.MaxLength())
+ aText.Append(InlineText()[i]);
+ }
+ break;
+
+ default:
+ Skip();
+ break;
+ }
+
+ return TRUE;
+ }
+
+/**
+ * Iterates past an X-Y position and returns the position information for
+ * the nearest edge to that point.
+ * @param aXyPos The point to find.
+ * @param aInfo Returns information about the edge found.
+ * @return
+ * ETrue if the operation was successful. It will succeed if the line
+ * containing aXyPos is formatted.
+ * @internalComponent
+ */
+TBool RTmGeneralInterpreter::FindXyPos(const TPoint& aXyPos,TTmPosInfo2& aInfo)
+ {
+ if (!YPosToLine(aXyPos.iY))
+ return EFalse;
+
+ aInfo.iDocPos = StartDocPos();
+ aInfo.iEdge = StartPen();
+
+ RTmGraphemeEdgeIterator edgeIterator;
+ edgeIterator.Begin(*this);
+ TTmVisualDocPos dummy;
+ if (!edgeIterator.FindEdgeByX(aXyPos.iX, aInfo, dummy))
+ return EFalse;
+ edgeIterator.Close();
+ return ETrue;
+ }
+
+/**
+ * Iterates to a document position and returns information about it.
+ * @param aDocPos The position to find.
+ * @param aInfo Returns information about the position found.
+ * @return
+ * ETrue if the operation was successful. It will succeed if the line
+ * containing aDocPos is formatted.
+ * @internalComponent
+ */
+TBool RTmGeneralInterpreter::FindDocPos(const TTmDocPosSpec& aDocPos,TTmPosInfo2& aInfo)
+ {
+ if (!DocPosToLine(aDocPos))
+ return FALSE;
+
+ aInfo.iDocPos = StartDocPos();
+ aInfo.iEdge = StartPen();
+
+ RTmGraphemeEdgeIterator edgeIterator;
+ edgeIterator.Begin(*this);
+ edgeIterator.FindEdge(aDocPos, aInfo);
+ edgeIterator.Close();
+ return TRUE;
+ }
+
+/**
+Gets the trailing edge of the previous line's last character, if this position
+is unambiguous and if the current position is at the visual start of the line.
+Which end the visual start of the line is depends on the paragraph
+directionality of the current paragraph.
+@param aPosOut The position found, if any.
+@param aOnRight Are we currently on the right hand extreme of the line?
+@return ETrue if there is such a position at the specified end.
+@internalComponent
+*/
+TBool RTmGeneralInterpreter::TrailingEdgeOfUnambiguousLineEnd(
+ TTmPosInfo2& aPosOut, TBool aOnRight) const
+ {
+ if (VisualStartOfLineIsAmbiguous())
+ return EFalse;
+ TBool atStart = LineInfo().iFlags & TTmLineInfo::EParRightToLeft?
+ aOnRight : !aOnRight;
+ if (!atStart)
+ return EFalse;
+ aPosOut.iDocPos.iLeadingEdge = EFalse;
+ aPosOut.iDocPos.iPos = LineInfo().iStart;
+ aPosOut.iEdge.iY = LineInfo().iBaseline;
+ aPosOut.iEdge.iX = aOnRight?
+ LineInfo().iInnerRect.iBr.iX : LineInfo().iInnerRect.iTl.iX;
+ aPosOut.iRightToLeft = aOnRight;
+ return ETrue;
+ }
+
+RTmDrawingInterpreter::RTmDrawingInterpreter(MTmSource& aSource,CGraphicsContext& aGc,
+ const TTmInterpreterParam& aParam,const TPoint& aTopLeft,
+ const TRect& aClipRect,const TLogicalRgb* aDocBackground,
+ TUint aFlags,const TCursorSelection* aHighlight,
+ const TTmHighlightExtensions* aHighlightExtensions,
+ TBool aDrawInvertedHighlight):
+ RTmGeneralInterpreter(aSource,aParam,&aTopLeft),
+ iGc(aGc),
+ iClipRect(aClipRect),
+ iFirstLine(TRUE),
+ iLabelled(FALSE),
+ iLabelLeft(KMaxTInt),
+ iLabelRight(KMinTInt),
+ iDocBackground(aDocBackground),
+ iFlags(aFlags | aParam.iDrawingInterpFlags),
+ iHighlightStartPos(0),
+ iHighlightEndPos(0),
+ iHighlightExtensions(NULL),
+ iHighlightedLineRect(),
+ iHighlightedLineRgn(4), // granularity = 4
+ iSource(aSource),
+ iParam(aParam),
+ iDrawInvertedHighlight(EFalse),
+ iHighlightXorColor()
+ {
+ iHighlightStartPos = 0;
+ iHighlightEndPos = 0;
+ if (aHighlight)
+ {
+ if (aHighlight->iAnchorPos < aHighlight->iCursorPos)
+ {
+ iHighlightStartPos = aHighlight->iAnchorPos;
+ iHighlightEndPos = aHighlight->iCursorPos;
+ }
+ else
+ {
+ iHighlightStartPos = aHighlight->iCursorPos;
+ iHighlightEndPos = aHighlight->iAnchorPos;
+ }
+ }
+
+ if (NULL != aHighlightExtensions && ! aHighlightExtensions->IsNull())
+ {
+ iHighlightExtensions = aHighlightExtensions;
+ }
+
+ TTmHighlightSource highlightSource(aSource);
+ iDrawInvertedHighlight = aDrawInvertedHighlight || ! highlightSource.HighlightVisible();
+ if (iDrawInvertedHighlight)
+ {
+ // Explicit construction of the 2nd parameter for SystemColor, TRgb, ensuring opaque alpha blending.
+ // FF is opaque alpha channel value.
+ TRgb defaultColorOfBackground(static_cast<TUint32>(TLogicalRgb::ESystemBackgroundColor), 0xFF);
+ TRgb backgroundColor = aSource.SystemColor(TLogicalRgb::ESystemBackgroundIndex,defaultColorOfBackground);
+
+ // Explicit construction of the 2nd parameter for SystemColor, TRgb, ensuring opaque alpha blending.
+ // FF is opaque alpha channel value.
+ TRgb defaultColorOfXorColor(static_cast<TUint32>(TLogicalRgb::ESystemSelectionBackgroundColor), 0xFF);
+ TRgb xorColor = aSource.SystemColor(TLogicalRgb::ESystemSelectionBackgroundIndex,defaultColorOfXorColor);
+
+ iHighlightXorColor = backgroundColor ^ xorColor;
+ }
+ }
+
+RTmDrawingInterpreter::~RTmDrawingInterpreter()
+ {
+ iHighlightedLineRgn.Close();
+ }
+
+TBool RTmDrawingInterpreter::GetHighlightPos(TInt& aHighlightStartPos, TInt& aHighlightEndPos) const
+ {
+ aHighlightStartPos = aHighlightEndPos = 0;
+
+ if (iHighlightEndPos > iHighlightStartPos)
+ {
+ aHighlightStartPos = iHighlightStartPos;
+ if (StartDocPos().iPos > iHighlightStartPos)
+ {
+ aHighlightStartPos = StartDocPos().iPos;
+ }
+
+ aHighlightEndPos = iHighlightEndPos;
+ if (EndDocPos().iPos < iHighlightEndPos)
+ {
+ aHighlightEndPos = EndDocPos().iPos;
+ }
+ }
+
+ return (aHighlightEndPos > aHighlightStartPos);
+ }
+
+TBool RTmDrawingInterpreter::GetHighlightClipRegion(const TRect& aClipRect, RRegion& aHighlightRegion) const
+ {
+ if (UsesAdjustedHighlight())
+ {
+ return GetAdjustedHighlightClipRegion(aClipRect, aHighlightRegion);
+ }
+
+ aHighlightRegion.Clear();
+
+ TInt highlightStartPos = 0;
+ TInt highlightEndPos = 0;
+ if (GetHighlightPos(highlightStartPos, highlightEndPos))
+ {
+ TRect rect(TRect::EUninitialized);
+ TTmDocPos start(highlightStartPos, ETrue);
+ TTmDocPos end(highlightEndPos, EFalse);
+ RTmBoundingRectInterpreter interpreter(iSource, iParam);
+ TBool found = interpreter.FirstRect(start.iPos, end.iPos, rect);
+ while (found)
+ {
+ if (!rect.IsEmpty())
+ {
+ rect.Move(TextLayoutTopLeft());
+ aHighlightRegion.AddRect(rect);
+
+ if (aHighlightRegion.CheckError())
+ {
+ break;
+ }
+ else
+ {
+ aHighlightRegion.Tidy();
+ }
+ }
+
+ found = interpreter.NextRect(rect);
+ }
+
+ interpreter.Close();
+ return ! aHighlightRegion.CheckError();
+ }
+ else
+ {
+ return ETrue;
+ }
+ }
+
+TBool RTmDrawingInterpreter::GetFirstHighlightClipRect(RTmBoundingRectInterpreter& aInterpreter,
+ const TRect& aClipRect, TRect& aRect) const
+ {
+ if (UsesAdjustedHighlight())
+ {
+ return GetFirstAdjustedHighlightClipRect(aInterpreter, aClipRect, aRect);
+ }
+
+ TInt highlightStartPos = 0;
+ TInt highlightEndPos = 0;
+ if (GetHighlightPos(highlightStartPos, highlightEndPos))
+ {
+ TTmDocPos start(highlightStartPos, ETrue);
+ TTmDocPos end(highlightEndPos, EFalse);
+ TBool found = aInterpreter.FirstRect(start.iPos, end.iPos, aRect);
+ while (found)
+ {
+ aRect.Move(TextLayoutTopLeft());
+ aRect.Intersection(aClipRect);
+ if (aRect.IsEmpty())
+ {
+ found = aInterpreter.NextRect(aRect);
+ }
+ else
+ {
+ return ETrue;
+ }
+ }
+ }
+
+ aRect = TRect(TRect::EUninitialized);
+ return EFalse;
+ }
+
+TBool RTmDrawingInterpreter::GetNextHighlightClipRect(RTmBoundingRectInterpreter& aInterpreter,
+ const TRect& aClipRect, TRect& aRect) const
+ {
+ if (UsesAdjustedHighlight())
+ {
+ return GetNextAdjustedHighlightClipRect(aInterpreter, aClipRect, aRect);
+ }
+
+ TBool found = aInterpreter.NextRect(aRect);
+ while (found)
+ {
+ aRect.Move(TextLayoutTopLeft());
+ aRect.Intersection(aClipRect);
+ if (aRect.IsEmpty())
+ {
+ found = aInterpreter.NextRect(aRect);
+ }
+ else
+ {
+ return ETrue;
+ }
+ }
+
+ aRect = TRect(TRect::EUninitialized);
+ return EFalse;
+ }
+
+TBool RTmDrawingInterpreter::GetAdjustedHighlightClipRegion(const TRect& aClipRect, RRegion& aHighlightRegion) const
+ {
+ __ASSERT_DEBUG(NULL != iHighlightExtensions, TmPanic(EInvariant));
+
+ aHighlightRegion.Clear();
+
+ if (iHighlightEndPos > iHighlightStartPos)
+ {
+ RTmBoundingRectInterpreter interpreter(iSource, iParam);
+
+ TRect rect(TRect::EUninitialized);
+ TBool found = interpreter.FirstRect(iHighlightStartPos, iHighlightEndPos, rect);
+ while (found)
+ {
+ if (! rect.IsEmpty())
+ {
+ rect.Move(TextLayoutTopLeft());
+ iHighlightExtensions->AdjustRect(rect);
+ if (rect.iBr.iY < aClipRect.iTl.iY)
+ {
+ // ignore it
+ }
+ else if (rect.iTl.iY > aClipRect.iBr.iY)
+ {
+ break;
+ }
+ else
+ {
+ aHighlightRegion.AddRect(rect);
+ if (aHighlightRegion.CheckError())
+ {
+ break;
+ }
+ else
+ {
+ aHighlightRegion.Tidy();
+ }
+ }
+ }
+
+ found = interpreter.NextRect(rect);
+ }
+
+ interpreter.Close();
+
+ if (aHighlightRegion.CheckError())
+ {
+ return EFalse;
+ }
+
+ aHighlightRegion.ClipRect(aClipRect);
+ }
+
+ return ETrue;
+ }
+
+TBool RTmDrawingInterpreter::GetFirstAdjustedHighlightClipRect(RTmBoundingRectInterpreter& aInterpreter,
+ const TRect& aClipRect, TRect& aRect) const
+ {
+ __ASSERT_DEBUG(NULL != iHighlightExtensions, TmPanic(EInvariant));
+
+ if (iHighlightEndPos > iHighlightStartPos)
+ {
+ TBool found = aInterpreter.FirstRect(iHighlightStartPos, iHighlightEndPos, aRect);
+ while (found)
+ {
+ if (! aRect.IsEmpty())
+ {
+ aRect.Move(TextLayoutTopLeft());
+ iHighlightExtensions->AdjustRect(aRect);
+ if (aRect.iBr.iY < aClipRect.iTl.iY)
+ {
+ // ignore it
+ }
+ else if (aRect.iTl.iY > aClipRect.iBr.iY)
+ {
+ break;
+ }
+ else
+ {
+ aRect.Intersection(aClipRect);
+ if (! aRect.IsEmpty())
+ {
+ return ETrue;
+ }
+ }
+ }
+
+ found = aInterpreter.NextRect(aRect);
+ }
+ }
+
+ aRect = TRect(TRect::EUninitialized);
+ return EFalse;
+ }
+
+TBool RTmDrawingInterpreter::GetNextAdjustedHighlightClipRect(RTmBoundingRectInterpreter& aInterpreter,
+ const TRect& aClipRect, TRect& aRect) const
+ {
+ __ASSERT_DEBUG(NULL != iHighlightExtensions, TmPanic(EInvariant));
+ __ASSERT_DEBUG(iHighlightEndPos > iHighlightStartPos, TmPanic(EInvariant));
+
+ TBool found = aInterpreter.NextRect(aRect);
+ while (found)
+ {
+ if (! aRect.IsEmpty())
+ {
+ aRect.Move(TextLayoutTopLeft());
+ iHighlightExtensions->AdjustRect(aRect);
+
+ if (aRect.iBr.iY < aClipRect.iTl.iY)
+ {
+ // ignore it
+ }
+ else if (aRect.iTl.iY > aClipRect.iBr.iY)
+ {
+ break;
+ }
+ else
+ {
+ aRect.Intersection(aClipRect);
+ if (! aRect.IsEmpty())
+ {
+ return ETrue;
+ }
+ }
+ }
+
+ found = aInterpreter.NextRect(aRect);
+ }
+
+ aRect = TRect(TRect::EUninitialized);
+ return EFalse;
+ }
+
+TBool RTmDrawingInterpreter::IsCurrentOpCompletelyHighlighted() const
+ {
+ if (iHighlightEndPos > iHighlightStartPos)
+ {
+ return iHighlightStartPos <= StartDocPos().iPos &&
+ iHighlightEndPos >= EndDocPos().iPos;
+ }
+ else
+ {
+ return EFalse;
+ }
+ }
+
+TBool RTmDrawingInterpreter::UsesExtendedHighlight() const
+ {
+ return NULL != iHighlightExtensions && iHighlightExtensions->Extends();
+ }
+
+TBool RTmDrawingInterpreter::UsesAdjustedHighlight() const
+ {
+ return NULL != iHighlightExtensions && ! iHighlightExtensions->IsNull();
+ }
+
+void RTmDrawingInterpreter::Draw()
+ {
+ if (iClipRect.iBr.iY < TextLayoutTopLeft().iY
+ || (iFlags & EInvisible))
+ return;
+
+ iGc.Reset();
+ InitGraphicsContext();
+ TRect drawn = iClipRect;
+ drawn.iTl.iY = drawn.iBr.iY = TextLayoutTopLeft().iY;
+ int error;
+
+ TBool done = FALSE;
+ while (Next() && !done)
+ {
+ switch (Op())
+ {
+ case EOpLine:
+ if (LineInfo().iOuterRect.iBr.iY < iClipRect.iTl.iY)
+ {
+ Skip();
+ break;
+ }
+ if (LineInfo().iOuterRect.iTl.iY > iClipRect.iBr.iY)
+ done = TRUE;
+ else
+ {
+ TRect line_rect;
+ Line(line_rect);
+
+ // Draw background to left and right of line where it intersects the clip rectangle.
+ if (!(iFlags & EIsLabel))
+ {
+ TRect r = iClipRect;
+ r.iTl.iY = Max(r.iTl.iY,line_rect.iTl.iY);
+ r.iBr.iY = Min(r.iBr.iY,line_rect.iBr.iY);
+ r.iBr.iX = line_rect.iTl.iX;
+ if (iDocBackground && !r.IsEmpty())
+ {
+ DrawBackground(r,*iDocBackground); // rectangle to left of line
+ InitGraphicsContext();
+ }
+ r.iBr.iX = iClipRect.iBr.iX;
+ r.iTl.iX = line_rect.iBr.iX;
+ if (iDocBackground && !r.IsEmpty())
+ {
+ DrawBackground(r,*iDocBackground); // rectangle to right of line
+ InitGraphicsContext();
+ }
+ if (drawn.IsEmpty())
+ drawn.iTl.iY = line_rect.iTl.iY;
+ drawn.iBr.iY = line_rect.iBr.iY;
+ }
+ }
+ break;
+ case EOpText:
+ Text();
+ break;
+ case EOpInlineText:
+ InlineText();
+ break;
+ case EOpSpecialChar:
+ TRAP(error,SpecialCharL());
+ break;
+ case EOpRule:
+ Rule();
+ break;
+ case EOpLabel:
+ Label();
+ break;
+ default:
+ Skip(); // unimplemented
+ break;
+ }
+ }
+
+ iParFormat.Close();
+
+ /*
+ Draw undrawn background areas outside the text. These may themselves be partially covered by
+ a custom background, so each one has to be drawn by DrawBackground, which can then
+ fill in any parts not custom-drawn.
+ */
+ if (iDocBackground && !(iFlags & EIsLabel))
+ {
+ if (iLabelLeft < drawn.iTl.iX)
+ drawn.iTl.iX = iLabelLeft;
+ drawn.Intersection(iClipRect);
+ if (drawn != iClipRect)
+ {
+ TRect r[4];
+ SubtractRect(iClipRect,drawn,r);
+ for (int i = 0; i < 4; i++)
+ if (!r[i].IsEmpty())
+ DrawBackground(r[i],*iDocBackground);
+ }
+ }
+
+ // if the highlight is not visible (because the custom drawer defines a
+ // highlighted background color identical to the highlighted foreground color)
+ // draw the selection inverting every pixel in the highlighted region.
+ if (! iHighlightedLineRect.IsEmpty())
+ {
+ InvertHighlightedLineRgn();
+ }
+ }
+
+void RTmDrawingInterpreter::InitGraphicsContext()
+ {
+ iGc.SetDrawMode(CGraphicsContext::EDrawModePEN);
+ iGc.SetBrushStyle(CGraphicsContext::ESolidBrush);
+ iGc.SetPenStyle(CGraphicsContext::ESolidPen);
+ iGc.SetClippingRect(iClipRect);
+ }
+
+void RTmDrawingInterpreter::Line(TRect& aDrawn)
+ {
+ if (! iHighlightedLineRect.IsEmpty())
+ {
+ InvertHighlightedLineRgn();
+ }
+
+ if (iFirstLine || ParStart())
+ {
+ int error;
+ TRAP(error,Source().GetParagraphFormatL(StartChar(),iParFormat));
+ if (LineInfo().iFlags & TTmLineInfo::EParRightToLeft)
+ iParFormat.iFlags |= RTmParFormat::ERightToLeft;
+ else
+ iParFormat.iFlags &= ~RTmParFormat::ERightToLeft;
+ iLabelled = FALSE;
+ MFormLabelApi* labelApi = (MFormLabelApi*)Source().GetExtendedInterface(KFormLabelApiExtensionUid);
+ if ((labelApi != NULL) && (labelApi->LabelModeSelect(MTmSource::EParLabel, StartChar())))
+ {
+ RTmParFormat pf;
+ TRAP(error,Source().GetParagraphFormatL(0,pf));
+ if (!error)
+ {
+ iLabelled = TRUE;
+ iLabelBackground = pf.iBackgroundColor;
+ }
+ labelApi->LabelModeCancel();
+ pf.Close();
+ }
+ TSize label_size(0, 0);
+ int margin_size = 0;
+ if (labelApi != NULL)
+ labelApi->LabelMetrics(MTmSource::EParLabel, label_size, margin_size);
+ iLabelLeft = LineInfo().iOuterRect.iTl.iX - margin_size;
+ iLabelRight = iLabelLeft + label_size.iWidth;
+ }
+
+ TRect inner_border_bounds;
+ TRect outer_border_bounds(LineInfo().iOuterRect);
+ TInt left_border_width = 0, right_border_width = 0, top_border_width = 0, bottom_border_width = 0;
+ GetBorderInfo(inner_border_bounds,outer_border_bounds,
+ left_border_width,right_border_width,top_border_width,bottom_border_width);
+ TRect r(outer_border_bounds);
+ r.BoundingRect(LineInfo().iOuterRect);
+ r.Intersection(iClipRect);
+
+ if (!(iFlags & EIsLabel))
+ {
+ // Draw paragraph background if specified.
+ if (iFlags & EDrawParBackground)
+ {
+ DrawBackground(r,iParFormat.iBackgroundColor);
+ InitGraphicsContext();
+ }
+ // Otherwise use the document background if any as a paragraph background.
+ else if (iDocBackground)
+ {
+ DrawBackground(r,*iDocBackground);
+ InitGraphicsContext();
+ }
+
+ DrawLineGraphics(r);
+
+ if (iLabelLeft < LineInfo().iOuterRect.iTl.iX)
+ DrawLabelBackground();
+
+ if (Source().PageBreakInRange(StartChar(),Min(EndChar(),Source().DocumentLength())))
+ DrawPageBreak();
+ }
+
+ if (ParStart() && iParFormat.Bullet())
+ DrawBullet();
+
+ if (Bordered())
+ DrawBorders(inner_border_bounds,outer_border_bounds,
+ left_border_width,right_border_width,top_border_width,bottom_border_width);
+
+ iFirstLine = FALSE;
+ aDrawn = r;
+ }
+
+void RTmDrawingInterpreter::DrawLineGraphics(const TRect& aRect)
+ {
+ Source().DrawLineGraphics(iGc, TextLayoutTopLeft(), aRect, LineInfo());
+ InitGraphicsContext();
+ }
+
+void RTmDrawingInterpreter::DrawLabelBackground()
+ {
+ TRect r = LineInfo().iOuterRect;
+ r.iTl.iX = iLabelLeft;
+ r.iBr.iX = iLabelRight;
+
+ // Draw the label background.
+ if (iFlags & EDrawParBackground)
+ DrawBackground(r,iLabelBackground);
+ else if (iDocBackground)
+ DrawBackground(r,*iDocBackground);
+
+ // Draw the gutter.
+ r.iTl.iX = iLabelRight;
+ r.iBr.iX = LineInfo().iOuterRect.iTl.iX;
+ if (iDocBackground && !r.IsEmpty())
+ DrawBackground(r,*iDocBackground);
+
+ InitGraphicsContext();
+ }
+
+void RTmDrawingInterpreter::DrawPageBreak()
+ {
+ iGc.SetPenStyle(CGraphicsContext::EDashedPen);
+ TLogicalRgb color = iParFormat.iBackgroundColor;
+ TUint index = color.SystemColorIndex();
+ if (index)
+ color = Source().SystemColor(index,color);
+ iGc.SetPenColor(~(color & 0xFFFFFF));
+ TPoint p = LineInfo().iOuterRect.iBr;
+ p.iY--;
+ TPoint q = p;
+ q.iX = LineInfo().iOuterRect.iTl.iX;
+ TmTextDrawExt().DrawLine(iGc, p, q);
+ InitGraphicsContext();
+ }
+
+void RTmDrawingInterpreter::DrawBullet()
+ {
+ int left_margin = 0;
+ int right_margin = 0;
+ int first_line_left_margin = 0;
+ int first_line_right_margin = 0;
+ int bullet_margin = 0;
+ int bullet_width = 0;
+ int bullet_ascent = 0;
+ int bullet_descent = 0;
+ CFont* font = NULL;
+ TRAPD(error,TmGetMarginsL(Device(),iParFormat,left_margin,right_margin,
+ first_line_left_margin,first_line_right_margin,
+ bullet_margin,bullet_width,bullet_ascent,bullet_descent,&font));
+ if (!error && font)
+ {
+ TBuf<1> bullet;
+ bullet.SetLength(1);
+ bullet[0] = (TText)iParFormat.Bullet()->iCharacterCode;
+ TPoint p;
+ if (iParFormat.RightToLeft())
+ {
+ p = EndPen();
+ p.iX -= bullet_margin - first_line_right_margin + bullet_width;
+ }
+ else
+ {
+ p = StartPen();
+ p.iX += bullet_margin - first_line_left_margin;
+ }
+ iGc.UseFont(font);
+ TRgb color = Source().SystemColor(iParFormat.Bullet()->iColor);
+ SetPenColor(color);
+ iGc.SetUnderlineStyle(EUnderlineOff);
+ TmTextDrawExt().DrawText(iGc, bullet, p);
+ iGc.DiscardFont();
+ Device().ReleaseFont(font);
+ }
+ }
+
+void RTmDrawingInterpreter::GetBorderInfo(TRect& aInnerBounds,TRect& aOuterBounds,
+ TInt& aLeftWidth,TInt& aRightWidth,TInt& aTopWidth,TInt& aBottomWidth)
+ {
+ const MGraphicsDeviceMap& device = Device();
+ int left = 0, right = 0, top = 0, bottom = 0;
+ int left_full = 0, right_full = 0, top_full = 0, bottom_full = 0;
+ TmGetBorderWidth(device,iParFormat,RTmParFormat::ELeadingBorder,left,left_full);
+ TmGetBorderWidth(device,iParFormat,RTmParFormat::ETrailingBorder,right,right_full);
+ if (ParStart())
+ TmGetBorderWidth(device,iParFormat,RTmParFormat::ETopBorder,top,top_full);
+ if (ParEnd())
+ TmGetBorderWidth(device,iParFormat,RTmParFormat::EBottomBorder,bottom,bottom_full);
+ int h_margin = device.HorizontalTwipsToPixels(iParFormat.iBorderMargin);
+ int v_margin = device.VerticalTwipsToPixels(iParFormat.iBorderMargin);
+
+ // Establish the outer bounds of the inner border rule.
+ TRect inner_bounds = LineInfo().iInnerRect;
+ int left_margin = device.HorizontalTwipsToPixels(iParFormat.iLeadingMargin);
+ int first_line_left_margin = left_margin + device.HorizontalTwipsToPixels(iParFormat.iFirstLineIndent);
+ inner_bounds.iTl.iX = LineInfo().iOuterRect.iTl.iX + Min(left_margin,first_line_left_margin);
+ inner_bounds.iBr.iX = LineInfo().iOuterRect.iBr.iX - device.HorizontalTwipsToPixels(iParFormat.iTrailingMargin);
+
+ // Include paragraph labels in the border rule area.
+ if (iLabelled)
+ inner_bounds.iTl.iX = Min(inner_bounds.iTl.iX,iLabelLeft);
+
+ if (left)
+ inner_bounds.iTl.iX -= h_margin + left;
+ if (right)
+ inner_bounds.iBr.iX += h_margin + right;
+ if (top)
+ inner_bounds.iTl.iY -= v_margin + top;
+ if (bottom)
+ inner_bounds.iBr.iY += v_margin + bottom;
+
+ // Establish the outer bounds of the outer border rule.
+ TRect outer_bounds = inner_bounds;
+ outer_bounds.iTl.iX -= left_full - left;
+ outer_bounds.iBr.iX += right_full - right;
+ outer_bounds.iTl.iY -= top_full - top;
+ outer_bounds.iBr.iY += bottom_full - bottom;
+
+ aInnerBounds = inner_bounds;
+ aOuterBounds = outer_bounds;
+ aLeftWidth = left;
+ aRightWidth = right;
+ aTopWidth = top;
+ aBottomWidth = bottom;
+ }
+
+void RTmDrawingInterpreter::DrawBorders(const TRect& aInnerBounds,const TRect& aOuterBounds,
+ TInt aLeftWidth,TInt aRightWidth,TInt aTopWidth,TInt aBottomWidth)
+ {
+ iGc.SetBrushStyle(CGraphicsContext::ESolidBrush);
+ TRect r;
+ const TTmParBorder* b = iParFormat.Border(RTmParFormat::ELeadingBorder);
+ if (b && (aLeftWidth != 0))
+ {
+ r = aInnerBounds;
+ r.iBr.iX = r.iTl.iX + aLeftWidth;
+ DrawBorderRect(*b,r);
+ r = aOuterBounds;
+ r.iBr.iX = r.iTl.iX + aLeftWidth;
+ DrawBorderRect(*b,r);
+ }
+ b = iParFormat.Border(RTmParFormat::ETrailingBorder);
+ if (b && (aRightWidth != 0))
+ {
+ r = aInnerBounds;
+ r.iTl.iX = r.iBr.iX - aRightWidth;
+ DrawBorderRect(*b,r);
+ r = aOuterBounds;
+ r.iTl.iX = r.iBr.iX - aRightWidth;
+ DrawBorderRect(*b,r);
+ }
+ b = iParFormat.Border(RTmParFormat::ETopBorder);
+ if (b && (aTopWidth != 0))
+ {
+ r = aInnerBounds;
+ r.iBr.iY = r.iTl.iY + aTopWidth;
+ DrawBorderRect(*b,r);
+ r = aOuterBounds;
+ r.iBr.iY = r.iTl.iY + aTopWidth;
+ DrawBorderRect(*b,r);
+ }
+ b = iParFormat.Border(RTmParFormat::EBottomBorder);
+ if (b && (aBottomWidth != 0))
+ {
+ r = aInnerBounds;
+ r.iTl.iY = r.iBr.iY - aBottomWidth;
+ DrawBorderRect(*b,r);
+ r = aOuterBounds;
+ r.iTl.iY = r.iBr.iY - aBottomWidth;
+ DrawBorderRect(*b,r);
+ }
+ }
+
+void RTmDrawingInterpreter::DrawBorderRect(const TTmParBorder& aBorder,const TRect& aRect)
+ {
+ if (aBorder.iStyle == TTmParBorder::ESolidStyle || aBorder.iStyle == TTmParBorder::EDoubleStyle)
+ {
+ iGc.SetPenStyle(CGraphicsContext::ENullPen);
+ SetBrushColor(aBorder.iColor);
+ TmTextDrawExt().DrawRect(iGc, aRect);
+ return;
+ }
+ switch (aBorder.iStyle)
+ {
+ case TTmParBorder::EDotStyle:
+ iGc.SetPenStyle(CGraphicsContext::EDottedPen);
+ break;
+ case TTmParBorder::EDashStyle:
+ iGc.SetPenStyle(CGraphicsContext::EDashedPen);
+ break;
+ case TTmParBorder::EDotDashStyle:
+ iGc.SetPenStyle(CGraphicsContext::EDotDashPen);
+ break;
+ case TTmParBorder::EDotDotDashStyle:
+ iGc.SetPenStyle(CGraphicsContext::EDotDotDashPen);
+ break;
+ default:
+ return;
+ }
+ SetPenColor(aBorder.iColor);
+ TPoint p = aRect.iTl;
+ TPoint q = aRect.iBr;
+ if (aRect.Width() > aRect.Height())
+ {
+ q.iY = p.iY;
+ TmTextDrawExt().DrawLine(iGc, p, q);
+ }
+ else
+ {
+ q.iX = p.iX;
+ // Set the top to the paragraph top and clip to the rectangle so that the dot or dash phase is consistent.
+ p.iY = LineInfo().iParTop;
+ TRect clip = aRect;
+ clip.Intersection(iClipRect);
+ iGc.SetClippingRect(clip);
+ TmTextDrawExt().DrawLine(iGc, p, q);
+ iGc.SetClippingRect(iClipRect);
+ }
+ iGc.SetPenStyle(CGraphicsContext::ESolidPen);
+ }
+
+/*
+Draw the background in aRect, using the custom background where possible, and filling in with
+the default background.
+*/
+void RTmDrawingInterpreter::DrawBackground(const TRect& aRect,const TLogicalRgb& aBackground)
+ {
+ if (aRect.IsEmpty())
+ {
+ return;
+ }
+
+ // 1 - first draw the background
+
+ TRect drawn;
+ Source().DrawBackground(iGc,TextLayoutTopLeft(),aRect,aBackground,drawn);
+
+
+ // 2 - then draw the highlighted part of the background
+
+ if (iDrawInvertedHighlight)
+ {
+ // if we are drawing an inverted selection, then we calculate the union of
+ // all the highlighted rects. Since we are clipping our drawing with the current Line()
+ // rect, we can assume that all the rects have the same height and their union is a
+ // single rectangle.
+ RTmBoundingRectInterpreter interpreter(iSource, iParam);
+ TRect rect;
+ TBool found = GetFirstHighlightClipRect(interpreter, aRect, rect);
+ while (found)
+ {
+ rect.Intersection(LineInfo().iOuterRect);
+ if (! rect.IsEmpty())
+ {
+ // iHighlightedLineRgn identifies the highlighted area in the current line.
+ // note that with Right-to-left text the selection on a single line can be
+ // composed by several disjoined rectangles.
+ iHighlightedLineRgn.AddRect(rect); // this may fail and set the error flag
+ iHighlightedLineRgn.Tidy(); // this allows us to compact the region in the smallest
+ // number of rectangles, making it very unlikely to run out of memory.
+
+ // iHighlightedLineRect keeps the bounding rect of the highlighted area in the current line.
+ // if highlight extensions are not used, this rect corresponds to the highlighted rect.
+ if (iHighlightedLineRect.IsEmpty())
+ {
+ iHighlightedLineRect = rect;
+ }
+ else
+ {
+ iHighlightedLineRect.BoundingRect(rect);
+ }
+ }
+ found = GetNextHighlightClipRect(interpreter, aRect, rect);
+ }
+ interpreter.Close();
+ }
+ else
+ {
+ // the selection should not be inverted but only drawn with different colors, using TTmHighlightSource.
+
+ RRegion highlightRgn;
+
+ // we are using a region that can grow to contain an arbitrary number of rectangles.
+ // so we must handle the case in which a rectangle allocation fails in a region operation.
+ TBool outOfMemory = ! GetHighlightClipRegion(aRect, highlightRgn);
+ if (outOfMemory)
+ {
+ // we ran out of memory allocating rectangles in the region.
+ // we can still draw the highlighted background area enumerating the rectangles that
+ // would compose the region and setting them as clipping rects in the graphics context.
+ RTmBoundingRectInterpreter interpreter(iSource, iParam);
+ TRect rect;
+ TBool found = GetFirstHighlightClipRect(interpreter, aRect, rect);
+ while (found)
+ {
+ iGc.SetClippingRect(rect);
+ TTmHighlightSource highlightSource(Source());
+ highlightSource.DrawBackground(iGc, TextLayoutTopLeft(), rect, aBackground, drawn);
+ iGc.SetClippingRect(iClipRect);
+ found = GetNextHighlightClipRect(interpreter, aRect, rect);
+ }
+ interpreter.Close();
+ }
+ else if (! highlightRgn.IsEmpty())
+ {
+ // here highlightRgn contains the highlighted region. We can use it to clip the graphics context
+ // and draw only the highlighted background area. This is faster than drawing it rect by rect.
+
+ highlightRgn.ClipRect(iClipRect); // ClipRect() is a safe operation, it never sets the region error flag
+
+ if (! highlightRgn.IsEmpty())
+ {
+ // if the selection should not be inverted but only drawn with different colors,
+ // then we clip the graphics context region and use a TTmHighlightSource to draw it.
+ TBool useClippingRgn = highlightRgn.Count() > 1;
+ if (useClippingRgn)
+ {
+ iGc.SetClippingRegion(highlightRgn);
+ }
+ else
+ {
+ iGc.SetClippingRect(highlightRgn[0]);
+ }
+
+ TRect clipRect = highlightRgn.BoundingRect();
+ clipRect.Intersection(aRect);
+
+ TTmHighlightSource highlightSource(Source());
+ highlightSource.DrawBackground(iGc, TextLayoutTopLeft(), clipRect, aBackground, drawn);
+
+ if (useClippingRgn)
+ {
+ iGc.CancelClippingRegion();
+ }
+ else
+ {
+ iGc.SetClippingRect(iClipRect);
+ }
+ }
+ }
+
+ highlightRgn.Close();
+ }
+ }
+
+void RTmDrawingInterpreter::Text()
+ {
+ // Copy all the text into a single buffer.
+ int buffer_length = EndChar() - StartChar();
+ // If the buffer is overlong, we will truncate. This happens when a huge
+ // number of spaces terminates a line.
+ // This causes two bad effects:
+ // 1) the cursor cannot be seen after the truncation position.
+ // 2) finding the end of the line just finds the truncation position, not
+ // the actual end of the line.
+ // TPB 2/7/2001 - fix for DAS-4XVEJ7
+ if (KMaxTextChunkSize < buffer_length)
+ buffer_length = KMaxTextChunkSize;
+ TText buffer[KMaxTextChunkSize + 2];
+ TTmCharFormat format;
+ CTmTextFontCache* font = NULL;
+ TInt err = TextCache().GetDisplayedText(StartChar(), StartChar() + buffer_length,
+ RightToLeft()?
+ RTmTextCache::EVisualRightToLeft : RTmTextCache::ELeftToRight,
+ buffer,ContextCharChar(), &format, &font);
+
+ if (err == KErrNone)
+ {
+ // Draw the text
+ TPtrC text(buffer, buffer_length + 2);
+ if (*text.Ptr() == 0xFFFF)
+ DrawText(text,0, text.Length() - 1, format,&font->Font());
+ else
+ DrawText(text,1, text.Length() - 1, format,&font->Font());
+ font->Close();
+ }
+ }
+
+void RTmDrawingInterpreter::InlineText()
+ {
+ TPtrC text;
+ TTmCharFormat format;
+ CTmTextFontCache* font = NULL;
+ GetInlineText(text,&format,&font);
+ if (font != NULL)
+ {
+ DrawText(text,0, text.Length() - 1, format,&font->Font());
+ font->Close();
+ }
+ }
+
+void RTmDrawingInterpreter::DrawText(const TDesC& aText,const TInt aStart, const TInt aEnd, const TTmCharFormat& aFormat,CFont* aFont)
+ {
+ if (!aFont)
+ return;
+
+ // Set the graphics context up for drawing the text.
+ iGc.UseFont(aFont);
+ iGc.SetPenStyle(CGraphicsContext::ESolidPen);
+ SetPenColor(aFormat.iTextColor);
+ if (aFormat.iEffects & TTmCharFormat::EUnderline)
+ iGc.SetUnderlineStyle(EUnderlineOn);
+ else
+ iGc.SetUnderlineStyle(EUnderlineOff);
+ if (aFormat.iEffects & TTmCharFormat::EStrikethrough)
+ iGc.SetStrikethroughStyle(EStrikethroughOn);
+ else
+ iGc.SetStrikethroughStyle(EStrikethroughOff);
+
+ // Calculate the text bounds.
+ TRect text_bounds = LineInfo().iInnerRect;
+ text_bounds.iTl.iX = StartPen().iX;
+ text_bounds.iBr.iX = EndPen().iX;
+
+ // Set character justification to perform copy fitting.
+ int extra_pixels = 0;
+ if (CopyFit() || (aFormat.iEffects & (TTmCharFormat::EUnderline | TTmCharFormat::EStrikethrough)))
+ {
+ CFont::TMeasureTextInput input;
+ input.iFlags = CFont::TMeasureTextInput::EFVisualOrder;
+ input.iStartInputChar = 1;
+ extra_pixels = text_bounds.Width() - aFont->MeasureText(aText, &input);
+ }
+
+ // Draw the text.
+
+ if (iDrawInvertedHighlight || ! IsCurrentOpCompletelyHighlighted() ||
+ (NULL != iHighlightExtensions && iHighlightExtensions->Shrinks()))
+ {
+ // If a custom interface to draw the text in context exists, then use it to call the appropriate DrawText method.
+ MTmCustomExtension* extensionInterface = reinterpret_cast <MTmCustomExtension*> (Source().GetExtendedInterface(KTmCustomExtensionUid));
+ if (extensionInterface)
+ {
+ extensionInterface->DrawText(iGc,TextLayoutTopLeft(),text_bounds,LineInfo(),aFormat,aText, aStart, aEnd, StartPen(),extra_pixels);
+ }
+ else
+ {
+ Source().DrawText(iGc,TextLayoutTopLeft(),text_bounds,LineInfo(),aFormat,aText.Mid(aStart,aEnd - aStart + 1),StartPen(),extra_pixels);
+ }
+ }
+
+ // Draw the text again if some part of it is highlighted.
+
+ // If we are inverting the highlighted area at the end of Line(), then we should not draw highlighted text.
+ if (! iDrawInvertedHighlight)
+ {
+ RRegion highlightRgn;
+
+ if (! GetHighlightClipRegion(text_bounds, highlightRgn))
+ {
+ // If the error flag is set, there was not enough memory to allocate all the rectangles.
+ // We must use a slower method: enumerate all the rectangles that compose the highlighted (and
+ // maybe extended) area of the textview and set each one of these rectangles as clipping rect
+ // of the graphics context. Then we draw the text.
+ RTmBoundingRectInterpreter interpreter(iSource, iParam);
+ TRect rect;
+ TBool found = GetFirstHighlightClipRect(interpreter, iClipRect, rect);
+ while (found)
+ {
+ iGc.SetClippingRect(rect);
+ TTmHighlightSource highlightSource(Source());
+ highlightSource.DrawText(iGc,TextLayoutTopLeft(),text_bounds,LineInfo(),aFormat,aText,aStart,aEnd,StartPen(),extra_pixels);
+ iGc.SetClippingRect(iClipRect);
+ found = GetNextHighlightClipRect(interpreter, iClipRect, rect);
+ }
+ interpreter.Close();
+ }
+ else
+ {
+ // If the error flag is not set, then there was enough memory to allocate the rectangles in the region.
+ // We can set a single clipping region in the GC, that should be faster
+ if (! highlightRgn.IsEmpty())
+ {
+ highlightRgn.ClipRect(iClipRect); // ClipRect() is safe (never sets the region error flag).
+
+ if (! highlightRgn.IsEmpty())
+ {
+ TBool useClippingRgn = highlightRgn.Count() > 1;
+ if (useClippingRgn)
+ {
+ iGc.SetClippingRegion(highlightRgn);
+ }
+ else
+ {
+ iGc.SetClippingRect(highlightRgn[0]); // much faster
+ }
+
+ TTmHighlightSource highlightSource(Source());
+ highlightSource.DrawText(iGc,TextLayoutTopLeft(),text_bounds,LineInfo(),aFormat,aText,aStart,aEnd,StartPen(),extra_pixels);
+
+ if (useClippingRgn)
+ {
+ iGc.CancelClippingRegion();
+ }
+ else
+ {
+ iGc.SetClippingRect(iClipRect);
+ }
+ }
+ }
+ }
+
+ highlightRgn.Close();
+ }
+
+ iGc.DiscardFont();
+ }
+
+void RTmDrawingInterpreter::SpecialCharL()
+ {
+ TPtrC text;
+ GetText(StartChar(),EndChar(),text);
+ if (text[0] == CEditableText::EPictureCharacter)
+ {
+ ReleaseFont();
+ CPicture* p = Source().PictureL(StartChar());
+ if (p)
+ {
+ DrawPicture(p);
+ }
+ }
+ }
+
+void RTmDrawingInterpreter::DrawPicture(CPicture* aPicture)
+ {
+ TSize size;
+ aPicture->GetSizeInPixels(&Device(),size);
+
+ TPoint top_left = StartPen();
+ top_left.iY -= size.iHeight - Subscript();
+ TRect clip(top_left,size);
+ clip.Intersection(iClipRect);
+
+ TInt highlightStartPos = 0;
+ TInt highlightEndPos = 0;
+ if (! iDrawInvertedHighlight &&
+ GetHighlightPos(highlightStartPos, highlightEndPos))
+ {
+ // if the highlighted interval [iHighlightStartPos, iHighlightEndPos] overlaps
+ // the current object [StartDocPos(), EndDocPos()], then the whole picture is selected.
+ TTmHighlightSource highlightSource(Source());
+ highlightSource.DrawPicture(iGc, top_left, clip, Device(), *aPicture);
+ }
+ else
+ {
+ Source().DrawPicture(iGc, top_left, clip, Device(), *aPicture);
+ }
+
+ iGc.Reset();
+ InitGraphicsContext();
+ }
+
+void RTmDrawingInterpreter::Rule()
+ {
+ SetBrushColor(RuleColour());
+ TmTextDrawExt().DrawRect(iGc, Bounds());
+ }
+
+void RTmDrawingInterpreter::Label()
+ {
+ ReleaseFont();
+
+ /*
+ The label rectangle is a hybrid; the source interface provides x coords but the y coords come from the bytecode.
+ This is so that the gutter between label and text can be adjusted at the time of drawing.
+ */
+ MFormLabelApi* labelApi = (MFormLabelApi*)Source().GetExtendedInterface(KFormLabelApiExtensionUid);
+ if ((labelApi != NULL) && (labelApi->LabelModeSelect(LabelType(), StartChar())))
+ {
+ TRect label_rect = Bounds();
+ label_rect.Move(LineInfo().iOuterRect.iTl);
+ label_rect.iTl.iX = iLabelLeft;
+ label_rect.iBr.iX = iLabelRight;
+
+ TTmInterpreterParam param(ByteCode());
+ param.iCodeStart = CodePos();
+ param.iCodeEnd = EndCodePos();
+ param.iTextStart = 0;
+ param.iWidth = Bounds().Width();
+ TRect label_clip_rect = label_rect;
+ label_clip_rect.Intersection(iClipRect);
+ RTmDrawingInterpreter interpreter(Source(), iGc,param, label_rect.iTl,
+ label_clip_rect, iDocBackground, iFlags | EIsLabel, NULL, NULL);
+ interpreter.Draw();
+ interpreter.Close();
+ iGc.Reset();
+ InitGraphicsContext();
+ labelApi->LabelModeCancel();
+ }
+ Skip();
+ }
+
+
+void RTmDrawingInterpreter::InvertHighlightedLineRgn()
+ {
+ if (! iHighlightedLineRect.IsEmpty())
+ {
+ TBool useClippingRgn = (! iHighlightedLineRgn.IsEmpty() && ! iHighlightedLineRgn.CheckError());
+ if (useClippingRgn)
+ {
+ if (1 == iHighlightedLineRgn.Count())
+ {
+ iHighlightedLineRect = iHighlightedLineRgn[0];
+ useClippingRgn = EFalse;
+ }
+ }
+
+ if (useClippingRgn)
+ {
+ iGc.SetClippingRegion(iHighlightedLineRgn);
+ }
+ else
+ {
+ iGc.SetClippingRect(iHighlightedLineRect);
+ }
+
+ iGc.SetBrushStyle(CGraphicsContext::ESolidBrush);
+ iGc.SetBrushColor(iHighlightXorColor);
+ iGc.SetPenStyle(CGraphicsContext::ENullPen);
+ iGc.SetDrawMode(CGraphicsContext::EDrawModeXOR);
+
+ TTmTextDrawExt tmTextDrawExtDefault; //The default implementation of MTmTextDrawExt
+ const MTmTextDrawExt* tmTextDrawExt = reinterpret_cast <const MTmTextDrawExt*>(
+ (MTmSource*)(iSource.GetExtendedInterface(KTmTextDrawExtId)));
+ if (NULL == tmTextDrawExt) {
+
+ tmTextDrawExt = &tmTextDrawExtDefault;
+ }
+ tmTextDrawExt->DrawRect(iGc, iHighlightedLineRect);
+
+ iGc.SetDrawMode(CGraphicsContext::EDrawModePEN);
+ iGc.SetPenStyle(CGraphicsContext::ESolidPen);
+
+ if (useClippingRgn)
+ {
+ iGc.CancelClippingRegion();
+ }
+ else
+ {
+ iGc.SetClippingRect(iClipRect);
+ }
+
+ iHighlightedLineRect = TRect(TRect::EUninitialized);
+ iHighlightedLineRgn.Clear();
+ }
+ }
+
+
+RTmBoundingRectInterpreter::RTmBoundingRectInterpreter(MTmSource& aSource,const TTmInterpreterParam& aParam):
+ RTmGeneralInterpreter(aSource,aParam),
+ iStartDocPos(-1,FALSE),
+ iEndDocPos(-1,FALSE),
+ iRangeState(EBeforeAllLines)
+ {
+ }
+
+TBool RTmBoundingRectInterpreter::FirstRect(TInt aStartDocPos,TInt aEndDocPos,TRect& aRect)
+ {
+ iStartDocPos = TTmDocPos(aStartDocPos,TRUE);
+ iEndDocPos = TTmDocPos(aEndDocPos,FALSE);
+ TInt dummy = StartDocPos().iPos;
+ if (aStartDocPos < StartDocPos().iPos && aEndDocPos > StartDocPos().iPos)
+ {//Highlight starts before formatted band but ends inside it
+ //Do nothing (do not attempt to run DocPosToLine)
+ }
+ else if (!DocPosToLine(TTmDocPosSpec(iStartDocPos)))
+ {
+ return FALSE;
+ }
+ if (iStartDocPos == iEndDocPos || PosIsInLine(iEndDocPos))
+ {
+ iRangeState = EInLastLine;
+ }
+ else
+ iRangeState = EInFirstLine;
+ return NextRect(aRect);
+ }
+
+TBool RTmBoundingRectInterpreter::NextRect(TRect& aRect)
+ {
+ if (iRangeState == EAfterAllLines || !Next())
+ {
+ aRect.SetRect(0,0,0,0);
+ return FALSE;
+ }
+ iRect.SetRect(0,0,0,0);
+ switch (Op())
+ {
+ case EOpLine:
+ Line();
+ break;
+ case EOpText:
+ Text();
+ break;
+ case EOpSpecialChar:
+ SpecialCharacter();
+ break;
+ default:
+ Skip();
+ break;
+ }
+ aRect = iRect;
+ return TRUE;
+ }
+
+void RTmBoundingRectInterpreter::Line()
+ {
+ if (iRangeState == EInFirstLine)
+ {
+ if (PosIsInLine(iEndDocPos))
+ iRangeState = EInLastLine;
+ else
+ iRangeState = EInMedialLine;
+ }
+ else if (iRangeState == EInMedialLine)
+ {
+ if (PosIsInLine(iEndDocPos))
+ iRangeState = EInLastLine;
+ }
+ else if (iRangeState == EInLastLine)
+ iRangeState = EAfterAllLines;
+ if (iRangeState == EInMedialLine)
+ {
+ iRect = LineInfo().iOuterRect;
+ iRect.iTl.iX = LineInfo().iInnerRect.iTl.iX;
+ iRect.iBr.iX = LineInfo().iInnerRect.iBr.iX;
+ Skip();
+ }
+ }
+
+void RTmBoundingRectInterpreter::Text()
+ {
+ if (DontMeasure())
+ return;
+
+ TTmDocPos startPos = StartDocPos();
+ TTmDocPos endPos = EndDocPos();
+
+ // Return with an empty rectangle if the range doesn't overlap this section
+ // of text.
+ if (iEndDocPos <= startPos || endPos <= iStartDocPos)
+ return;
+
+ iRect = LineInfo().iOuterRect;
+ iRect.iTl.iX = StartPen().iX;
+ iRect.iBr.iX = EndPen().iX;
+
+ // Work out if we need to find the start and end within this text chunk
+ TBool startRequired = startPos.iPos < iStartDocPos.iPos? ETrue : EFalse;
+ TBool endRequired = iEndDocPos.iPos < endPos.iPos? ETrue : EFalse;
+
+ // Return with a full rectangle if the range totally covers this section of
+ // text.
+ if (!startRequired && !endRequired)
+ return;
+
+ // Sort the end points left to right.
+ TBool leftRequired = startRequired;
+ TBool rightRequired = endRequired;
+ TTmDocPos leftPos = iStartDocPos;
+ TTmDocPos rightPos = iEndDocPos;
+ if (RightToLeft())
+ {
+ leftRequired = endRequired;
+ rightRequired = startRequired;
+ leftPos = iEndDocPos;
+ rightPos = iStartDocPos;
+ }
+
+ // Truncate the rectangle as required.
+ RTmGraphemeInTextChunkIteratorNice edgeIterator(*this);
+ if (leftRequired)
+ {
+ edgeIterator.FindEdge(leftPos);
+ iRect.iTl.iX = edgeIterator.Get()->iPos.iEdge.iX;
+ }
+ if (rightRequired)
+ {
+ edgeIterator.FindEdge(rightPos);
+ iRect.iBr.iX = edgeIterator.Get()->iPos.iEdge.iX;
+ }
+ edgeIterator.Close();
+ }
+
+void RTmBoundingRectInterpreter::SpecialCharacter()
+ {
+ // Return with an empty rectangle if the range doesn't overlap this section
+ // of text.
+ if (iEndDocPos.iPos <= StartChar() || EndChar() <= iStartDocPos.iPos)
+ return;
+
+ // Otherwise return the full rectangle: special characters are indivisible.
+ iRect = LineInfo().iOuterRect;
+ iRect.iTl.iX = StartPen().iX;
+ iRect.iBr.iX = EndPen().iX;
+ }
+
+TTmByteCodeFinder::TTmByteCodeFinder(const TTmInterpreterParam& aParam,TInt aStartDocPos,TInt aEndDocPos):
+ TTmInterpreter(aParam),
+ iStartDocPos(aStartDocPos),
+ iEndDocPos(aEndDocPos)
+ {
+ }
+
+/*
+Find the bytecode for the range iStartDocPos...iEndDocPos.
+
+If aToParStart is ETrue the bytecode is extended to the start of the paragraph,
+otherwise to one line before the one containing iStartDocPos.
+
+If aToParEnd is ETrue the bytecode is extended to the end of the paragraph,
+otherwise to one line after the one containing iEndDocPos. However, aToParEnd
+is constrained by aMaxExtraLines, which is the maximum number of lines
+allowed after the line containing iEndDocPos.
+
+The bidirectional contexts at the start and end are placed in iStartBdState and
+iEndBdState if both pointers are non-null.
+*/
+TBool TTmByteCodeFinder::FindByteCode(TBool aToParStart,TBool aToParEnd,TInt aMaxExtraLines,TInfo& aInfo,
+ TBidirectionalContext* aStartBdState,TBidirectionalContext* aEndBdState)
+ {
+ __ASSERT_DEBUG((aStartBdState && aEndBdState)
+ || (!aStartBdState && !aEndBdState), TmPanic(EBadArg));
+ if (aStartBdState)
+ {
+ aStartBdState->Reset();
+ aEndBdState->Reset();
+ }
+
+ // Use aEndBdState to hold the bidirectional state temporarily, so that the following line doesn't overwrite it.
+ SetBdStatePtr(aEndBdState);
+ // We are looking for the context of the previous line in the byte code, to pass it on to the next line.
+ TUint contextChar = 0;
+ TUint prevContextChar = 0;
+ // Find iStartDocPos.
+ TBool foundStart = EFalse;
+ TBool get_info = EFalse;
+ while (Next())
+ {
+ __ASSERT_ALWAYS(Op() == EOpLine, Panic(ECorrupt));
+ contextChar = LineContextCharChar();
+ foundStart = iStartDocPos >= StartChar() && iStartDocPos < EndChar();
+ get_info = LineInfo().iLineNumber == 0 || ParStart();
+ if (!aToParStart)
+ get_info = get_info || !foundStart;
+ if (get_info)
+ {
+ aInfo.iBounds.iTl = LineInfo().iOuterRect.iTl;
+ aInfo.iStartCodePos = StartCodePos();
+ aInfo.iStartInfo = LineInfo();
+ aInfo.iContextCharPerLine = prevContextChar;
+ if (aStartBdState && aEndBdState)
+ *aStartBdState = *aEndBdState;
+ }
+ if (foundStart)
+ break;
+
+ Skip();
+ prevContextChar = contextChar;
+ }
+ if (!foundStart)
+ return EFalse;
+
+ // Find iEndDocPos.
+ SetBdStatePtr(aEndBdState);
+ if (aEndBdState)
+ aEndBdState->Reset();
+ TBool foundEnd = EFalse;
+ TInt extra_lines = 0;
+ do
+ {
+ // Operators must all be EOpLine because we're skipping from line to line
+ __ASSERT_ALWAYS(Op() == EOpLine, Panic(ECorrupt));
+ aInfo.iBounds.iBr = LineInfo().iOuterRect.iBr;
+ aInfo.iEndCodePos = EndCodePos();
+ aInfo.iEndInfo = LineInfo();
+ if (foundEnd)
+ {
+ extra_lines++;
+ SetBdStatePtr(NULL);
+ if (!aToParEnd || extra_lines >= aMaxExtraLines)
+ break;
+ }
+ if (!foundEnd)
+ foundEnd = iEndDocPos >= StartChar() && iEndDocPos < EndChar();
+ if (foundEnd && ParEnd())
+ break;
+ Skip();
+ } while (Next());
+
+ return foundEnd;
+ }
+
+/*
+Find the bytecode at the end of the formatted range.
+The section returned must not be bigger than aMaxHeight: if aMaxHeight is sufficiently
+small that we cannot retrieve any whole lines without going over, return EFalse.
+
+Otherwise place information about the section of bytecode in aInfo.
+
+The bidirectional contexts at the start and end are placed in iStartBdState and
+iEndBdState if both pointers are non-null.
+*/
+TBool TTmByteCodeFinder::FindByteCodeAtEnd(TInfo& aInfo, TInt aMaxHeight, TInt aTotalHeight,
+ TBidirectionalContext* aStartBdState,TBidirectionalContext* aEndBdState)
+ {
+ TInt heightToSkip = aTotalHeight - aMaxHeight;
+ TInt cumulativeHeight = 0;
+
+ __ASSERT_DEBUG((aStartBdState && aEndBdState) || (!aStartBdState && !aEndBdState), TmPanic(EBadArg));
+ if (aStartBdState)
+ {
+ aStartBdState->Reset();
+ aEndBdState->Reset();
+ }
+
+ // Use aEndBdState to hold the bidirectional state temporarily, so that the following line doesn't overwrite it.
+ SetBdStatePtr(aEndBdState);
+ // Find iStartDocPos.
+ TBool foundStart = EFalse;
+ while (Next())
+ {
+ __ASSERT_ALWAYS(Op() == EOpLine, Panic(ECorrupt));
+ if (cumulativeHeight > heightToSkip)
+ {
+ foundStart = ETrue;
+ aInfo.iBounds.iTl = LineInfo().iOuterRect.iTl;
+ aInfo.iStartCodePos = StartCodePos();
+ aInfo.iStartInfo = LineInfo();
+ if (aStartBdState && aEndBdState)
+ {
+ *aStartBdState = *aEndBdState;
+ }
+ Skip();
+ break;
+ }
+ cumulativeHeight += LineInfo().iOuterRect.Height();
+ Skip();
+ }
+
+ TBool foundEnd = EFalse;
+ while (Next())
+ {
+ __ASSERT_ALWAYS(Op() == EOpLine, Panic(ECorrupt));
+ foundEnd = ETrue;
+ aInfo.iBounds.iBr = LineInfo().iOuterRect.iBr;
+ aInfo.iEndCodePos = EndCodePos();
+ aInfo.iEndInfo = LineInfo();
+ Skip();
+ }
+
+ SetBdStatePtr(NULL);
+ return foundStart && foundEnd;
+ }
+