diff -r 000000000000 -r 7f656887cf89 libraries/iosrv/client/text_formatter.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libraries/iosrv/client/text_formatter.cpp Wed Jun 23 15:52:26 2010 +0100 @@ -0,0 +1,1295 @@ +// text_formatter.cpp +// +// Copyright (c) 2006 - 2010 Accenture. All rights reserved. +// This component and the accompanying materials are made available +// under the terms of the "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: +// Accenture - Initial contribution +// + +#include +#include "ioutils.h" +#include "pod_lexer.h" +#include "pod_formatter.h" + +using namespace IoUtils; + + +// +// Constants. +// + +_LIT(KSpace, " "); +_LIT(KNewLine, "\r\n"); + + +// +// CTextBuffer. +// + +EXPORT_C CTextBuffer* CTextBuffer::NewL(TInt aExpandSize) + { + CTextBuffer* self = CTextBuffer::NewLC(aExpandSize); + CleanupStack::Pop(self); + return self; + } + +EXPORT_C CTextBuffer* CTextBuffer::NewLC(TInt aExpandSize) + { + CTextBuffer* self = new(ELeave) CTextBuffer(); + CleanupStack::PushL(self); + self->ConstructL(aExpandSize); + return self; + } + +EXPORT_C CTextBuffer::~CTextBuffer() + { + Cancel(); + delete iBuf; + delete iScratchBuf; + delete iScratchBuf8; + iAttributes.Close(); + } + +EXPORT_C void CTextBuffer::Zero() + { + iBuf->Delete(0, iBuf->Size()); + iAttributes.Reset(); + } + +EXPORT_C void CTextBuffer::Reset() + { + iBuf->Reset(); + iAttributes.Reset(); + } + +EXPORT_C void CTextBuffer::ResetText() + { + iBuf->Reset(); + } + +EXPORT_C void CTextBuffer::SetAttributesL(TUint aAttributes, ConsoleAttributes::TColor aForegroundColor, ConsoleAttributes::TColor aBackgroundColor) + { + const TInt numAttributes = iAttributes.Count(); + if (numAttributes == 0) + { + if (Length() == 0) + { + TAttributes attributes(0, aAttributes, aForegroundColor, aBackgroundColor); + iAttributes.AppendL(attributes); + } + else + { + TAttributes attributes(Length(), aAttributes, aForegroundColor, aBackgroundColor); + if (!attributes.Matches(ConsoleAttributes::ENone, ConsoleAttributes::EUnchanged, ConsoleAttributes::EUnchanged)) + { + TAttributes defaultAttributes(0, ConsoleAttributes::ENone, ConsoleAttributes::EUnchanged, ConsoleAttributes::EUnchanged); + iAttributes.AppendL(defaultAttributes); + TInt err = iAttributes.Append(attributes); + if (err != KErrNone) + { + iAttributes.Reset(); + User::Leave(err); + } + } + } + } + else if (iAttributes[numAttributes - 1].Matches(aAttributes, aForegroundColor, aBackgroundColor)) + { + // Do nothing. + } + else if (iAttributes[numAttributes - 1].iPosition == Length()) + { + TAttributes& att = iAttributes[numAttributes - 1]; + if (aAttributes & ConsoleAttributes::ENone) + { + att.iAttributes.iAttributes = aAttributes; + att.iAttributes.iForegroundColor = aForegroundColor; + att.iAttributes.iBackgroundColor = aBackgroundColor; + } + else + { + // Merge on top of existing attributes + att.iAttributes.iAttributes |= aAttributes; + if (aForegroundColor != ConsoleAttributes::EUnchanged) att.iAttributes.iForegroundColor = aForegroundColor; + if (aBackgroundColor != ConsoleAttributes::EUnchanged) att.iAttributes.iBackgroundColor = aBackgroundColor; + } + } + else + { + TAttributes attributes(Length(), aAttributes, aForegroundColor, aBackgroundColor); + iAttributes.AppendL(attributes); + } + } + +EXPORT_C void CTextBuffer::SetAttributesL(const ConsoleAttributes::TAttributes& aAttributes) + { + SetAttributesL(aAttributes.iAttributes, aAttributes.iForegroundColor, aAttributes.iBackgroundColor); + } + +EXPORT_C void CTextBuffer::GetCurrentAttributes(TUint& aAttributes, ConsoleAttributes::TColor& aForegroundColor, ConsoleAttributes::TColor& aBackgroundColor) const + { + if (iAttributes.Count() > 0) + { + const TAttributes& att = iAttributes[iAttributes.Count() - 1]; + aAttributes = att.iAttributes.iAttributes; + aForegroundColor = att.iAttributes.iForegroundColor; + aBackgroundColor = att.iAttributes.iBackgroundColor; + } + else + { + aAttributes = ConsoleAttributes::ENone; + aForegroundColor = ConsoleAttributes::EUnchanged; + aBackgroundColor = ConsoleAttributes::EUnchanged; + } + } + +EXPORT_C void CTextBuffer::GetAttributes(TInt aPos, TUint& aAttributes, ConsoleAttributes::TColor& aForegroundColor, ConsoleAttributes::TColor& aBackgroundColor) const + { + ASSERT(aPos < Length()); + + aAttributes = ConsoleAttributes::ENone; + aForegroundColor = ConsoleAttributes::EUnchanged; + aBackgroundColor = ConsoleAttributes::EUnchanged; + + const TInt numAttributes = iAttributes.Count(); + for (TInt i = 0; i < numAttributes; ++i) + { + const TAttributes& att = iAttributes[i]; + if (aPos < att.iPosition) + { + return; + } + else + { + aAttributes = att.iAttributes.iAttributes; + aForegroundColor = att.iAttributes.iForegroundColor; + aBackgroundColor = att.iAttributes.iBackgroundColor; + } + } + } + +EXPORT_C void CTextBuffer::AppendL(const TChar& aChar) + { + TUint16 ch = (TUint16)TUint(aChar); // We don't support surrogate pairs + iBuf->InsertL(iBuf->Size(), &ch, 2); + } + +EXPORT_C void CTextBuffer::AppendL(const TDesC& aText) + { + iBuf->InsertL(iBuf->Size(), aText.Ptr(), aText.Size()); + } + +EXPORT_C void CTextBuffer::AppendL(const TDesC8& aText) + { + if (!iScratchBuf) + { + iScratchBuf = HBufC::NewL(Max(aText.Length(), 256)); + } + if (iScratchBuf->Des().MaxLength() < aText.Length()) + { + iScratchBuf = iScratchBuf->ReAllocL(Max(iScratchBuf->Des().MaxLength()*2, aText.Length())); + } + iScratchBuf->Des().Copy(aText); + iBuf->InsertL(iBuf->Size(), iScratchBuf->Ptr(), iScratchBuf->Size()); + } + +EXPORT_C void CTextBuffer::AppendL(const CTextBuffer& aText) + { + AppendL(aText, 0, aText.Length()); + } + +EXPORT_C void CTextBuffer::AppendL(const CTextBuffer& aText, TInt aPosition) + { + AppendL(aText, aPosition, aText.Length() - aPosition); + } + +EXPORT_C void CTextBuffer::AppendL(const CTextBuffer& aText, TInt aPosition, TInt aLength) + { + const TInt numAttributes = aText.iAttributes.Count(); + if (numAttributes > 0) + { + TInt textPos = 0; + TInt appendCount = 0; + TInt blockIndex = 0; + const TDesC* block = NULL; + do + { + TUint attributes; + ConsoleAttributes::TColor foregroundColor; + ConsoleAttributes::TColor backgroundColor; + aText.NextBlock(blockIndex, block, attributes, foregroundColor, backgroundColor); + if (block != NULL) + { + if ((textPos + block->Length()) > aPosition) + { + SetAttributesL(attributes, foregroundColor, backgroundColor); + TInt blockOffset = aPosition - textPos; + if (blockOffset < 0) + { + blockOffset = 0; + } + TPtrC appendBlock(block->Mid(blockOffset, Min(aLength - appendCount, block->Length() - blockOffset))); + AppendL(appendBlock); + appendCount += appendBlock.Length(); + if (appendCount == aLength) + { + break; + } + } + textPos += block->Length(); + } + } + while (block != NULL); + } + else + { + AppendL(aText.Descriptor().Mid(aPosition, aLength)); + } + } + +EXPORT_C void CTextBuffer::AppendFormatL(TRefByValue aFmt, ...) + { + VA_LIST list; + VA_START(list, aFmt); + AppendFormatListL(aFmt, list); + } + +EXPORT_C void CTextBuffer::AppendFormatListL(const TDesC16& aFmt, VA_LIST& aList) + { + TOverflowLeave overflow; + TInt err = KErrNone; + do + { + TPtr ptr(iScratchBuf->Des()); + ptr.Zero(); + TRAP(err, ptr.AppendFormatList(aFmt, aList, &overflow)); + if (err == KErrOverflow) + { + iScratchBuf = iScratchBuf->ReAllocL(ptr.MaxLength() * 2); + } + } + while (err == KErrOverflow); + + User::LeaveIfError(err); + iBuf->InsertL(iBuf->Size(), iScratchBuf->Ptr(), iScratchBuf->Size()); + } + +EXPORT_C void CTextBuffer::AppendFormatL(TRefByValue aFmt, ...) + { + VA_LIST list; + VA_START(list, aFmt); + AppendFormatListL(aFmt, list); + } + +EXPORT_C void CTextBuffer::AppendFormatListL(const TDesC8& aFmt, VA_LIST& aList) + { + TOverflowLeave8 overflow; + TInt err = KErrNone; + if (!iScratchBuf8) + { + iScratchBuf8 = HBufC8::NewL(0x100); + } + + do + { + TPtr8 ptr(iScratchBuf8->Des()); + ptr.Zero(); + TRAP(err, ptr.AppendFormatList(aFmt, aList, &overflow)); + if (err == KErrOverflow) + { + iScratchBuf8 = iScratchBuf8->ReAllocL(ptr.MaxLength() * 2); + } + } + while (err == KErrOverflow); + + User::LeaveIfError(err); + + TPtr8 ptr(iScratchBuf8->Des()); + if (iScratchBuf->Des().MaxLength() < ptr.Length()) + { + iScratchBuf = iScratchBuf->ReAllocL(Max(iScratchBuf->Des().MaxLength()*2, ptr.Length())); + } + iScratchBuf->Des().Copy(*iScratchBuf8); + + iBuf->InsertL(iBuf->Size(), iScratchBuf->Ptr(), iScratchBuf->Size()); + } + +EXPORT_C void CTextBuffer::AppendHumanReadableSizeL(TInt aSize) + { + AppendHumanReadableSizeL(TInt64(aSize), EColumnAlignedRight); + } + +EXPORT_C void CTextBuffer::AppendHumanReadableSizeL(TInt64 aSize) + { + AppendHumanReadableSizeL(aSize, EColumnAlignedRight); + } + +EXPORT_C void CTextBuffer::AppendHumanReadableSizeL(TInt aSize, TAlignment aAlignment) + { + AppendHumanReadableSizeL(TInt64(aSize), aAlignment); + } + +EXPORT_C void CTextBuffer::AppendHumanReadableSizeL(TInt64 aSize, TAlignment aAlignment) + { + const TInt64 KB = 1024; + const TInt64 MB = KB * KB; + const TInt64 GB = MB * KB; + const TInt64 TB = GB * KB; + _LIT(KBytes, " B "); + _LIT(KKilobytes, " KB"); + _LIT(KMegabytes, " MB"); + _LIT(KGigabytes, " GB"); + _LIT(KTerabytes, " TB"); + + _LIT(KLeftWhole, "%- 7.0f"); + _LIT(KLeftFrac, "%- 7.2f"); + _LIT(KRightWhole, "%+ 7.0f"); + _LIT(KRightFrac, "%+ 7.2f"); + _LIT(KNormalWhole, "%.0f"); + _LIT(KNormalFrac, "%.2f"); + + const TDesC* suff = &KBytes; + TReal n = aSize; + TInt64 factor = 1; + + TInt64 absSize = aSize; + if (absSize < 0) absSize = -absSize; + + if (absSize >= TB) + { + suff = &KTerabytes; + factor = TB; + } + else if (absSize >= GB) + { + suff = &KGigabytes; + factor = GB; + } + else if (absSize >= MB) + { + suff = &KMegabytes; + factor = MB; + } + else if (absSize >= KB) + { + suff = &KKilobytes; + factor = KB; + } + + n = n / (TReal)factor; + TBool wholeNumUnits = (absSize & (factor-1)) == 0; // ie aSize % factor == 0 + + const TDesC* fmt = NULL; + if (aAlignment == EColumnAlignedLeft) + { + fmt = wholeNumUnits ? &KLeftWhole : &KLeftFrac; + } + else if (aAlignment == EColumnAlignedRight) + { + fmt = wholeNumUnits ? &KRightWhole : &KRightFrac; + } + else + { + fmt = wholeNumUnits ? &KNormalWhole : &KNormalFrac; + } + + AppendFormatL(*fmt, n); + AppendL(*suff); + } + +EXPORT_C void CTextBuffer::AppendSpacesL(TInt aCount) + { + ASSERT(aCount >= 0); + while (aCount > 0) + { + AppendL(KSpace); + --aCount; + } + } + +EXPORT_C void CTextBuffer::Delete(TInt aPos, TInt aLength) + { + //for (TInt i = 0; i < iAttributes.Count(); i++) + // { + // RDebug::Printf("attribute %d: pos %d", i, iAttributes[i].iPosition); + // } + + // Find the first and last attributes that are inside the delete region. + TInt firstAttribute = -1; + TInt lastAttribute = -1; + TInt numAttributes = iAttributes.Count(); + for (TInt i = (numAttributes - 1); i >= 0; --i) + { + const TAttributes& thisAttribute = iAttributes[i]; + if ((lastAttribute == -1) && (thisAttribute.iPosition >= aPos) && (thisAttribute.iPosition < aPos + aLength)) + { + lastAttribute = i; + } + if (lastAttribute >= 0 && thisAttribute.iPosition >= aPos) + { + firstAttribute = i; + } + } + //RDebug::Printf("firstAttribute=%d lastAttribute=%d", firstAttribute, lastAttribute); + + if ((lastAttribute > 0)) + { + // Set the position of the last affected attribute to just after the deleted chunk. + iAttributes[lastAttribute].iPosition = aPos + aLength; + + // Remove all but the last affected attribute. Need to keep the last affected attribute + // because it has an impact on the formatting of text that appears after the deleted chunk. + for (TInt i = (lastAttribute - 1); i >= firstAttribute; --i) + { + iAttributes.Remove(i); + } + } + + // Update the positions of attributes after the block being deleted. + numAttributes = iAttributes.Count(); + for (TInt i = Max(0, firstAttribute); i < numAttributes; ++i) + { + TAttributes& thisAttribute = iAttributes[i]; + if (thisAttribute.iPosition >= aPos + aLength) + { + thisAttribute.iPosition -= aLength; + } + } + iBuf->Delete(aPos * 2, aLength * 2); + } + +EXPORT_C TInt CTextBuffer::Length() const + { + return iBuf->Size() / 2; + } + +EXPORT_C const TDesC& CTextBuffer::Descriptor() const + { + TPtr8 narrowPtr(iBuf->Ptr(0)); + iPtr.Set((const TUint16 *)narrowPtr.Ptr(), narrowPtr.Length() / 2); + return iPtr; + } + +EXPORT_C const TDesC& CTextBuffer::Descriptor(TInt aPos, TInt aLength) const + { + TPtr8 narrowPtr(iBuf->Ptr(aPos * 2)); + iPtr.Set((const TUint16 *)narrowPtr.Ptr(), aLength); + return iPtr; + } + +EXPORT_C TPtrC8 CTextBuffer::Collapse() + { + TPtr8 narrowPtr(iBuf->Ptr(0)); + TPtr ptr((TUint16 *)narrowPtr.Ptr(), narrowPtr.Length() / 2, narrowPtr.Length() / 2); + return ptr.Collapse(); + } + +EXPORT_C TInt CTextBuffer::Write(RIoWriteHandle& aWriteHandle) const + { + return Write(aWriteHandle, 0, Length()); + } + +EXPORT_C void CTextBuffer::Write(RIoWriteHandle& aWriteHandle, TRequestStatus& aStatus) const + { + Write(aWriteHandle, 0, Length(), aStatus); + } + +EXPORT_C TInt CTextBuffer::Write(RIoWriteHandle& aWriteHandle, TInt aPosition, TInt aLength) const + { + const TInt numAttributes = iAttributes.Count(); + if ((aWriteHandle.AttachedToConsole() > 0) && (numAttributes > 0)) + { + // Write the text buffer to the console a block at a time, updating the console's attributes at the beginning of each block. + TInt blockIndex = 0; + TInt textPos = 0; + TInt writeCount = 0; + const TDesC* block = NULL; + do + { + TUint attributes; + ConsoleAttributes::TColor foregroundColor; + ConsoleAttributes::TColor backgroundColor; + NextBlock(blockIndex, block, attributes, foregroundColor, backgroundColor); + if (block != NULL) + { + if ((textPos + block->Length()) > aPosition) + { + RIoConsoleWriteHandle(aWriteHandle).SetAttributes(attributes, foregroundColor, backgroundColor); // Ignore error - may not be supported. + TInt blockOffset = aPosition - textPos; + if (blockOffset < 0) + { + blockOffset = 0; + } + TPtrC toWrite(block->Mid(blockOffset, Min(aLength - writeCount, block->Length() - blockOffset))); + TInt err = aWriteHandle.Write(toWrite); + if (err) + { + return err; + } + writeCount += toWrite.Length(); + if (writeCount == aLength) + { + break; + } + } + textPos += block->Length(); + } + } + while (block != NULL); + } + else + { + return aWriteHandle.Write(Descriptor().Mid(aPosition, aLength)); + } + + return KErrNone; + } + +EXPORT_C void CTextBuffer::Write(RIoWriteHandle& aWriteHandle, TInt aPosition, TInt aLength, TRequestStatus& aStatus) const + { + const TInt numAttributes = iAttributes.Count(); + if ((aWriteHandle.AttachedToConsole() > 0) && (numAttributes > 0)) + { + // Write the text buffer asynchronously to the console a block at a time, updating the console's attributes at the beginning of each block. + if (!IsAdded()) + { + CActiveScheduler::Add(const_cast(this)); + } + aStatus = KRequestPending; + iWriteStatus = &aStatus; + iAsyncBlockIndex = 0; + iAsyncWritePos = 0; + iAsyncWriteStartPos = aPosition; + iAsyncWriteLength = aLength; + iConsoleWriteHandle = aWriteHandle; + AsyncWriteNextBlock(); + } + else + { + aWriteHandle.Write(Descriptor().Mid(aPosition, aLength), aStatus); + } + } + +CTextBuffer::CTextBuffer() + : CActive(CActive::EPriorityStandard), iPtr(NULL, 0) + { + } + +void CTextBuffer::ConstructL(TInt aExpandSize) + { + iBuf = CBufFlat::NewL(aExpandSize); + iScratchBuf = HBufC::NewL(aExpandSize); + } + +void CTextBuffer::NextBlock(TInt& aBlockIndex, const TDesC*& aText, TUint& aAttributes, ConsoleAttributes::TColor& aForegroundColor, ConsoleAttributes::TColor& aBackgroundColor) const + { + if (iAttributes.Count() > 0) + { + if (aBlockIndex < iAttributes.Count()) + { + const TAttributes& thisAttribute = iAttributes[aBlockIndex]; + aAttributes = thisAttribute.iAttributes.iAttributes; + aForegroundColor = thisAttribute.iAttributes.iForegroundColor; + aBackgroundColor = thisAttribute.iAttributes.iBackgroundColor; + TInt length; + if (aBlockIndex < (iAttributes.Count() - 1)) + { + length = iAttributes[aBlockIndex + 1].iPosition - thisAttribute.iPosition; + } + else + { + length = Length() - thisAttribute.iPosition; + } + aText = &Descriptor(thisAttribute.iPosition, length); + ++aBlockIndex; + } + else + { + aText = NULL; + } + } + else + { + // There are no attributes, so block zero is the whole buffer. + ASSERT((aBlockIndex == 0) || (aBlockIndex == 1)); + if (aBlockIndex == 0) + { + aAttributes = ConsoleAttributes::ENone; + aForegroundColor = ConsoleAttributes::EUnchanged; + aBackgroundColor = ConsoleAttributes::EUnchanged; + aText = &Descriptor(0, Length()); + ++aBlockIndex; + } + else + { + aText = NULL; + } + } + } + +void CTextBuffer::AsyncWriteNextBlock() const + { + const TDesC* block = NULL; + TUint attributes; + ConsoleAttributes::TColor foregroundColor; + ConsoleAttributes::TColor backgroundColor; + NextBlock(iAsyncBlockIndex, block, attributes, foregroundColor, backgroundColor); + if (block != NULL) + { + if ((iAsyncWritePos + block->Length()) > iAsyncWriteStartPos) + { + iConsoleWriteHandle.SetAttributes(attributes, foregroundColor, backgroundColor); // Ignore error - may not be supported. + TInt blockOffset = iAsyncWriteStartPos - iAsyncWritePos; + if (blockOffset < 0) + { + blockOffset = 0; + } + iAsyncWritePtr.Set(block->Mid(blockOffset, Min(iAsyncWriteLength, block->Length() - blockOffset))); + iAsyncWriteLength -= iAsyncWritePtr.Length(); + iConsoleWriteHandle.Write(iAsyncWritePtr, const_cast(iStatus)); + const_cast(this)->SetActive(); + } + iAsyncWritePos += block->Length(); + } + else + { + User::RequestComplete(iWriteStatus, KErrNone); + } + } + +void CTextBuffer::RunL() + { + if (iStatus.Int() || (iAsyncWriteLength <= 0)) + { + User::RequestComplete(iWriteStatus, iStatus.Int()); + } + else + { + AsyncWriteNextBlock(); + } + } + +void CTextBuffer::DoCancel() + { + iConsoleWriteHandle.WriteCancel(); + } + +CTextBuffer::TAttributes::TAttributes(TInt aPosition, TUint aAttributes, ConsoleAttributes::TColor aForegroundColor, ConsoleAttributes::TColor aBackgroundColor) + : iPosition(aPosition), iAttributes(aAttributes, aForegroundColor, aBackgroundColor) + { + } + +TBool CTextBuffer::TAttributes::Matches(TUint aAttributes, ConsoleAttributes::TColor aForegroundColor, ConsoleAttributes::TColor aBackgroundColor) const + { + return ((aAttributes == iAttributes.iAttributes) && (aForegroundColor == iAttributes.iForegroundColor) && (aBackgroundColor == iAttributes.iBackgroundColor)); + } + + +// +// CTextFormatter. +// + +EXPORT_C CTextFormatter* CTextFormatter::NewL(TInt aAvailableWidth) + { + CTextFormatter* self = CTextFormatter::NewLC(aAvailableWidth); + CleanupStack::Pop(self); + return self; + } + +EXPORT_C CTextFormatter* CTextFormatter::NewLC(TInt aAvailableWidth) + { + CTextFormatter* self = new(ELeave) CTextFormatter(aAvailableWidth); + CleanupStack::PushL(self); + self->ConstructL(); + return self; + } + +EXPORT_C CTextFormatter* CTextFormatter::NewL(RIoConsoleWriteHandle& aConsoleWriteHandle) + { + CTextFormatter* self = CTextFormatter::NewLC(aConsoleWriteHandle); + CleanupStack::Pop(self); + return self; + } + +EXPORT_C CTextFormatter* CTextFormatter::NewLC(RIoConsoleWriteHandle& aConsoleWriteHandle) + { + TSize size; + if (aConsoleWriteHandle.AttachedToConsole()) + { + User::LeaveIfError(aConsoleWriteHandle.GetScreenSize(size)); + } + else + { + size.iWidth = 80; + } + CTextFormatter* self = CTextFormatter::NewLC(size.iWidth); + self->iWriteHandle = &aConsoleWriteHandle; + self->iAttributesSupported = (aConsoleWriteHandle.SetAttributes(0) == KErrNone); + return self; + } + +EXPORT_C CTextFormatter::~CTextFormatter() + { + } + +CTextFormatter::CTextFormatter(TInt aAvailableWidth) + : iAvailableWidth(aAvailableWidth - 1) + { + } + +void CTextFormatter::ConstructL() + { + CTextBuffer::ConstructL(0x100); + } + +EXPORT_C void CTextFormatter::WrapL(TInt aIndent, const TDesC& aText) + { + AppendSpacesL(aIndent); + WrapL(aIndent, aIndent, aText); + } + +EXPORT_C void CTextFormatter::WrapL(TInt aStartPosition, TInt aIndent, const TDesC& aText) + { + TUint originalAttributes = ConsoleAttributes::ENone; + ConsoleAttributes::TColor foregroundColor = ConsoleAttributes::EUnchanged; + ConsoleAttributes::TColor backgroundColor = ConsoleAttributes::EUnchanged; + if (iAttributesSupported) + { + GetCurrentAttributes(originalAttributes, foregroundColor, backgroundColor); + } + + CTextBuffer* buffer = CTextBuffer::NewLC(0x100); + DecodeInteriorPodSequencesL(aText, *buffer); + TLex lex(buffer->Descriptor()); + TInt linePos = aStartPosition; + while (!lex.Eos()) + { + lex.Mark(); + lex.SkipSpace(); + TPtrC whiteSpace(lex.MarkedToken()); + if (whiteSpace.Length() > 0) + { + TInt newLinePos; + do + { + newLinePos = whiteSpace.Find(KNewLine); + if (newLinePos >= 0) + { + AppendSpacesL(iAvailableWidth - linePos - 1); + AppendL(KNewLine); + AppendSpacesL(aIndent); + linePos = aIndent; + whiteSpace.Set(whiteSpace.Mid(newLinePos + KNewLine().Length())); + } + } + while (newLinePos >= 0); + + if (iAvailableWidth < whiteSpace.Length()) + { + AppendSpacesL(iAvailableWidth - linePos - 1); + AppendL(KNewLine); + AppendSpacesL(aIndent); + linePos = aIndent; + } + else + { + AppendL(whiteSpace); + linePos += whiteSpace.Length(); + } + } + + lex.Mark(); + lex.SkipCharacters(); + TPtrC word(lex.MarkedToken()); + const TInt wordLength = word.Length(); + if (wordLength > 0) + { + if ((linePos + wordLength + KNewLine().Length()) > iAvailableWidth) + { + AppendL(KNewLine); + AppendSpacesL(aIndent); + linePos = aIndent; + } + + if (iAttributesSupported) + { + for (TInt i = 0; i < wordLength; ++i) + { + TUint attributes; + buffer->GetAttributes(lex.Offset() - wordLength + i, attributes, foregroundColor, backgroundColor); + SetAttributesL(originalAttributes | attributes, foregroundColor, backgroundColor); + AppendL(word[i]); + } + } + else + { + AppendL(word); + } + linePos += wordLength; + } + } + + CleanupStack::PopAndDestroy(buffer); + SetAttributesL(originalAttributes, foregroundColor, backgroundColor); + } + +EXPORT_C void CTextFormatter::TabulateL(TInt aIndent, TInt aGap, const TDesC& aText) + { + TabulateL(aIndent, aGap, aText, EWrapLastColumn); + } + +EXPORT_C void CTextFormatter::TabulateL(TInt aIndent, TInt aGap, const TDesC& aText, TTabMode aMode) + { + CTextBuffer* scratchBuffer = NULL; + RArray columnWidths; + CleanupClosePushL(columnWidths); + TLex lineLexer(aText); + + // Find column widths. + TPtrC line; + while (NextLine(lineLexer, line)) + { + TLex columnLexer(line); + TInt col = 0; + TPtrC column; + while (NextColumn(columnLexer, column)) + { + if (col >= columnWidths.Count()) + { + User::LeaveIfError(columnWidths.Append(aGap)); + } + TInt thisColumnWidth = ActualLength(column); + if (columnWidths[col] < (thisColumnWidth + aGap)) + { + columnWidths[col] = (thisColumnWidth + aGap); + } + ++col; + } + } + + const TInt numCols = columnWidths.Count(); + + if (numCols == 0) + { + // No columns found, so just copy aText directly. + AppendL(aText); + CleanupStack::PopAndDestroy(&columnWidths); + return; + } + + if (aMode == EWrapLastColumn) + { + // Find the widest of all but the last column. + TInt sum = aIndent; + for (TInt i = 0; i < (numCols - 1); ++i) + { + sum += columnWidths[i]; + } + TInt remainingSpace = iAvailableWidth - sum; + if (remainingSpace < 0) + { + // If you hit this, it means that the text cannot be columnised because the columns preceding the last one take up more space than the overall. + User::Leave(KErrTooBig); // Let's go for something marginally more descriptive than KErrGeneral + } + // Assign the remaining space to the last column (which will be wrapped). + columnWidths[numCols - 1] = remainingSpace; + } + else if (aMode == ETruncateLongestColumn) + { + // Find the total width of all the columns. + TInt sum = aIndent; + for (TInt i = 0; i < numCols; ++i) + { + sum += columnWidths[i]; + } + TInt excess = iAvailableWidth - sum; + if (excess < 0) + { + // Not enough space, so steal from the widest column until there is. + while (excess < 0) + { + TInt widestColumn = -1; + TInt widestColumnWidth = 0; + for (TInt i = 0; i < numCols; ++i) + { + if (columnWidths[i] > widestColumnWidth) + { + widestColumnWidth = columnWidths[i]; + widestColumn = i; + } + } + if (widestColumn < 0) + { + User::Leave(KErrGeneral); + } + --columnWidths[widestColumn]; + ++excess; + } + } + else + { + // Assign execess to the last column. + columnWidths[numCols - 1] += excess; + } + } + else if (aMode == EIgnoreAvailableWidth) + { + // just use the column widths we've already worked out, no matter if they're too wide. + } + else + { + ASSERT(EFalse); + } + + // Write formatted text to the buffer. + lineLexer = TLex(aText); + TInt linePos = 0; + while (NextLine(lineLexer, line)) + { + AppendSpacesL(aIndent); + linePos += aIndent; + TLex columnLexer(line); + TInt col = 0; + TPtrC column; + while (NextColumn(columnLexer, column)) + { + TBool isLastColumn(col == (numCols - 1)); + const TInt columnWidth = columnWidths[col]; + if ((aMode == ETruncateLongestColumn) || !isLastColumn) + { + if (scratchBuffer == NULL) + { + scratchBuffer = CTextBuffer::NewLC(0x100); + } + else + { + scratchBuffer->Zero(); + } + DecodeInteriorPodSequencesL(column, *scratchBuffer); + TInt availableWidth = columnWidth; + if (!isLastColumn) + { + availableWidth -= aGap; + } + if (scratchBuffer->Descriptor().Length() > availableWidth) + { + scratchBuffer->Delete(availableWidth, scratchBuffer->Descriptor().Length() - availableWidth); + } + AppendL(*scratchBuffer); + AppendSpacesL(columnWidth - scratchBuffer->Descriptor().Length()); + linePos += columnWidth; + } + else if (aMode == EIgnoreAvailableWidth) + { + AppendL(column); + if (!isLastColumn) + { + AppendSpacesL(columnWidth - column.Length()); + } + linePos += columnWidth; + } + else + { + WrapL(linePos, linePos, column); + } + ++col; + } + AppendL(KNewLine); + linePos = 0; + } + + if (scratchBuffer) + { + CleanupStack::PopAndDestroy(scratchBuffer); + } + CleanupStack::PopAndDestroy(&columnWidths); + } + +EXPORT_C void CTextFormatter::ColumnizeL(TInt aIndent, TInt aGap, const TDesC& aText) + { + // Store the tab delimited text as an array of TPtrCs. + RArray items; + CleanupClosePushL(items); + TLex lex(aText); + TPtrC column; + while (NextColumn(lex, column)) + { + User::LeaveIfError(items.Append(column)); + } + ColumnizeL(aIndent, aGap, items.Array()); + CleanupStack::PopAndDestroy(1, &items); + } + +EXPORT_C void CTextFormatter::ColumnizeL(TInt aIndent, TInt aGap, const TArray& aItems) + { + // Lays out tab delimited data as a set of columns that read like a news paper + // (i.e. starting with the left most column, you read down to the bottom of it + // and then start reading the next column from the top, etc.). + + // Determine the smallest number of rows that allows all the data to fit + // the available horizontal space. The algorithm works by iteratively increasing + // the number of rows from one until a fit is found. + const TInt numItems = aItems.Count(); + RArray columnWidths; + CleanupClosePushL(columnWidths); + TInt numRows = 0; + TBool fits(EFalse); + while (!fits) + { + ++numRows; + columnWidths.Reset(); + TInt requiredWidth = aIndent; + TInt col = 0; + TInt i = 0; + while (i < numItems) + { + for (TInt row = 0; row < numRows; ++row) + { + if (columnWidths.Count() <= col) + { + User::LeaveIfError(columnWidths.Append(0)); + } + TInt thisItemWidth = ActualLength(aItems[i]); + if (thisItemWidth > columnWidths[col]) + { + columnWidths[col] = thisItemWidth; + } + if (++i >= numItems) + { + break; + } + } + requiredWidth += columnWidths[col]; + if ((requiredWidth + aGap) > iAvailableWidth) + { + if ((requiredWidth + KNewLine().Length()) > iAvailableWidth) + { + if (col > 0) + { + // Not enough space with this number of rows, so try another row. + i = 0; + break; + } + else + { + // Not enough space even with just one column. Lay out as a single column anyway and accept ugly wrapping by the console. + CleanupStack::PopAndDestroy(1, &columnWidths); + for (TInt i = 0; i < numItems; ++i) + { + AppendSpacesL(aIndent); + AppendL(aItems[i]); + AppendL(KNewLine); + } + return; + } + } + else + { + requiredWidth += KNewLine().Length(); + } + } + else + { + requiredWidth += aGap; + } + ++col; + } + if (i >= numItems) + { + fits = ETrue; + } + } + + // Layout the columns. + const TInt numCols = columnWidths.Count(); + for (TInt row = 0; row < numRows; ++row) + { + AppendSpacesL(aIndent); + for (TInt col = 0; col < numCols; ++col) + { + TInt index = (numRows * col) + row; + if (index < numItems) + { + TPtrC item(aItems[index]); + DoAppendPodL(item); + TInt gap = (col == (numCols - 1)) ? 0 : aGap; + AppendSpacesL((columnWidths[col] + gap) - ActualLength(item)); + } + } + AppendL(KNewLine); + } + + CleanupStack::PopAndDestroy(1, &columnWidths); + } + +EXPORT_C void CTextFormatter::AppendPodL(const TDesC& aPod) + { + TPodFormatter podFormatter(*this); + podFormatter.FormatL(aPod); + } + +EXPORT_C TInt CTextFormatter::Write() + { + return CTextBuffer::Write(*iWriteHandle); + } + +void CTextFormatter::DoAppendPodL(const TDesC& aPod) + { + DecodeInteriorPodSequencesL(aPod, *this); + } + +void CTextFormatter::DecodeInteriorPodSequencesL(const TDesC& aPod, CTextBuffer& aBuffer) const + { + TPodLexer lexer(aPod); + TBool eop(EFalse); + TBool eos(EFalse); + + TUint originalAttributes = ConsoleAttributes::ENone; + ConsoleAttributes::TColor foregroundColor = ConsoleAttributes::EUnchanged; + ConsoleAttributes::TColor backgroundColor = ConsoleAttributes::EUnchanged; + if (iAttributesSupported) + { + aBuffer.GetCurrentAttributes(originalAttributes, foregroundColor, backgroundColor); + } + TUint currentAttributes = originalAttributes; + + while (!eos) + { + TPtrC token; + TPodLexer::TTokenType tokenType; + TUint attributes; + lexer.NextTokenL(token, tokenType, attributes, eop, eos); + + switch (tokenType) + { + case TPodLexer::EAttributePush: + case TPodLexer::EAttributePop: + if (!iAttributesSupported && (attributes & TPodLexer::EBold)) + { + aBuffer.AppendL('*'); + } + else if (!iAttributesSupported && (attributes & TPodLexer::EItalic)) + { + aBuffer.AppendL('\''); + } + else if ((!iAttributesSupported && (attributes & TPodLexer::EFileName)) || (attributes & TPodLexer::TPodLexer::ECode)) + { + aBuffer.AppendL('"'); + } + break; + case TPodLexer::ELink: + { + TInt pos = token.Locate('|'); + if (pos >= 0) + { + token.Set(token.Left(pos)); + } + } + // Deliberate fall through. + case TPodLexer::ETextBlock: + case TPodLexer::ECodeBlock: + case TPodLexer::EIndexEntry: + if (iAttributesSupported) + { + if ((attributes == 0) || (attributes & (TPodLexer::ENull | TPodLexer::ECode))) + { + currentAttributes = originalAttributes; + } + if (attributes & (TPodLexer::EBold | TPodLexer::EItalic | TPodLexer::EFileName)) + { + currentAttributes |= ConsoleAttributes::EBold; + } + aBuffer.SetAttributesL(currentAttributes); + } + aBuffer.AppendL(token); + break; + default: + ASSERT(EFalse); + } + } + + if (iAttributesSupported && (currentAttributes != originalAttributes)) + { + aBuffer.SetAttributesL(originalAttributes); + } + } + +EXPORT_C void CTextFormatter::Zero() + { + CTextBuffer::Zero(); + } + +EXPORT_C void CTextFormatter::Reset() + { + CTextBuffer::Reset(); + } + +EXPORT_C void CTextFormatter::ResetText() + { + CTextBuffer::ResetText(); + } + +TBool CTextFormatter::NextColumn(TLex& aLex, TPtrC& aPtr) const + { + aLex.Mark(); + while (!aLex.Eos()) + { + if (aLex.Get() == '\t') + { + aLex.UnGet(); + aPtr.Set(aLex.MarkedToken()); + aLex.Get(); + return ETrue; + } + } + if (aLex.TokenLength() > 0) + { + aPtr.Set(aLex.MarkedToken()); + return ETrue; + } + return EFalse; + } + +TBool CTextFormatter::NextLine(TLex& aLex, TPtrC& aPtr) const + { + aLex.Mark(); + while (!aLex.Eos()) + { + TChar ch = aLex.Get(); + if (ch == '\r') + { + if (aLex.Get() == '\n') + { + aLex.UnGet(); + aLex.UnGet(); + aPtr.Set(aLex.MarkedToken()); + aLex.Get(); + aLex.Get(); + return ETrue; + } + else + { + aLex.UnGet(); + } + } + } + if (aLex.TokenLength() > 0) + { + aPtr.Set(aLex.MarkedToken()); + return ETrue; + } + return EFalse; + } + +TInt CTextFormatter::ActualLength(const TDesC& aPod) const + { + CTextBuffer* buffer = CTextBuffer::NewLC(0x100); + DecodeInteriorPodSequencesL(aPod, *buffer); + TInt length = buffer->Descriptor().Length(); + CleanupStack::PopAndDestroy(buffer); + return length; + }