1 /* -*- mode: C++; tab-width: 4; c-basic-offset: 4; -*- */
2 /* AbiWord
3  * Copyright (C) 1998 AbiSource, Inc.
4  * Copyright (C) 2001,2002 Tomas Frydrych
5  * Some change tracking code was added in 2011 by Ben Martin
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20  * 02110-1301 USA.
21  */
22 
23 
24 // deleteSpan-related routines for class pt_PieceTable
25 
26 #include "ut_types.h"
27 #include "ut_misc.h"
28 #include "ut_assert.h"
29 #include "ut_debugmsg.h"
30 #include "ut_growbuf.h"
31 #include "ut_stack.h"
32 #include "pt_PieceTable.h"
33 #include "pf_Frag.h"
34 #include "pf_Frag_FmtMark.h"
35 #include "pf_Frag_Object.h"
36 #include "pf_Frag_Strux.h"
37 #include "pf_Frag_Strux_Block.h"
38 #include "pf_Frag_Strux_Section.h"
39 #include "pf_Frag_Text.h"
40 #include "pf_Fragments.h"
41 #include "px_ChangeRecord.h"
42 #include "px_CR_Span.h"
43 #include "px_CR_SpanChange.h"
44 #include "px_CR_Strux.h"
45 #include "pp_Revision.h"
46 
47 #include "ut_conversion.h"
48 
49 #define SETP(p,v)	do { if (p) (*(p)) = (v); } while (0)
50 
51 /****************************************************************/
52 /****************************************************************/
deleteSpan(PT_DocPosition dpos1,PT_DocPosition dpos2,PP_AttrProp * p_AttrProp_Before,UT_uint32 & iRealDeleteCount,bool bDontGlob)53 bool pt_PieceTable::deleteSpan(PT_DocPosition dpos1,
54 							   PT_DocPosition dpos2,
55 							   PP_AttrProp *p_AttrProp_Before,
56 							   UT_uint32 &iRealDeleteCount,
57 							   bool bDontGlob)
58 {
59 	return deleteSpan(dpos1,
60 					  dpos2,
61 					  p_AttrProp_Before,
62 					  iRealDeleteCount,
63 					  true,
64 					  bDontGlob);
65 }
66 
67 /****************************************************************/
deleteSpanWithTable(PT_DocPosition dpos1,PT_DocPosition dpos2,PP_AttrProp * p_AttrProp_Before,UT_uint32 & iRealDeleteCount,bool bDeleteTableStruxes)68 bool pt_PieceTable::deleteSpanWithTable(PT_DocPosition dpos1,
69 										PT_DocPosition dpos2,
70 										PP_AttrProp *p_AttrProp_Before,
71 										UT_uint32 &iRealDeleteCount,
72 										bool bDeleteTableStruxes)
73 {
74   //        getFragments().verifyDoc();
75 	return deleteSpan(dpos1,
76 					  dpos2,
77 					  p_AttrProp_Before,
78 					  iRealDeleteCount,
79 					  bDeleteTableStruxes,
80 					  false);
81 }
82 
83 
84 
85 /****************************************************************/
86 
dumpDoc(const char * msg,PT_DocPosition currentpos,PT_DocPosition endpos)87 bool pt_PieceTable::dumpDoc(
88     const char* msg,
89     PT_DocPosition currentpos,
90     PT_DocPosition endpos )
91 {
92     UT_DEBUG_ONLY_ARG(msg);
93 
94     if( !endpos )
95     {
96         m_pDocument->getBounds( true, endpos );
97     }
98 
99     pf_Frag *pf;
100     PT_BlockOffset Offset;
101 
102     UT_DEBUGMSG(("=========================================\n" ));
103     UT_DEBUGMSG(("dumpDoc(%s) showing range:%d to %d\n", msg, currentpos, endpos ));
104     for( PT_DocPosition pos = currentpos; pos < endpos; )
105     {
106         if(!getFragFromPosition( pos, &pf, &Offset ))
107         {
108             UT_DEBUGMSG(("dumpDoc() NO FRAG AT pos:%d\n", pos ));
109             break;
110         }
111         std::string fragTypeStr = "            ";
112         switch( pf->getType() )
113         {
114             case pf_Frag::PFT_Text:     fragTypeStr = "PFT_Text    "; break;
115             case pf_Frag::PFT_Object:   fragTypeStr = "PFT_Object  "; break;
116             case pf_Frag::PFT_Strux:    fragTypeStr = "PFT_Strux   "; break;
117             case pf_Frag::PFT_EndOfDoc: fragTypeStr = "PFT_EndOfDoc"; break;
118             case pf_Frag::PFT_FmtMark:  fragTypeStr = "PFT_FmtMark "; break;
119         }
120         std::string extra = "";
121         if( pf->getType() == pf_Frag::PFT_Text )
122         {
123             if( pf_Frag_Text* pft = static_cast<pf_Frag_Text*>(pf))
124             {
125                 extra = pft->toString().substr( 0, 20 );
126             }
127         }
128 
129         if( pf->getType() == pf_Frag::PFT_Strux && tryDownCastStrux( pf, PTX_Block ) ) {
130             UT_DEBUGMSG(("dumpDoc()\n"));
131 	}
132 
133         UT_DEBUGMSG(("dumpDoc() %s pos:%d frag:%p len:%d frag type:%d extra:%s\n",
134                      fragTypeStr.c_str(), pos, pf, pf->getLength(), pf->getType(), extra.c_str() ));
135 
136         if( pf->getType() == pf_Frag::PFT_Object )
137         {
138 			pf_Frag_Object * po = (pf_Frag_Object*) pf;
139             std::string typeStr = "";
140             switch( po->getObjectType() )
141             {
142                 case PTO_Image: typeStr = "PTO_Image    "; break;
143                 case PTO_Field: typeStr = "PTO_Field    "; break;
144                 case PTO_Bookmark: typeStr = "PTO_Bookmark    "; break;
145                 case PTO_Hyperlink: typeStr = "PTO_Hyperlink    "; break;
146                 case PTO_Math: typeStr = "PTO_Math    "; break;
147                 case PTO_Embed: typeStr = "PTO_Embed    "; break;
148                 case PTO_Annotation: typeStr = "PTO_Annotation    "; break;
149                 case PTO_RDFAnchor: typeStr = "PTO_RDFAnchor    "; break;
150             }
151             UT_DEBUGMSG(("          %s objectType:%d\n", typeStr.c_str(), po->getObjectType() ));
152         }
153 
154         if( pf->getType() == pf_Frag::PFT_Strux )
155         {
156             PTStruxType eStruxType = static_cast<pf_Frag_Strux*>(pf)->getStruxType();
157             std::string eStruxTypeStr;
158             switch(eStruxType)
159             {
160                 case PTX_Section:           eStruxTypeStr = "PTX_Section          "; break;
161                 case PTX_Block:             eStruxTypeStr = "PTX_Block            "; break;
162                 case PTX_SectionHdrFtr:     eStruxTypeStr = "PTX_SectionHdrFtr    "; break;
163                 case PTX_SectionEndnote:    eStruxTypeStr = "PTX_SectionEndnote   "; break;
164                 case PTX_SectionTable:      eStruxTypeStr = "PTX_SectionTable     "; break;
165                 case PTX_SectionCell:       eStruxTypeStr = "PTX_SectionCell      "; break;
166                 case PTX_SectionFootnote:   eStruxTypeStr = "PTX_SectionFootnote  "; break;
167                 case PTX_SectionMarginnote: eStruxTypeStr = "PTX_SectionMarginnote"; break;
168                 case PTX_SectionAnnotation: eStruxTypeStr = "PTX_SectionAnnotation"; break;
169                 case PTX_SectionFrame:      eStruxTypeStr = "PTX_SectionFrame     "; break;
170                 case PTX_SectionTOC:        eStruxTypeStr = "PTX_SectionTOC       "; break;
171                 case PTX_EndCell:           eStruxTypeStr = "PTX_EndCell          "; break;
172                 case PTX_EndTable:          eStruxTypeStr = "PTX_EndTable         "; break;
173                 case PTX_EndFootnote:       eStruxTypeStr = "PTX_EndFootnote      "; break;
174                 case PTX_EndMarginnote:     eStruxTypeStr = "PTX_EndMarginnote    "; break;
175                 case PTX_EndEndnote:        eStruxTypeStr = "PTX_EndEndnote       "; break;
176                 case PTX_EndAnnotation:     eStruxTypeStr = "PTX_EndAnnotation    "; break;
177                 case PTX_EndFrame:          eStruxTypeStr = "PTX_EndFrame         "; break;
178                 case PTX_EndTOC:            eStruxTypeStr = "PTX_EndTOC           "; break;
179                 case PTX_StruxDummy:        eStruxTypeStr = "PTX_StruxDummy       "; break;
180             }
181 
182             UT_DEBUGMSG(("          %s eStruxType:%d\n", eStruxTypeStr.c_str(), eStruxType ));
183         }
184         pos += pf->getLength();
185     }
186 
187     return true;
188 }
189 
190 
191 
192 
193 
194 #ifdef BUILD_ODT_GCT
195 /**
196  * Given a pointer to the start of a block, find the last PFT_Text
197  * contained in that block or null.
198  *
199  */
findLastTextFragOfBlock(pf_Frag_Strux * pfblock)200 static pf_Frag_Text* findLastTextFragOfBlock( pf_Frag_Strux* pfblock )
201 {
202     UT_DEBUGMSG(("ODTCT: findLastTxt() start:%p\n", pfblock ));
203     if( !pfblock )
204         return 0;
205 
206     pf_Frag* pf = pfblock->getNext();
207     pf_Frag_Text* ret = 0;
208     while( pf )
209     {
210         UT_DEBUGMSG(("ODTCT: findLastTxt() pftype:%d offset:%d len:%d\n", pf->getType(), pf->getPos(), pf->getLength() ));
211 
212         if( pf->getType() == pf_Frag::PFT_Text )
213         {
214 			ret = static_cast<pf_Frag_Text*>(pf);
215         }
216         if( tryDownCastStrux( pf, PTX_Block ))
217         {
218             //
219             // we have hit another block so return the
220             // "ret" which is a cache of the last PFT_Text
221             return ret;
222         }
223 
224         pf = pf->getNext();
225     }
226 
227     // we might have a ret!=0 if we started in the last block...
228     return ret;
229 }
230 #endif
231 
232 /**
233  * Return the strux PTX_Block if both startpos and endpos are
234  * contained in the same block. This way the method can be used both
235  * as a test to see if this is the case and the return value has added
236  * value if you actually want to inspect the block containing these
237  * positions too.
238  */
inSameBlock(PT_DocPosition startpos,PT_DocPosition endpos)239 pf_Frag_Strux* pt_PieceTable::inSameBlock( PT_DocPosition startpos, PT_DocPosition endpos )
240 {
241     pf_Frag_Strux* ret = 0;
242     pf_Frag_Strux* startBlock = _getBlockFromPosition( startpos );
243     pf_Frag_Strux* endBlock   = 0;
244     if(!_getStruxOfTypeFromPosition( endpos, PTX_Block, &endBlock ))
245     {
246         return ret;
247     }
248 
249     // pf_Frag *startFrag;
250     // PT_BlockOffset os;
251 
252     // if(!getFragFromPosition( startpos, &startFrag, &os ))
253     // {
254     //     UT_DEBUGMSG(("ODTCT: inSameBlock() can not get start, startpos:%d endpos:%d\n", startpos, endpos ));
255     //     return ret;
256     // }
257 
258     // pf_Frag_Strux *startBlock, *endBlock;
259     // if( pf_Frag_Strux* pfs = tryDownCastStrux( startFrag, PTX_Block ))
260     // {
261     //     startBlock = pfs;
262     // }
263     // else if(!_getStruxOfTypeFromPosition( startpos, PTX_Block, &startBlock ))
264     // {
265     //     return ret;
266     // }
267     // if(!_getStruxOfTypeFromPosition( endpos, PTX_Block, &endBlock ))
268     // {
269     //     return ret;
270     // }
271 
272     if( startBlock == endBlock )
273         ret = startBlock;
274 
275     return ret;
276 }
277 
278 
279 /**
280  *
281  * MIQ11: Work out if we are deleting from one paragraph to another in
282  * a way that we want to emit a delta:merge to have the two paragraphs
283  * merged in ODT. Appologies for the complexity of this method, there
284  * seem to be an interesting range of "what if..." questions that come
285  * up when you are deciding if something is a delta:merge or a
286  * delta:removed-content. Having a single method to trap all of those
287  * allows the caller to just mark things appropriately using the
288  * "revision" attribute.
289  *
290  * For .abw format the explicit marking of start/end/para deleted
291  * matters less. But the marking needs to be made so that ODT can work
292  * properly.
293  *
294  * The delta:merge and delta:removed-content allow two or more XML
295  * elements to be joined or removed respectively in ODT+GCT [1].
296  * Although I talk about these markup constructs from a serialized ODT
297  * file, it is best to work out what has happened at the time of
298  * document editing rather than try to work backwards at save time.
299  * These pieces of markup are put in place again when a ODT+GCT file
300  * is read so that they can again be used during a save.
301  *
302  * Basically delta:merge lets to join two paragraphs together into a
303  * single one. If you are just removing content from the start or end
304  * of a paragraph then you are better off just using
305  * delta:removed-content. If the deletion is for one or more complete
306  * paragraphs then it is cleaner to use delta:removed-content to get
307  * rid of those paragraphs. Likewise, if deleting from mid way through
308  * a paragraph to the end of a subsequent one then you are best off
309  * using delta:removed-content on the first paragraph content and
310  * delta:removed-content on the subsequent paragraphs. It is just
311  * these edge cases that this method contains and allows the caller to
312  * simply react if we are performing a delta:merge by marking things
313  * as needed.
314  *
315  *
316  * There are three attributes which should be set to the revision
317  * number in which they occur. The start and end deleted attributes
318  * should only be added when we are performing a delta:merge.
319  *
320  * ABIATTR_PARA_DELETED_REVISION
321  *     The whole paragraph is deleted
322  * ABIATTR_PARA_START_DELETED_REVISION
323  *     The start of the paragraph was deleted, causing it to join with
324  *     the previous paragraph
325  * ABIATTR_PARA_END_DELETED_REVISION
326  *     The end of this paragraph is deleted, causing a subsequent paragraph
327  *     to join it's content with this one.
328  *
329  * Consider first ABIATTR_PARA_END_DELETED_REVISION which should be
330  * set to the current revision if the deletion of the selection can
331  * join the next object at the same document scope.
332  *
333  * Note that this should only be done when the deletion ends in the
334  * middle of a paragraph. Otherwise, we should delete the end content
335  * of the paragraph (its pcdata or PFT_Text) and contain the other
336  * paragraph(s) in delta:removed-content.
337  *
338  * This is the case when:
339  *
340  ** deleting from a para to another para.
341  *
342  ** deleting from a para through an entire table to
343  *  another para
344  *
345  * This is NOT the case when:
346  *
347  ** deleting from a para into an image (draw:frame). In this case the
348  *  para content to its end is deleted and the image is wrapped in
349  *  delta:removed-content.
350  *
351  ** deleting from a para to a para in a table or to a para in another
352  *  cell of this or another table. In this case starting with para1,
353  *  para2...paran, tableA, ... celly If the selection extends from ra1
354  *  through into celly then The content of para1 "ra1" is deleted with
355  *  delta:removed-content. para2...paran are enclosed in
356  *  delta:removed-content to be deleted. Each cell in the selection
357  *  which from tableA is handled as a subdocument.
358  *
359  ***********************
360  *
361  * Firstly, this is not a delta:merge if the start and end pos are
362  * contained within the same paragraph (and startpos+1 != endpos, see
363  * below).
364  *
365  * __Document Level Considerations__
366  *
367  * Failing that, we want to get the paragraph (PTX_Block) for both the
368  * first and second position, if both exist then we need to make sure
369  * they are in the same table cell or no cell at all. Finding the cell
370  * is done by seeking backward from the Block.
371  *
372  * If we hit a table end or cell end marker (PTX_EndTable |
373  * PTX_EndCell) before we find a cell start (PTX_SectionCell) then we
374  * are in a paragraph that is at the top document scope. ie, a
375  * paragraph with "no cell".
376  *
377  * __Delete To and From the exact Start/End of Paragraph__
378  *
379  * Another two special cases; if we are deleting right through to the
380  * end of the last paragraph then we do not mark it as a delta:merge
381  * because we perfer to delete the end content of the first paragraph
382  * and delta:removed-content the other paragraphs as that is a simpler
383  * document. Similarly, if we are deleting from the start of a para
384  * through it's end then we mark that para with delta:removed-content
385  * and remove the starting content from the last para. If there are
386  * one or more paragraphs between these start/end cases they should be
387  * wrapped in delta:removed-content too.
388  *
389  * __Single Character Delete/Backspace To Merge__
390  * __Delete from starting PTX_Block into the para__
391  *
392  * When marging two paragraphs using the backspace or delete key, the
393  * range is only a single character: startpos+1 == endpos. The
394  * startpos will be the PTX_Block and the endpos likely the first char
395  * in the PFT_Text. This case is a delta:merge because we are merging the
396  * second paragraph into the content of the first. Note that we handle
397  * this case when the start and end blocks are the same even when
398  * endpos > startpos+1 because the user might have a selection
399  * starting at the PTX_Block and into the second paragraph and delete
400  * the lot in one operation.
401  *
402  *
403  ***********************
404  *
405  * NOTES:
406  *
407  * __Selection to Delete Para__
408  *
409  * Note that in the GUI you have to select from the start of the
410  * previous line in order to delete a paragraph or to merge it with
411  * the previous one. eg, if the document text is as below, to merge
412  * the second paragraph you either place the carrot at the start of
413  * the second line and backspace or the end of the first line and
414  * delete.
415  *
416  * This is para1
417  * And the second one.
418  *
419  * This seems natural enough, but to delete the whole second paragraph
420  * you have to start the text selection from the carrot position just
421  * after the "para1" string, ie, the end of the first line instead of
422  * the start of the second line.
423  *
424  * __Variable Names__
425  *
426  * MIQ11: I have omitted the pf prefix to pointers to fragments
427  * because most of the local variables are of that type.
428  *
429  *
430  ***********************
431  *
432  * [1]  http://monkeyiq.blogspot.com/2011/04/change-tracking-why.html
433  */
434 #ifdef BUILD_ODT_GCT
deleteSpanChangeTrackingAreWeMarkingDeltaMerge(PT_DocPosition startpos,PT_DocPosition endpos)435 bool pt_PieceTable::deleteSpanChangeTrackingAreWeMarkingDeltaMerge( PT_DocPosition startpos,
436                                                                     PT_DocPosition endpos )
437 {
438     bool ret = false;
439 
440 #if DEBUG
441     dumpDoc( "areWeMarkingDM(top)", 0, 0 );
442     UT_DEBUGMSG(("ODTCT: areWeMarkingDM() startpos:%d endpos:%d\n", startpos, endpos ));
443 #endif
444 
445     pf_Frag *startFrag, *endFrag;
446     PT_BlockOffset os,oe;
447 
448     if(!getFragFromPosition( startpos, &startFrag, &os ))
449     {
450         UT_DEBUGMSG(("ODTCT: areWeMarkingDM() can not get start, startpos:%d endpos:%d\n", startpos, endpos ));
451         return ret;
452     }
453 
454     if(!getFragFromPosition( endpos, &endFrag, &oe ))
455     {
456         UT_DEBUGMSG(("ODTCT: areWeMarkingDM() can not get end, startpos:%d endpos:%d\n", startpos, endpos ));
457         return ret;
458     }
459     pf_Frag_Strux *startBlock, *endBlock;
460     if( pf_Frag_Strux* pfs = tryDownCastStrux( startFrag, PTX_Block ))
461     {
462         UT_DEBUGMSG(("ODTCT: areWeMarkingDM() deleting right from start of para block...\n" ));
463         startBlock = pfs;
464     }
465     else if(!_getStruxOfTypeFromPosition( startpos, PTX_Block, &startBlock ))
466     {
467         UT_DEBUGMSG(("ODTCT: areWeMarkingDM() can not get start block, startpos:%d endpos:%d\n", startpos, endpos ));
468         return ret;
469     }
470 
471 
472     if(!_getStruxOfTypeFromPosition( endpos, PTX_Block, &endBlock ))
473     {
474         UT_DEBUGMSG(("ODTCT: areWeMarkingDM() can not get end block, startpos:%d endpos:%d\n", startpos, endpos ));
475         return ret;
476     }
477 
478 #if DEBUG
479     UT_DEBUGMSG(("ODTCT: areWeMarkingDM(starting work...) startpos:%d endpos:%d\n", startpos, endpos ));
480     UT_DEBUGMSG(("ODTCT: areWeMarkingDM(soffset) pos:%d len:%d\n", startFrag->getPos(), startFrag->getLength() ));
481     UT_DEBUGMSG(("ODTCT: areWeMarkingDM(eoffset) pos:%d len:%d\n", endFrag->getPos(), endFrag->getLength() ));
482     UT_DEBUGMSG(("ODTCT: areWeMarkingDM(sblock ) pos:%d len:%d\n", startBlock->getPos(), startBlock->getLength() ));
483     UT_DEBUGMSG(("ODTCT: areWeMarkingDM(eblock ) pos:%d len:%d\n", endBlock->getPos(), endBlock->getLength() ));
484 #endif
485 
486     /*
487      * Very likely not a delta:merge if we are in the same block.
488      */
489     if( startBlock && startBlock == endBlock )
490     {
491         /*
492          * The special case here is merging of two paragraphs using
493          * the delete or backspace key or a selection which causes
494          * merge. In this case the startBlock is right on the
495          * PTX_Block and the endBlock should be the start of the
496          * PFT_Text (for delete or backspace). However, if the
497          * selection extends into the para more than 1 char we are
498          * still wanting to merge.
499          */
500         if( tryDownCastStrux( startFrag, PTX_Block ) )
501         {
502             pf_Frag* lastFragOfEnd = findLastTextFragOfBlock( endBlock );
503             if( lastFragOfEnd->getPos() + lastFragOfEnd->getLength() == endpos )
504             {
505                 UT_DEBUGMSG(("ODTCT: areWeMarkingDM() start and end are the same block and you are deleting the whole para.\n" ));
506                 return false;
507             }
508 
509             UT_DEBUGMSG(("ODTCT: areWeMarkingDM() start and end are the same block BUT we are merging two paras with delete or backspace\n" ));
510             return true;
511         }
512 
513         UT_DEBUGMSG(("ODTCT: areWeMarkingDM() start and end are the same block... not a delta:merge\n" ));
514         return false;
515     }
516 
517 
518     pf_Frag* lastFragOfEnd = findLastTextFragOfBlock( endBlock );
519     if( lastFragOfEnd )
520         UT_DEBUGMSG(("ODTCT: areWeMarkingDM(elastB ) pos:%d len:%d\n", lastFragOfEnd->getPos(), lastFragOfEnd->getLength() ));
521 
522     PTStruxType stopCondition[] = { PTX_EndCell, PTX_EndTable, PTX_StruxDummy };
523     bool bSkipEmbededSections = true;
524     pf_Frag_Strux *startCell = _findLastStruxOfType( startBlock, PTX_SectionCell, stopCondition, bSkipEmbededSections );
525     pf_Frag_Strux *endCell   = _findLastStruxOfType( endBlock,   PTX_SectionCell, stopCondition, bSkipEmbededSections );
526 
527 #if DEBUG
528     UT_DEBUGMSG(("ODTCT: areWeMarkingDM(cells ) start:%p end:%p\n", startCell, endCell ));
529     if( startCell )
530         UT_DEBUGMSG(("ODTCT: areWeMarkingDM(scell ) pos:%d len:%d\n", startCell->getPos(), startCell->getLength() ));
531     if( endCell )
532         UT_DEBUGMSG(("ODTCT: areWeMarkingDM(ecell ) pos:%d len:%d\n", endCell->getPos(), endCell->getLength() ));
533     if( lastFragOfEnd )
534         UT_DEBUGMSG(("ODTCT: areWeMarkingDM(elast ) pos:%d len:%d\n", lastFragOfEnd->getPos(), lastFragOfEnd->getLength() ));
535     if( endBlock )
536         UT_DEBUGMSG(("ODTCT: areWeMarkingDM(block ) pos:%d len:%d\n", endBlock->getPos(), endBlock->getLength() ));
537 #endif
538 
539     // no cell or same cell.
540     if( (!startCell && !endCell) || (startCell == endCell) )
541     {
542         /*
543         * If we are deleting through to the middle of another
544         * paragraph then we need to use delta:merge to join them.
545         */
546         ret = true;
547         UT_DEBUGMSG(("ODTCT: areWeMarkingDM(ret) in same or no cell, startBlock:%d startpos:%d\n",
548                      startBlock->getPos(), startpos ));
549 
550         /*
551         * On the other hand, if we are deleting from a paragraph
552         * through to the end of another paragraph then we prefer
553         * instead to mark the end content of the first paragraph as
554         * deleted and use delta:removed-content to mark the other
555         * paragraph(s) as deleted. Simpler markup this way.
556         *
557         * lastFragOfEnd is the last fragment contained in the last block.
558         * if it has an ending that is exactly the deletion endpos
559         * then we are not deleting *into* the paragraph but to the end of it.
560         */
561         if( lastFragOfEnd &&
562             ( lastFragOfEnd->getPos() + lastFragOfEnd->getLength() == endpos ))
563         {
564             UT_DEBUGMSG(("ODTCT: areWeMarkingDM(ret) deletion is right to end of last paragraph!\n" ));
565             ret = false;
566         }
567 
568 
569         /*
570          * Likewise, if we are deleting from the very start of a paragraph through
571          * to another para then we mark the entire first para as delta:removed-content
572          * and just remove the leading content in the last para as deleted.
573          */
574         if( startBlock && startBlock->getPos() == startpos )
575         {
576             UT_DEBUGMSG(("ODTCT: areWeMarkingDM(ret) deletion from right at start of first paragraph!\n" ));
577             ret = false;
578         }
579 
580     }
581 
582     return ret;
583 }
584 #endif
585 
586 /**
587  * Get the first strux that marks the end of the block containing
588  * currentpos or null. The end of block strux has to be positioned
589  * before endpos or null is returned.
590  *
591  * Note that if currentpos is itself a block this method will move
592  * over that PTX_Block before searching for the end of block.
593  */
getEndOfBlock(PT_DocPosition currentpos,PT_DocPosition endpos)594 pf_Frag* pt_PieceTable::getEndOfBlock( PT_DocPosition currentpos, PT_DocPosition endpos )
595 {
596     pf_Frag *pf;
597     PT_BlockOffset Offset;
598     bool bFoundEndBlockBeforeEndpos = false;
599 
600     UT_DEBUGMSG(("ODTCT: getEndOfBlock() cpos:%d e:%d\n", currentpos, endpos ));
601 
602     // If the frag at currentpos is a block move past it.
603     if(getFragFromPosition( currentpos, &pf, &Offset ))
604     {
605         if( tryDownCastStrux( pf, PTX_Block ))
606         {
607             ++currentpos;
608         }
609     }
610 
611     for( PT_DocPosition pos = currentpos; !bFoundEndBlockBeforeEndpos && pos <= endpos; )
612     {
613         if(!getFragFromPosition( pos, &pf, &Offset ))
614         {
615             UT_DEBUGMSG(("ODTCT: getEndOfBlock() NO FRAG AT pos:%d\n", pos ));
616             break;
617         }
618 
619         UT_DEBUGMSG(("ODTCT: getEndOfBlock() pos:%d frag:%p len:%d frag type:%d\n", pos, pf, pf->getLength(), pf->getType() ));
620 
621         if( pf->getType() == pf_Frag::PFT_EndOfDoc )
622         {
623             return 0;
624         }
625 
626         if( pf->getType() == pf_Frag::PFT_Strux )
627         {
628             PTStruxType eStruxType = static_cast<pf_Frag_Strux*>(pf)->getStruxType();
629             switch (eStruxType)
630             {
631                 case PTX_SectionFootnote:
632                 case PTX_SectionEndnote:
633                 case PTX_SectionAnnotation:
634                     break;
635                 default:
636                     return pf;
637             }
638         }
639 
640         pos = pf->getPos() + pf->getLength();
641     }
642     return 0;
643 }
644 
645 /**
646  * Add a change tracking attribute/value to the given fragment. If the
647  * attribute already exists on the fragment then it is not modified.
648  */
649 #ifdef BUILD_ODT_GCT
changeTrackingAddParaAttribute(pf_Frag_Strux * pfs,const char * attr,std::string v)650 bool pt_PieceTable::changeTrackingAddParaAttribute( pf_Frag_Strux* pfs,
651                                                     const char* attr,
652                                                     std::string v )
653 {
654     const PP_AttrProp * pAP2;
655     if(!getAttrProp(pfs->getIndexAP(),&pAP2))
656     {
657         UT_DEBUGMSG(("Can not get the attrProp for a fragment when adding change tracking information!\n" ));
658         return false;
659     }
660     else
661     {
662         const gchar name[] = "revision";
663         const gchar * pRevision = NULL;
664 
665         if(!pAP2->getAttribute(name, pRevision))
666             pRevision = NULL;
667         PP_RevisionAttr Revisions(pRevision);
668         if( pRevision && strstr(pRevision, attr ))
669         {
670             // already have that attribute..
671             return true;
672         }
673         else
674         {
675             Revisions.mergeAttr( 1, PP_REVISION_ADDITION_AND_FMT,
676                                  attr, v.c_str() );
677 
678             const gchar * ppRevAttrib[3];
679             ppRevAttrib[0] = name;
680             ppRevAttrib[1] = Revisions.getXMLstring();
681             ppRevAttrib[2] = NULL;
682 
683             UT_DEBUGMSG(("ODTCT: changeTrackingAddParaAttribute() adding attr:%s v:%s for block at:%d\n",
684                          attr, v.c_str(), pfs->getPos() ));
685 
686             int iLen = pf_FRAG_STRUX_BLOCK_LENGTH;
687             PTStruxType eStruxType = pfs->getStruxType();
688 
689             if(! _realChangeStruxFmt(PTC_AddFmt, pfs->getPos() + iLen, pfs->getPos() + iLen,
690                                      ppRevAttrib, NULL,
691                                      eStruxType, true))
692             {
693                 return false;
694             }
695         }
696     }
697 
698     return true;
699 }
700 #endif
701 
702 /*
703  * If we are deleting a selection for which a delta:merge is in
704  * progress, this method adds ABIATTR_PARA_END_DELETED_REVISION to the
705  * block only if the block ends before endpos
706  */
707 #ifdef BUILD_ODT_GCT
deleteSpanChangeTrackingMaybeMarkParagraphEndDeletion(PT_DocPosition currentpos,PT_DocPosition endpos)708 bool pt_PieceTable::deleteSpanChangeTrackingMaybeMarkParagraphEndDeletion( PT_DocPosition currentpos,
709                                                                            PT_DocPosition endpos )
710 {
711 #if DEBUG
712     dumpDoc( "deleteSpanChangeTrackingMaybeMarkParagraphEndDeletion(top)", 0, 0 );
713     UT_DEBUGMSG(("ODTCT: deleteSpanChangeTrackingMaybeMarkParagraphEndDeletion() cpos:%d endpos:%d\n", currentpos, endpos ));
714 #endif
715 
716     //
717     // MIQ11: If we are deleting from the middle through the end of
718     // a paragraph then we want to record when the end of the
719     // paragraph was deleted. We need to get the PTX_Block, say
720     // (a), containing the start marker currentpos and make sure that
721     // (a) is closed before endpos is reached.
722     //
723     // First, walk forwards to see if the block will end before
724     // endpos is reached. If so, find the strux (a) that contains
725     // currentpos and mark it as having it's end of block at this
726     // revision.
727     {
728         //
729         // Find the first end-of-block condition that is yonder of
730         // currentpos
731         //
732         UT_DEBUGMSG(("ODTCT: deleteSpan(revisionsEP) searching for eob from:%d\n", currentpos ));
733         pf_Frag *pf = getEndOfBlock( currentpos, endpos );
734         if( !pf )
735         {
736             UT_DEBUGMSG(("ODTCT: deleteSpan(revisionsEP) no end of block!\n" ));
737             return false;
738         }
739 
740         UT_DEBUGMSG(("ODTCT: deleteSpan(revisionsEP) block that ends the currentpos starter is %p at offset:%d len:%d\n",
741                      pf, pf->getPos(), pf->getLength() ));
742 
743 
744         //
745         // find and mark the block containing currentpos as having its
746         // ending deleted in this revision.
747         //
748         pf_Frag_Strux * pfs;
749         PTStruxType eStruxType = PTX_Block;
750         if(!_getStruxOfTypeFromPosition( currentpos, eStruxType, &pfs ))
751         {
752             // failed
753             UT_DEBUGMSG(("ODTCT: deleteSpan(revisionsEP) delete started not inside a ptx_block! currentpos:%d\n", currentpos ));
754             return false;
755         }
756         else
757         {
758             UT_DEBUGMSG(("ODTCT: deleteSpan(revisionsEP) TOP currentpos:%d\n", currentpos ));
759             UT_DEBUGMSG(("ODTCT: deleteSpan(revisionsEP) TOP text strux:%p\n", pfs ));
760 
761             changeTrackingAddParaAttribute( pfs,
762                                             ABIATTR_PARA_END_DELETED_REVISION,
763                                             tostr(m_pDocument->getRevisionId()));
764         }
765     }
766 
767     return true;
768 }
769 #endif
770 
771 
772 
773 /****************************************************************/
deleteSpan(PT_DocPosition dpos1,PT_DocPosition dpos2,PP_AttrProp * p_AttrProp_Before,UT_uint32 & iRealDeleteCount,bool bDeleteTableStruxes,bool bDontGlob)774 bool pt_PieceTable::deleteSpan(PT_DocPosition dpos1,
775 							   PT_DocPosition dpos2,
776 							   PP_AttrProp *p_AttrProp_Before,
777 							   UT_uint32 &iRealDeleteCount,
778 							   bool bDeleteTableStruxes,
779 							   bool bDontGlob)
780 {
781 #ifdef BUILD_ODT_GCT
782     PT_DocPosition startOfRange = dpos1;
783 #endif
784 
785   //        getFragments().verifyDoc();
786 	if(m_pDocument->isMarkRevisions())
787 	{
788 
789 #if DEBUG
790         UT_DEBUGMSG(("ODTCT: deleteSpan(revisions) TOP dpos1:%d dpos2:%d\n", dpos1, dpos2 ));
791         dumpDoc( "deleteSpan(top)", 0, 0 );
792 #endif
793 
794 		bool bHdrFtr = false;
795 		// if the user selected the whole document for deletion, we will not delete the
796 		// first block (we need always a visible block in any document); we make an
797 		// exception to this in VDND, because in that case the original block is
798 		// guaranteed to come back
799 		//
800 		// NB: it is possible that the user might delete all contents in several separate
801 		// steps; there is no easy way to protect against that
802 		bool bWholeDoc = false;
803 		if(!m_pDocument->isVDNDinProgress())
804 		{
805 			pf_Frag * pLast = getFragments().getLast();
806 			bWholeDoc = (dpos1 <= 2 && pLast->getPos() == dpos2);
807 		}
808 
809 		iRealDeleteCount = 0;
810 
811 		const gchar name[] = "revision";
812 		const gchar * pRevision = NULL;
813 
814 
815 #ifdef BUILD_ODT_GCT
816         bool MarkingDeltaMerge = deleteSpanChangeTrackingAreWeMarkingDeltaMerge( dpos1, dpos2 );
817         UT_DEBUGMSG(("ODTCT: deleteSpan(revisions) TOP2 dpos1:%d dpos2:%d\n", dpos1, dpos2 ));
818         UT_DEBUGMSG(("ODTCT: deleteSpan(revisions) TOP3 MarkingDeltaMerge:%d\n", MarkingDeltaMerge ));
819 
820         if( MarkingDeltaMerge )
821         {
822             deleteSpanChangeTrackingMaybeMarkParagraphEndDeletion( dpos1, dpos2 );
823 
824             /*
825              * For merging paragraphs by pressing delete when the carrot is on
826              * the end of the last line of a paragraph. So dpos1 is the PTX_Block itself
827              * and we need to grab the previous block to dpos1 and mark it's end deleted.
828              */
829             if(pf_Frag_Strux* pfs = inSameBlock( dpos1, dpos2 ))
830             {
831                 UT_DEBUGMSG(("ODTCT: deleteSpan(revisions) isSameBlock at:%d\n", pfs->getPos() ));
832                 bool bSkipEmbededSections = true;
833                 pf_Frag_Strux* prevBlock = _findLastStruxOfType( pfs->getPrev(), PTX_Block, bSkipEmbededSections );
834                 if( prevBlock )
835                 {
836                     UT_DEBUGMSG(("ODTCT: deleteSpan(revisions) prevBlock at:%d\n", prevBlock->getPos() ));
837 
838                     changeTrackingAddParaAttribute( prevBlock,
839                                                     ABIATTR_PARA_END_DELETED_REVISION,
840                                                     tostr(m_pDocument->getRevisionId()));
841                 }
842             }
843         }
844 #endif
845 
846 
847 		// we cannot retrieve the start and end fragments here and
848 		// then work between them in a loop using getNext() because
849 		// processing might result in merging of fragments. so we have
850 		// to use the doc position to keep track of where we are and
851 		// retrieve the fragments afresh in each step of the loop
852 		// Tomas, Oct 28, 2003
853 
854 		bool bRet = false;
855 		while(dpos1 < dpos2)
856 		{
857 			// first retrive the starting and ending fragments
858 			pf_Frag * pf1, * pf2;
859 			PT_BlockOffset Offset1, Offset2;
860 			bool bTableStrux = false;
861 			bool bHasEndStrux = false;
862 			UT_sint32 iTableDepth = 0;
863 
864             // UT_DEBUGMSG(("ODTCT: deleteSpan(revisions) while.top dpos1:%d dpos2:%d\n", dpos1, dpos2 ));
865 
866 			if(!getFragsFromPositions(dpos1,dpos2, &pf1, &Offset1, &pf2, &Offset2))
867 				return bRet;
868 			else
869 				bRet = true;
870 
871             // UT_DEBUGMSG(("ODTCT: deleteSpan(revisions) while.2 dpos1:%d dpos2:%d\n", dpos1, dpos2 ));
872             // UT_DEBUGMSG(("ODTCT: deleteSpan(revisions) while.2 types pf1:%d pf2:%d pf1.len:%d\n",
873             //              pf1->getType(), pf2->getType(), pf1->getLength() ));
874 
875 			// get attributes for this fragement
876 			const PP_AttrProp * pAP2;
877 			pf_Frag::PFType eType = pf1->getType();
878 			UT_uint32 iLen = 1;
879 			PTStruxType eStruxType = PTX_StruxDummy;
880 
881 			if(eType == pf_Frag::PFT_Text)
882 			{
883 				if(!getAttrProp(static_cast<pf_Frag_Text*>(pf1)->getIndexAP(),&pAP2))
884 					return false;
885 			}
886 			else if(eType == pf_Frag::PFT_Strux)
887 			{
888 				if(!getAttrProp(static_cast<pf_Frag_Strux*>(pf1)->getIndexAP(),&pAP2))
889 					return false;
890 
891 				eStruxType = static_cast<pf_Frag_Strux*>(pf1)->getStruxType();
892 
893 #ifdef BUILD_ODT_GCT
894                 UT_DEBUGMSG(("ODTCT: deleteSpan(revisions) while... eStruxType:%d\n", eStruxType ));
895 
896                 if( dpos1 > startOfRange )
897                 {
898                     // Mark close of block
899                     switch (eStruxType)
900                     {
901                         case PTX_SectionFootnote:
902                         case PTX_SectionEndnote:
903                         case PTX_SectionAnnotation:
904                             break;
905                         default:
906                             // If this block ends before dpos2,
907                             // we should also explicitly mark that it's end is deleted.
908                             if( MarkingDeltaMerge )
909                             {
910                                 deleteSpanChangeTrackingMaybeMarkParagraphEndDeletion( dpos1-1, dpos2 );
911                             }
912                             break;
913                     }
914                 }
915 #endif
916 
917 
918 				switch (eStruxType)
919 				{
920 					case PTX_Block:
921 						iLen = pf_FRAG_STRUX_BLOCK_LENGTH;
922 						if(bWholeDoc && dpos1 == 2)
923 						{
924 							dpos1 += iLen;
925 							continue;
926 						}
927 
928                         // UT_DEBUGMSG(("ODTCT: deleteSpan(revisions) block... dpos1:%d dpos2:%d len:%d\n", dpos1, dpos2, iLen ));
929 						break;
930 
931 					case PTX_SectionTable:
932 						iTableDepth = 1;
933 						// fall through
934 					case PTX_SectionCell:
935 						bTableStrux = true;
936 						bHasEndStrux = true;
937 						iLen = pf_FRAG_STRUX_SECTION_LENGTH;
938 						break;
939 
940 					case PTX_EndCell:
941 					case PTX_EndTable:
942 						bTableStrux = true;
943 						iLen = pf_FRAG_STRUX_SECTION_LENGTH;
944 						break;
945 
946 					case PTX_SectionEndnote:
947 					case PTX_SectionFootnote:
948 					case PTX_SectionAnnotation:
949 					case PTX_SectionFrame:
950 					case PTX_SectionTOC:
951 						bHasEndStrux = true;
952 						// fall through ...
953 					case PTX_SectionHdrFtr:
954 						bHdrFtr = true;
955 						// fall through
956 					case PTX_Section:
957 				    case PTX_EndFootnote:
958 				    case PTX_EndEndnote:
959 				    case PTX_EndAnnotation:
960 				    case PTX_EndFrame:
961 					case PTX_EndTOC:
962 						iLen = pf_FRAG_STRUX_SECTION_LENGTH;
963 						break;
964 
965 					default:
966 						UT_ASSERT_HARMLESS(UT_NOT_IMPLEMENTED);
967 						iLen = 1;
968 						break;
969 				}
970 
971 			}
972 			else if(eType == pf_Frag::PFT_Object)
973 			{
974 				if(!getAttrProp(static_cast<pf_Frag_Object*>(pf1)->getIndexAP(),&pAP2))
975 					return false;
976 			}
977 			else if(eType == pf_Frag:: PFT_FmtMark)
978 			{
979 				iLen = 0;
980 				if(!getAttrProp(static_cast<pf_Frag_Object*>(pf1)->getIndexAP(),&pAP2))
981 					return false;
982 				// something that does not carry AP
983 			}
984 			else if (eType == pf_Frag:: PFT_EndOfDoc)
985 			{
986 				break;
987 			}
988 			else
989 			{
990 				UT_ASSERT(0); // Dunno what this could be
991 				break;
992 			}
993 
994             // UT_DEBUGMSG(("ODTCT: deleteSpan(revisions) while bTableStrux:%d bDeleteTableStruxes:%d\n",
995             //              bTableStrux, bDeleteTableStruxes ));
996 			if(bTableStrux && !bDeleteTableStruxes)
997 			{
998 				// skip over this frag
999 				dpos1 += iLen;
1000 				continue;
1001 			}
1002 
1003 			if(!pAP2->getAttribute(name, pRevision))
1004 				pRevision = NULL;
1005 
1006             // UT_DEBUGMSG(("ODTCT: deleteSpan(revisions) while.3 dpos1:%d dpos2:%d pRevision:%p\n", dpos1, dpos2, pRevision ));
1007             // if( pRevision )
1008                 // UT_DEBUGMSG(("ODTCT: deleteSpan(revisions) while.3 pRevision.str:%s\n", pRevision ));
1009 
1010 
1011 			PP_RevisionAttr Revisions(pRevision);
1012 
1013 			// now we need to see if revision with this id is already
1014 			// present, and if it is, whether it might not be addition
1015 			UT_uint32 iId = m_pDocument->getRevisionId();
1016 			const PP_Revision * pS;
1017 			const PP_Revision * pRev = Revisions.getGreatestLesserOrEqualRevision(iId, &pS);
1018 
1019 			PT_DocPosition dposEnd = UT_MIN(dpos2,dpos1 + pf1->getLength());
1020 
1021 #ifdef BUILD_ODT_GCT
1022 #if DEBUG
1023             {
1024                 UT_uint32 gid = 100;
1025                 if( pRev )
1026                 {
1027                     UT_DEBUGMSG(("ODTCT: deleteSpan(revisions) while.5 have pRev, iId:%d pRev->id::%d\n", iId, pRev->getId() ));
1028                     gid = pRev->getId();
1029                 }
1030                 UT_DEBUGMSG(("ODTCT: deleteSpan(revisions) while.4 pRev:%d iId:%d getId:%d dposEnd:%d\n", pRev!=0, iId, gid, dposEnd ));
1031             }
1032 #endif
1033 #endif
1034 
1035 			if(pRev && iId == pRev->getId())
1036 			{
1037 				// OK, we already have a revision with this id here,
1038 				// which means that the user made a change earlier
1039 				// (insertion or format change) but now wants this deleted
1040 				//
1041 				// so if the previous revision is an addition, we just
1042 				// remove this fragment as if this was regular delete
1043 				if(   (pRev->getType() == PP_REVISION_ADDITION)
1044 				   || (pRev->getType() == PP_REVISION_ADDITION_AND_FMT ))
1045 				{
1046 					// if this fragment is one of the struxes that is paired with an end-strux, we
1047 					// need to delete both struxes and everything in between
1048 					if(bHasEndStrux || bHdrFtr)
1049 					{
1050 						PT_DocPosition posEnd = dposEnd;
1051 						for(pf_Frag * pf = pf1->getNext(); pf != NULL; pf = pf->getNext())
1052 						{
1053 							posEnd += pf->getLength();
1054 
1055 							if(pf_Frag::PFT_Strux != pf->getType())
1056 								continue;
1057 
1058 							pf_Frag_Strux * pfs = (pf_Frag_Strux*) pf;
1059 							PTStruxType eStrux2Type = pfs->getStruxType();
1060 
1061 							if(eStrux2Type == PTX_SectionTable)
1062 								iTableDepth++;
1063 							else if (eStrux2Type == PTX_EndTable)
1064 								iTableDepth--;
1065 
1066 
1067 							switch(eStruxType )
1068 							{
1069 								case PTX_SectionEndnote:
1070 									if(eStrux2Type != PTX_EndEndnote)
1071 										continue;
1072 									break;
1073 								case PTX_SectionTable:
1074 									if(iTableDepth > 0 || eStrux2Type != PTX_EndTable)
1075 										continue;
1076 									break;
1077 								case PTX_SectionCell:
1078 									if(iTableDepth > 0 || eStrux2Type != PTX_EndCell)
1079 										continue;
1080 									break;
1081 								case PTX_SectionFootnote:
1082 									if(eStrux2Type != PTX_EndFootnote)
1083 										continue;
1084 									break;
1085 								case PTX_SectionAnnotation:
1086 									if(eStrux2Type != PTX_EndAnnotation)
1087 										continue;
1088 									break;
1089 								case PTX_SectionFrame:
1090 									if(eStrux2Type != PTX_EndFrame)
1091 										continue;
1092 									break;
1093 								case PTX_SectionTOC:
1094 									if(eStrux2Type != PTX_EndTOC)
1095 										continue;
1096 									break;
1097 								case PTX_SectionHdrFtr:
1098 									if(eStrux2Type != PTX_SectionHdrFtr)
1099 										continue;
1100 									break;
1101 							    default:
1102 									break;
1103 							}
1104 
1105 							// if we got this far, we found what we are looking for and we have the
1106 							// correct end position
1107 							break;
1108 						}
1109 
1110 						dposEnd = posEnd;
1111 					}
1112 
1113 					if(bHdrFtr)
1114 					{
1115 						bHdrFtr = false; // only do this once
1116 						pf_Frag_Strux_SectionHdrFtr * pfHdr = static_cast<pf_Frag_Strux_SectionHdrFtr *>(pf1);
1117 
1118 						const PP_AttrProp * pAP = NULL;
1119 
1120 						if(!getAttrProp(pfHdr->getIndexAP(),&pAP) || !pAP)
1121 							return false;
1122 
1123 						const gchar * pszHdrId;
1124 						if(!pAP->getAttribute("id", pszHdrId) || !pszHdrId)
1125 							return false;
1126 
1127 						const gchar * pszHdrType;
1128 						if(!pAP->getAttribute("type", pszHdrType) || !pszHdrType)
1129 							return false;
1130 
1131 						// needs to be in this order because of undo
1132 						_realDeleteHdrFtrStrux(static_cast<pf_Frag_Strux*>(pf1));
1133 						_fixHdrFtrReferences(pszHdrType, pszHdrId);
1134 					}
1135 					else
1136 					{
1137                         // UT_DEBUGMSG(("ODTCT: deleteSpan(revisions) calling realDeleteSpan %d %d\n", dpos1, dposEnd ));
1138 
1139 						if(!_realDeleteSpan(dpos1, dposEnd, p_AttrProp_Before,bDeleteTableStruxes,
1140 											bDontGlob))
1141 							return false;
1142 					}
1143 
1144 					UT_uint32 iDeleteThisStep = dposEnd - dpos1;
1145 
1146 					iRealDeleteCount += iDeleteThisStep;
1147 
1148 					// because we removed stuff, the position dpos1 remains the same and dpos2 needs
1149 					// to be adjusted
1150 					if(dpos2 > iDeleteThisStep)
1151 						dpos2 -= iDeleteThisStep;
1152 					else
1153 						dpos2 = 0;
1154 
1155 					continue;
1156 				}
1157 			}
1158 
1159 #ifdef BUILD_ODT_GCT
1160             //
1161             // handle the marking of DELETED and START_DELETED
1162             //
1163             if( eStruxType == PTX_Block )
1164             {
1165                 std::string idstr = tostr(iId);
1166                 UT_DEBUGMSG(("ODTCT: deleteSpan(revisions) old:%s\n", Revisions.getXMLstring() ));
1167                 UT_DEBUGMSG(("ODTCT: deleteSpan(revisions) finding eob...\n" ));
1168 
1169                 if( pf_Frag *endOfblock = getEndOfBlock( dpos1, dpos2 ) )
1170                 {
1171                     UT_DEBUGMSG(("ODTCT: deleteSpan(revisions) eob pos:%d len:%d dpos1:%d dpos2:%d\n",
1172                                  endOfblock->getPos(), endOfblock->getLength(), dpos1, dpos2 ));
1173 
1174                     Revisions.mergeAttrIfNotAlreadyThere( 1, PP_REVISION_ADDITION_AND_FMT,
1175                                                           ABIATTR_PARA_DELETED_REVISION,
1176                                                           idstr.c_str() );
1177                 }
1178 
1179                 if( MarkingDeltaMerge )
1180                 {
1181                     UT_DEBUGMSG(("ODTCT: deleteSpan(revisions) marking delta:merge...\n" ));
1182 
1183                     Revisions.mergeAttrIfNotAlreadyThere( 1, PP_REVISION_ADDITION_AND_FMT,
1184                                                           ABIATTR_PARA_START_DELETED_REVISION,
1185                                                           idstr.c_str() );
1186                 }
1187 
1188                 UT_DEBUGMSG(("ODTCT: deleteSpan(revisions) DONE adding, revs:%s\n", Revisions.getXMLstring() ));
1189             }
1190 #endif
1191 
1192 			Revisions.addRevision(iId,PP_REVISION_DELETION,NULL,NULL);
1193             // UT_DEBUGMSG(("ODTCT: deleteSpan(revisions) 2...\n" ));
1194 			const gchar * ppRevAttrib[3];
1195 			ppRevAttrib[0] = name;
1196 			ppRevAttrib[1] = Revisions.getXMLstring();
1197 			ppRevAttrib[2] = NULL;
1198 
1199             // UT_DEBUGMSG(("ODTCT: deleteSpan(revisions) while.6 dpos1:%d dpos2:%d eType:%d RX:%s\n",
1200             //             dpos1, dpos2, eType, Revisions.getXMLstring() ));
1201 
1202 			switch (eType)
1203 			{
1204 				case pf_Frag::PFT_Object:
1205 				case pf_Frag::PFT_Text:
1206                     // UT_DEBUGMSG(("ODTCT: deleteSpan(revisions) addfmt dpos1:%d dposEnd:%d\n", dpos1, dposEnd ));
1207 					if(! _realChangeSpanFmt(PTC_AddFmt, dpos1, dposEnd, ppRevAttrib,NULL,true))
1208 						return false;
1209 
1210 
1211                     //
1212                     // MIQ11: The above _realChangeSpanFmt() may have split the fragment,
1213                     //        So we should get the pf_Frag again and adjust dposEnd if nessesary
1214                     //
1215                     {
1216                         pf_Frag * pft;
1217                         PT_BlockOffset toffset;
1218                         getFragFromPosition(dpos1, &pft, &toffset);
1219                         UT_DEBUGMSG(("ODTCT: deleteSpan(revisions) addfmt dpos1:%d dposEnd:%d toffset:%d pftpos:%d pftlen:%d\n",
1220                                      dpos1, dposEnd, toffset, pft->getPos(), pft->getLength() ));
1221 
1222                         dposEnd = pft->getPos() + pft->getLength();
1223                     }
1224 
1225 					break;
1226 
1227 				case pf_Frag::PFT_Strux:
1228                     UT_DEBUGMSG(("ODTCT: deleteSpan(revisions) strux dpos1:%d iLen:%d\n", dpos1, iLen ));
1229 					// _realChangeStruxFmt() changes the strux
1230 					// *containing* the given position, hence we pass
1231 					// it the position immediately after the strux; we
1232 					// only want the one strux changed, so we pass
1233 					// identical position in both parameters
1234 					if(! _realChangeStruxFmt(PTC_AddFmt, dpos1 + iLen, dpos1 + iLen /*2*iLen*/, ppRevAttrib,NULL,
1235 											 eStruxType,true))
1236 						return false;
1237 
1238 					if(bHdrFtr)
1239 					{
1240 						// even though this is just a notional removal, we still have to
1241 						// fix the references
1242 						bHdrFtr = false; // only do this once
1243 						pf_Frag_Strux_SectionHdrFtr * pfHdr = static_cast<pf_Frag_Strux_SectionHdrFtr *>(pf1);
1244 
1245 						const PP_AttrProp * pAP = NULL;
1246 
1247 						if(!getAttrProp(pfHdr->getIndexAP(),&pAP) || !pAP)
1248 							return false;
1249 
1250 						const gchar * pszHdrId;
1251 						if(!pAP->getAttribute("id", pszHdrId) || !pszHdrId)
1252 							return false;
1253 
1254 						const gchar * pszHdrType;
1255 						if(!pAP->getAttribute("type", pszHdrType) || !pszHdrType)
1256 							return false;
1257 
1258 						_fixHdrFtrReferences(pszHdrType, pszHdrId, true);
1259                         // empty the strux listener chache since the pointers are now
1260                         // invalid
1261                         pfHdr->clearAllFmtHandles();
1262 					}
1263 
1264 					break;
1265 
1266 				default:;
1267 			}
1268 
1269 			dpos1 = dposEnd;
1270 		}
1271 
1272 		return true;
1273 	}
1274 	else
1275 		return _realDeleteSpan(dpos1, dpos2, p_AttrProp_Before, bDeleteTableStruxes,
1276 							   bDontGlob);
1277 }
1278 
1279 /*!
1280     scan piecetable and remove any references to the hdr/ftr located at pos dpos
1281     bNotional indicates that the header has been marked deleted in revison mode, but not
1282     physically removed from the document
1283 */
_fixHdrFtrReferences(const gchar * pszHdrType,const gchar * pszHdrId,bool bNotional)1284 bool pt_PieceTable::_fixHdrFtrReferences(const gchar * pszHdrType, const gchar * pszHdrId,
1285 										 bool bNotional /* = false */)
1286 {
1287 	UT_return_val_if_fail( pszHdrType && pszHdrId, false );
1288 
1289 	bool bRet = true;
1290 	const PP_AttrProp * pAP = NULL;
1291 
1292 	// look for any doc sections that referrence this header type and id
1293 	const pf_Frag * pFrag = m_fragments.getFirst();
1294 	while(pFrag)
1295 	{
1296 		if(pFrag->getType() == pf_Frag::PFT_Strux &&
1297 		   static_cast<const pf_Frag_Strux*>(pFrag)->getStruxType()==PTX_Section)
1298 		{
1299 			if(!getAttrProp(pFrag->getIndexAP(),&pAP) || !pAP)
1300 				continue;
1301 
1302 			// check for normal attribute
1303 			const gchar * pszMyHdrId2 = NULL;
1304 			if(pAP->getAttribute(pszHdrType, pszMyHdrId2) && pszMyHdrId2)
1305 			{
1306 				if(0 == strcmp(pszMyHdrId2, pszHdrId))
1307 				{
1308 					const gchar* pAttrs [3];
1309 					pAttrs[0] = pszHdrType;
1310 					pAttrs[1] = pszMyHdrId2;
1311 					pAttrs[2] = NULL;
1312 
1313 					bRet &= _fmtChangeStruxWithNotify(PTC_RemoveFmt, (pf_Frag_Strux*)pFrag,
1314 													  pAttrs, NULL, false);
1315 				}
1316 			}
1317 
1318 			// now check for revision attribute ...
1319 			const gchar * pszRevision;
1320 			if(pAP->getAttribute("revision", pszRevision) && pszRevision)
1321 			{
1322 				bool bFound = false;
1323 				PP_RevisionAttr Revisions(pszRevision);
1324 
1325 				for(UT_uint32 i = 0; i < Revisions.getRevisionsCount(); ++i)
1326 				{
1327 					const PP_Revision * pRev2 = Revisions.getNthRevision(i);
1328 					UT_return_val_if_fail( pRev2, false );
1329 
1330 					const gchar * pszMyHdrId = NULL;
1331 					if(pRev2->getAttribute(pszHdrType, pszMyHdrId) && pszMyHdrId)
1332 					{
1333 						if(0 != strcmp(pszHdrId, pszMyHdrId))
1334 							continue;
1335 
1336 						if(!bNotional)
1337 						{
1338 							// NB: this is safe, since we own the PP_RevisionAttr object
1339 							// of local scope which in turn owns this revisions
1340 							const_cast<PP_Revision*>(pRev2)->setAttribute(pszHdrType, "");
1341 						}
1342 						else
1343 						{
1344 							UT_uint32 iId = m_pDocument->getRevisionId();
1345 							UT_uint32 iMinId;
1346 							const PP_Revision * pRev = Revisions.getRevisionWithId(iId, iMinId);
1347 							if(pRev)
1348 							{
1349 								// NB: this is safe, since we own the PP_RevisionAttr object
1350 								// of local scope which in turn owns this revisions
1351 								const_cast<PP_Revision*>(pRev)->setAttribute(pszHdrType, "");
1352 							}
1353 							else
1354 							{
1355 								// we have a section that references this header in
1356 								// previous revision and has no changes in the current
1357 								// revision, so we need to add a new revisions in which
1358 								// the header is not referenced
1359 								const gchar * pAttrs [3] = {pszHdrType, pszHdrId, NULL};
1360 								Revisions.addRevision(iId, PP_REVISION_FMT_CHANGE, pAttrs, NULL);
1361 							}
1362 						}
1363 
1364 						Revisions.forceDirty();
1365 						bFound = true;
1366 					}
1367 				}
1368 
1369 				if(bFound)
1370 				{
1371 					const gchar* pAttrs [3];
1372 					pAttrs[0] = "revision";
1373 					pAttrs[1] = Revisions.getXMLstring();
1374 					pAttrs[2] = NULL;
1375 
1376 					bRet &= _fmtChangeStruxWithNotify(PTC_SetFmt, (pf_Frag_Strux*)pFrag,
1377 													  pAttrs, NULL, false);
1378 				}
1379 			}
1380 
1381 
1382 		}
1383 
1384 		pFrag = pFrag->getNext();
1385 	}
1386 
1387 
1388 	return bRet;
1389 }
1390 
_deleteSpan(pf_Frag_Text * pft,UT_uint32 fragOffset,PT_BufIndex bi,UT_uint32 length,pf_Frag ** ppfEnd,UT_uint32 * pfragOffsetEnd)1391 bool pt_PieceTable::_deleteSpan(pf_Frag_Text * pft, UT_uint32 fragOffset,
1392 								PT_BufIndex bi, UT_uint32 length,
1393 								pf_Frag ** ppfEnd, UT_uint32 * pfragOffsetEnd)
1394 {
1395 	// perform simple delete of a span of text.
1396 	// we assume that it is completely contained within this fragment.
1397 
1398 	UT_return_val_if_fail (fragOffset+length <= pft->getLength(),false);
1399 
1400 	SETP(ppfEnd, pft);
1401 	SETP(pfragOffsetEnd, fragOffset);
1402 
1403 	if (fragOffset == 0)
1404 	{
1405 		// the change is at the beginning of the fragment,
1406 
1407 		if (length == pft->getLength())
1408 		{
1409 			// the change exactly matches the fragment, just delete the fragment.
1410 			// as we delete it, see if the fragments around it can be coalesced.
1411 
1412 			_unlinkFrag(pft,ppfEnd,pfragOffsetEnd);
1413 			delete pft;
1414 			return true;
1415 		}
1416 
1417 		// the change is a proper prefix within the fragment,
1418 		// do a left-truncate on it.
1419 
1420 		pft->adjustOffsetLength(m_varset.getBufIndex(bi,length),pft->getLength()-length);
1421 		return true;
1422 	}
1423 
1424 	if (fragOffset+length == pft->getLength())
1425 	{
1426 		// the change is a proper suffix within the fragment,
1427 		// do a right-truncate on it.
1428 
1429 		pft->changeLength(fragOffset);
1430 
1431 		SETP(ppfEnd, pft->getNext());
1432 		SETP(pfragOffsetEnd, 0);
1433 
1434 		return true;
1435 	}
1436 
1437 	// otherwise, the change is in the middle of the fragment.
1438 	// we right-truncate the current fragment at the deletion
1439 	// point and create a new fragment for the tail piece
1440 	// beyond the end of the deletion.
1441 
1442 	UT_uint32 startTail = fragOffset + length;
1443 	UT_uint32 lenTail = pft->getLength() - startTail;
1444 	PT_BufIndex biTail = m_varset.getBufIndex(pft->getBufIndex(),startTail);
1445 	pf_Frag_Text * pftTail = new pf_Frag_Text(this,biTail,lenTail,pft->getIndexAP(),pft->getField());
1446 	UT_return_val_if_fail (pftTail, false);
1447 	pft->changeLength(fragOffset);
1448 	m_fragments.insertFrag(pft,pftTail);
1449 
1450 	SETP(ppfEnd, pftTail);
1451 	SETP(pfragOffsetEnd, 0);
1452 
1453 	return true;
1454 }
1455 
_deleteSpanWithNotify(PT_DocPosition dpos,pf_Frag_Text * pft,UT_uint32 fragOffset,UT_uint32 length,pf_Frag_Strux * pfs,pf_Frag ** ppfEnd,UT_uint32 * pfragOffsetEnd,bool bAddChangeRec)1456 bool pt_PieceTable::_deleteSpanWithNotify(PT_DocPosition dpos,
1457 										  pf_Frag_Text * pft, UT_uint32 fragOffset,
1458 										  UT_uint32 length,
1459 										  pf_Frag_Strux * pfs,
1460 										  pf_Frag ** ppfEnd, UT_uint32 * pfragOffsetEnd,
1461 										  bool bAddChangeRec)
1462 {
1463 	// create a change record for this change and put it in the history.
1464 
1465 	UT_return_val_if_fail (pfs, false);
1466 
1467 	if (length == 0)					// TODO decide if this is an error.
1468 	{
1469 		xxx_UT_DEBUGMSG(("_deleteSpanWithNotify: length==0\n"));
1470 		SETP(ppfEnd, pft->getNext());
1471 		SETP(pfragOffsetEnd, 0);
1472 		return true;
1473 	}
1474 
1475 	// we do this before the actual change because various fields that
1476 	// we need are blown away during the delete.  we then notify all
1477 	// listeners of the change.
1478 
1479 	PT_BlockOffset blockOffset = _computeBlockOffset(pfs,pft) + fragOffset;
1480 
1481 	PX_ChangeRecord_Span * pcr
1482 		= new PX_ChangeRecord_Span(PX_ChangeRecord::PXT_DeleteSpan,
1483 								   dpos, pft->getIndexAP(),
1484 								   m_varset.getBufIndex(pft->getBufIndex(),fragOffset),
1485 								   length,blockOffset,pft->getField());
1486 	UT_return_val_if_fail (pcr, false);
1487 	pcr->setDocument(m_pDocument);
1488 	bool bResult = _deleteSpan(pft,fragOffset,pft->getBufIndex(),length,ppfEnd,pfragOffsetEnd);
1489 
1490 	bool canCoalesce = _canCoalesceDeleteSpan(pcr);
1491 	if (!bAddChangeRec || (canCoalesce && !m_pDocument->isCoalescingMasked()))
1492 	{
1493 		if (canCoalesce)
1494 			m_history.coalesceHistory(pcr);
1495 
1496 		m_pDocument->notifyListeners(pfs,pcr);
1497 		delete pcr;
1498 	}
1499 	else
1500 	{
1501 		m_history.addChangeRecord(pcr);
1502 		m_pDocument->notifyListeners(pfs,pcr);
1503 	}
1504 
1505 	return bResult;
1506 }
1507 
_canCoalesceDeleteSpan(PX_ChangeRecord_Span * pcrSpan) const1508 bool pt_PieceTable::_canCoalesceDeleteSpan(PX_ChangeRecord_Span * pcrSpan) const
1509 {
1510 	// see if this record can be coalesced with the most recent undo record.
1511 
1512 	UT_return_val_if_fail (pcrSpan->getType() == PX_ChangeRecord::PXT_DeleteSpan, false);
1513 
1514 	PX_ChangeRecord * pcrUndo;
1515 	if (!m_history.getUndo(&pcrUndo,true))
1516 		return false;
1517 	if (pcrSpan->getType() != pcrUndo->getType())
1518 		return false;
1519 	if (pcrSpan->getIndexAP() != pcrUndo->getIndexAP())
1520 		return false;
1521 	if((pcrUndo->isFromThisDoc() != pcrSpan->isFromThisDoc()))
1522 	   return false;
1523 
1524 	PX_ChangeRecord_Span * pcrUndoSpan = static_cast<PX_ChangeRecord_Span *>(pcrUndo);
1525 	UT_uint32 lengthUndo = pcrUndoSpan->getLength();
1526 	PT_BufIndex biUndo = pcrUndoSpan->getBufIndex();
1527 
1528 	UT_uint32 lengthSpan = pcrSpan->getLength();
1529 	PT_BufIndex biSpan = pcrSpan->getBufIndex();
1530 
1531 	if (pcrSpan->getPosition() == pcrUndo->getPosition())
1532 	{
1533 		if (m_varset.getBufIndex(biUndo,lengthUndo) == biSpan)
1534 			return true;				// a forward delete
1535 
1536 		return false;
1537 	}
1538 	else if ((pcrSpan->getPosition() + lengthSpan) == pcrUndo->getPosition())
1539 	{
1540 		if (m_varset.getBufIndex(biSpan,lengthSpan) == biUndo)
1541 			return true;				// a backward delete
1542 
1543 		return false;
1544 	}
1545 	else
1546 	{
1547 		return false;
1548 	}
1549 }
1550 
_isSimpleDeleteSpan(PT_DocPosition dpos1,PT_DocPosition dpos2) const1551 bool pt_PieceTable::_isSimpleDeleteSpan(PT_DocPosition dpos1,
1552 										PT_DocPosition dpos2) const
1553 {
1554 	// see if the amount of text to be deleted is completely
1555 	// contained within the fragment found.  if so, we have
1556 	// a simple delete.  otherwise, we need to set up a multi-step
1557 	// delete -- it may not actually take more than one step, but
1558 	// it is too complicated to tell at this point, so we assume
1559 	// it will and don't worry about it.
1560 	//
1561 	// we are in a simple change if the beginning and end are
1562 	// within the same fragment.
1563 
1564 	pf_Frag * pf_First;
1565 	pf_Frag * pf_End;
1566 	PT_BlockOffset fragOffset_First;
1567 	PT_BlockOffset fragOffset_End;
1568 
1569 	bool bFound = getFragsFromPositions(dpos1,dpos2,&pf_First,&fragOffset_First,&pf_End,&fragOffset_End);
1570 	UT_return_val_if_fail (bFound, false);
1571 
1572 	if ((fragOffset_End==0) && pf_End->getPrev() && (pf_End->getPrev()->getType() == pf_Frag::PFT_Text))
1573 	{
1574 		pf_End = pf_End->getPrev();
1575 		fragOffset_End = pf_End->getLength();
1576 	}
1577 
1578 	return (pf_First == pf_End);
1579 }
1580 
_tweakDeleteSpanOnce(PT_DocPosition & dpos1,PT_DocPosition & dpos2,UT_Stack * pstDelayStruxDelete) const1581 bool pt_PieceTable::_tweakDeleteSpanOnce(PT_DocPosition & dpos1,
1582 										 PT_DocPosition & dpos2,
1583 										 UT_Stack * pstDelayStruxDelete) const
1584 {
1585 	if(m_bDoNotTweakPosition)
1586 		return true;
1587 
1588 	//  Our job is to adjust the end positions of the delete
1589 	//  operating to delete those structural object that the
1590 	//  user will expect to have deleted, even if the dpositions
1591 	//  aren't quite right to encompass those.
1592 
1593 	pf_Frag * pf_First;
1594 	pf_Frag * pf_End;
1595 	pf_Frag * pf_Other;
1596 	PT_BlockOffset fragOffset_First;
1597 	PT_BlockOffset fragOffset_End;
1598 
1599 	bool bFound = getFragsFromPositions(dpos1,dpos2,&pf_First,&fragOffset_First,&pf_End,&fragOffset_End);
1600 	UT_return_val_if_fail (bFound, false);
1601 
1602 	pf_Frag_Strux * pfsContainer = NULL;
1603 	bool bFoundStrux = _getStruxFromPosition(dpos1,&pfsContainer);
1604 	UT_return_val_if_fail (bFoundStrux,false);
1605 
1606     _tweakFieldSpan(dpos1,dpos2);
1607 
1608 	switch (pfsContainer->getStruxType())
1609 	{
1610 	default:
1611 		UT_ASSERT_HARMLESS(0);
1612 		return false;
1613 
1614 	case PTX_Section:
1615 		// if the previous container is a section, then pf_First
1616 		// must be the first block in the section.
1617 		UT_return_val_if_fail ((pf_First->getPrev() == pfsContainer),false);
1618 		UT_return_val_if_fail ((pf_First->getType() == pf_Frag::PFT_Strux),false);
1619 		UT_return_val_if_fail (((static_cast<pf_Frag_Strux *>(pf_First))->getStruxType() == PTX_Block),false);
1620 		// We can delete the first block in a section only if the section then start
1621 		// with either a new block or the table. We will allow it here.
1622 		return true;
1623 	case PTX_SectionHdrFtr:
1624 		// if the previous container is a Header/Footersection, then pf_First
1625 		// must be the first block or the first Table in the section.
1626 		UT_return_val_if_fail ((pf_First->getPrev() == pfsContainer),false);
1627 		UT_return_val_if_fail ((pf_First->getType() == pf_Frag::PFT_Strux),false);
1628 		UT_return_val_if_fail ((((static_cast<pf_Frag_Strux *>(pf_First))->getStruxType() == PTX_Block) || (static_cast<pf_Frag_Strux *>(pf_First))->getStruxType() == PTX_SectionTable),false);
1629 
1630 		//
1631 		// This allows us to delete the first Table in a section
1632 		//
1633 		if(static_cast<pf_Frag_Strux *>(pf_First)->getStruxType() == PTX_SectionTable)
1634 		{
1635 		     return true;
1636 		}
1637 		// since, we cannot delete the first block in a section, we
1638 		// secretly translate this into a request to delete the section;
1639 		// the block we have will then be slurped into the previous
1640 		// section.
1641 		dpos1 -= pfsContainer->getLength();
1642 
1643 		return true;
1644 
1645 	case PTX_SectionTable:
1646 	case PTX_SectionCell:
1647 	case PTX_SectionFrame:
1648 	case PTX_EndTable:
1649 	case PTX_EndCell:
1650 	case PTX_EndFrame:
1651 	case PTX_SectionTOC:
1652 	case PTX_EndTOC:
1653 //
1654 // We've set things up so that deleting table struxes is done very deliberately.//  Don't mess with the end points here
1655 //
1656 		return true;
1657 	case PTX_SectionFootnote:
1658 	case PTX_SectionAnnotation:
1659 	case PTX_SectionEndnote:
1660 	{
1661 //
1662 // Get the actual block strux container for the endnote.
1663 //
1664  		xxx_UT_DEBUGMSG(("_deleteSpan 1: orig pfsContainer %x type %d \n",pfsContainer,pfsContainer->getStruxType()));
1665  		_getStruxFromFragSkip(pfsContainer,&pfsContainer);
1666  		xxx_UT_DEBUGMSG(("_deleteSpan 2: After skip  pfsContainer %x type %d \n",pfsContainer,pfsContainer->getStruxType()));
1667  		break;
1668 	}
1669  	case PTX_EndFootnote:
1670  	case PTX_EndEndnote:
1671  	case PTX_EndAnnotation:
1672  	{
1673 //
1674 // Get the actual block strux container for the endnote.
1675 //
1676  		xxx_UT_DEBUGMSG(("_deleteSpan 1: orig pfsContainer %x type %d \n",pfsContainer,pfsContainer->getStruxType()));
1677  		_getStruxFromFragSkip(pfsContainer,&pfsContainer);
1678  		xxx_UT_DEBUGMSG(("_deleteSpan 2: After skip  pfsContainer %x type %d \n",pfsContainer,pfsContainer->getStruxType()));
1679  		break;
1680 	}
1681 	case PTX_Block:
1682 		// if the previous container is a block, we're ok.
1683 		// the loop below will take care of everything.
1684 		break;
1685 	}
1686 
1687 	if (pf_First->getType() == pf_Frag::PFT_Strux)
1688 	{
1689 		switch(static_cast<pf_Frag_Strux *>(pf_First)->getStruxType())
1690 		{
1691 		default:
1692 			break;
1693 
1694 		case PTX_Section:
1695 			UT_return_val_if_fail (fragOffset_First == 0,false);
1696 			if (dpos2 == dpos1 + pf_First->getLength())
1697 			{
1698 				//  If we are just deleting a section break, then
1699 				//  we should delete the first block marker in the
1700 				//  next section, combining the blocks before and
1701 				//  after the section break.
1702 				pf_Other = pf_First->getNext();
1703 				UT_return_val_if_fail (pf_Other, false);
1704 				UT_return_val_if_fail (pf_Other->getType() == pf_Frag::PFT_Strux,false);
1705 				UT_return_val_if_fail (((static_cast<pf_Frag_Strux *>(pf_Other))->getStruxType() == PTX_Block),false);
1706 				dpos2 += pf_Other->getLength();
1707 				return true;
1708 			}
1709 		case PTX_SectionHdrFtr:
1710 			UT_return_val_if_fail (fragOffset_First == 0, false);
1711 			if (dpos2 == dpos1 + pf_First->getLength())
1712 			{
1713 				//  If we are just deleting a section break, then
1714 				//  we should delete the first block marker in the
1715 				//  next section, combining the blocks before and
1716 				//  after the section break.
1717 				pf_Other = pf_First->getNext();
1718 				UT_return_val_if_fail (pf_Other,false);
1719 				UT_return_val_if_fail (pf_Other->getType() == pf_Frag::PFT_Strux,false);
1720 				UT_return_val_if_fail (((static_cast<pf_Frag_Strux *>(pf_Other))->getStruxType() == PTX_Block),false);
1721 				dpos2 += pf_Other->getLength();
1722 				return true;
1723 			}
1724 
1725 			break;
1726 		}
1727 	}
1728 
1729 	if(pf_End->getType() ==  pf_Frag::PFT_Strux)
1730 	{
1731 	    if(static_cast<pf_Frag_Strux *>(pf_End)->getStruxType() == PTX_EndTOC)
1732 		{
1733 			dpos2++;
1734 		}
1735 	}
1736 
1737 	if (fragOffset_First == 0 && fragOffset_End == 0 && pf_First != pf_End)
1738 	{
1739 		pf_Frag * pf_Before = pf_First->getPrev();
1740 		while (pf_Before && pf_Before->getType() == pf_Frag::PFT_FmtMark)
1741 			pf_Before = pf_Before->getPrev();
1742 		pf_Frag * pf_Last = pf_End->getPrev();
1743 		while (pf_Last && pf_Last->getType() == pf_Frag::PFT_FmtMark)
1744 			pf_Last = pf_Last->getPrev();
1745 
1746 		if (pf_Before && pf_Before->getType() == pf_Frag::PFT_Strux &&
1747 			pf_Last && pf_Last->getType() == pf_Frag::PFT_Strux)
1748 		{
1749 			PTStruxType pt_BeforeType = static_cast<pf_Frag_Strux *>(pf_Before)->getStruxType();
1750 			PTStruxType pt_LastType = static_cast<pf_Frag_Strux *>(pf_Last)->getStruxType();
1751 
1752 			if (pt_BeforeType == PTX_Block && pt_LastType == PTX_Block)
1753 			{
1754 				//  Check that there is something between the pf_Before and pf_Last, otherwise
1755                 //  This leads to a segfault from continually pushing pf_Before onto the stack
1756                 //  if we delete a whole lot of blank lines. These get popped off then deleted
1757                 //  only to find the same pointer waiting to come off the stack.
1758 				pf_Frag * pScan = pf_Before->getNext();
1759 				while(pScan && pScan != pf_Last && (pScan->getType() != pf_Frag::PFT_Strux))
1760 				{
1761 					pScan = pScan->getNext();
1762 				}
1763 				if(pScan == pf_Last)
1764 				{
1765 
1766 				//  If we are the structure of the document is
1767 				//  '[Block] ... [Block]' and we are deleting the
1768 				//  '... [Block]' part, then the user is probably expecting
1769 				//  us to delete '[Block] ... ' instead, so that any text
1770 				//  following the second block marker retains its properties.
1771 				//  The problem is that it might not be safe to delete the
1772 				//  first block marker until the '...' is deleted because
1773 				//  it might be the first block of the section.  So, we
1774 				//  want to delete the '...' first, and then get around
1775 				//  to deleting the block later.
1776 
1777 					pf_Frag_Strux * pfs_BeforeSection, * pfs_LastSection;
1778 					_getStruxOfTypeFromPosition(dpos1 - 1,
1779 												PTX_Section, &pfs_BeforeSection);
1780 					_getStruxOfTypeFromPosition(dpos2 - 1,
1781 												PTX_Section, &pfs_LastSection);
1782 
1783 					if ((pfs_BeforeSection == pfs_LastSection) && (dpos2 > dpos1 +1))
1784 					{
1785 						dpos2 -= pf_Last->getLength();
1786 						pstDelayStruxDelete->push(pf_Before);
1787 						return true;
1788 					}
1789 				}
1790 			}
1791 		}
1792 	}
1793 
1794 	return true;
1795 }
1796 
_tweakDeleteSpan(PT_DocPosition & dpos1,PT_DocPosition & dpos2,UT_Stack * pstDelayStruxDelete) const1797 bool pt_PieceTable::_tweakDeleteSpan(PT_DocPosition & dpos1,
1798 									 PT_DocPosition & dpos2,
1799 									 UT_Stack * pstDelayStruxDelete) const
1800 {
1801 	if(m_bDoNotTweakPosition)
1802 		return true;
1803 
1804 	//
1805 // First we want to handle hyperlinks. If we delete all the text within
1806 // a hyperlink or annotation, then we should also delete the start
1807 // and end point of the
1808 // hyperlink or annotation.
1809 //
1810 	pf_Frag * pf_First;
1811 	pf_Frag * pf_End;
1812 	PT_BlockOffset fragOffset_First;
1813 	PT_BlockOffset fragOffset_End;
1814 
1815 	bool bFound = getFragsFromPositions(dpos1,dpos2,&pf_First,&fragOffset_First,&pf_End,&fragOffset_End);
1816 	UT_return_val_if_fail (bFound,false);
1817 	while(pf_First && (pf_First->getLength() == 0))
1818 	{
1819 		pf_First = pf_First->getNext();
1820 	}
1821 	if(pf_First)
1822 	{
1823 		while(pf_End && (pf_End->getLength() == 0))
1824 		{
1825 			pf_End = pf_End->getPrev();
1826 		}
1827 		bool bDoit = false;
1828 		if(pf_End && ((pf_End->getPos() + pf_End->getLength() - pf_First->getPos())  == (dpos2 - dpos1 +1)))
1829 		{
1830 			if((pf_First->getType() == pf_Frag::PFT_Text) && (pf_First->getLength() == 2))
1831 			{
1832 				bDoit = false;
1833 			}
1834 			else if((pf_First->getType() == pf_Frag::PFT_Text) && (pf_End->getType() == pf_Frag::PFT_Text) && (pf_First != pf_End))
1835 			{
1836 				bDoit = false;
1837 			}
1838 			else
1839 			{
1840 				bDoit = true;
1841 			}
1842 		}
1843 		if(pf_End && ((pf_End->getPos() + pf_End->getLength() - pf_First->getPos())  == (dpos2 - dpos1)))
1844 		{
1845 			bDoit = true;
1846 		}
1847 		if(bDoit)
1848 		{
1849 //
1850 // OK these frags are entirely contained by dpos1  and dpos2
1851 // OK now look to see if there is a hyperlink or annotation just before and after these
1852 //
1853 			if(pf_End->getType() != pf_Frag::PFT_Object)
1854 			{
1855 				pf_End = pf_End->getNext();
1856 			}
1857 			while(pf_End && (pf_End->getLength() == 0))
1858 			{
1859 				pf_End = pf_End->getNext();
1860 			}
1861 			if(pf_First->getType() != pf_Frag::PFT_Object)
1862 			{
1863 				pf_First = pf_First->getPrev();
1864 			}
1865 			while(pf_First && (pf_First->getLength() == 0))
1866 			{
1867 				pf_First = pf_First->getPrev();
1868 			}
1869 			if(pf_First && (pf_First->getType() == pf_Frag::PFT_Object))
1870 			{
1871 				pf_Frag_Object *pFO = static_cast<pf_Frag_Object *>(pf_First);
1872 				bool bFoundBook = false;
1873 				bool bFoundHype = false;
1874 				bool bFoundAnn = false;
1875 				if(pFO->getObjectType() == PTO_Bookmark)
1876 				{
1877 					bFoundBook = true;
1878 				}
1879 				if(pFO->getObjectType() == PTO_Hyperlink)
1880 				{
1881 					bFoundHype = true;
1882 				}
1883 				if(pFO->getObjectType() == PTO_Annotation)
1884 				{
1885 					bFoundAnn = true;
1886 				}
1887 				if(pf_End && (pf_End->getType() == pf_Frag::PFT_Object) && (pf_End != pf_First))
1888 				{
1889 					pFO = static_cast<pf_Frag_Object *>(pf_End);
1890 					if(pFO->getObjectType() == PTO_Bookmark && bFoundBook)
1891 					{
1892 //
1893 // Found a bookmark which will have all contents deleted so delete it too
1894 //
1895 						dpos1--;
1896 						dpos2++;
1897 					}
1898 					else if(pFO->getObjectType() == PTO_Hyperlink && bFoundHype)
1899 					{
1900 //
1901 // Found a Hyperlink which will have all contents deleted so delte it too
1902 //
1903 						dpos1--;
1904 						dpos2++;
1905 					}
1906 					else if(pFO->getObjectType() == PTO_Annotation && bFoundAnn)
1907 					{
1908 //
1909 // Found a Annotation which will have all contents deleted so delte it too
1910 //
1911 						dpos1--;
1912 						dpos2++;
1913 					}
1914 				}
1915 			}
1916 		}
1917 	}
1918 //
1919 // Can't handle a delete span start from an endTOC. sum1 has arranged corner
1920 // cases where this is possible. HAndle this corner case by starting at the
1921 // next strux
1922 //
1923 	if(!pf_First)
1924 	{
1925 	    return false;
1926 	}
1927 	if(pf_First->getType() == pf_Frag::PFT_Strux)
1928 	{
1929 		pf_Frag_Strux * pfs = static_cast<pf_Frag_Strux *>(pf_First);
1930 		if(pfs->getStruxType() == PTX_EndTOC)
1931 		{
1932 			pf_Frag * pf = pf_First->getNext();
1933 			while(pf && pf->getLength() == 0)
1934 			{
1935 				pf = pf->getNext();
1936 			}
1937 			if(pf && pf->getType() ==  pf_Frag::PFT_Strux)
1938 			{
1939 				pfs = static_cast<pf_Frag_Strux *>(pf);
1940 				if(pfs->getStruxType() == PTX_Block)
1941 				{
1942 					dpos1++;
1943 				}
1944 			}
1945 		}
1946 	}
1947 	//  We want to keep tweaking the delete span until there is nothing
1948 	//  more to tweak.  We check to see if nothing has changed in the
1949 	//  last tweak, and if so, we are done.
1950 	while (1)
1951 	{
1952 		PT_DocPosition old_dpos1 = dpos1;
1953 		PT_DocPosition old_dpos2 = dpos2;
1954 		UT_sint32 old_iStackSize = pstDelayStruxDelete->getDepth();
1955 
1956 		if(!_tweakDeleteSpanOnce(dpos1, dpos2, pstDelayStruxDelete))
1957 			return false;
1958 
1959 		if (dpos1 == old_dpos1 && dpos2 == old_dpos2
1960 			&& pstDelayStruxDelete->getDepth() == old_iStackSize)
1961 			return true;
1962 	}
1963 }
1964 
_deleteFormatting(PT_DocPosition dpos1,PT_DocPosition dpos2)1965 bool pt_PieceTable::_deleteFormatting(PT_DocPosition dpos1,
1966 									  PT_DocPosition dpos2)
1967 {
1968 	pf_Frag * pf_First;
1969 	pf_Frag * pf_End;
1970 	PT_BlockOffset fragOffset_First;
1971 	PT_BlockOffset fragOffset_End;
1972 
1973 	bool bFound = getFragsFromPositions(dpos1,dpos2,&pf_First,&fragOffset_First,&pf_End,&fragOffset_End);
1974 	UT_return_val_if_fail (bFound,false);
1975 
1976 	// before we delete the content, we do a quick scan and delete
1977 	// any FmtMarks first -- this let's us avoid problems with
1978 	// coalescing FmtMarks only to be deleted.
1979 
1980 	pf_Frag * pfTemp = pf_First;
1981 	PT_BlockOffset fragOffsetTemp = fragOffset_First;
1982 
1983 	PT_DocPosition dposTemp = dpos1;
1984 	while (dposTemp <= dpos2)
1985 	{
1986 		if (pfTemp->getType() == pf_Frag::PFT_EndOfDoc)
1987 			break;
1988 
1989 		if (pfTemp->getType() == pf_Frag::PFT_FmtMark)
1990 		{
1991 			pf_Frag * pfNewTemp;
1992 			PT_BlockOffset fragOffsetNewTemp;
1993 			pf_Frag_Strux * pfsContainerTemp = NULL;
1994 			bool bFoundStrux = _getStruxFromPosition(dposTemp,&pfsContainerTemp);
1995 			if(isEndFootnote(pfsContainerTemp))
1996 			{
1997 				xxx_UT_DEBUGMSG(("_deleteSpan 5: orig pfsContainer %x type %d \n",pfsContainerTemp,pfsContainerTemp->getStruxType()));
1998 				_getStruxFromFragSkip(pfsContainerTemp,&pfsContainerTemp);
1999 				xxx_UT_DEBUGMSG(("_deleteSpan 6: After skip  pfsContainer %x type %d \n",pfsContainerTemp,pfsContainerTemp->getStruxType()));
2000 			}
2001 			UT_return_val_if_fail (bFoundStrux,false);
2002 			bool bResult = _deleteFmtMarkWithNotify(dposTemp,static_cast<pf_Frag_FmtMark *>(pfTemp),
2003 										 pfsContainerTemp,&pfNewTemp,&fragOffsetNewTemp);
2004 			UT_return_val_if_fail (bResult,false);
2005 
2006 			// FmtMarks have length zero, so we don't need to update dposTemp.
2007 			pfTemp = pfNewTemp;
2008 			fragOffsetTemp = fragOffsetNewTemp;
2009 		}
2010 		else if(pfTemp->getType() == pf_Frag::PFT_Strux)
2011 		{
2012 			pf_Frag_Strux * pfFragStrux = static_cast<pf_Frag_Strux *>(pfTemp);
2013 			if(pfFragStrux->getStruxType() == PTX_Section)
2014 			{
2015 				pf_Frag_Strux_Section * pfSec = static_cast<pf_Frag_Strux_Section *>(pfFragStrux);
2016 				_deleteHdrFtrsFromSectionStruxIfPresent(pfSec);
2017 			}
2018 			dposTemp += pfTemp->getLength() - fragOffsetTemp;
2019 			pfTemp = pfTemp->getNext();
2020 			fragOffsetTemp = 0;
2021 		}
2022 		else
2023 		{
2024 			dposTemp += pfTemp->getLength() - fragOffsetTemp;
2025 			pfTemp = pfTemp->getNext();
2026 			fragOffsetTemp = 0;
2027 		}
2028 	}
2029 
2030 	return true;
2031 }
2032 
2033 /*!
2034  * Returns true if pfs is not a strux connected with a table or frame
2035  */
_StruxIsNotTable(pf_Frag_Strux * pfs)2036 bool pt_PieceTable::_StruxIsNotTable(pf_Frag_Strux * pfs)
2037 {
2038 	PTStruxType its = pfs->getStruxType();
2039 	bool b = ((its != PTX_SectionTable) && (its != PTX_SectionCell)
2040 			  && (its != PTX_EndTable) && (its != PTX_EndCell)
2041 			  && (its != PTX_SectionFrame) && (its != PTX_EndFrame));
2042 	return b;
2043 }
2044 
2045 
2046 /**
2047  * Since hyperlinks, annotations, and rdf anchors are all very
2048  * similar code, they are abstracted out here.
2049  */
2050 bool
_deleteComplexSpanHAR(pf_Frag_Object * pO,PT_DocPosition dpos1,PT_DocPosition,UT_uint32 & length,PT_BlockOffset & fragOffset_First,UT_uint32 & lengthThisStep,pf_Frag_Strux * & pfsContainer,pf_Frag * & pfNewEnd,UT_uint32 & fragOffsetNewEnd,const char * startAttrCSTR)2051 pt_PieceTable::_deleteComplexSpanHAR( pf_Frag_Object *pO,
2052                                       PT_DocPosition dpos1,
2053                                       PT_DocPosition /*dpos2*/,
2054                                       UT_uint32& length,
2055                                       PT_BlockOffset& fragOffset_First,
2056                                       UT_uint32& lengthThisStep,
2057                                       pf_Frag_Strux*& pfsContainer,
2058                                       pf_Frag*& pfNewEnd,
2059                                       UT_uint32& fragOffsetNewEnd,
2060                                       const char* startAttrCSTR )
2061 {
2062     UT_DEBUGMSG(("_deleteComplexSpanHAR() pO:%p\n", pO ));
2063 
2064     PTObjectType objType = pO->getObjectType();
2065     bool bFoundStrux2;
2066     bool bResult = false;
2067     UT_DebugOnly<bool> bResult2;
2068     PT_DocPosition posComrade;
2069     pf_Frag_Strux * pfsContainer2 = NULL;
2070     pf_Frag * pF;
2071     std::string startAttr = startAttrCSTR;
2072     std::string startAttrInitialCap = startAttr;
2073     if( !startAttrInitialCap.empty() )
2074         startAttrInitialCap[0] = toupper( startAttrInitialCap[0] );
2075 
2076     const PP_AttrProp * pAP = NULL;
2077     pO->getPieceTable()->getAttrProp(pO->getIndexAP(),&pAP);
2078     UT_return_val_if_fail (pAP, false);
2079     const gchar* pszHref = NULL;
2080     const gchar* pszHname  = NULL;
2081     UT_uint32 k = 0;
2082     bool bStart = false;
2083     while((pAP)->getNthAttribute(k++,pszHname, pszHref))
2084     {
2085         if((strcmp(pszHname, startAttr.c_str()) == 0) ||(strcmp(pszHname, startAttrInitialCap.c_str()) == 0) )
2086         {
2087             bStart = true;
2088             break;
2089         }
2090     }
2091     UT_DEBUGMSG(("_deleteComplexSpanHAR() bStart:%d\n", bStart ));
2092 
2093     if(!bStart)
2094     {
2095         // in this case we are looking for the start marker
2096         // and so we delete it and then move on
2097         pF = pO->getPrev();
2098         while(pF)
2099         {
2100             if(pF->getType() == pf_Frag::PFT_Object)
2101             {
2102                 pf_Frag_Object *pOb = static_cast<pf_Frag_Object*>(pF);
2103                 if( pOb->getObjectType() == objType )
2104                 {
2105                     posComrade = getFragPosition(pOb);
2106                     bFoundStrux2 = _getStruxFromFragSkip(pOb,&pfsContainer2);
2107                     UT_return_val_if_fail (bFoundStrux2, false);
2108 
2109                     bResult2 =
2110                         _deleteObjectWithNotify(posComrade,pOb,0,1,
2111                                                 pfsContainer2,0,0);
2112 		    UT_ASSERT(bResult2);
2113 
2114                     // now adjusting the positional variables
2115                     if(posComrade <= dpos1)
2116                         // delete before the start of the segement we are working on
2117                         dpos1--;
2118                     else
2119                     {
2120                         // we are inside that section
2121                         length--;
2122                     }
2123                     break;
2124                 }
2125             }
2126             pF = pF->getPrev();
2127         }
2128         UT_ASSERT(pO->getObjectType() == objType);
2129         bResult
2130             = _deleteObjectWithNotify(dpos1,pO,fragOffset_First,lengthThisStep,
2131                                       pfsContainer,&pfNewEnd,&fragOffsetNewEnd);
2132 
2133     }
2134     else
2135     {
2136         // in this case we are looking for the end marker,
2137         // so we have to be careful to get rid of the start
2138         // marker first
2139         pF = pO->getNext();
2140         while(pF)
2141         {
2142             UT_DEBUGMSG(("_deleteComplexSpanHAR() loop pF:%p\n", pF ));
2143             if(pF->getType() == pf_Frag::PFT_Object)
2144             {
2145                 pf_Frag_Object *pOb = static_cast<pf_Frag_Object*>(pF);
2146                 if( pOb->getObjectType() == objType )
2147                 {
2148                     posComrade = getFragPosition(pOb);
2149                     bFoundStrux2 = _getStruxFromFragSkip(pOb,&pfsContainer2);
2150                     UT_return_val_if_fail (bFoundStrux2, false);
2151 
2152                     UT_DEBUGMSG(("_deleteComplexSpanHAR() posComrade:%d\n", posComrade ));
2153 
2154                     // delete the original start marker
2155                     bResult
2156                         = _deleteObjectWithNotify(dpos1,pO,fragOffset_First,lengthThisStep,
2157                                                   pfsContainer,&pfNewEnd,&fragOffsetNewEnd);
2158 
2159                     // now adjusting the positional variables
2160                     posComrade--;
2161 
2162 
2163                     // One last twist make sure the next frag from the previous
2164                     // delete isn't the same as this this other we need to get the next frag from the
2165                     // second delete
2166                     if(pfNewEnd != static_cast<pf_Frag *>(pOb))
2167                     {
2168                         bResult2 =
2169                             _deleteObjectWithNotify(posComrade,pOb,0,1,
2170                                                     pfsContainer2,0,0);
2171 			UT_ASSERT(bResult2);
2172                     }
2173                     else
2174                     {
2175                         bResult2 =
2176                             _deleteObjectWithNotify(posComrade,pOb,0,1,
2177                                                     pfsContainer2,&pfNewEnd,&fragOffsetNewEnd);
2178 			UT_ASSERT(bResult2);
2179                     }
2180 
2181                     if( objType == PTO_Annotation )
2182                     {
2183                         // FIXME!! Need to work out how to delete the content of the annotation
2184                         // at this point
2185                     }
2186 
2187                     if(posComrade >= dpos1 && posComrade <= dpos1 + length - 2)
2188                     {
2189                         // the endmarker was inside of the segment we are working
2190                         // so we have to adjust the length
2191                         length--;
2192                     }
2193 
2194                     break;
2195                 }
2196             }
2197             pF = pF->getNext();
2198         }
2199     }
2200 
2201     return bResult;
2202 }
2203 
2204 
2205 /*
2206     Because complex span can involve deletion of a bookmark the comrade of which is outside of the
2207     deletion range, this function can change dpos1 and dpos2 to indicate which document positions
2208     after the deletion correspond to the original values passed to the function -- the caller needs
2209     to take this into account
2210  */
_deleteComplexSpan(PT_DocPosition & origPos1,PT_DocPosition & origPos2,UT_Stack * stDelayStruxDelete)2211 bool pt_PieceTable::_deleteComplexSpan(PT_DocPosition & origPos1,
2212 									   PT_DocPosition & origPos2,
2213 									   UT_Stack * stDelayStruxDelete)
2214 {
2215 	pf_Frag * pfNewEnd;
2216 	UT_uint32 fragOffsetNewEnd;
2217 	bool bPrevWasCell = false;
2218 	bool bPrevWasEndTable = false;
2219 	bool bPrevWasFrame = false;
2220 	pf_Frag * pf_First;
2221 	pf_Frag * pf_End;
2222 	PT_BlockOffset fragOffset_First;
2223 	PT_BlockOffset fragOffset_End;
2224 
2225 	PT_DocPosition dpos1 = origPos1;
2226 	PT_DocPosition dpos2 = origPos2;
2227 
2228 	bool bFound = getFragsFromPositions(dpos1,dpos2,&pf_First,&fragOffset_First,&pf_End,&fragOffset_End);
2229 	UT_return_val_if_fail (bFound, false);
2230 	UT_DEBUGMSG(("deleteComplex span dpos1 %d dpos2 %d pf_First %p pf_First pos %d \n",dpos1,dpos2,pf_First,pf_First->getPos()));
2231 	pf_Frag_Strux * pfsFirstBlock = NULL;
2232 	if ((pf_First !=pf_End) && (pf_First->getType() == pf_Frag::PFT_Strux))
2233 	{
2234 	    pf_Frag_Strux * pfs = static_cast<pf_Frag_Strux*>(pf_First);
2235 		if ((pfs->getStruxType() == PTX_Block) && pfs->getPrev() &&
2236 			(pfs->getPrev()->getType() == pf_Frag::PFT_Strux) &&
2237 			(static_cast<pf_Frag_Strux*>(pfs->getPrev())->getStruxType() == PTX_Section))
2238 		{
2239 			// We are trying to delete the first block of the section. We will keep that for the end
2240 			pfsFirstBlock = static_cast<pf_Frag_Strux*>(pf_First);
2241 			dpos1 = dpos1 + 1;
2242 			bFound = getFragsFromPositions(dpos1,dpos2,&pf_First,&fragOffset_First,&pf_End,&fragOffset_End);
2243 			UT_return_val_if_fail (bFound, false);
2244 		}
2245 	}
2246 
2247 	pf_Frag_Strux * pfsContainer = NULL;
2248 	bool bFoundStrux = _getStruxFromPosition(dpos1,&pfsContainer);
2249 	UT_return_val_if_fail (bFoundStrux, false);
2250 	if(isEndFootnote(pfsContainer))
2251 	{
2252 		xxx_UT_DEBUGMSG(("_deleteSpan 3: orig pfsContainer %x type %d \n",pfsContainer,pfsContainer->getStruxType()));
2253 		_getStruxFromFragSkip(pfsContainer,&pfsContainer);
2254 		xxx_UT_DEBUGMSG(("_deleteSpan 4: After skip  pfsContainer %x type %d \n",pfsContainer,pfsContainer->getStruxType()));
2255 	}
2256 	// loop to delete the amount requested, one text fragment at a time.
2257 	// if we encounter any non-text fragments along the way, we delete
2258 	// them too.  that is, we implicitly delete Strux and Objects here.
2259 
2260 	UT_uint32 length = dpos2 - dpos1;
2261 	UT_uint32 iTable = 0;
2262 	UT_sint32 iFootnoteCount = 0;
2263 	if(pfsContainer->getStruxType() == PTX_SectionFrame)
2264 	{
2265 		bPrevWasFrame = true;
2266 	        if(pf_First->getType() == pf_Frag::PFT_Strux)
2267 		{
2268 		     pf_Frag_Strux * pfs = static_cast<pf_Frag_Strux *>(pf_First);
2269 		     if(pfs->getStruxType() == PTX_SectionTable)
2270 		     {
2271 		          bPrevWasFrame = false;
2272 		     }
2273 		}
2274 	}
2275 	if(pfsContainer->getStruxType() == PTX_SectionCell)
2276 	{
2277 		bPrevWasCell = true;
2278 	}
2279 	if(pfsContainer->getStruxType() == PTX_EndTable)
2280 	{
2281 		bPrevWasEndTable = true;
2282 	}
2283 	bool bPrevWasFootnote = false;
2284 	UT_sint32 iLoopCount = -1;
2285 	while ((length > 0) || (iFootnoteCount > 0))
2286 	{
2287 		UT_DEBUGMSG(("_deleteComplexSpan() len:%d\n", length ));
2288 		iLoopCount++;
2289 		UT_uint32 lengthInFrag = pf_First->getLength() - fragOffset_First;
2290 		UT_uint32 lengthThisStep = UT_MIN(lengthInFrag, length);
2291 
2292 		switch (pf_First->getType())
2293 		{
2294 		case pf_Frag::PFT_EndOfDoc:
2295 		default:
2296 			UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
2297 			return false;
2298 
2299 		case pf_Frag::PFT_Strux:
2300 		{
2301 //
2302 // OK this code is leave the cell/table structures in place unless we
2303 // defiantely want to delete them.
2304 //
2305 			pf_Frag_Strux * pfs = static_cast<pf_Frag_Strux *> (pf_First);
2306 			bool bResult = true;
2307 			if(bPrevWasCell && (iLoopCount == 0))
2308 			{
2309 //
2310 // Dont' delete this Block strux if the previous strux is a cell and this
2311 // is the start of the delete phase.
2312 //
2313 				pfNewEnd = pfs->getNext();
2314 				fragOffsetNewEnd = 0;
2315 				pfsContainer = pfs;
2316 				dpos1 = dpos1 +  lengthInFrag;
2317 				break;
2318 			}
2319 			if(bPrevWasFrame && (iLoopCount == 0))
2320 			{
2321 //
2322 // Dont' delete this Block strux if the previous strux is a cell and this
2323 // is the start of the delete phase.
2324 //
2325 				pfNewEnd = pfs->getNext();
2326 				fragOffsetNewEnd = 0;
2327 				pfsContainer = pfs;
2328 				dpos1 = dpos1 +  lengthInFrag;
2329 				break;
2330 			}
2331 			if(_StruxIsNotTable(pfs))
2332 			{
2333 				if((bPrevWasCell || bPrevWasFootnote || bPrevWasEndTable|| bPrevWasFrame)
2334 				   && pfs->getStruxType()== PTX_Block)
2335 				{
2336 					bPrevWasCell = false;
2337 					bPrevWasFootnote = false;
2338 					bPrevWasEndTable = false;
2339 					bPrevWasFrame = false;
2340 					pfNewEnd = pfs->getNext();
2341 					fragOffsetNewEnd = 0;
2342 					pfsContainer = pfs;
2343 					dpos1 = dpos1 +  lengthInFrag;
2344 					stDelayStruxDelete->push(pfs);
2345 				}
2346 				else
2347 				{
2348 //
2349 // Now put in code to handle deleting footnote struxtures. We have to reverse
2350 // the order of deleting footnote and Endfootnotes.
2351 //
2352 					if(!isFootnote(pfs) && !isEndFootnote(pfs))
2353 					{
2354 						UT_DEBUGMSG(("Delete Block strux dpos1 %d \n",dpos1));
2355 						bResult = _deleteStruxWithNotify(dpos1,pfs,
2356 										 &pfNewEnd,&fragOffsetNewEnd);
2357 
2358 						if(!bResult) // can't delete this block strux
2359 						             // but can delete the rest of the content
2360 						{
2361 						  UT_DEBUGMSG(("dpos1 = %d \n",dpos1));
2362 						  pfNewEnd = pfs->getNext();
2363 						  dpos1 += pfs->getLength();
2364 						  pfsContainer = pfs;
2365 						  fragOffsetNewEnd = 0;
2366 						}
2367 						// UT_return_val_if_fail(bResult,false);
2368 						bPrevWasCell = false;
2369 						bPrevWasFrame = false;
2370 						bPrevWasEndTable = false;
2371 						break;
2372 					}
2373 					else
2374 					{
2375 						if(isFootnote(pfs))
2376 						{
2377 							pfNewEnd = pfs->getNext();
2378 							fragOffsetNewEnd = 0;
2379 							pfsContainer = pfs;
2380 							dpos1 = dpos1 +  lengthInFrag;
2381 							UT_DEBUGMSG(("Push footnote strux \n"));
2382 							stDelayStruxDelete->push(pfs);
2383 							iFootnoteCount++;
2384 							bPrevWasFootnote = true;
2385 						}
2386 						else
2387 						{
2388 							UT_DEBUGMSG(("Push endfootnote strux \n"));
2389 							stDelayStruxDelete->push(pfs);
2390 						}
2391 					}
2392 				}
2393 			}
2394 			else
2395 			{
2396 			  bPrevWasCell = (pfs->getStruxType() == PTX_SectionCell );
2397 			  bPrevWasFrame = (pfs->getStruxType() == PTX_SectionFrame );
2398 			  bPrevWasEndTable = (pfs->getStruxType() == PTX_EndTable);
2399 				if(pfs->getStruxType() == PTX_SectionTable)
2400 				{
2401 					iTable++;
2402 				}
2403 				if((pfs->getStruxType() != PTX_EndTable) || (iTable != 1))
2404 				{
2405 					pfNewEnd = pfs->getNext();
2406 					fragOffsetNewEnd = 0;
2407 					dpos1 = dpos1 + lengthInFrag;
2408 				}
2409 				stDelayStruxDelete->push(pfs);
2410 			}
2411 //
2412 // Look to see if we've reached the end of the table, if so delete it all now
2413 //
2414 			pf_Frag *pff;
2415 			PT_DocPosition dpp;
2416 			if(pfs->getStruxType() == PTX_EndTable)
2417 			{
2418 				iTable--;
2419 				if(iTable==0)
2420 				{
2421 //
2422 // Deleting the table so don't delay deleting the following strux.
2423 //
2424 					bPrevWasEndTable = false;
2425 					UT_DEBUGMSG(("Doing Table delete immediately \n"));
2426 //					iTable = 1;
2427 					UT_sint32 myTable =1;
2428 //
2429 // First delete the EndTable Strux
2430 //
2431 					stDelayStruxDelete->pop(reinterpret_cast<void **>(&pfs));
2432 					PT_DocPosition myPos2 = pfs->getPos();
2433 					_deleteFormatting(myPos2 - pfs->getLength(), myPos2);
2434 					UT_DEBUGMSG(("DELeteing EndTable Strux, pos= %d \n",pfs->getPos()));
2435 					bResult = _deleteStruxWithNotify(myPos2, pfs,
2436 													  &pfNewEnd,
2437 													  &fragOffsetNewEnd);
2438 					while(bResult && myTable > 0)
2439 					{
2440 						stDelayStruxDelete->pop(reinterpret_cast<void **>(&pfs));
2441 						if(pfs->getStruxType() == PTX_SectionTable)
2442 						{
2443 							myTable--;
2444 						}
2445 						else if(pfs->getStruxType() == PTX_EndTable)
2446 						{
2447 							myTable++;
2448 						}
2449 						PT_DocPosition myPos = pfs->getPos();
2450 						_deleteFormatting(myPos - pfs->getLength(), myPos);
2451 						bResult = _deleteStruxWithNotify(myPos, pfs, &pff, &dpp);
2452 //
2453 // Each strux is one in length, we've added one while delaying the delete
2454 // so subtract it now.
2455 //
2456 						dpos1 -= 1;
2457 					}
2458 //
2459 // Now we have to update pfsContainer from dpos1
2460 //
2461 					bFoundStrux = _getStruxFromPosition(dpos1,&pfsContainer);
2462 				}
2463 				UT_DEBUGMSG(("Finished doing table delete -1 \n"));
2464 				break;
2465 			}
2466 //
2467 // Look to see if we've reached the end of a footnote section.
2468 //
2469 			if (isEndFootnote(pfs) && (iFootnoteCount > 0))
2470 			{
2471 //
2472 // First delete the EndFootnote Strux
2473 //
2474 				UT_DEBUGMSG(("Doing Footnote delete immediately \n"));
2475 				stDelayStruxDelete->pop(reinterpret_cast<void **>(&pfs));
2476 
2477 				PT_DocPosition myPos2 = pfs->getPos();
2478 				_deleteFormatting(myPos2 - pfs->getLength(), myPos2);
2479 				bResult = _deleteStruxWithNotify(myPos2, pfs,
2480 												  &pfNewEnd,
2481 												  &fragOffsetNewEnd);
2482 //
2483 // Now delete the Footnote strux. Doing things in this order works for
2484 // the layout classes for both the delete (needs the endFootnote strux
2485 // deleted first) and for
2486 // undo where we want the Footnote Strux inserted first.
2487 //
2488 				while(bResult && iFootnoteCount > 0)
2489 				{
2490 					stDelayStruxDelete->pop(reinterpret_cast<void **>(&pfs));
2491 					if(isFootnote(pfs))
2492 					{
2493 						UT_DEBUGMSG(("Found and deleted footnote strux \n"));
2494 						iFootnoteCount--;
2495 					}
2496 					else
2497 					{
2498 						UT_DEBUGMSG(("Found and deleted Block strux in footnote \n"));
2499 					}
2500 					PT_DocPosition myPos = pfs->getPos();
2501 					_deleteFormatting(myPos - pfs->getLength(), myPos);
2502 					bResult = _deleteStruxWithNotify(myPos, pfs, &pff, &dpp);
2503 //
2504 // Each strux is one in length, we've added one while delaying the delete
2505 // so subtract it now.
2506 //
2507 					dpos1 -= 1;
2508 				}
2509 //
2510 // Now we have to update pfsContainer from dpos1
2511 //
2512 				bFoundStrux = _getStruxFromPosition(dpos1,&pfsContainer);
2513 				break;
2514 			}
2515 			else if(isEndFootnote(pfs))
2516 			{
2517 //
2518 // Attempting to delete an EndFootnote end strux without a matching begin.
2519 // terminate the loop now.
2520 //
2521 				return false;
2522 
2523 			}
2524 //
2525 // Look to see if we've reached the end of a Frame section.
2526 //
2527 			if (pfs->getStruxType() == PTX_EndFrame)
2528 			{
2529 //
2530 // First delete the EndFrame Strux
2531 //
2532 				UT_DEBUGMSG(("Doing Frame delete now \n"));
2533 				stDelayStruxDelete->pop(reinterpret_cast<void **>(&pfs));
2534 				PT_DocPosition myPos2 = pfs->getPos();
2535 				_deleteFormatting(myPos2 - pfs->getLength(), myPos2);
2536 				bool isFrame =  pfs->getStruxType() == PTX_SectionFrame;
2537 				bResult = _deleteStruxWithNotify(myPos2, pfs,
2538 												  &pfNewEnd,
2539 												  &fragOffsetNewEnd);
2540 //
2541 // Each strux is one in length, we've added one while delaying the delete
2542 // so subtract it now.
2543 //
2544 				dpos1 -= 1;
2545 //
2546 // Now delete the Frame strux. Doing things in this order works for
2547 // the layout classes for both the delete (needs the endFrame strux
2548 // deleted first) and for
2549 // undo where we want the Frame Strux inserted first.
2550 //
2551 				while(bResult && !isFrame)
2552 				{
2553 					stDelayStruxDelete->pop(reinterpret_cast<void **>(&pfs));
2554 					PT_DocPosition myPos = 0;
2555 					if(pfs)
2556 					{
2557 						myPos = pfs->getPos();
2558 						isFrame =  pfs->getStruxType() == PTX_SectionFrame;
2559 						_deleteFormatting(myPos - pfs->getLength(), myPos);
2560 						bResult = _deleteStruxWithNotify(myPos, pfs, &pff, &dpp);
2561 					}
2562 //
2563 // Each strux is one in length, we've added one while delaying the delete
2564 // so subtract it now.
2565 //
2566 					dpos1 -= 1;
2567 				}
2568 //
2569 // Now we have to update pfsContainer from dpos1
2570 //
2571 				bFoundStrux = _getStruxFromPosition(dpos1,&pfsContainer);
2572 				break;
2573 			}
2574 
2575 			UT_return_val_if_fail (bResult,false);
2576 			// we do not update pfsContainer because we just deleted pfs.
2577 		}
2578 		break;
2579 
2580 		case pf_Frag::PFT_Text:
2581 		{
2582 			if(isEndFootnote(pfsContainer))
2583 			{
2584 				_getStruxFromFragSkip(static_cast<pf_Frag *>(pfsContainer),&pfsContainer);
2585 			}
2586 			bool bResult = _deleteSpanWithNotify(dpos1,static_cast<pf_Frag_Text *>(pf_First),
2587 									  fragOffset_First,lengthThisStep,
2588 									  pfsContainer,&pfNewEnd,&fragOffsetNewEnd);
2589 			UT_return_val_if_fail (bResult, false);
2590 		}
2591 		break;
2592         //
2593 		// the bookmark, hyperlink and annotation objects require special treatment; since
2594 		// they come in pairs, we have to ensure that both are deleted together
2595 		// so we have to find the other part of the pair, delete it, adjust the
2596 		// positional variables and the delete the one we were originally asked
2597 		// to delete
2598         //
2599 		case pf_Frag::PFT_Object:
2600 		{
2601 			if(isEndFootnote(pfsContainer))
2602 			{
2603 				_getStruxFromFragSkip(static_cast<pf_Frag *>(pfsContainer),&pfsContainer);
2604 			}
2605 			bool bResult = false;
2606 			UT_DebugOnly<bool> bResult2;
2607 			pf_Frag_Object *pO = static_cast<pf_Frag_Object *>(pf_First);
2608 			switch(pO->getObjectType())
2609 			{
2610 				case PTO_Bookmark:
2611 				{
2612 					bool bFoundStrux3;
2613 					PT_DocPosition posComrade;
2614 					pf_Frag_Strux * pfsContainer2 = NULL;
2615 
2616 					po_Bookmark * pB = pO->getBookmark();
2617 					UT_return_val_if_fail (pB, false);
2618 					pf_Frag * pF;
2619 					if(pB->getBookmarkType() == po_Bookmark::POBOOKMARK_END)
2620 					{
2621 				    	pF = pO->getPrev();
2622 				    	while(pF)
2623 				    	{
2624 							if(pF->getType() == pf_Frag::PFT_Object)
2625 							{
2626 								pf_Frag_Object *pOb = static_cast<pf_Frag_Object*>(pF);
2627 								po_Bookmark * pB1 = pOb->getBookmark();
2628 								if(pB1 && !strcmp(pB->getName(),pB1->getName()))
2629 								{
2630 									m_pDocument->removeBookmark(pB1->getName());
2631 
2632 									posComrade = getFragPosition(pOb);
2633 
2634 									if(posComrade < origPos1)
2635 									{
2636 										origPos1--;
2637 									}
2638 
2639 									bFoundStrux3 = _getStruxFromFragSkip(pOb,&pfsContainer2);
2640 									UT_return_val_if_fail (bFoundStrux3, false);
2641 
2642 									bResult2 =
2643 											_deleteObjectWithNotify(posComrade,pOb,0,1,
2644 										  							pfsContainer2,0,0);
2645 									UT_ASSERT(bResult2);
2646 
2647 									// now adjusting the positional variables
2648 									if(posComrade <= dpos1)
2649 										// delete before the start of the segement we are working on
2650 										dpos1--;
2651 									else
2652 									{
2653 										// we are inside that section
2654 										length--;
2655 
2656 									}
2657 									break;
2658 								}
2659 							}
2660 							pF = pF->getPrev();
2661 				    	}
2662 					}
2663 					else
2664 					{
2665 				    	pF = pO->getNext();
2666 				    	while(pF)
2667 				    	{
2668 							if(pF->getType() == pf_Frag::PFT_Object)
2669 							{
2670 								pf_Frag_Object *pOb = static_cast<pf_Frag_Object*>(pF);
2671 								po_Bookmark * pB1 = pOb->getBookmark();
2672 								if(pB1 && !strcmp(pB->getName(),pB1->getName()))
2673 								{
2674 									m_pDocument->removeBookmark(pB1->getName());
2675 
2676 									posComrade = getFragPosition(pOb);
2677 									bool bFoundStrux2 = _getStruxFromFragSkip(pOb,&pfsContainer2);
2678 									UT_return_val_if_fail (bFoundStrux2, false);
2679 
2680 									bResult2 =
2681 											_deleteObjectWithNotify(posComrade,pOb,0,1,
2682 										  							pfsContainer2,0,0);
2683 									if(posComrade < dpos1 + length)
2684 										length--;
2685 									break;
2686 								}
2687 							}
2688 							pF = pF->getNext();
2689 				    	}
2690 					}
2691 				bResult
2692 					= _deleteObjectWithNotify(dpos1,pO,fragOffset_First,lengthThisStep,
2693 									  pfsContainer,&pfNewEnd,&fragOffsetNewEnd);
2694 
2695 				}
2696 				break;
2697                 //
2698 				// the one singnificant difference compared to the bookmarks is
2699 				// that we have to always delete the start marker first; this is
2700 				// so that in the case of undo the endmarker would be restored
2701 				// first, because the mechanism that marks runs between them
2702 				// as a hyperlink depends on the end-marker being in place before
2703 				// the start marker
2704                 //
2705 				case PTO_Hyperlink:
2706                     bResult = _deleteComplexSpanHAR( pO,
2707                                                       dpos1,
2708                                                       dpos2,
2709                                                       length,
2710                                                       fragOffset_First,
2711                                                       lengthThisStep,
2712                                                       pfsContainer,
2713                                                       pfNewEnd,
2714                                                       fragOffsetNewEnd,
2715                                                       "xlink:href" );
2716                 // {
2717 				//      bool bFoundStrux2;
2718 				//      PT_DocPosition posComrade;
2719 				//      pf_Frag_Strux * pfsContainer2 = NULL;
2720 
2721 				//      pf_Frag * pF;
2722 
2723 				//      const PP_AttrProp * pAP = NULL;
2724 				//      pO->getPieceTable()->getAttrProp(pO->getIndexAP(),&pAP);
2725 				//      UT_return_val_if_fail (pAP, false);
2726 				//      const gchar* pszHref = NULL;
2727 				//      const gchar* pszHname  = NULL;
2728 				//      UT_uint32 k = 0;
2729 				//      bool bStart = false;
2730 				//      while((pAP)->getNthAttribute(k++,pszHname, pszHref))
2731 				//      {
2732 				//           if(!strcmp(pszHname, "xlink:href"))
2733 				//     	  {
2734 				//     	      bStart = true;
2735 				// 	      break;
2736 				// 	  }
2737 				//      }
2738 
2739 				//      if(!bStart)
2740 				//      {
2741 				// 		// in this case we are looking for the start marker
2742 				// 		// and so we delete it and then move on
2743 				//           pF = pO->getPrev();
2744 				// 	  while(pF)
2745 				// 	  {
2746 				// 	      if(pF->getType() == pf_Frag::PFT_Object)
2747 				// 	      {
2748 				// 		   pf_Frag_Object *pOb = static_cast<pf_Frag_Object*>(pF);
2749 				// 		   if(pOb->getObjectType() == PTO_Hyperlink)
2750 				// 		   {
2751 				// 		        posComrade = getFragPosition(pOb);
2752 				// 			bFoundStrux2 = _getStruxFromFragSkip(pOb,&pfsContainer2);
2753 				// 			UT_return_val_if_fail (bFoundStrux2, false);
2754 
2755 
2756 				// 			xxx_UT_DEBUGMSG(("Deleting End Hyperlink 1 %p \n",pOb));
2757 				// 			bResult2 =
2758 				// 			  _deleteObjectWithNotify(posComrade,pOb,0,1,
2759 				// 						  pfsContainer2,0,0);
2760 
2761 				// 			// now adjusting the positional variables
2762 				// 			if(posComrade <= dpos1)
2763 				// 			  // delete before the start of the segement we are working on
2764 				// 			     dpos1--;
2765 				// 			else
2766 				// 			{
2767 				// 			     // we are inside that section
2768 				// 			     length--;
2769 				// 			}
2770 				// 			break;
2771 				// 		   }
2772 				// 	      }
2773 				// 	      pF = pF->getPrev();
2774 				// 	  }
2775 
2776 				// 	  xxx_UT_DEBUGMSG(("Deleting Start Hyperlink 1 %p \n",pO));
2777 				// 	  bResult
2778 				// 	    = _deleteObjectWithNotify(dpos1,pO,fragOffset_First,lengthThisStep,
2779 				// 						  pfsContainer,&pfNewEnd,&fragOffsetNewEnd);
2780 
2781 				//      }
2782 				//      else
2783 				//      {
2784 				//        // in this case we are looking for the end marker,
2785 				//        // so we have to be carefult the get rid of the start
2786 				//        // marker first
2787 				//     	   pF = pO->getNext();
2788 				// 	   while(pF)
2789 				// 	   {
2790 				// 	        if(pF->getType() == pf_Frag::PFT_Object)
2791 				// 		{
2792 				// 		     pf_Frag_Object *pOb = static_cast<pf_Frag_Object*>(pF);
2793 				// 		     if(pOb->getObjectType() == PTO_Hyperlink)
2794 				// 		     {
2795 				// 		          posComrade = getFragPosition(pOb);
2796 				// 			  bFoundStrux2 = _getStruxFromFragSkip(pOb,&pfsContainer2);
2797 				// 			  UT_return_val_if_fail (bFoundStrux2, false);
2798 				// 			  // delete the original start marker
2799 
2800 				// 			  xxx_UT_DEBUGMSG(("Deleting Start Hyperlink 2 %p \n",pO));
2801 				// 			  bResult
2802 				// 			    = _deleteObjectWithNotify(dpos1,pO,fragOffset_First,lengthThisStep,
2803 				// 						      pfsContainer,&pfNewEnd,&fragOffsetNewEnd);
2804 
2805 				// 			  // now adjusting the positional variables
2806 				// 			  posComrade--;
2807 				// 			  xxx_UT_DEBUGMSG(("Deleting End Hyperlink 2 %p \n",pOb));
2808 				// 			  // One last twist make sure the next frag from the previous
2809 				// 			  // delete isn't the same as this this other we need to get the next frag from thi
2810 				// 			  // second delete
2811 				// 			  if(pfNewEnd != static_cast<pf_Frag *>(pOb))
2812 				// 			  {
2813 				// 			        bResult2 =
2814 				// 				  _deleteObjectWithNotify(posComrade,pOb,0,1,
2815 				// 							pfsContainer2,0,0);
2816 				// 			  }
2817 				// 			  else
2818 				// 			  {
2819 				// 			        bResult2 =
2820 				// 				  _deleteObjectWithNotify(posComrade,pOb,0,1,
2821 				// 							pfsContainer2,&pfNewEnd,&fragOffsetNewEnd);
2822 				// 			  }
2823 
2824 				// 			  if(posComrade >= dpos1 && posComrade <= dpos1 + length - 2)
2825 				// 			  {
2826 				// 			    // the endmarker was inside of the segment we are working
2827 				// 			    // so we have to adjust the length
2828 				// 			       length--;
2829 				// 			  }
2830 
2831 				// 			  break;
2832 				// 		     }
2833 				// 		}
2834 				// 		pF = pF->getNext();
2835 				// 	   }
2836 				//      }
2837 				// }
2838 				xxx_UT_DEBUGMSG(("Finished Deleting Hyperlink %p \n",pO));
2839 				break;
2840 
2841 				//
2842 				// When deleting either the start or end of annotation we have to deleted
2843 				// the content of the annotation as well.
2844 				// We have to always delete the start marker first; this is
2845 				// so that in the case of undo the endmarker would be restored
2846 				// first, because the mechanism that marks runs between them
2847 				// as a hyperlink depents on the end-marker being in place before
2848 				// the start marker
2849                 //
2850 				case PTO_Annotation:
2851                     bResult = _deleteComplexSpanHAR( pO,
2852                                                       dpos1,
2853                                                       dpos2,
2854                                                       length,
2855                                                       fragOffset_First,
2856                                                       lengthThisStep,
2857                                                       pfsContainer,
2858                                                       pfNewEnd,
2859                                                       fragOffsetNewEnd,
2860                                                       "annotation" );
2861                     //   {
2862                 //     UT_ASSERT(pO->getObjectType() == PTO_Annotation);
2863 				//     bool bFoundStrux2;
2864 				//     PT_DocPosition posComrade;
2865 				//     pf_Frag_Strux * pfsContainer2 = NULL;
2866 
2867 				//     pf_Frag * pF;
2868 
2869 				//     const PP_AttrProp * pAP = NULL;
2870 				//     pO->getPieceTable()->getAttrProp(pO->getIndexAP(),&pAP);
2871 				//     UT_return_val_if_fail (pAP, false);
2872 				//     const gchar* pszHref = NULL;
2873 				//     const gchar* pszHname  = NULL;
2874 				//     UT_uint32 k = 0;
2875 				//     bool bStart = false;
2876 				//     while((pAP)->getNthAttribute(k++,pszHname, pszHref))
2877 				//     {
2878 				//       if((strcmp(pszHname, "annotation") == 0) ||(strcmp(pszHname, "Annotation") == 0) )
2879 				//     	{
2880 				//     		bStart = true;
2881 		    	// 			break;
2882 				//     	}
2883 				//     }
2884 
2885 				// 	if(!bStart)
2886 				// 	{
2887 				// 		// in this case we are looking for the start marker
2888 				// 		// and so we delete it and then move on
2889 				//     	     pF = pO->getPrev();
2890 				// 	     while(pF)
2891 				// 	     {
2892 				// 	          if(pF->getType() == pf_Frag::PFT_Object)
2893 				// 		  {
2894 				// 		       pf_Frag_Object *pOb = static_cast<pf_Frag_Object*>(pF);
2895 				// 		       if(pOb->getObjectType() == PTO_Annotation)
2896 				// 		       {
2897 				// 			   posComrade = getFragPosition(pOb);
2898 				// 			   bFoundStrux2 = _getStruxFromFragSkip(pOb,&pfsContainer2);
2899 				// 			   UT_return_val_if_fail (bFoundStrux2, false);
2900 
2901 				// 			   bResult2 =
2902 				// 			     _deleteObjectWithNotify(posComrade,pOb,0,1,
2903 				// 						     pfsContainer2,0,0);
2904 
2905 				// 			   // now adjusting the positional variables
2906 				// 			   if(posComrade <= dpos1)
2907 				// 			     // delete before the start of the segement we are working on
2908 				// 			        dpos1--;
2909 				// 			   else
2910 				// 			   {
2911 				// 			        // we are inside that section
2912 				// 			        length--;
2913 				// 			   }
2914 				// 			   break;
2915 				// 		       }
2916 				// 		  }
2917 				// 		  pF = pF->getPrev();
2918 				// 	     }
2919 				// 	     UT_ASSERT(pO->getObjectType() == PTO_Annotation);
2920 				// 	     bResult
2921 				// 	       = _deleteObjectWithNotify(dpos1,pO,fragOffset_First,lengthThisStep,
2922 				// 				    pfsContainer,&pfNewEnd,&fragOffsetNewEnd);
2923 
2924 				// 	}
2925 				// 	else
2926 				// 	{
2927 				// 		// in this case we are looking for the end marker,
2928 				// 		// so we have to be carefult the get rid of the start
2929 				// 		// marker first
2930 				// 	     pF = pO->getNext();
2931 				// 	     while(pF)
2932 				// 	     {
2933 				// 	         if(pF->getType() == pf_Frag::PFT_Object)
2934 				// 		 {
2935 				// 		     pf_Frag_Object *pOb = static_cast<pf_Frag_Object*>(pF);
2936 				// 		     if(pOb->getObjectType() == PTO_Annotation)
2937 				// 		     {
2938 				// 		         posComrade = getFragPosition(pOb);
2939 				// 			 bFoundStrux2 = _getStruxFromFragSkip(pOb,&pfsContainer2);
2940 				// 			 UT_return_val_if_fail (bFoundStrux2, false);
2941 				// 	                // delete the original start marker
2942 				// 			 bResult
2943 				// 			   = _deleteObjectWithNotify(dpos1,pO,fragOffset_First,lengthThisStep,
2944 				// 						     pfsContainer,&pfNewEnd,&fragOffsetNewEnd);
2945 
2946 				// 			 // now adjusting the positional variables
2947 				// 			 posComrade--;
2948 
2949 
2950 				// 			 // One last twist make sure the next frag from the previous
2951 				// 			 // delete isn't the same as this this other we need to get the next frag from thi
2952 				// 			 // second delete
2953 				// 			 if(pfNewEnd != static_cast<pf_Frag *>(pOb))
2954 				// 			 {
2955 				// 			      bResult2 =
2956 				// 				_deleteObjectWithNotify(posComrade,pOb,0,1,
2957 				// 							pfsContainer2,0,0);
2958 				// 			 }
2959 				// 			 else
2960 				// 			 {
2961 				// 			      bResult2 =
2962 				// 				_deleteObjectWithNotify(posComrade,pOb,0,1,
2963 				// 							pfsContainer2,&pfNewEnd,&fragOffsetNewEnd);
2964 				// 			 }
2965 
2966 				// 			 // FIXME!! Need to work out how to delete the content of the annotation
2967 				// 			 // at this point
2968 
2969 				// 			 if(posComrade >= dpos1 && posComrade <= dpos1 + length - 2)
2970 				// 			 {
2971 				// 			      // the endmarker was inside of the segment we are working
2972 				// 			      // so we have to adjust the length
2973 				// 			      length--;
2974 				// 			 }
2975 
2976 				// 			 break;
2977 				// 		     }
2978 				// 		 }
2979 				// 		 pF = pF->getNext();
2980 				// 	     }
2981 				// 	}
2982 				// }
2983 				break;
2984 
2985 				case PTO_RDFAnchor:
2986                     bResult = _deleteComplexSpanHAR( pO,
2987                                                      dpos1,
2988                                                      dpos2,
2989                                                      length,
2990                                                      fragOffset_First,
2991                                                      lengthThisStep,
2992                                                      pfsContainer,
2993                                                      pfNewEnd,
2994                                                      fragOffsetNewEnd,
2995                                                      PT_XMLID );
2996                     break;
2997 
2998 				case PTO_Field:
2999 				{
3000 				}
3001 				default:
3002 					bResult
3003 						= _deleteObjectWithNotify(dpos1,pO,fragOffset_First,lengthThisStep,
3004 									  pfsContainer,&pfNewEnd,&fragOffsetNewEnd);
3005 
3006 			}
3007 
3008 
3009 			UT_return_val_if_fail (bResult, false);
3010 		}
3011 		break;
3012 
3013 		case pf_Frag::PFT_FmtMark:
3014 			// we already took care of these...
3015 			UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
3016 			break;
3017 
3018 		}
3019 
3020 		// we do not change dpos1, since we are deleting stuff, but we
3021 		// do decrement the length-remaining.
3022 		// dpos2 becomes bogus at this point.
3023 
3024 		length -= lengthThisStep;
3025 
3026 		// since _delete{*}WithNotify(), can delete pf_First, mess with the
3027 		// fragment list, and does some aggressive coalescing of
3028 		// fragments, we cannot just do a pf_First->getNext() here.
3029 		// to advance to the next fragment, we use the *NewEnd variables
3030 		// that each of the _delete routines gave us.
3031 
3032 		pf_First = pfNewEnd;
3033 		if (!pf_First)
3034 			length = 0;
3035 		fragOffset_First = fragOffsetNewEnd;
3036 	}
3037 
3038 	if (pfsFirstBlock)
3039 	{
3040 		UT_DEBUGMSG(("Delete first block of a section\n"));
3041 		bool bResult = _deleteStruxWithNotify(pfsFirstBlock->getPos(),pfsFirstBlock,
3042 											  &pfNewEnd,&fragOffsetNewEnd);
3043 		UT_return_val_if_fail (bResult, false);
3044 	}
3045 
3046 	return true;
3047 }
3048 
3049 
_deleteComplexSpan_norec(PT_DocPosition dpos1,PT_DocPosition dpos2)3050 bool pt_PieceTable::_deleteComplexSpan_norec(PT_DocPosition dpos1,
3051 											 PT_DocPosition dpos2)
3052 {
3053 	pf_Frag * pfNewEnd;
3054 	UT_uint32 fragOffsetNewEnd;
3055 
3056 	pf_Frag * pf_First;
3057 	pf_Frag * pf_End;
3058 	PT_BlockOffset fragOffset_First;
3059 	PT_BlockOffset fragOffset_End;
3060 
3061 	bool bFound = getFragsFromPositions(dpos1,dpos2,&pf_First,&fragOffset_First,&pf_End,&fragOffset_End);
3062 	UT_return_val_if_fail (bFound, false);
3063 
3064 	pf_Frag_Strux * pfsContainer = NULL;
3065 	bool bFoundStrux = _getStruxFromPosition(dpos1,&pfsContainer);
3066 	UT_return_val_if_fail (bFoundStrux, false);
3067 
3068 	// loop to delete the amount requested, one text fragment at a time.
3069 	// if we encounter any non-text fragments along the way, we delete
3070 	// them too.  that is, we implicitly delete Strux and Objects here.
3071 
3072 	UT_uint32 length = dpos2 - dpos1;
3073 	while (length > 0)
3074 	{
3075 		UT_uint32 lengthInFrag = pf_First->getLength() - fragOffset_First;
3076 		UT_uint32 lengthThisStep = UT_MIN(lengthInFrag, length);
3077 
3078 		switch (pf_First->getType())
3079 		{
3080 		case pf_Frag::PFT_EndOfDoc:
3081 		default:
3082 			UT_ASSERT_HARMLESS(0);
3083 			return false;
3084 
3085 		case pf_Frag::PFT_Strux:
3086 		{
3087 			pf_Frag_Strux * pfs = static_cast<pf_Frag_Strux *> (pf_First);
3088 
3089 			bool bResult = _deleteStruxWithNotify(dpos1,pfs,
3090 									   &pfNewEnd,&fragOffsetNewEnd,
3091 									   false);
3092 			UT_return_val_if_fail (bResult, false);
3093 			// we do not update pfsContainer because we just deleted pfs.
3094 		}
3095 		break;
3096 
3097 		case pf_Frag::PFT_Text:
3098 		{
3099 			bool bResult = _deleteSpanWithNotify(dpos1,
3100 									  static_cast<pf_Frag_Text *>(pf_First),
3101 									  fragOffset_First,lengthThisStep,
3102 									  pfsContainer,&pfNewEnd,
3103 									  &fragOffsetNewEnd, false);
3104 			UT_return_val_if_fail (bResult, false);
3105 		}
3106 		break;
3107 
3108 		case pf_Frag::PFT_Object:
3109 		{
3110 			bool bResult = _deleteObjectWithNotify(dpos1,static_cast<pf_Frag_Object *>(pf_First),
3111 									fragOffset_First,lengthThisStep,
3112 									pfsContainer,&pfNewEnd,&fragOffsetNewEnd,
3113 									false);
3114 			UT_return_val_if_fail (bResult, false);
3115 		}
3116 		break;
3117 
3118 		case pf_Frag::PFT_FmtMark:
3119 			// we already took care of these...
3120 			UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
3121 			break;
3122 
3123 		}
3124 
3125 		// we do not change dpos1, since we are deleting stuff, but we
3126 		// do decrement the length-remaining.
3127 		// dpos2 becomes bogus at this point.
3128 
3129 		length -= lengthThisStep;
3130 
3131 		// since _delete{*}WithNotify(), can delete pf_First, mess with the
3132 		// fragment list, and does some aggressive coalescing of
3133 		// fragments, we cannot just do a pf_First->getNext() here.
3134 		// to advance to the next fragment, we use the *NewEnd variables
3135 		// that each of the _delete routines gave us.
3136 
3137 		pf_First = pfNewEnd;
3138 		if (!pf_First)
3139 			length = 0;
3140 		fragOffset_First = fragOffsetNewEnd;
3141 	}
3142 
3143 	return true;
3144 }
3145 
3146 
3147 
_realDeleteSpan(PT_DocPosition dpos1,PT_DocPosition dpos2,PP_AttrProp * p_AttrProp_Before,bool bDeleteTableStruxes,bool bDontGlob)3148 bool pt_PieceTable::_realDeleteSpan(PT_DocPosition dpos1,
3149 									PT_DocPosition dpos2,
3150 									PP_AttrProp *p_AttrProp_Before,
3151 									bool bDeleteTableStruxes,
3152 									bool bDontGlob)
3153 {
3154     UT_DEBUGMSG(("_realDeleteSpan() dpos1:%d dpos2:%d\n", dpos1, dpos2 ));
3155 	// remove (dpos2-dpos1) characters from the document at the given position.
3156 
3157 	UT_return_val_if_fail (m_pts==PTS_Editing, false);
3158 	UT_return_val_if_fail (dpos2 > dpos1, false);
3159 
3160 	bool bSuccess = true;
3161 	UT_Stack stDelayStruxDelete;
3162 
3163 	PT_DocPosition old_dpos2 = dpos2;
3164 
3165 	//  Before we begin the delete proper, we might want to adjust the ends
3166 	//  of the delete slightly to account for expected behavior on
3167 	//  structural boundaries.
3168 	bSuccess = _tweakDeleteSpan(dpos1, dpos2, &stDelayStruxDelete);
3169 	if (!bSuccess)
3170 	{
3171 		return false;
3172 	}
3173 
3174 	// Get the attribute properties before the delete.
3175 
3176 	PP_AttrProp AttrProp_Before;
3177 
3178 	{
3179 		pf_Frag * pf1;
3180 		PT_BlockOffset Offset1;
3181 		getFragFromPosition(dpos1, &pf1, &Offset1);
3182 		if(pf1->getType() == pf_Frag::PFT_Text)
3183 		{
3184 			const PP_AttrProp *p_AttrProp;
3185 			getAttrProp(static_cast<pf_Frag_Text *>(pf1)->getIndexAP(), &p_AttrProp);
3186 
3187 			AttrProp_Before = *p_AttrProp;
3188 			if(p_AttrProp_Before)
3189 				*p_AttrProp_Before = *p_AttrProp;
3190 
3191 			// we do not want to inherit revision attribute
3192 			AttrProp_Before.setAttribute("revision", "");
3193 		}
3194 	}
3195 
3196 	// The code used to only glob for the complex case. But when
3197 	// there's a simple delete, we may still end up adding the
3198 	// formatmark below (i.e., when deleting all the text in a
3199 	// document), and thus creating a two-step undo for a perceived
3200 	// one-step operation. See Bug FIXME
3201 	if(!bDontGlob)
3202 	{
3203 		beginMultiStepGlob();
3204 	}
3205 
3206 	bool bIsSimple = _isSimpleDeleteSpan(dpos1, dpos2) && stDelayStruxDelete.getDepth() == 0;
3207 	if (bIsSimple)
3208 	{
3209 		//  If the delete is sure to be within a fragment, we don't
3210 		//  need to worry about much of the bookkeeping of a complex
3211 		//  delete.
3212 		bSuccess = _deleteComplexSpan(dpos1, dpos2, &stDelayStruxDelete);
3213 	}
3214 	else
3215 	{
3216 		//  If the delete spans multiple fragments, we need to
3217 		//  be a bit more careful about deleting the formatting
3218 		//  first, and then the actual spans.
3219 		_changePointWithNotify(old_dpos2);
3220 
3221 		UT_sint32 oldDepth = stDelayStruxDelete.getDepth();
3222 		bSuccess = _deleteFormatting(dpos1, dpos2);
3223 		if (bSuccess)
3224 		{
3225 			bSuccess = _deleteComplexSpan(dpos1, dpos2, &stDelayStruxDelete);
3226 		}
3227 
3228 		bool prevDepthReached = false;
3229 		PT_DocPosition finalPos = dpos1;
3230 		while (bSuccess && stDelayStruxDelete.getDepth() > 0)
3231 		{
3232 			pf_Frag_Strux * pfs;
3233 			if(stDelayStruxDelete.getDepth() <= oldDepth)
3234 			{
3235 				prevDepthReached = true;
3236 			}
3237 			stDelayStruxDelete.pop(reinterpret_cast<void **>(&pfs));
3238 
3239  			pf_Frag *pf;
3240 			PT_DocPosition dp;
3241 			if(bDeleteTableStruxes || prevDepthReached )
3242 			{
3243 				if(!prevDepthReached)
3244 				{
3245 					_deleteFormatting(dpos1 - pfs->getLength(), dpos1);
3246 //
3247 // FIXME this code should be removed if undo/redo on table manipulations
3248 //       works fine.
3249 #if 0
3250 //					_deleteFormatting(myPos - pfs->getLength(), myPos);
3251 //					bSuccess = _deleteStruxWithNotify(myPos - pfs->getLength(), pfs,
3252 //													  &pf, &dp);
3253 #endif
3254 
3255 					PT_DocPosition myPos = pfs->getPos();
3256 					bSuccess = _deleteStruxWithNotify(myPos, pfs, &pf, &dp);
3257 				}
3258 				else if(pfs->getPos() >= dpos1)
3259 				{
3260 					_deleteFormatting(dpos1 - pfs->getLength(), dpos1);
3261 					bSuccess = _deleteStruxWithNotify(dpos1 - pfs->getLength(), pfs,
3262 													  &pf, &dp);
3263 				}
3264 			}
3265 			else
3266 			{
3267 				bSuccess = true;
3268 				pf = pfs->getNext();
3269 				dp = 0;
3270 				dpos1 = dpos1 + pfs->getLength();
3271 			}
3272 		}
3273 
3274 		_changePointWithNotify(finalPos);
3275 	}
3276 
3277 	// Have we deleted all the text in a paragraph.
3278 
3279 	pf_Frag * p_frag_before;
3280 	PT_BlockOffset Offset_before;
3281 	getFragFromPosition(dpos1 - 1, &p_frag_before, &Offset_before);
3282 
3283 	pf_Frag * p_frag_after;
3284 	PT_BlockOffset Offset_after;
3285 	getFragFromPosition(dpos1, &p_frag_after, &Offset_after);
3286 
3287 	if(((p_frag_before->getType() == pf_Frag::PFT_Strux) ||
3288 		(p_frag_before->getType() == pf_Frag::PFT_EndOfDoc)) &&
3289 	   ((p_frag_after->getType() == pf_Frag::PFT_Strux) ||
3290 		(p_frag_after->getType() == pf_Frag::PFT_EndOfDoc)))
3291 	{
3292 		xxx_UT_DEBUGMSG(("pt_PieceTable::deleteSpan Paragraph empty\n"));
3293 
3294 		// All text in paragraph is deleted so insert a text format
3295 		// Unless we've deleted all text in a footnote type structure.
3296 		// or if bDontGlob is true.
3297 		// If we insert an FmtMark is an empty footnote it
3298 		// will appear in the enclosing block and
3299 		// screw up the run list.
3300 		//
3301 		bool bDoit = !bDontGlob;
3302 		if(bDoit && (p_frag_after->getType() == pf_Frag::PFT_Strux))
3303 		{
3304 		     pf_Frag_Strux * pfsa = static_cast<pf_Frag_Strux *>(p_frag_after);
3305 		     if(isEndFootnote(pfsa))
3306 		     {
3307 			 bDoit = false;
3308 		     }
3309 		}
3310 		pf_Frag_Strux * pfs = static_cast<pf_Frag_Strux *>(p_frag_before);
3311 		if(bDoit && ((pfs->getStruxType() == PTX_Block) || (p_frag_before->getType() == pf_Frag::PFT_EndOfDoc) ))
3312 			_insertFmtMarkFragWithNotify(PTC_AddFmt, dpos1, &AttrProp_Before);
3313 
3314 	}
3315 
3316 	// End the glob after (maybe) having inserted the FmtMark
3317 	if (!bDontGlob)
3318 	{
3319 		endMultiStepGlob();
3320 	}
3321 
3322 	return bSuccess;
3323 }
3324 
3325 // need a special delete for a field update because otherwise
3326 // the whole field object would be deleted by _tweakDeleteSpan
deleteFieldFrag(pf_Frag * pf)3327 bool pt_PieceTable::deleteFieldFrag(pf_Frag * pf)
3328 {
3329 
3330 
3331 	UT_return_val_if_fail (m_pts==PTS_Editing,false);
3332 
3333 	bool bSuccess = true;
3334 	UT_Stack stDelayStruxDelete;
3335 
3336 	PT_DocPosition dpos1 = getFragPosition(pf);
3337 	UT_return_val_if_fail (dpos1,false);
3338 	PT_DocPosition dpos2 = dpos1 + pf->getLength();
3339 
3340 
3341 	//  If the delete is sure to be within a fragment, we don't
3342 	//  need to worry about much of the bookkeeping of a complex
3343 	//  delete.
3344 	bSuccess = _deleteComplexSpan_norec(dpos1, dpos2);
3345 	return bSuccess;
3346 }
3347 
_tweakFieldSpan(PT_DocPosition & dpos1,PT_DocPosition & dpos2) const3348 void pt_PieceTable::_tweakFieldSpan(PT_DocPosition & dpos1,
3349                                     PT_DocPosition & dpos2) const
3350 {
3351 	if(m_bDoNotTweakPosition)
3352 		return;
3353 
3354 	//  Our job is to adjust the end positions of the delete
3355 	//  operating to delete those structural object that the
3356 	//  user will expect to have deleted, even if the dpositions
3357 	//  aren't quite right to encompass those.
3358 
3359 	pf_Frag * pf_First;
3360 	pf_Frag * pf_End;
3361 	pf_Frag * pf_Other;
3362 	PT_BlockOffset fragOffset_First;
3363 	PT_BlockOffset fragOffset_End;
3364 
3365 	bool bFound = getFragsFromPositions(dpos1,dpos2,&pf_First,&fragOffset_First,&pf_End,&fragOffset_End);
3366 	UT_return_if_fail (bFound);
3367 
3368 	pf_Frag_Strux * pfsContainer = NULL;
3369 	bool bFoundStrux = _getStruxFromPosition(dpos1,&pfsContainer);
3370 	UT_return_if_fail (bFoundStrux);
3371 
3372     // if start in middle of field widen to include object
3373     if ((pf_First->getType() == pf_Frag::PFT_Text)&&
3374         (static_cast<pf_Frag_Text *>(pf_First)->getField()))
3375     {
3376         pf_Frag_Text * pft = static_cast<pf_Frag_Text *>(pf_First);
3377         pf_Frag_Text * pft2 = NULL;
3378         // we can't delete part of a field so widen deletion to
3379         // include object at start
3380         while (pft->getPrev()->getType() == pf_Frag::PFT_Text)
3381         {
3382             pft2 = static_cast<pf_Frag_Text *>(pft->getPrev());
3383             UT_ASSERT_HARMLESS(pft->getField() == pft2->getField());
3384             pft = pft2;
3385         }
3386         UT_return_if_fail (pft->getPrev()->getType() == pf_Frag::PFT_Object);
3387         pf_Frag_Object *pfo =
3388             static_cast<pf_Frag_Object *>(pft->getPrev());
3389         UT_return_if_fail (pfo->getObjectType()==PTO_Field);
3390         UT_return_if_fail (pfo->getField()==pft->getField());
3391         dpos1 = getFragPosition(pfo);
3392     }
3393     // if end in middle of field widen to include whole Frag_Text
3394     if (((pf_End->getType() == pf_Frag::PFT_Text)&&
3395          (pf_End)->getField())/*||
3396 								((pf_End->getType() == pf_Frag::PFT_Object
3397 								)&&
3398 								(static_cast<pf_Frag_Object *>(pf_End)
3399 								->getObjectType()==PTO_Field))*/)
3400     {
3401         fd_Field * pField = pf_End->getField();
3402         UT_return_if_fail (pField);
3403         pf_Other = pf_End->getNext();
3404         UT_return_if_fail (pf_Other);
3405         while (pf_Other->getField()==pField)
3406         {
3407             pf_Other = pf_Other->getNext();
3408             UT_return_if_fail (pf_Other);
3409         }
3410         dpos2 = getFragPosition(pf_Other);
3411     }
3412 }
3413