textrendering/texthandling/sfields/FLDSTRM.CPP
changeset 0 1fb32624e06b
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/textrendering/texthandling/sfields/FLDSTRM.CPP	Tue Feb 02 02:02:46 2010 +0200
@@ -0,0 +1,481 @@
+/*
+* Copyright (c) 1997-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 <e32std.h>
+#include <e32base.h>
+
+#include <s32strm.h>
+#include <s32stor.h>
+
+#include "FLDDEF.H"
+#include "FLDSET.H"
+#include "FLDARRAY.H"
+
+#include "FLDSTD.H"
+
+
+
+EXPORT_C TFieldMapExternalizer::TFieldMapExternalizer(const CStoreMap& aMap)
+	: iMap(&aMap)
+	{}
+
+EXPORT_C void TFieldMapExternalizer::ExternalizeL(const TStreamRef& aRef,RWriteStream& aStream) const
+// Write the stream id bound to aRef to aStream. If not bound, write KNullStreamId
+//
+	{
+	TSwizzleC<TAny> swizzle=aRef;
+	aStream<<iMap->At(swizzle);
+	}
+
+
+EXPORT_C TStreamId CTextFieldSet::StoreL(CStreamStore& aStore)const
+// Save the fields and the fieldSet in their own streams
+// Encapsulates the storing of its components.
+//
+	{
+	CStoreMap* map=CStoreMap::NewLC(aStore);
+	StoreFieldsL(aStore,*map);  // binds id's to swizzles
+//
+	// create custom externalizer over the map
+	TFieldMapExternalizer fMap(*map);
+	RStoreWriteStream stream(fMap);
+	TStreamId id=stream.CreateLC(aStore);
+	stream<< *this;
+	stream.CommitL();
+	CleanupStack::PopAndDestroy();  // stream
+//
+	map->Reset();
+	CleanupStack::PopAndDestroy();  // map
+	return id;
+	}
+
+
+EXPORT_C void CTextFieldSet::StoreFieldsL(CStreamStore& aStore,CStoreMap& aMap)const
+// Stores all fields in the set
+//
+	{StoreFieldsL(aStore,aMap,iFieldArray);}
+
+
+void CTextFieldSet::StoreFieldsL(CStreamStore& aStore,CStoreMap& aMap,CArrayFixSeg<TTextFieldEntry>* aArray)const
+// Stores all fields contained in the set provided
+//
+	{
+	__TEST_INVARIANT;
+
+	for (TInt i=0 ; i<(aArray->Count()-1) ; i++)
+		{
+		TStreamId id=(*aArray)[i].iFieldHeader.iField->StoreL(aStore);
+		if (id!=KNullStreamId)
+			aMap.BindL((*aArray)[i].iFieldHeader.iField,id);
+		}
+	}
+
+
+EXPORT_C void CTextFieldSet::ExternalizeL(RWriteStream& aStream)const
+	{
+	__TEST_INVARIANT;
+
+	ExternalizeL(aStream,iFieldArray);
+	}
+
+
+void CTextFieldSet::ExternalizeL(RWriteStream& aStream,CArrayFixSeg<TTextFieldEntry>* aArray)const
+	{
+	TInt numFieldEntries = aArray->Count();
+	aStream.WriteInt32L(numFieldEntries);
+	// write out fields
+	for (TInt i=0 ; i<numFieldEntries-1 ; i++)
+		aStream<< (*aArray)[i];
+	// write out last entry in array: the bit after the last field
+	aStream.WriteInt32L((*aArray)[numFieldEntries-1].iPreFieldLen);
+	}
+
+
+EXPORT_C void CTextFieldSet::RestoreL(const CStreamStore& aFieldStore,TStreamId aStreamId)
+	{
+	// reset the array and stream into it
+	Reset();
+	DoRestoreL(aFieldStore,aStreamId);
+	}
+
+
+EXPORT_C void CTextFieldSet::RestoreFieldsL(const CStreamStore& aFieldStore)
+	{
+	DoRestoreFieldsL(iFieldArray,aFieldStore); // restore the fields individually from their own streams
+	}
+
+
+void CTextFieldSet::DoRestoreL(const CStreamStore& aFieldStore,TStreamId aStreamId)
+// Restores a field set and its associated fields from the store provded.
+// 
+	{
+	__ASSERT_ALWAYS(iFieldArray->Count()==1,Panic(EArrayNotEmptyOnRestore)); // array must be empty
+	__ASSERT_ALWAYS((*iFieldArray)[0].iPreFieldLen==0,Panic(EArrayNotEmptyOnRestore));
+
+	// retrieve the headstream from the store
+	RStoreReadStream stream;
+	stream.OpenLC(aFieldStore,aStreamId);
+	// restore the set, then the individual fields
+	stream>> *this;  // internalize the field set (the headers)
+	CleanupStack::PopAndDestroy(); // stream
+	DoRestoreFieldsL(iFieldArray,aFieldStore); // restore the fields individually from their own streams
+	}
+
+
+EXPORT_C void CTextFieldSet::InternalizeL(RReadStream& aStream)
+	{
+	InternalizeL(iFieldArray,aStream);
+
+	__TEST_INVARIANT;
+	}
+
+
+void CTextFieldSet::InternalizeL(CArrayFixSeg<TTextFieldEntry>* aArray,RReadStream& aStream)
+	{// assume the array is empty
+	TInt numFieldEntries = aStream.ReadInt32L();
+	// read in the fields
+	TTextFieldEntry entry;
+	for (TInt i=0 ; i<numFieldEntries-1 ; i++)
+		{
+		aStream>> entry;
+		InsertEntryL(i,entry,aArray); // insert new entry
+		}
+	// read in the last entry: the bit after the last field. This will not contain a field
+	(*aArray)[numFieldEntries-1].iPreFieldLen = aStream.ReadInt32L();
+	}
+
+
+void CTextFieldSet::DoRestoreFieldsL(CArrayFixSeg<TTextFieldEntry>* aArray,const CStreamStore& aFieldStore,TInt aStartIndex)
+// This fn is called after all FieldHeaders have been internalized - the Swizzles hold the stream id's.
+// One by one the fields are created (with the factory) and then have their settings streamed in from the store.
+// If no factory exists all fields are converted to text.
+//
+	{
+	TInt ii=aArray->Count()-2;  // -2 because we skip the last (empty) entry
+	while (ii>=aStartIndex)
+		{
+		if ((*aArray)[ii].iFieldHeader.iField.IsId()) 
+			{// restore the field only if it isn't the very last (dummy) entry
+			if (iFieldFactory==NULL)
+				// no factory - convert the field to text
+				DeleteFieldEntry(aArray,ii);
+			else
+				{
+				TStreamId id = (*aArray)[ii].iFieldHeader.iField.AsId();
+				(*aArray)[ii].iFieldHeader.iField = iFieldFactory->NewFieldL((*aArray)[ii].iFieldHeader.iFieldType);
+				if ((*aArray)[ii].iFieldHeader.iField!=NULL)
+					(*aArray)[ii].iFieldHeader.iField->RestoreL(aFieldStore,id);
+				else
+					DeleteFieldEntry(aArray,ii); // handle "field type not recognised" by converting field to text
+				}
+			}
+		ii--;
+		}
+	}
+
+
+/***************************************** cut & paste *******************************************/
+
+
+EXPORT_C TStreamId CTextFieldSet::CopyToStoreL(CStreamStore& aStore,TInt aPos,TInt aLength)const
+// Copy any fields in the selected region to the specified store, returning the id of the head-stream.
+//
+	{
+	__TEST_INVARIANT;
+	__ASSERT_ALWAYS(aPos>=0,Panic(EPosOutOfRange));
+	__ASSERT_ALWAYS(aLength>=0,Panic(ENegativeRange));
+	__ASSERT_DEBUG((aPos+aLength)<=CharCount(),Panic(EPosOutOfRange));
+
+	// Create a store map and store the fields themselves
+	CStoreMap* map=CStoreMap::NewLC(aStore);
+	CopyComponentsL(aStore,*map,aPos,aLength);
+
+	// Create a head-stream in which to store the field entries and do so
+	RStoreWriteStream stream(*map);
+	TStreamId id=stream.CreateLC(aStore);
+	CopyToStreamL(stream,aPos,aLength);
+
+	// tidy up
+	stream.CommitL();
+	map->Reset();
+	CleanupStack::PopAndDestroy(2); // map, stream
+	return id;
+	}
+
+
+EXPORT_C void CTextFieldSet::CopyComponentsL(CStreamStore& aStore,CStoreMap& aMap,TInt aPos,TInt aLength)const
+// Stores all fields in the set
+//
+	{
+	__TEST_INVARIANT;
+	__ASSERT_ALWAYS(aPos>=0,Panic(EPosOutOfRange));
+	__ASSERT_ALWAYS(aLength>=0,Panic(ENegativeRange));
+	__ASSERT_DEBUG((aPos+aLength)<=CharCount(),Panic(EPosOutOfRange));
+
+	// Create an array of the fields to be cut/copied
+	CArrayFixSeg<TTextFieldEntry>* tempArray = new(ELeave) CArrayFixSeg<TTextFieldEntry>(KFieldArrayGranularity);
+	CleanupStack::PushL(tempArray);
+	CopyToArrayL(tempArray,aPos,aLength);
+
+	// stream the required fields in their own streams
+	StoreFieldsL(aStore,aMap,tempArray);
+	CleanupStack::PopAndDestroy(); // tempArray
+	}
+
+
+EXPORT_C void CTextFieldSet::CopyToStreamL(RWriteStream& aStream,TInt aPos,TInt aLength)const
+// Stores all fields in the set
+//
+	{
+	__TEST_INVARIANT;
+	__ASSERT_ALWAYS(aPos>=0,Panic(EPosOutOfRange));
+	__ASSERT_ALWAYS(aLength>=0,Panic(ENegativeRange));
+	__ASSERT_DEBUG((aPos+aLength)<=CharCount(),Panic(EPosOutOfRange));
+
+	// Create an array of the fields to be cut/copied
+	CArrayFixSeg<TTextFieldEntry>* tempArray = new(ELeave) CArrayFixSeg<TTextFieldEntry>(KFieldArrayGranularity);
+	CleanupStack::PushL(tempArray);
+	CopyToArrayL(tempArray,aPos,aLength);
+
+	// stream the field entries in the temp array
+	ExternalizeL(aStream,tempArray);
+	CleanupStack::PopAndDestroy(); // tempArray
+	}
+
+
+void CTextFieldSet::CopyToArrayL(CArrayFixSeg<TTextFieldEntry>* aArray,TInt aPos,TInt aLength)const
+	{
+	TInt index; TInt offset;
+	if (InField(aPos,index,offset))
+		offset += (*iFieldArray)[index].iPreFieldLen; // make offset relative to start of entry
+	// split first entry in range
+	TTextFieldEntry entry = SplitEntry(index,offset,aLength);
+	index++;
+	TInt charsCopied=EntryLen(entry);
+	// split second if neccessary
+	if ((!entry.iFieldHeader.iField)&&(charsCopied<aLength))
+		{
+		TInt preFieldLen = entry.iPreFieldLen;
+		entry = SplitEntry(index,0,aLength-preFieldLen);
+		entry.iPreFieldLen += preFieldLen;
+		charsCopied = EntryLen(entry);
+		index++;
+		}
+	((CTextFieldSet*)this)->AppendEntryL(entry,aArray); // append the first entry to the storage array
+	// write out all whole entries
+	while (charsCopied<aLength)
+		{
+		if ((charsCopied+EntryLen(index))<=aLength)
+			((CTextFieldSet*)this)->AppendEntryL((*iFieldArray)[index],aArray); 
+		charsCopied += EntryLen(index);
+		index++;
+		}
+	// split last entry if neccessary
+	if (charsCopied>aLength)
+		{// The last entry needs to be split
+		// first get back to the beginning of the entry
+		index--;
+		charsCopied -= EntryLen(index);
+		entry = SplitEntry(index,0,aLength-charsCopied); // split up the last entry as required
+		((CTextFieldSet*)this)->AppendEntryL(entry,aArray); // append the last entry to the storage array
+		}
+	// add an empty last entry if neccessary
+	TInt numFieldEntries = aArray->Count();
+	if (((*aArray)[numFieldEntries-1].iFieldHeader.iField) || ((*aArray)[numFieldEntries-1].iFieldValueLen!=0))
+		{
+		entry.iPreFieldLen = 0;
+		entry.iFieldValueLen = 0;
+		entry.iFieldHeader.iFieldType = KNullUid;
+		entry.iFieldHeader.iField = NULL;
+		((CTextFieldSet*)this)->AppendEntryL(entry,aArray);
+		numFieldEntries++;
+		}
+	}
+
+
+EXPORT_C void CTextFieldSet::PasteFromStoreL(const CStreamStore& aFieldStore,TStreamId aStreamId,TInt aPos,TInt aMaxLen)
+// Paste from aStore into the document at insert position aPos.
+// Optionally the pasted text can be clipped to a maximum length aMaxLen.
+//
+	{
+	__ASSERT_ALWAYS(aPos>=0,Panic(EPosOutOfRange));
+	__ASSERT_DEBUG(aPos<=CharCount(),Panic(EPosOutOfRange));
+
+	// retrieve the headstream from the store
+	RStoreReadStream stream;
+	stream.OpenLC(aFieldStore,aStreamId);
+
+	// restore the set...
+	PasteFromStreamL(stream,aPos,aMaxLen); // internalize the field set (the headers)
+	CleanupStack::PopAndDestroy(); // stream
+
+	// ...then the individual fields
+	PasteComponentsL(aFieldStore,aPos); // restore the fields individually from their own streams
+	}
+
+
+EXPORT_C void CTextFieldSet::PasteFromStreamL(RReadStream& aStream,TInt aPos,TInt aMaxLen)
+// streams the field entries into a temporary array, which is returned. 
+// PasteComponents() must be called after this to actually carry out the paste...
+	{
+	// create a temporary array to stream in to, inserting the first entry
+	CArrayFixSeg<TTextFieldEntry>* tempFieldArray = new(ELeave) CArrayFixSeg<TTextFieldEntry>(KFieldArrayGranularity);
+	CleanupStack::PushL(tempFieldArray);
+	AddInitialFieldEntryL(tempFieldArray,0);
+
+	// internalize the field entries
+	InternalizeL(tempFieldArray,aStream);
+
+	// trim off any entries that lie beyond aMaxLength
+	if (aMaxLen!=ENoPasteLimit)
+		{// if aMaxLen is not ENoPasteLimit discard the excess fields
+		__ASSERT_ALWAYS(aMaxLen>=0,Panic(ELengthOutOfRange));
+		//
+		TInt length=0;
+		TInt i=0;
+		for (i=0 ; (length<aMaxLen)&&(i<tempFieldArray->Count()) ; i++)
+			length += EntryLen((*tempFieldArray)[i]);
+		if (aMaxLen==0)
+			{// make first entry zero len, delete all others
+			i++;
+			(*tempFieldArray)[i-1].iPreFieldLen = 0;
+			}
+		else if (length>aMaxLen)
+			// truncate the last field in range
+			(*tempFieldArray)[i-1].iPreFieldLen += (*tempFieldArray)[i-1].iFieldValueLen-(length-aMaxLen);
+		else if ((length==aMaxLen) && ((*tempFieldArray)[i-1].iFieldHeader.iField!=NULL))
+			{// if the terminating entry has a field add a zero length entry, the mandatory last entry
+			i++;
+			(*tempFieldArray)[i-1].iPreFieldLen = 0;
+			}
+		// ensure the last entry is of the correct format
+		(*tempFieldArray)[i-1].iFieldValueLen = 0;
+		(*tempFieldArray)[i-1].iFieldHeader.iFieldType = KNullUid;
+		(*tempFieldArray)[i-1].iFieldHeader.iField = NULL;
+		// delete all the fields wholely out of range
+		for (TInt index=i ; index<tempFieldArray->Count() ; index++)
+			(*tempFieldArray)[index].iFieldHeader.iField = NULL;
+		tempFieldArray->Delete(i,tempFieldArray->Count()-i); // pos,count
+		}
+
+	DoPasteL(tempFieldArray,aPos);
+	CleanupStack::PopAndDestroy(); // tempFieldArray
+	}
+
+
+EXPORT_C void CTextFieldSet::PasteComponentsL(const CStreamStore& aFieldStore,TInt aPos)
+	{
+	// Restore the fields individually from their own streams
+	TInt index; TInt offset;
+	// We don't need to make any difference between in and not in field situation here
+	// all we need is the index
+	TBool isInField = InField(aPos,index,offset);
+	DoRestoreFieldsL(iFieldArray,aFieldStore,index); 
+	}
+
+
+void CTextFieldSet::DoPasteL(CArrayFixSeg<TTextFieldEntry>* aSourceArray,TInt aPos)
+// Insert into this instance, at character position aPos, the entire (field entry) contents of the field array aSourceArray.
+// All iField objects in aSourceArray are ID's at this time.
+//
+	{
+	// are we inserting into a field?
+	TInt numFieldEntries = aSourceArray->Count();
+	TInt index; TInt offset;
+	
+	TBool inField = InField(aPos,index,offset);
+	// record the rollback info
+	RecordRollbackInfoL(index);
+	if ((inField)&&(offset!=0))
+		{// everything we insert will become text - no chance of leaving
+		// insert all but last entry
+		TInt i=0;
+		for (; i<numFieldEntries-1 ; i++)
+			{// copy text (no need to delete field)
+			(*iFieldArray)[index].iFieldValueLen += EntryLen((*aSourceArray)[i]);
+			}
+		// read in the last entry (has no field attached)
+		(*iFieldArray)[index].iFieldValueLen += (*aSourceArray)[i].iPreFieldLen;
+		}
+	else
+		{// else split the entry we are going to bisect - this may leave
+		if (inField)
+			offset = (*iFieldArray)[index].iPreFieldLen; // must be at start of field
+		if (numFieldEntries>1)
+			{// read 1st field & carry out split.
+			InsertEntryL(index,(*aSourceArray)[0]); // if this leaves the model will be intact
+			(*iFieldArray)[index].iPreFieldLen += offset;
+			(*iFieldArray)[index+1].iPreFieldLen -= offset;
+			index++;
+			}
+		// insert all other fields except last.
+		for (TInt i=1 ; i<numFieldEntries-1 ; i++)
+			{
+			TRAPD(ret,\
+				InsertEntryL(index,(*aSourceArray)[i]));
+			if (ret!=KErrNone)
+				{// do rollback, then propagate leave
+				RollbackPaste();
+				User::Leave(ret);
+				}
+			index++;
+			}
+		// join last field up to successor
+		(*iFieldArray)[index].iPreFieldLen += (*aSourceArray)[numFieldEntries-1].iPreFieldLen;
+		}
+
+	__TEST_INVARIANT;
+	}
+
+
+void CTextFieldSet::RecordRollbackInfoL(TInt aIndex)
+	{
+	delete iRollbackInfo;
+	iRollbackInfo = new(ELeave) TRollbackInfo();
+	iRollbackInfo->iEntryNum = aIndex;
+	iRollbackInfo->iPreFieldLen = (*iFieldArray)[aIndex].iPreFieldLen;
+	iRollbackInfo->iFieldValueLen = (*iFieldArray)[aIndex].iFieldValueLen;
+	iRollbackInfo->iTotalEntries = iFieldArray->Count();
+	}
+
+
+EXPORT_C void CTextFieldSet::RollbackPaste()
+// Carries out rollback from a paste function
+// This will only have an effect after a PasteFromStream() has been called
+// nb it would be distasterous if this were called at random some time after a paste!
+//
+	{
+	if (!iRollbackInfo)
+		return; // nothing to do
+	// remove added entries from array
+	TInt entriesToRemove=iFieldArray->Count()-iRollbackInfo->iTotalEntries;
+	TInt i=0;
+	for (i=iRollbackInfo->iEntryNum ; i<iRollbackInfo->iEntryNum+entriesToRemove ; i++)
+		{
+		if ((*iFieldArray)[i].iFieldHeader.iField.IsPtr())
+			delete (*iFieldArray)[i].iFieldHeader.iField.AsPtr(); // Delete the textField object
+		iFieldArray->Delete(i);
+		}
+	// now right num entries, but wrong length - use backup info to correct length
+	(*iFieldArray)[i].iPreFieldLen = iRollbackInfo->iPreFieldLen;
+	(*iFieldArray)[i].iFieldValueLen = iRollbackInfo->iFieldValueLen;
+
+	__ASSERT_DEBUG(iFieldArray->Count()==iRollbackInfo->iTotalEntries,Panic(EDebug));
+	delete iRollbackInfo;
+	}
+