|
1 /* |
|
2 * Copyright (c) 1997-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: |
|
15 * |
|
16 */ |
|
17 |
|
18 |
|
19 #include <e32std.h> |
|
20 #include <e32base.h> |
|
21 |
|
22 #include <s32strm.h> |
|
23 #include <s32stor.h> |
|
24 |
|
25 #include "FLDDEF.H" |
|
26 #include "FLDSET.H" |
|
27 #include "FLDARRAY.H" |
|
28 |
|
29 #include "FLDSTD.H" |
|
30 |
|
31 |
|
32 |
|
33 EXPORT_C TFieldMapExternalizer::TFieldMapExternalizer(const CStoreMap& aMap) |
|
34 : iMap(&aMap) |
|
35 {} |
|
36 |
|
37 EXPORT_C void TFieldMapExternalizer::ExternalizeL(const TStreamRef& aRef,RWriteStream& aStream) const |
|
38 // Write the stream id bound to aRef to aStream. If not bound, write KNullStreamId |
|
39 // |
|
40 { |
|
41 TSwizzleC<TAny> swizzle=aRef; |
|
42 aStream<<iMap->At(swizzle); |
|
43 } |
|
44 |
|
45 |
|
46 EXPORT_C TStreamId CTextFieldSet::StoreL(CStreamStore& aStore)const |
|
47 // Save the fields and the fieldSet in their own streams |
|
48 // Encapsulates the storing of its components. |
|
49 // |
|
50 { |
|
51 CStoreMap* map=CStoreMap::NewLC(aStore); |
|
52 StoreFieldsL(aStore,*map); // binds id's to swizzles |
|
53 // |
|
54 // create custom externalizer over the map |
|
55 TFieldMapExternalizer fMap(*map); |
|
56 RStoreWriteStream stream(fMap); |
|
57 TStreamId id=stream.CreateLC(aStore); |
|
58 stream<< *this; |
|
59 stream.CommitL(); |
|
60 CleanupStack::PopAndDestroy(); // stream |
|
61 // |
|
62 map->Reset(); |
|
63 CleanupStack::PopAndDestroy(); // map |
|
64 return id; |
|
65 } |
|
66 |
|
67 |
|
68 EXPORT_C void CTextFieldSet::StoreFieldsL(CStreamStore& aStore,CStoreMap& aMap)const |
|
69 // Stores all fields in the set |
|
70 // |
|
71 {StoreFieldsL(aStore,aMap,iFieldArray);} |
|
72 |
|
73 |
|
74 void CTextFieldSet::StoreFieldsL(CStreamStore& aStore,CStoreMap& aMap,CArrayFixSeg<TTextFieldEntry>* aArray)const |
|
75 // Stores all fields contained in the set provided |
|
76 // |
|
77 { |
|
78 __TEST_INVARIANT; |
|
79 |
|
80 for (TInt i=0 ; i<(aArray->Count()-1) ; i++) |
|
81 { |
|
82 TStreamId id=(*aArray)[i].iFieldHeader.iField->StoreL(aStore); |
|
83 if (id!=KNullStreamId) |
|
84 aMap.BindL((*aArray)[i].iFieldHeader.iField,id); |
|
85 } |
|
86 } |
|
87 |
|
88 |
|
89 EXPORT_C void CTextFieldSet::ExternalizeL(RWriteStream& aStream)const |
|
90 { |
|
91 __TEST_INVARIANT; |
|
92 |
|
93 ExternalizeL(aStream,iFieldArray); |
|
94 } |
|
95 |
|
96 |
|
97 void CTextFieldSet::ExternalizeL(RWriteStream& aStream,CArrayFixSeg<TTextFieldEntry>* aArray)const |
|
98 { |
|
99 TInt numFieldEntries = aArray->Count(); |
|
100 aStream.WriteInt32L(numFieldEntries); |
|
101 // write out fields |
|
102 for (TInt i=0 ; i<numFieldEntries-1 ; i++) |
|
103 aStream<< (*aArray)[i]; |
|
104 // write out last entry in array: the bit after the last field |
|
105 aStream.WriteInt32L((*aArray)[numFieldEntries-1].iPreFieldLen); |
|
106 } |
|
107 |
|
108 |
|
109 EXPORT_C void CTextFieldSet::RestoreL(const CStreamStore& aFieldStore,TStreamId aStreamId) |
|
110 { |
|
111 // reset the array and stream into it |
|
112 Reset(); |
|
113 DoRestoreL(aFieldStore,aStreamId); |
|
114 } |
|
115 |
|
116 |
|
117 EXPORT_C void CTextFieldSet::RestoreFieldsL(const CStreamStore& aFieldStore) |
|
118 { |
|
119 DoRestoreFieldsL(iFieldArray,aFieldStore); // restore the fields individually from their own streams |
|
120 } |
|
121 |
|
122 |
|
123 void CTextFieldSet::DoRestoreL(const CStreamStore& aFieldStore,TStreamId aStreamId) |
|
124 // Restores a field set and its associated fields from the store provded. |
|
125 // |
|
126 { |
|
127 __ASSERT_ALWAYS(iFieldArray->Count()==1,Panic(EArrayNotEmptyOnRestore)); // array must be empty |
|
128 __ASSERT_ALWAYS((*iFieldArray)[0].iPreFieldLen==0,Panic(EArrayNotEmptyOnRestore)); |
|
129 |
|
130 // retrieve the headstream from the store |
|
131 RStoreReadStream stream; |
|
132 stream.OpenLC(aFieldStore,aStreamId); |
|
133 // restore the set, then the individual fields |
|
134 stream>> *this; // internalize the field set (the headers) |
|
135 CleanupStack::PopAndDestroy(); // stream |
|
136 DoRestoreFieldsL(iFieldArray,aFieldStore); // restore the fields individually from their own streams |
|
137 } |
|
138 |
|
139 |
|
140 EXPORT_C void CTextFieldSet::InternalizeL(RReadStream& aStream) |
|
141 { |
|
142 InternalizeL(iFieldArray,aStream); |
|
143 |
|
144 __TEST_INVARIANT; |
|
145 } |
|
146 |
|
147 |
|
148 void CTextFieldSet::InternalizeL(CArrayFixSeg<TTextFieldEntry>* aArray,RReadStream& aStream) |
|
149 {// assume the array is empty |
|
150 TInt numFieldEntries = aStream.ReadInt32L(); |
|
151 // read in the fields |
|
152 TTextFieldEntry entry; |
|
153 for (TInt i=0 ; i<numFieldEntries-1 ; i++) |
|
154 { |
|
155 aStream>> entry; |
|
156 InsertEntryL(i,entry,aArray); // insert new entry |
|
157 } |
|
158 // read in the last entry: the bit after the last field. This will not contain a field |
|
159 (*aArray)[numFieldEntries-1].iPreFieldLen = aStream.ReadInt32L(); |
|
160 } |
|
161 |
|
162 |
|
163 void CTextFieldSet::DoRestoreFieldsL(CArrayFixSeg<TTextFieldEntry>* aArray,const CStreamStore& aFieldStore,TInt aStartIndex) |
|
164 // This fn is called after all FieldHeaders have been internalized - the Swizzles hold the stream id's. |
|
165 // One by one the fields are created (with the factory) and then have their settings streamed in from the store. |
|
166 // If no factory exists all fields are converted to text. |
|
167 // |
|
168 { |
|
169 TInt ii=aArray->Count()-2; // -2 because we skip the last (empty) entry |
|
170 while (ii>=aStartIndex) |
|
171 { |
|
172 if ((*aArray)[ii].iFieldHeader.iField.IsId()) |
|
173 {// restore the field only if it isn't the very last (dummy) entry |
|
174 if (iFieldFactory==NULL) |
|
175 // no factory - convert the field to text |
|
176 DeleteFieldEntry(aArray,ii); |
|
177 else |
|
178 { |
|
179 TStreamId id = (*aArray)[ii].iFieldHeader.iField.AsId(); |
|
180 (*aArray)[ii].iFieldHeader.iField = iFieldFactory->NewFieldL((*aArray)[ii].iFieldHeader.iFieldType); |
|
181 if ((*aArray)[ii].iFieldHeader.iField!=NULL) |
|
182 (*aArray)[ii].iFieldHeader.iField->RestoreL(aFieldStore,id); |
|
183 else |
|
184 DeleteFieldEntry(aArray,ii); // handle "field type not recognised" by converting field to text |
|
185 } |
|
186 } |
|
187 ii--; |
|
188 } |
|
189 } |
|
190 |
|
191 |
|
192 /***************************************** cut & paste *******************************************/ |
|
193 |
|
194 |
|
195 EXPORT_C TStreamId CTextFieldSet::CopyToStoreL(CStreamStore& aStore,TInt aPos,TInt aLength)const |
|
196 // Copy any fields in the selected region to the specified store, returning the id of the head-stream. |
|
197 // |
|
198 { |
|
199 __TEST_INVARIANT; |
|
200 __ASSERT_ALWAYS(aPos>=0,Panic(EPosOutOfRange)); |
|
201 __ASSERT_ALWAYS(aLength>=0,Panic(ENegativeRange)); |
|
202 __ASSERT_DEBUG((aPos+aLength)<=CharCount(),Panic(EPosOutOfRange)); |
|
203 |
|
204 // Create a store map and store the fields themselves |
|
205 CStoreMap* map=CStoreMap::NewLC(aStore); |
|
206 CopyComponentsL(aStore,*map,aPos,aLength); |
|
207 |
|
208 // Create a head-stream in which to store the field entries and do so |
|
209 RStoreWriteStream stream(*map); |
|
210 TStreamId id=stream.CreateLC(aStore); |
|
211 CopyToStreamL(stream,aPos,aLength); |
|
212 |
|
213 // tidy up |
|
214 stream.CommitL(); |
|
215 map->Reset(); |
|
216 CleanupStack::PopAndDestroy(2); // map, stream |
|
217 return id; |
|
218 } |
|
219 |
|
220 |
|
221 EXPORT_C void CTextFieldSet::CopyComponentsL(CStreamStore& aStore,CStoreMap& aMap,TInt aPos,TInt aLength)const |
|
222 // Stores all fields in the set |
|
223 // |
|
224 { |
|
225 __TEST_INVARIANT; |
|
226 __ASSERT_ALWAYS(aPos>=0,Panic(EPosOutOfRange)); |
|
227 __ASSERT_ALWAYS(aLength>=0,Panic(ENegativeRange)); |
|
228 __ASSERT_DEBUG((aPos+aLength)<=CharCount(),Panic(EPosOutOfRange)); |
|
229 |
|
230 // Create an array of the fields to be cut/copied |
|
231 CArrayFixSeg<TTextFieldEntry>* tempArray = new(ELeave) CArrayFixSeg<TTextFieldEntry>(KFieldArrayGranularity); |
|
232 CleanupStack::PushL(tempArray); |
|
233 CopyToArrayL(tempArray,aPos,aLength); |
|
234 |
|
235 // stream the required fields in their own streams |
|
236 StoreFieldsL(aStore,aMap,tempArray); |
|
237 CleanupStack::PopAndDestroy(); // tempArray |
|
238 } |
|
239 |
|
240 |
|
241 EXPORT_C void CTextFieldSet::CopyToStreamL(RWriteStream& aStream,TInt aPos,TInt aLength)const |
|
242 // Stores all fields in the set |
|
243 // |
|
244 { |
|
245 __TEST_INVARIANT; |
|
246 __ASSERT_ALWAYS(aPos>=0,Panic(EPosOutOfRange)); |
|
247 __ASSERT_ALWAYS(aLength>=0,Panic(ENegativeRange)); |
|
248 __ASSERT_DEBUG((aPos+aLength)<=CharCount(),Panic(EPosOutOfRange)); |
|
249 |
|
250 // Create an array of the fields to be cut/copied |
|
251 CArrayFixSeg<TTextFieldEntry>* tempArray = new(ELeave) CArrayFixSeg<TTextFieldEntry>(KFieldArrayGranularity); |
|
252 CleanupStack::PushL(tempArray); |
|
253 CopyToArrayL(tempArray,aPos,aLength); |
|
254 |
|
255 // stream the field entries in the temp array |
|
256 ExternalizeL(aStream,tempArray); |
|
257 CleanupStack::PopAndDestroy(); // tempArray |
|
258 } |
|
259 |
|
260 |
|
261 void CTextFieldSet::CopyToArrayL(CArrayFixSeg<TTextFieldEntry>* aArray,TInt aPos,TInt aLength)const |
|
262 { |
|
263 TInt index; TInt offset; |
|
264 if (InField(aPos,index,offset)) |
|
265 offset += (*iFieldArray)[index].iPreFieldLen; // make offset relative to start of entry |
|
266 // split first entry in range |
|
267 TTextFieldEntry entry = SplitEntry(index,offset,aLength); |
|
268 index++; |
|
269 TInt charsCopied=EntryLen(entry); |
|
270 // split second if neccessary |
|
271 if ((!entry.iFieldHeader.iField)&&(charsCopied<aLength)) |
|
272 { |
|
273 TInt preFieldLen = entry.iPreFieldLen; |
|
274 entry = SplitEntry(index,0,aLength-preFieldLen); |
|
275 entry.iPreFieldLen += preFieldLen; |
|
276 charsCopied = EntryLen(entry); |
|
277 index++; |
|
278 } |
|
279 ((CTextFieldSet*)this)->AppendEntryL(entry,aArray); // append the first entry to the storage array |
|
280 // write out all whole entries |
|
281 while (charsCopied<aLength) |
|
282 { |
|
283 if ((charsCopied+EntryLen(index))<=aLength) |
|
284 ((CTextFieldSet*)this)->AppendEntryL((*iFieldArray)[index],aArray); |
|
285 charsCopied += EntryLen(index); |
|
286 index++; |
|
287 } |
|
288 // split last entry if neccessary |
|
289 if (charsCopied>aLength) |
|
290 {// The last entry needs to be split |
|
291 // first get back to the beginning of the entry |
|
292 index--; |
|
293 charsCopied -= EntryLen(index); |
|
294 entry = SplitEntry(index,0,aLength-charsCopied); // split up the last entry as required |
|
295 ((CTextFieldSet*)this)->AppendEntryL(entry,aArray); // append the last entry to the storage array |
|
296 } |
|
297 // add an empty last entry if neccessary |
|
298 TInt numFieldEntries = aArray->Count(); |
|
299 if (((*aArray)[numFieldEntries-1].iFieldHeader.iField) || ((*aArray)[numFieldEntries-1].iFieldValueLen!=0)) |
|
300 { |
|
301 entry.iPreFieldLen = 0; |
|
302 entry.iFieldValueLen = 0; |
|
303 entry.iFieldHeader.iFieldType = KNullUid; |
|
304 entry.iFieldHeader.iField = NULL; |
|
305 ((CTextFieldSet*)this)->AppendEntryL(entry,aArray); |
|
306 numFieldEntries++; |
|
307 } |
|
308 } |
|
309 |
|
310 |
|
311 EXPORT_C void CTextFieldSet::PasteFromStoreL(const CStreamStore& aFieldStore,TStreamId aStreamId,TInt aPos,TInt aMaxLen) |
|
312 // Paste from aStore into the document at insert position aPos. |
|
313 // Optionally the pasted text can be clipped to a maximum length aMaxLen. |
|
314 // |
|
315 { |
|
316 __ASSERT_ALWAYS(aPos>=0,Panic(EPosOutOfRange)); |
|
317 __ASSERT_DEBUG(aPos<=CharCount(),Panic(EPosOutOfRange)); |
|
318 |
|
319 // retrieve the headstream from the store |
|
320 RStoreReadStream stream; |
|
321 stream.OpenLC(aFieldStore,aStreamId); |
|
322 |
|
323 // restore the set... |
|
324 PasteFromStreamL(stream,aPos,aMaxLen); // internalize the field set (the headers) |
|
325 CleanupStack::PopAndDestroy(); // stream |
|
326 |
|
327 // ...then the individual fields |
|
328 PasteComponentsL(aFieldStore,aPos); // restore the fields individually from their own streams |
|
329 } |
|
330 |
|
331 |
|
332 EXPORT_C void CTextFieldSet::PasteFromStreamL(RReadStream& aStream,TInt aPos,TInt aMaxLen) |
|
333 // streams the field entries into a temporary array, which is returned. |
|
334 // PasteComponents() must be called after this to actually carry out the paste... |
|
335 { |
|
336 // create a temporary array to stream in to, inserting the first entry |
|
337 CArrayFixSeg<TTextFieldEntry>* tempFieldArray = new(ELeave) CArrayFixSeg<TTextFieldEntry>(KFieldArrayGranularity); |
|
338 CleanupStack::PushL(tempFieldArray); |
|
339 AddInitialFieldEntryL(tempFieldArray,0); |
|
340 |
|
341 // internalize the field entries |
|
342 InternalizeL(tempFieldArray,aStream); |
|
343 |
|
344 // trim off any entries that lie beyond aMaxLength |
|
345 if (aMaxLen!=ENoPasteLimit) |
|
346 {// if aMaxLen is not ENoPasteLimit discard the excess fields |
|
347 __ASSERT_ALWAYS(aMaxLen>=0,Panic(ELengthOutOfRange)); |
|
348 // |
|
349 TInt length=0; |
|
350 TInt i=0; |
|
351 for (i=0 ; (length<aMaxLen)&&(i<tempFieldArray->Count()) ; i++) |
|
352 length += EntryLen((*tempFieldArray)[i]); |
|
353 if (aMaxLen==0) |
|
354 {// make first entry zero len, delete all others |
|
355 i++; |
|
356 (*tempFieldArray)[i-1].iPreFieldLen = 0; |
|
357 } |
|
358 else if (length>aMaxLen) |
|
359 // truncate the last field in range |
|
360 (*tempFieldArray)[i-1].iPreFieldLen += (*tempFieldArray)[i-1].iFieldValueLen-(length-aMaxLen); |
|
361 else if ((length==aMaxLen) && ((*tempFieldArray)[i-1].iFieldHeader.iField!=NULL)) |
|
362 {// if the terminating entry has a field add a zero length entry, the mandatory last entry |
|
363 i++; |
|
364 (*tempFieldArray)[i-1].iPreFieldLen = 0; |
|
365 } |
|
366 // ensure the last entry is of the correct format |
|
367 (*tempFieldArray)[i-1].iFieldValueLen = 0; |
|
368 (*tempFieldArray)[i-1].iFieldHeader.iFieldType = KNullUid; |
|
369 (*tempFieldArray)[i-1].iFieldHeader.iField = NULL; |
|
370 // delete all the fields wholely out of range |
|
371 for (TInt index=i ; index<tempFieldArray->Count() ; index++) |
|
372 (*tempFieldArray)[index].iFieldHeader.iField = NULL; |
|
373 tempFieldArray->Delete(i,tempFieldArray->Count()-i); // pos,count |
|
374 } |
|
375 |
|
376 DoPasteL(tempFieldArray,aPos); |
|
377 CleanupStack::PopAndDestroy(); // tempFieldArray |
|
378 } |
|
379 |
|
380 |
|
381 EXPORT_C void CTextFieldSet::PasteComponentsL(const CStreamStore& aFieldStore,TInt aPos) |
|
382 { |
|
383 // Restore the fields individually from their own streams |
|
384 TInt index; TInt offset; |
|
385 // We don't need to make any difference between in and not in field situation here |
|
386 // all we need is the index |
|
387 TBool isInField = InField(aPos,index,offset); |
|
388 DoRestoreFieldsL(iFieldArray,aFieldStore,index); |
|
389 } |
|
390 |
|
391 |
|
392 void CTextFieldSet::DoPasteL(CArrayFixSeg<TTextFieldEntry>* aSourceArray,TInt aPos) |
|
393 // Insert into this instance, at character position aPos, the entire (field entry) contents of the field array aSourceArray. |
|
394 // All iField objects in aSourceArray are ID's at this time. |
|
395 // |
|
396 { |
|
397 // are we inserting into a field? |
|
398 TInt numFieldEntries = aSourceArray->Count(); |
|
399 TInt index; TInt offset; |
|
400 |
|
401 TBool inField = InField(aPos,index,offset); |
|
402 // record the rollback info |
|
403 RecordRollbackInfoL(index); |
|
404 if ((inField)&&(offset!=0)) |
|
405 {// everything we insert will become text - no chance of leaving |
|
406 // insert all but last entry |
|
407 TInt i=0; |
|
408 for (; i<numFieldEntries-1 ; i++) |
|
409 {// copy text (no need to delete field) |
|
410 (*iFieldArray)[index].iFieldValueLen += EntryLen((*aSourceArray)[i]); |
|
411 } |
|
412 // read in the last entry (has no field attached) |
|
413 (*iFieldArray)[index].iFieldValueLen += (*aSourceArray)[i].iPreFieldLen; |
|
414 } |
|
415 else |
|
416 {// else split the entry we are going to bisect - this may leave |
|
417 if (inField) |
|
418 offset = (*iFieldArray)[index].iPreFieldLen; // must be at start of field |
|
419 if (numFieldEntries>1) |
|
420 {// read 1st field & carry out split. |
|
421 InsertEntryL(index,(*aSourceArray)[0]); // if this leaves the model will be intact |
|
422 (*iFieldArray)[index].iPreFieldLen += offset; |
|
423 (*iFieldArray)[index+1].iPreFieldLen -= offset; |
|
424 index++; |
|
425 } |
|
426 // insert all other fields except last. |
|
427 for (TInt i=1 ; i<numFieldEntries-1 ; i++) |
|
428 { |
|
429 TRAPD(ret,\ |
|
430 InsertEntryL(index,(*aSourceArray)[i])); |
|
431 if (ret!=KErrNone) |
|
432 {// do rollback, then propagate leave |
|
433 RollbackPaste(); |
|
434 User::Leave(ret); |
|
435 } |
|
436 index++; |
|
437 } |
|
438 // join last field up to successor |
|
439 (*iFieldArray)[index].iPreFieldLen += (*aSourceArray)[numFieldEntries-1].iPreFieldLen; |
|
440 } |
|
441 |
|
442 __TEST_INVARIANT; |
|
443 } |
|
444 |
|
445 |
|
446 void CTextFieldSet::RecordRollbackInfoL(TInt aIndex) |
|
447 { |
|
448 delete iRollbackInfo; |
|
449 iRollbackInfo = new(ELeave) TRollbackInfo(); |
|
450 iRollbackInfo->iEntryNum = aIndex; |
|
451 iRollbackInfo->iPreFieldLen = (*iFieldArray)[aIndex].iPreFieldLen; |
|
452 iRollbackInfo->iFieldValueLen = (*iFieldArray)[aIndex].iFieldValueLen; |
|
453 iRollbackInfo->iTotalEntries = iFieldArray->Count(); |
|
454 } |
|
455 |
|
456 |
|
457 EXPORT_C void CTextFieldSet::RollbackPaste() |
|
458 // Carries out rollback from a paste function |
|
459 // This will only have an effect after a PasteFromStream() has been called |
|
460 // nb it would be distasterous if this were called at random some time after a paste! |
|
461 // |
|
462 { |
|
463 if (!iRollbackInfo) |
|
464 return; // nothing to do |
|
465 // remove added entries from array |
|
466 TInt entriesToRemove=iFieldArray->Count()-iRollbackInfo->iTotalEntries; |
|
467 TInt i=0; |
|
468 for (i=iRollbackInfo->iEntryNum ; i<iRollbackInfo->iEntryNum+entriesToRemove ; i++) |
|
469 { |
|
470 if ((*iFieldArray)[i].iFieldHeader.iField.IsPtr()) |
|
471 delete (*iFieldArray)[i].iFieldHeader.iField.AsPtr(); // Delete the textField object |
|
472 iFieldArray->Delete(i); |
|
473 } |
|
474 // now right num entries, but wrong length - use backup info to correct length |
|
475 (*iFieldArray)[i].iPreFieldLen = iRollbackInfo->iPreFieldLen; |
|
476 (*iFieldArray)[i].iFieldValueLen = iRollbackInfo->iFieldValueLen; |
|
477 |
|
478 __ASSERT_DEBUG(iFieldArray->Count()==iRollbackInfo->iTotalEntries,Panic(EDebug)); |
|
479 delete iRollbackInfo; |
|
480 } |
|
481 |