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