--- /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 <e32base.h>
+#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<const TDesC> 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<const TDesC8> 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<CTextBuffer*>(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<TRequestStatus&>(iStatus));
+ const_cast<CTextBuffer*>(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<TInt> 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<TPtrC> 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<TPtrC>& 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<TInt> 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;
+ }