diff -r 000000000000 -r 1fb32624e06b textrendering/textformatting/tagma/TMTEXT.CPP --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/textrendering/textformatting/tagma/TMTEXT.CPP Tue Feb 02 02:02:46 2010 +0200 @@ -0,0 +1,729 @@ +/* +* 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: +* +*/ + + +#include "TmText.h" +#include +#include +#include "TMSTD.H" + +/** + * Creates a CTmText object using the specified device map and formatting parameters. + */ +EXPORT_C CTmText* CTmText::NewL(MGraphicsDeviceMap& aDevice,const TTmFormatParamBase& aFormatParam) + { + CTmText* t = new(ELeave) CTmText; + CleanupStack::PushL(t); + t->iImp = new(ELeave) CTmTextImp(aDevice,aFormatParam); + CleanupStack::Pop(); + return t; + } + +/** + * Creates a CTmText object using the specified device map and formatting + * parameters that have default values (as in the default constructor for + * TTmFormatParamBase) apart from the wrap width and flags. The values of the + * flags are those used in for TTmFormatParamBase::iFlags. + */ +EXPORT_C CTmText* CTmText::NewL(MGraphicsDeviceMap& aDevice,TInt aWrapWidth,TInt aFlags) + { + TTmFormatParamBase param; + param.iWrapWidth = aWrapWidth; + param.iFlags = aFlags; + return NewL(aDevice,param); + } + +CTmText::CTmText() + { + } +/** + * Destroys a CTmText object. + */ +EXPORT_C CTmText::~CTmText() + { + delete iImp; + } + +/** + * Inserts the text aText at the document position aPos. The other arguments + * are all pointers which have default values of null. The first two, + * aCharFormat and aParFormat, specify the format for the inserted text. If + * either is null the format immediately before the insertion point is used. + * + * Put the coordinates of the area that has been reformatted and needs to be + * redrawn, relative to the origin of the CText object, in aRedrawRect. Put the + * amount by which the text after the redrawn text needs to be scrolled up or + * down in aScroll: this is the amount by which the reformatted text has + * changed in height, and thus is positive if the text after the redrawn text + * has increased in height, which is usually, but not always, the case. + */ +EXPORT_C void CTmText::InsertL(TInt aPos,const TDesC& aText, + const TTmCharFormat* aCharFormat,const RTmParFormat* aParFormat, + TRect* aRedrawRect,TInt* aScroll) + { + iImp->InsertL(aPos,aText,aCharFormat,aParFormat,aRedrawRect,aScroll); + } + +/** + * Retrieves the current formatting parameters. + */ +EXPORT_C void CTmText::GetFormat(TTmFormatParamBase& aFormatParam) const + { + iImp->GetFormat(aFormatParam); + } + +/** + * Sets the wrap width and reformat the text. + */ +EXPORT_C void CTmText::SetWrapWidthL(TInt aWrapWidth) + { + iImp->SetWrapWidthL(aWrapWidth); + } + +/** + * Set the formatting parameters and reformat the text. + */ +EXPORT_C void CTmText::ChangeFormatL(const TTmFormatParamBase& aFormatParam) + { + iImp->ChangeFormatL(aFormatParam); + } + +/** + * Deletes all the text. + */ +EXPORT_C void CTmText::Clear() + { + iImp->Clear(); + } + +/** + * Specifies custom drawing or layout for the object, or if aCustom is NULL, + * removes any customization. Reformats the object according to the new + * customized layout. See the MTmCustom class for details. Custom drawing + * allows background graphics to be drawn, and custom layout allows, among + * other things, control over the way line height is calculated and special + * characters are displayed. + */ +EXPORT_C void CTmText::CustomizeL(const MTmCustom* aCustom) + { + iImp->CustomizeL(aCustom); + } + +/** + * Returns the number of bytes of memory used by a CTmText object, allowing 8 + * bytes overhead per heap allocation. +*/ +EXPORT_C TInt CTmText::MemoryUsed() const + { + return sizeof(*this) + iImp->MemoryUsed() + 8; + } + +/** + * Returns a const reference to the CTmTextLayout object owned by this object. + * This function implements MTmTextLayoutForwarder::TextLayout. + */ +const CTmTextLayout& CTmText::TextLayout() const + { + return iImp->TextLayout(); + } + +template TInt RRefCountedArray::Insert(const T& aEntry,TInt& aIndex) + { + //+ replace this linear search with a binary search + int n = iArray.Count(); + for (int i = 0; i < n; i++) + if (aEntry == iArray[i].iEntry) + { + iArray[i].iRefCount++; + aIndex = i; + return KErrNone; + } + aIndex = n; + TCountedEntry new_entry; + new_entry.iRefCount = 1; + int error = iArray.Append(new_entry); + if (!error) + error = iArray[iArray.Count() - 1].iEntry.Copy(aEntry); + return error; + } + +template void RRefCountedArray::Remove(TInt aIndex) + { + if (--iArray[aIndex].iRefCount == 0) + iArray.Remove(aIndex); + } + +template TInt RRefCountedArray::MemoryUsed() const + { + TInt bytes = sizeof(*this); + if (iArray.Count()) + bytes += User::AllocLen(&iArray[0]) + 8; + return bytes; + } + +CTmTextImp::~CTmTextImp() + { + iCharFormatRun.Close(); + iParFormatRun.Close(); + iCharFormat.Close(); + for (int i = 0; i < iParFormat.Count(); i++) + iParFormat[i].Close(); + iParFormat.Close(); + } + +void CTmTextImp::InsertL(TInt aPos,const TDesC& aText, + const TTmCharFormat* aCharFormat,const RTmParFormat* aParFormat, + TRect* aRedrawRect,TInt* aScroll) + { + __ASSERT_DEBUG(0 <= aPos, TmPanic(EBadArg)); + + // Don't insert zero-length text. + if (aText.Length() == 0) + return; + + // Clamp aPos to the end of the document. + if (iTextLayout.EndChar() <= aPos) + aPos = Max(0,iTextLayout.EndChar() - 1); + + // If the document is empty and character or paragraph format is unspecified, use a default format. + TTmCharFormat default_char_format; + default_char_format.iFontSpec.SetHeight(12 * 20); // set height, which must be in twips, to 12pt + RTmParFormat default_par_format; + if (iText.Length() == 0) + { + if (aCharFormat == NULL) + aCharFormat = &default_char_format; + if (aParFormat == NULL) + aParFormat = &default_par_format; + } + + // Insert the character format. + int error = 0; + int char_format_index = -1; + if (aCharFormat) + error = iCharFormat.Insert((TMyCharFormat&)*aCharFormat,char_format_index); + + // Insert the paragraph format. + int par_format_index = -1; + if (!error && aParFormat) + error = iParFormat.Insert((RMyParFormat&)*aParFormat,par_format_index); + + // Insert the character format run. + if (!error) + error = iCharFormatRun.Insert(aPos,aText.Length(),char_format_index); + + // If the paragraph format has changed extend it to the paragraph bounds. + int old_par_format_index = -1; + if (!error && iText.Length() > 0) + { + if (par_format_index != -1) + old_par_format_index = iParFormatRun.Index(aPos); + if (par_format_index != old_par_format_index) + { + int par_start = ParagraphStart(aPos); + int par_end = ParagraphEnd(aPos); + if (iText.Length() == par_end - 1) + --par_end; + error = iParFormatRun.Set(par_start, par_end - par_start, + par_format_index); + } + } + + // Insert the paragraph format run. + if (!error) + error = iParFormatRun.Insert(aPos,aText.Length(),par_format_index); + + // Insert the text. + if (!error) + { + TRAP(error,iText.InsertL(aPos,aText)); + } + + default_par_format.Close(); + + User::LeaveIfError(error); + + if (iTextLayout.EndChar() > 0) + { + TTmReformatParam reformat_param; + reformat_param.iStartChar = aPos; + reformat_param.iOldLength = 0; + reformat_param.iNewLength = aText.Length(); + reformat_param.iParFormatChanged = par_format_index != old_par_format_index; + TTmReformatResult result; + iTextLayout.FormatL(iFormatParam,reformat_param,result); + if (aRedrawRect) + *aRedrawRect = result.iRedrawRect; + if (aScroll) + *aScroll = result.iHeightChange; + } + else + { + iTextLayout.SetTextL(*this,iFormatParam); + if (aRedrawRect) + *aRedrawRect = TRect(0,0,iTextLayout.LayoutWidth(),iTextLayout.LayoutHeight()); + if (aScroll) + *aScroll = 0; + } + } + +void CTmTextImp::ChangeFormatL(const TTmFormatParamBase& aFormatParam) + { + iFormatParam = aFormatParam; + ReformatL(); + } + +void CTmTextImp::ReformatL() + { + if (iText.Length() > 0) + iTextLayout.SetTextL(*this,iFormatParam); + } + +void CTmTextImp::Clear() + { + iText.Reset(); + iCharFormatRun.Reset(); + iParFormatRun.Reset(); + iCharFormat.Reset(); + for (int i = 0; i < iParFormat.Count(); i++) + iParFormat[i].Close(); + iParFormat.Reset(); + iTextLayout.Clear(); + } + +void CTmTextImp::CustomizeL(const MTmCustom* aCustom) + { + iCustom = aCustom; + ReformatL(); + } + +TInt CTmTextImp::MemoryUsed() const + { + return sizeof(*this) + + iText.MemoryUsed() - sizeof(iText) + + iCharFormatRun.MemoryUsed() - sizeof(iCharFormatRun) + + iParFormatRun.MemoryUsed() - sizeof(iParFormatRun) + + iCharFormat.MemoryUsed() - sizeof(iCharFormat) + + iParFormat.MemoryUsed() - sizeof(iParFormat) + + iTextLayout.MemoryUsed() - sizeof(iTextLayout); + } + +MGraphicsDeviceMap& CTmTextImp::FormatDevice() const + { + return iDevice; + } + +MGraphicsDeviceMap& CTmTextImp::InterpretDevice() const + { + return iDevice; + } + +TInt CTmTextImp::DocumentLength() const + { + return iText.Length(); + } + +void CTmTextImp::GetText(TInt aPos,TPtrC& aText,TTmCharFormat& aFormat) const + { + static const TText end_par = CEditableText::EParagraphDelimiter; + TInt index = 0; + TInt textLength = iText.Length(); + __ASSERT_DEBUG(aPos <= textLength + 1, TmPanic(EIndexOutOfRange)); + if (aPos < textLength) + { + TPtrC p = iText.PtrC(aPos); + TInt pLength = p.Length(); + TRun charRun; + TInt charRunStart = iCharFormatRun.RunAndStartPos(aPos, charRun); + TInt remainingLength = charRunStart + charRun.iLength - aPos; + aText.Set(p.Ptr(), remainingLength < pLength? remainingLength : pLength); + index = charRun.iIndex; + } + else if (aPos == textLength) + { + aText.Set(&end_par,1); + if (0 < aPos) + index = iCharFormatRun.Index(aPos - 1); + else + { + TTmCharFormat d; + aFormat = d; + return; + } + } + else + { + aText.Set(0, 0); + return; + } + aFormat = iCharFormat[index]; + } + +void CTmTextImp::GetParagraphFormatL(TInt aPos,RTmParFormat& aFormat) const + { + __ASSERT_DEBUG(0 <= aPos && aPos <= iText.Length(), TmPanic(EBadArg)); + TInt textLength = iText.Length(); + if (0 == textLength) + { + RTmParFormat d; + CleanupClosePushL(d); + aFormat.CopyL(d); + CleanupStack::PopAndDestroy(&d); + return; + } + if (aPos != textLength) + ++aPos; + aFormat.CopyL(iParFormat[iParFormatRun.Index(aPos)]); + } + +TInt CTmTextImp::ParagraphStart(TInt aPos) const + { + while (aPos > 0) + { + TPtrC text = iText.BackPtrC(aPos); + const TText *p = text.Ptr(); + const TText *q = p + text.Length(); + while (p < q) + { + q--; + aPos--; + if (*q == CEditableText::EParagraphDelimiter) + return aPos + 1; + } + } + return 0; + } + +TRgb CTmTextImp::SystemColor(TUint aColorIndex,TRgb aDefaultColor) const + { + if (iCustom) + return iCustom->SystemColor(aColorIndex,aDefaultColor); + else + return MTmSource::SystemColor(aColorIndex,aDefaultColor); + } + +TInt CTmTextImp::Stretch(TUint aChar) const + { + if (iCustom) + return iCustom->Stretch(aChar); + else + return MTmSource::Stretch(aChar); + } + +TUint CTmTextImp::Map(TUint aChar) const + { + if (iCustom) + return iCustom->Map(aChar); + else + return MTmSource::Map(aChar); + } + +void CTmTextImp::SetLineHeight(const TLineHeightParam& aParam,TInt& aAscent,TInt& aDescent) const + { + if (iCustom) + iCustom->SetLineHeight(aParam,aAscent,aDescent); + else + MTmSource::SetLineHeight(aParam,aAscent,aDescent); + } + +void CTmTextImp::DrawBackground(CGraphicsContext& aGc,const TPoint& aTextLayoutTopLeft,const TRect& aRect, + const TLogicalRgb& aBackground,TRect& aRectDrawn) const + { + if (iCustom) + iCustom->DrawBackground(aGc,aTextLayoutTopLeft,aRect,aBackground,aRectDrawn); + else + MTmSource::DrawBackground(aGc,aTextLayoutTopLeft,aRect,aBackground,aRectDrawn); + } + +void CTmTextImp::DrawLineGraphics(CGraphicsContext& aGc,const TPoint& aTextLayoutTopLeft,const TRect& aRect, + const TTmLineInfo& aLineInfo) const + { + if (iCustom) + iCustom->DrawLineGraphics(aGc,aTextLayoutTopLeft,aRect,aLineInfo); + else + MTmSource::DrawLineGraphics(aGc,aTextLayoutTopLeft,aRect,aLineInfo); + } + +void CTmTextImp::DrawText(CGraphicsContext& aGc,const TPoint& aTextLayoutTopLeft,const TRect& aRect, + const TTmLineInfo& aLineInfo,const TTmCharFormat& aFormat, + const TDesC& aText,const TPoint& aTextOrigin,TInt aExtraPixels) const + { + if (iCustom) + iCustom->DrawText(aGc,aTextLayoutTopLeft,aRect,aLineInfo,aFormat,aText,aTextOrigin,aExtraPixels); + else + MTmSource::DrawText(aGc,aTextLayoutTopLeft,aRect,aLineInfo,aFormat,aText,aTextOrigin,aExtraPixels); + } + +TInt CTmTextImp::RRunArray::SplitRun(TInt aRunIndex,TInt aOffset) + { + TRun& run = iRun[aRunIndex]; + TRun new_run; + new_run.iIndex = run.iIndex; + new_run.iLength = run.iLength - aOffset; + int error = iRun.Insert(new_run, aRunIndex + 1); + if (!error) + run.iLength = aOffset; + return error; + } + +/* +Find the run containing aPos. The position at the end of a run is in the run. +The position at the start of a run is NOT in the run, but in the preceding run, if any. +*/ +void CTmTextImp::RRunArray::FindRun(TInt aPos,TInt& aRunIndex,TInt& aOffset) const + { +#ifdef _DEBUG + TInt error = +#endif + FindRunNonstrict(aPos, aRunIndex, aOffset); + __ASSERT_DEBUG(!error, TmPanic(ETextRunNotFound)); + } + +TInt CTmTextImp::RRunArray::FindRunNonstrict(TInt aPos,TInt& aRunIndex,TInt& aOffset) const + { + int n = iRun.Count(); + int start = 0; + int end = 0; + for (int i = 0; i < n; i++) + { + __ASSERT_DEBUG(0 <= iRun[i].iLength, TmPanic(EInvalidTextRunLength)); + __ASSERT_DEBUG(0 <= iRun[i].iIndex, TmPanic(EInvalidTextRunIndex)); + end += iRun[i].iLength; + if (end >= aPos) + { + aRunIndex = i; + aOffset = aPos - start; + return KErrNone; + } + start = end; + } + + aRunIndex = aOffset = -1; + return KErrNotFound; + } + +/* Find the latest run that starts less than or equal to aPos. Return it + * in aRunOut, and return its start position. + */ +TInt CTmTextImp::RRunArray::RunAndStartPos(TInt aPos, TRun& aRunOut) const + { + TInt runs = iRun.Count(); + __ASSERT_DEBUG(0 < runs, TmPanic(ETextRunNotFound)); + TInt start = 0; + TInt end = 0; + for (TInt i = 0; i != runs && end <= aPos; ++i) + { + aRunOut = iRun[i]; + start = end; + end = start + aRunOut.iLength; + } + return start; + } + +// Insert a run of length aLength and attribute aIndex at aPos; if aIndex is < 0, use the index at aPos. +TInt CTmTextImp::RRunArray::Insert(TInt aPos,TInt aLength,TInt aIndex) + { + __ASSERT_DEBUG(0 < aLength, TmPanic(EInvalidTextRunLength)); + __ASSERT_DEBUG(0 <= aIndex || 0 < iRun.Count(), TmPanic(EParagraphFormatRequired)); + + int error = 0; + TRun new_run; + if (iRun.Count() == 0) + { + new_run.iLength = 0; + new_run.iIndex = aIndex; + error = iRun.Append(new_run); + if (error) + return error; + } + + // Find the run; see if the index matches; if on a join between runs, check both. + int run_index, offset; + FindRun(aPos,run_index,offset); + if (offset == iRun[run_index].iLength && iRun[run_index].iIndex != aIndex && run_index < iRun.Count() - 1) + { + run_index++; + offset = 0; + } + TRun& run = iRun[run_index]; + + __ASSERT_DEBUG(0 <= offset && offset <= run.iLength, TmPanic(EInvariant)); + + // If aIndex is negative, it means don't change the attribute. + if (aIndex < 0) + aIndex = run.iIndex; + + // No need to create a new run; just extend the existing one + if (run.iIndex == aIndex) + run.iLength += aLength; + + // Split the run if necessary, then add a new run. + else + { + if (run.iLength == offset) + ++run_index; + else if (offset != 0) + { + error = SplitRun(run_index,offset); + run_index++; + } + if (!error) + { + new_run.iIndex = aIndex; + new_run.iLength = aLength; + error = iRun.Insert(new_run, run_index); + } + } + + return error; + } + +TInt CTmTextImp::RRunArray::Set(TInt aPos,TInt aLength,TInt aIndex) + { + __ASSERT_DEBUG(0 <= aLength, TmPanic(EInvalidTextRunLength)); + __ASSERT_DEBUG(0 <= aIndex, TmPanic(EInvalidTextRunIndex)); + + if (aLength == 0) + return KErrNone; + + // Find the run start. + int start_run_index, start_run_offset; + FindRun(aPos,start_run_index,start_run_offset); + + // Adjust the run start if there is no change to the attribute; return if this eliminates the range to be set. + if (iRun[start_run_index].iIndex == aIndex) + { + int n = iRun[start_run_index].iLength - start_run_offset; + aPos += n; + aLength -= n; + if (aLength <= 0) + return KErrNone; + } + + // Find the run end. + int end_run_index, end_run_offset; + FindRun(aPos + aLength,end_run_index,end_run_offset); + + // Adjust the run end if there is no change to the attribute; return if this eliminates the range to be set. + if (iRun[end_run_index].iIndex == aIndex) + { + aLength -= end_run_offset; + if (aLength <= 0) + return KErrNone; + } + + // Determine the change in the number of runs. The maximum increase is 2. + int runs_increase = start_run_index - end_run_index + 2; + + // Insert extra runs if needed. + int error = KErrNone; + if (runs_increase > 0) + { + error = SplitRun(start_run_index,start_run_offset); + runs_increase--; + if (start_run_index == end_run_index) + end_run_offset -= start_run_offset; + end_run_index++; + } + if (!error && runs_increase > 0) + { + error = SplitRun(end_run_index,end_run_offset); + end_run_index++; + end_run_offset = 0; + runs_increase--; + } + if (!error) + { + // Delete unneeded runs. + while (runs_increase < 0) + { + iRun.Remove(start_run_index + 2); + end_run_index--; + ++runs_increase; + } + + // Adjust the size of the start run. + iRun[start_run_index].iLength = start_run_offset; + + // Set the run after the start run to the new attribute. + TRun& run = iRun[start_run_index + 1]; + run.iLength = aLength; + run.iIndex = aIndex; + + // Adjust the size of the end run. + iRun[end_run_index].iLength -= end_run_offset; + } + + return error; + } + +void CTmTextImp::RRunArray::Delete(TInt aPos,TInt aLength) + { + __ASSERT_DEBUG(0 <= aLength, TmPanic(EInvalidTextRunLength)); + + if (aLength == 0) + return; + + // Find the start. + int start_run_index, start_run_offset; + FindRun(aPos,start_run_index,start_run_offset); + + // Find the end. + int end_run_index, end_run_offset; + FindRun(aPos + aLength,end_run_index,end_run_offset); + + // If the runs are the same it's trivial. + if (start_run_index == end_run_index) + { + iRun[start_run_index].iLength -= (end_run_offset - start_run_offset); + return; + } + + // Shorten the start and end runs and determine the range of runs to delete. + iRun[start_run_index].iLength = start_run_offset; + int first_deleted_run = start_run_index + 1; + if (start_run_offset == 0) + first_deleted_run--; + iRun[end_run_index].iLength -= end_run_offset; + int last_deleted_run = end_run_index - 1; + if (iRun[end_run_index].iLength == 0) + last_deleted_run++; + + // Delete runs. + for (int i = first_deleted_run; i <= last_deleted_run; i++) + iRun.Remove(first_deleted_run); + } + +TInt CTmTextImp::RRunArray::Index(TInt aPos) const + { + int run_index, offset; + TInt error = FindRunNonstrict(aPos,run_index,offset); + return error? error : iRun[run_index].iIndex; + } + +TInt CTmTextImp::RRunArray::MemoryUsed() const + { + TInt bytes = sizeof(*this); + if (iRun.Count()) + bytes += User::AllocLen(&iRun[0]) + 8; + return bytes; + } + +EXPORT_C void CTmText::Spare1() + { + TmPanic(EUnimplemented); + }