1 /* -*- mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: t -*- */
2 /* AbiWord
3 * Copyright (C) 1998 AbiSource, Inc.
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 * 02110-1301 USA.
19 */
20
21
22 // changeSpanFmt-related fuctions for class pt_PieceTable
23
24 #include "ut_types.h"
25 #include "ut_misc.h"
26 #include "ut_assert.h"
27 #include "ut_debugmsg.h"
28 #include "ut_growbuf.h"
29 #include "pt_PieceTable.h"
30 #include "pf_Frag.h"
31 #include "pf_Frag_Object.h"
32 #include "pf_Frag_Strux.h"
33 #include "pf_Frag_Strux_Block.h"
34 #include "pf_Frag_Strux_Section.h"
35 #include "pf_Frag_Text.h"
36 #include "pf_Frag_FmtMark.h"
37 #include "pf_Fragments.h"
38 #include "px_ChangeRecord.h"
39 #include "px_CR_Span.h"
40 #include "px_CR_SpanChange.h"
41 #include "px_CR_Strux.h"
42 #include "pd_Style.h"
43 #include "pp_Revision.h"
44
45 #define SETP(p,v) do { if (p) (*(p)) = (v); } while (0)
46
47 /****************************************************************/
48 /****************************************************************/
49
changeSpanFmt(PTChangeFmt ptc,PT_DocPosition dpos1,PT_DocPosition dpos2,const gchar ** attributes,const gchar ** properties)50 bool pt_PieceTable::changeSpanFmt(PTChangeFmt ptc,
51 PT_DocPosition dpos1,
52 PT_DocPosition dpos2,
53 const gchar ** attributes,
54 const gchar ** properties)
55 {
56 // if dpos1 == dpos2 we are inserting a fmt mark; this must be chanelled throught
57 // the non-revision branch ...
58 if(m_pDocument->isMarkRevisions() && dpos1 != dpos2)
59 {
60 const gchar name[] = "revision";
61 const gchar * pRevision = NULL;
62
63 // we cannot retrieve the start and end fragments here and
64 // then work between them in a loop using getNext() because
65 // processing might result in merging of fargments. so we have
66 // to use the doc position to keep track of where we are and
67 // retrieve the fragments afresh in each step of the loop
68 // Tomas, Dec 29, 2004
69
70 bool bRet = false;
71 while(dpos1 < dpos2)
72 {
73 // first retrive the starting and ending fragments
74 pf_Frag * pf1, * pf2;
75 PT_BlockOffset Offset1, Offset2;
76
77 if(!getFragsFromPositions(dpos1,dpos2, &pf1, &Offset1, &pf2, &Offset2) ||
78 pf1->getType() == pf_Frag::PFT_EndOfDoc)
79 return bRet;
80 else
81 bRet = true;
82
83 // get attributes for this fragement
84 const PP_AttrProp * pAP;
85 pRevision = NULL;
86
87 if(_getSpanAttrPropHelper(pf1, &pAP))
88 {
89 pAP->getAttribute(name, pRevision);
90 }
91
92 PP_RevisionAttr Revisions(pRevision);
93
94
95 // if the request is for removal of fmt, in the revision mode, we still
96 // have to add these props (the removal is indicated by their emptiness)
97 // as we cannot rely on callers to set these correctly, we have to emtpy
98 // them ourselves
99 const gchar ** attrs = attributes;
100 const gchar ** props = properties;
101
102 if(ptc == PTC_RemoveFmt)
103 {
104 attrs = UT_setPropsToNothing(attributes);
105 props = UT_setPropsToNothing(properties);
106 }
107
108 Revisions.addRevision(m_pDocument->getRevisionId(),PP_REVISION_FMT_CHANGE,attrs,props);
109
110 if(attrs != attributes)
111 delete[] attrs;
112
113 if(props != properties)
114 delete[] props;
115
116 const gchar * ppRevAttrib[3];
117 ppRevAttrib[0] = name;
118 ppRevAttrib[1] = Revisions.getXMLstring();
119 ppRevAttrib[2] = NULL;
120
121 PT_DocPosition dposEnd = UT_MIN(dpos2,dpos1 + pf1->getLength());
122
123 if(!_realChangeSpanFmt(PTC_AddFmt, dpos1, dposEnd, ppRevAttrib,NULL, false))
124 return false;
125
126 dpos1 = dposEnd;
127 }
128
129 return true;
130 }
131 else
132 {
133 return _realChangeSpanFmt(ptc, dpos1, dpos2, attributes, properties, false);
134 }
135 }
136
_fmtChangeSpan(pf_Frag_Text * pft,UT_uint32 fragOffset,UT_uint32 length,PT_AttrPropIndex indexNewAP,pf_Frag ** ppfNewEnd,UT_uint32 * pfragOffsetNewEnd)137 bool pt_PieceTable::_fmtChangeSpan(pf_Frag_Text * pft, UT_uint32 fragOffset, UT_uint32 length,
138 PT_AttrPropIndex indexNewAP,
139 pf_Frag ** ppfNewEnd, UT_uint32 * pfragOffsetNewEnd)
140 {
141 UT_return_val_if_fail (length > 0,false);
142 UT_return_val_if_fail (fragOffset+length <= pft->getLength(), false);
143
144 // insert a format change within this text fragment.
145
146 // TODO for each place in this function where we apply a change
147 // TODO see if the new fragment could be coalesced with something
148 // TODO already in the fragment list.
149
150 if ((fragOffset == 0) && (length == pft->getLength()))
151 {
152 // we have an exact match (we are changing the entire fragment).
153
154 // try to coalesce this modified fragment with one of its neighbors.
155 // first we try the pft->next -- if that works, we can stop because
156 // the unlink will take care of checking pft->next with pft->prev.
157 // if it doesn't work, we then try pft->prev.
158
159 pf_Frag * pfNext = pft->getNext();
160 if (pfNext && pfNext->getType()==pf_Frag::PFT_Text)
161 {
162 pf_Frag_Text * pftNext = static_cast<pf_Frag_Text *>(pfNext);
163 if ( (pftNext->getIndexAP() == indexNewAP)
164 && (m_varset.isContiguous(pft->getBufIndex(),length,pftNext->getBufIndex())))
165 {
166 // the pft given and the pft->next can be coalesced.
167 // let's donate all of our document data to pft->next,
168 // set pft to be empty and then let _unlinkFrag() take
169 // care of all the other details (like checking to see
170 // if pft->next and pft->prev can be coalesced after pft
171 // is out of the way....)
172
173 pftNext->adjustOffsetLength(pft->getBufIndex(),length+pftNext->getLength());
174 // we could do a: pft->changeLength(0); but it causes an assert and
175 // besides _unlinkFrag() doesn't look at it and we're going to delete it.
176 _unlinkFrag(pft,ppfNewEnd,pfragOffsetNewEnd);
177 delete pft;
178 return true;
179 }
180 }
181
182 pf_Frag * pfPrev = pft->getPrev();
183 if (pfPrev && pfPrev->getType()==pf_Frag::PFT_Text)
184 {
185 pf_Frag_Text * pftPrev = static_cast<pf_Frag_Text *>(pfPrev);
186 if ( (pftPrev->getIndexAP() == indexNewAP)
187 && (m_varset.isContiguous(pftPrev->getBufIndex(),pftPrev->getLength(),pft->getBufIndex())))
188 {
189 // the pft given and the pft->prev can be coalesced.
190 // let's donate all of our document data to the pft->prev,
191 // set pft to be empty and then let _unlinkFrag() take
192 // care of the dirty work.
193
194 pftPrev->changeLength(pftPrev->getLength()+length);
195 // we could do a: pft->changeLength(0); but it causes an assert and
196 // besides _unlinkFrag() doesn't look at it and we're going to delete it.
197 _unlinkFrag(pft,ppfNewEnd,pfragOffsetNewEnd);
198 delete pft;
199 return true;
200 }
201 }
202
203 // otherwise, we just overwrite the indexAP on this fragment.
204
205 pft->setIndexAP(indexNewAP);
206 SETP(ppfNewEnd, pft->getNext());
207 SETP(pfragOffsetNewEnd, 0);
208
209 return true;
210 }
211
212 if (fragOffset == 0)
213 {
214 // the change is at the beginning of the fragment.
215 // we need to split the existing fragment into 2 parts
216 // and apply the new formatting to the new first half.
217 // before we actually create the new one, we see if we
218 // can coalesce the first half (with the new formatting)
219 // with the previous fragment. if not, then we cut
220 // the existing fragment into 2 parts.
221
222 UT_uint32 len_1 = length;
223 UT_uint32 len_2 = pft->getLength() - len_1;
224 PT_BufIndex bi_1 = m_varset.getBufIndex(pft->getBufIndex(),0);
225 PT_BufIndex bi_2 = m_varset.getBufIndex(pft->getBufIndex(),len_1);
226
227 pf_Frag * pfPrev = pft->getPrev();
228 if (pfPrev && pfPrev->getType()==pf_Frag::PFT_Text)
229 {
230 pf_Frag_Text * pftPrev = static_cast<pf_Frag_Text *>(pfPrev);
231 if ( (pftPrev->getIndexAP() == indexNewAP)
232 && (m_varset.isContiguous(pftPrev->getBufIndex(),pftPrev->getLength(),pft->getBufIndex())))
233 {
234 // yes we can coalesce. move the first half into the previous fragment.
235
236 pftPrev->changeLength(pftPrev->getLength()+length);
237 pft->adjustOffsetLength(bi_2,len_2);
238 SETP(ppfNewEnd, pft);
239 SETP(pfragOffsetNewEnd, 0);
240
241 return true;
242 }
243 }
244
245 // otherwise, we need to actually split this one....
246
247 pf_Frag_Text * pftNew = new pf_Frag_Text(this,bi_1,len_1,indexNewAP,pft->getField());
248 if (!pftNew)
249 return false;
250
251 pft->adjustOffsetLength(bi_2,len_2);
252 m_fragments.insertFrag(pft->getPrev(),pftNew);
253
254 SETP(ppfNewEnd, pft);
255 SETP(pfragOffsetNewEnd, 0);
256
257 return true;
258 }
259
260 if (fragOffset+length == pft->getLength())
261 {
262 // the change is at the end of the fragment, we cut
263 // the existing fragment into 2 parts and apply the new
264 // formatting to the new second half. before we actually
265 // create the new one, we see if we can coalesce the
266 // second half (with the new formatting) with the next
267 // fragment.
268
269 UT_uint32 len_1 = fragOffset;
270 UT_uint32 len_2 = length;
271 PT_BufIndex bi_2 = m_varset.getBufIndex(pft->getBufIndex(),len_1);
272
273 pf_Frag * pfNext = pft->getNext();
274 if (pfNext && pfNext->getType()==pf_Frag::PFT_Text)
275 {
276 pf_Frag_Text * pftNext = static_cast<pf_Frag_Text *>(pfNext);
277 if ( (pftNext->getIndexAP() == indexNewAP)
278 && (m_varset.isContiguous(bi_2,len_2,pftNext->getBufIndex())))
279 {
280 // yes we can coalesce. move the second half into the next fragment.
281
282 pftNext->adjustOffsetLength(bi_2,len_2+pftNext->getLength());
283 pft->changeLength(len_1);
284 SETP(ppfNewEnd,pftNext);
285 SETP(pfragOffsetNewEnd,len_2);
286 return true;
287 }
288 }
289
290 // otherwise, we actually need to split this one....
291
292 pf_Frag_Text * pftNew = new pf_Frag_Text(this,bi_2,len_2,indexNewAP,pft->getField());
293 if (!pftNew)
294 return false;
295
296 pft->changeLength(len_1);
297 m_fragments.insertFrag(pft,pftNew);
298
299 SETP(ppfNewEnd, pftNew->getNext());
300 SETP(pfragOffsetNewEnd, 0);
301
302 return true;
303 }
304
305 // otherwise, change is in the middle of the fragment. we
306 // need to cut the existing fragment into 3 parts and apply
307 // the new formatting to the middle one.
308
309 UT_uint32 len_1 = fragOffset;
310 UT_uint32 len_2 = length;
311 UT_uint32 len_3 = pft->getLength() - (fragOffset+length);
312 PT_BufIndex bi_2 = m_varset.getBufIndex(pft->getBufIndex(),fragOffset);
313 PT_BufIndex bi_3 = m_varset.getBufIndex(pft->getBufIndex(),fragOffset+length);
314 pf_Frag_Text * pft_2 = new pf_Frag_Text(this,bi_2,len_2,indexNewAP,pft->getField());
315 UT_return_val_if_fail (pft_2, false);
316 pf_Frag_Text * pft_3 = new pf_Frag_Text(this,bi_3,len_3,pft->getIndexAP(),pft->getField());
317 UT_return_val_if_fail (pft_3, false);
318
319 pft->changeLength(len_1);
320 m_fragments.insertFrag(pft,pft_2);
321 m_fragments.insertFrag(pft_2,pft_3);
322
323 SETP(ppfNewEnd, pft_3);
324 SETP(pfragOffsetNewEnd, 0);
325
326 return true;
327 }
328
_fmtChangeSpanWithNotify(PTChangeFmt ptc,pf_Frag_Text * pft,UT_uint32 fragOffset,PT_DocPosition dpos,UT_uint32 length,const gchar ** attributes,const gchar ** properties,pf_Frag_Strux * pfs,pf_Frag ** ppfNewEnd,UT_uint32 * pfragOffsetNewEnd,bool bRevisionDelete)329 bool pt_PieceTable::_fmtChangeSpanWithNotify(PTChangeFmt ptc,
330 pf_Frag_Text * pft, UT_uint32 fragOffset,
331 PT_DocPosition dpos,
332 UT_uint32 length,
333 const gchar ** attributes,
334 const gchar ** properties,
335 pf_Frag_Strux * pfs,
336 pf_Frag ** ppfNewEnd,
337 UT_uint32 * pfragOffsetNewEnd,
338 bool bRevisionDelete)
339 {
340 // create a change record for this change and put it in the history.
341
342 if (length == 0) // TODO decide if this is an error.
343 {
344 UT_DEBUGMSG(("_fmtChangeSpanWithNotify: length==0\n"));
345 SETP(ppfNewEnd, pft->getNext());
346 SETP(pfragOffsetNewEnd, 0);
347 return true;
348 }
349
350 UT_return_val_if_fail (fragOffset+length <= pft->getLength(), false);
351
352 PT_AttrPropIndex indexNewAP;
353 PT_AttrPropIndex indexOldAP = pft->getIndexAP();
354 UT_DebugOnly<bool> bMerged;
355 if(attributes && properties && (attributes[0] == NULL) && (properties[0] == NULL))
356 {
357 //
358 // Clear out all attributes/properties and set to the first index
359 //
360 bMerged = true;
361 indexNewAP = 0;
362 }
363 else
364 bMerged = m_varset.mergeAP(ptc,indexOldAP,attributes,properties,&indexNewAP,getDocument());
365
366 UT_ASSERT_HARMLESS(bMerged);
367
368 if (indexOldAP == indexNewAP) // the requested change will have no effect on this fragment.
369 {
370 if (fragOffset+length == pft->getLength())
371 {
372 SETP(ppfNewEnd, pft->getNext());
373 SETP(pfragOffsetNewEnd, 0);
374 }
375 else
376 {
377 SETP(ppfNewEnd, pft);
378 SETP(pfragOffsetNewEnd, fragOffset+length);
379 }
380
381 return true;
382 }
383
384 // we do this before the actual change because various fields that
385 // we need may be blown away during the change. we then notify all
386 // listeners of the change.
387
388 PT_BlockOffset blockOffset = _computeBlockOffset(pfs,pft) + fragOffset;
389
390 PX_ChangeRecord_SpanChange * pcr
391 = new PX_ChangeRecord_SpanChange(PX_ChangeRecord::PXT_ChangeSpan,
392 dpos, indexOldAP,indexNewAP,
393 m_varset.getBufIndex(pft->getBufIndex(),fragOffset),
394 length,blockOffset,bRevisionDelete);
395 UT_return_val_if_fail (pcr,false);
396 bool bResult = _fmtChangeSpan(pft,fragOffset,length,indexNewAP,ppfNewEnd,pfragOffsetNewEnd);
397
398 // add record to history. we do not attempt to coalesce these.
399 m_history.addChangeRecord(pcr);
400 m_pDocument->notifyListeners(pfs,pcr);
401
402 return bResult;
403 }
404
_realChangeSpanFmt(PTChangeFmt ptc,PT_DocPosition dpos1,PT_DocPosition dpos2,const gchar ** attributes,const gchar ** properties,bool bRevisionDelete)405 bool pt_PieceTable::_realChangeSpanFmt(PTChangeFmt ptc,
406 PT_DocPosition dpos1,
407 PT_DocPosition dpos2,
408 const gchar ** attributes,
409 const gchar ** properties,
410 bool bRevisionDelete)
411 {
412 // apply a span-level formatting change to the given region.
413
414 UT_return_val_if_fail (m_pts==PTS_Editing,false);
415 _tweakFieldSpan(dpos1,dpos2);
416 //
417 // Deal with case of exactly selecting the endOfFootnote
418 //
419 pf_Frag * pfEndDum = m_fragments.findFirstFragBeforePos(dpos2);
420 if(isEndFootnote(pfEndDum))
421 {
422 if(dpos2 > dpos1)
423 {
424 dpos2--;
425 }
426 }
427 //
428 // Deal with addStyle
429 //
430 bool bApplyStyle = (PTC_AddStyle == ptc);
431 const gchar ** sProps = NULL;
432 const gchar ** lProps = properties;
433 if(bApplyStyle)
434 {
435 //
436 // OK for styles we expand out all defined properties including BasedOn styles
437 // Then we use these to eliminate any specfic properties in the current strux
438 // Then properties in the current strux will resolve to those defined in the
439 // style (they exist there) to specifc values in strux (if not overridden by
440 // the style) then finally to default value.
441 //
442 const gchar * szStyle = UT_getAttribute(PT_STYLE_ATTRIBUTE_NAME,attributes);
443 PD_Style * pStyle = NULL;
444 UT_return_val_if_fail (szStyle,false);
445 getDocument()->getStyle(szStyle,&pStyle);
446 UT_return_val_if_fail (pStyle,false);
447 UT_Vector vProps;
448 //
449 // Get the vector of properties
450 //
451 pStyle->getAllProperties(&vProps,0);
452 //
453 // Finally make the const gchar * array of properties
454 //
455 UT_uint32 countp = vProps.getItemCount() + 1;
456 sProps = (const gchar **) UT_calloc(countp, sizeof(gchar *));
457 countp--;
458 UT_uint32 i;
459 for(i=0; i<countp; i++)
460 {
461 sProps[i] = (const gchar *) vProps.getNthItem(i);
462 }
463 sProps[i] = NULL;
464 lProps = sProps;
465 }
466 if (dpos1 == dpos2) // if length of change is zero, then we have a toggle format.
467 {
468 UT_uint32 startUndoPos = m_history.getUndoPos();
469 bool bRes = _insertFmtMarkFragWithNotify(ptc,dpos1,attributes,lProps);
470 UT_uint32 endUndoPos = m_history.getUndoPos();
471 // Won't be a persistant change if it's just a toggle
472 PX_ChangeRecord *pcr=0;
473 m_history.getUndo(&pcr,true);
474 if (pcr && (startUndoPos != endUndoPos) )
475 {
476 UT_DEBUGMSG(("Setting persistance of change to false\n"));
477 pcr->setPersistance(false);
478 m_history.setSavePosition(m_history.getSavePosition()+1);
479 }
480 if(bApplyStyle)
481 {
482 FREEP(sProps);
483 }
484 return bRes;
485 }
486
487 UT_return_val_if_fail (dpos1 < dpos2,false);
488
489 pf_Frag * pf_First;
490 pf_Frag * pf_End;
491 PT_BlockOffset fragOffset_First;
492 PT_BlockOffset fragOffset_End;
493
494 bool bFound;
495 bFound = getFragsFromPositions(dpos1,dpos2,&pf_First,&fragOffset_First,&pf_End,&fragOffset_End);
496 UT_return_val_if_fail (bFound, false);
497 bool bSkipFootnote = _checkSkipFootnote(dpos1,dpos2,pf_End);
498
499 #if 0
500 {
501 pf_Frag * pf1, * pf2;
502 PT_BlockOffset fo1, fo2;
503
504 bool bFound1 = getFragFromPosition(dpos1,&pf1,&fo1);
505 bool bFound2 = getFragFromPosition(dpos2,&pf2,&fo2);
506 UT_return_val_if_fail (bFound1 && bFound2, false);
507 UT_return_val_if_fail ((pf1==pf_First) && (fragOffset_First==fo1), false);
508 UT_return_val_if_fail ((pf2==pf_End) && (fragOffset_End==fo2), false);
509 }
510 #endif
511
512 // see if the amount of text to be changed is completely
513 // contained within a single fragment. if so, we have a
514 // simple change. otherwise, we need to set up a multi-step
515 // change -- it may not actually take more than one step,
516 // but it is too complicated to tell at this point, so we
517 // assume it will and don't worry about it.
518 //
519 // we are in a simple change if the beginning and end are
520 // within the same fragment.
521
522 // NOTE: if we call beginMultiStepGlob() we ***MUST*** call
523 // NOTE: endMultiStepGlob() before we return -- otherwise,
524 // NOTE: the undo/redo won't be properly bracketed.
525
526 bool bSimple = (pf_First == pf_End);
527 if (!bSimple)
528 beginMultiStepGlob();
529 // UT_DEBUGMSG(("ODTCT: realChangeSpanFmt() bSimple:%d\n", bSimple ));
530
531 pf_Frag_Strux * pfsContainer = NULL;
532 pf_Frag * pfNewEnd;
533 UT_uint32 fragOffsetNewEnd;
534
535 UT_uint32 length = dpos2 - dpos1;
536 while (length != 0)
537 {
538 // FIXME: Special check to support a FmtMark at the end of the
539 // document. This is necessary because FmtMarks don't have a
540 // length... See bug 452.
541 if (0 == length
542 && (!pf_First || pf_Frag::PFT_FmtMark != pf_First->getType()))
543 break;
544
545 UT_return_val_if_fail (dpos1+length==dpos2, false);
546
547 UT_uint32 lengthInFrag = pf_First->getLength() - fragOffset_First;
548 UT_uint32 lengthThisStep = UT_MIN(lengthInFrag, length);
549
550 switch (pf_First->getType())
551 {
552 case pf_Frag::PFT_EndOfDoc:
553 default:
554 UT_DEBUGMSG(("fragment type: %d\n",pf_First->getType()));
555 UT_ASSERT_HARMLESS(0);
556 if(bApplyStyle)
557 {
558 FREEP(sProps);
559 }
560 return false;
561
562 case pf_Frag::PFT_Strux:
563 {
564 // we are only applying span-level changes, so we ignore strux.
565 // but we still need to update our loop indices.
566 if (bSkipFootnote && isFootnote(pf_First))
567 {
568 UT_uint32 extraLength = 0;
569 pfNewEnd = pf_First;
570 while(pfNewEnd && !isEndFootnote(pfNewEnd))
571 {
572 pfNewEnd = pfNewEnd->getNext();
573 extraLength += pfNewEnd->getLength();
574 }
575 if(lengthThisStep + extraLength <= length)
576 {
577 lengthThisStep += extraLength;
578 }
579 else
580 {
581 UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
582 lengthThisStep = length;
583 }
584 pfNewEnd = pfNewEnd->getNext();
585 fragOffsetNewEnd = 0;
586 }
587 else
588 {
589 pfNewEnd = pf_First->getNext();
590 pfsContainer = static_cast<pf_Frag_Strux *> (pf_First);
591 fragOffsetNewEnd = 0;
592 bool bFoundStrux = false;
593 if(isEndFootnote(pfsContainer))
594 {
595 bFoundStrux = _getStruxFromFragSkip(pfsContainer,&pfsContainer);
596 UT_return_val_if_fail (bFoundStrux, false);
597 }
598 }
599 }
600 break;
601
602 case pf_Frag::PFT_Text:
603 {
604 if (!pfsContainer)
605 {
606 bool bFoundStrux;
607 bFoundStrux = _getStruxFromPosition(dpos1,&pfsContainer);
608 UT_return_val_if_fail (bFoundStrux,false);
609 if(isEndFootnote(pfsContainer))
610 {
611 bFoundStrux = _getStruxFromFragSkip(pfsContainer,&pfsContainer);
612 UT_return_val_if_fail (bFoundStrux,false);
613 }
614 }
615
616 // UT_DEBUGMSG(("ODTCT: realChangeSpanFmt() text...A\n" ));
617 bool bResult;
618 bResult = _fmtChangeSpanWithNotify(ptc,static_cast<pf_Frag_Text *>(pf_First),
619 fragOffset_First,dpos1,lengthThisStep,
620 attributes,lProps,
621 pfsContainer,&pfNewEnd,&fragOffsetNewEnd,bRevisionDelete);
622 // UT_DEBUGMSG(("ODTCT: realChangeSpanFmt() text...B\n" ));
623 UT_return_val_if_fail (bResult,false);
624 }
625 break;
626
627 case pf_Frag::PFT_Object:
628 {
629 if (!pfsContainer)
630 {
631 bool bFoundStrux;
632 bFoundStrux = _getStruxFromPosition(dpos1,&pfsContainer);
633 UT_return_val_if_fail (bFoundStrux,false);
634 if(isEndFootnote(pfsContainer))
635 {
636 bFoundStrux = _getStruxFromFragSkip(pfsContainer,&pfsContainer);
637 UT_return_val_if_fail (bFoundStrux,false);
638 }
639 }
640
641 bool bResult;
642 bResult = _fmtChangeObjectWithNotify(ptc,static_cast<pf_Frag_Object *>(pf_First),
643 fragOffset_First,dpos1,lengthThisStep,
644 attributes,lProps,
645 pfsContainer,&pfNewEnd,&fragOffsetNewEnd,false);
646 UT_return_val_if_fail (bResult,false);
647 }
648 break;
649
650 case pf_Frag::PFT_FmtMark:
651 {
652 if (!pfsContainer)
653 {
654 bool bFoundStrux;
655 bFoundStrux = _getStruxFromPosition(dpos1,&pfsContainer);
656 UT_return_val_if_fail (bFoundStrux,false);
657 if(isEndFootnote(pfsContainer))
658 {
659 bFoundStrux = _getStruxFromFragSkip(pfsContainer,&pfsContainer);
660 UT_return_val_if_fail (bFoundStrux,false);
661 }
662
663 }
664
665 bool bResult;
666 bResult = _fmtChangeFmtMarkWithNotify(ptc,static_cast<pf_Frag_FmtMark *>(pf_First),
667 dpos1, attributes,lProps,
668 pfsContainer,&pfNewEnd,&fragOffsetNewEnd);
669 UT_return_val_if_fail (bResult,false);
670 }
671 break;
672
673 }
674
675 dpos1 += lengthThisStep;
676 length -= lengthThisStep;
677
678 // since _fmtChange{Span,FmtMark,...}WithNotify(), can delete pf_First, mess with the
679 // fragment list, and does some aggressive coalescing of
680 // fragments, we cannot just do a pf_First->getNext() here.
681 // to advance to the next fragment, we use the *NewEnd variables
682 // that each of the cases routines gave us.
683
684 pf_First = pfNewEnd;
685 if (!pf_First)
686 length = 0;
687 fragOffset_First = fragOffsetNewEnd;
688 }
689 if(bApplyStyle)
690 {
691 FREEP(sProps);
692 }
693
694 if (!bSimple)
695 endMultiStepGlob();
696
697 return true;
698 }
699