1 /* AbiWord
2  * Copyright (C) 1998 AbiSource, Inc.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301 USA.
18  */
19 
20 
21 #include "ut_types.h"
22 #include "ut_misc.h"
23 #include "ut_assert.h"
24 #include "ut_debugmsg.h"
25 #include "ut_growbuf.h"
26 #include "pt_PieceTable.h"
27 #include "pf_Frag.h"
28 #include "pf_Frag_FmtMark.h"
29 #include "pf_Frag_Object.h"
30 #include "pf_Frag_Strux.h"
31 #include "pf_Frag_Strux_Block.h"
32 #include "pf_Frag_Strux_Section.h"
33 #include "pf_Frag_Text.h"
34 #include "pf_Fragments.h"
35 #include "px_ChangeRecord.h"
36 #include "px_CR_Object.h"
37 #include "px_CR_Span.h"
38 #include "px_CR_SpanChange.h"
39 #include "px_CR_Strux.h"
40 
41 
42 /*****************************************************************/
43 /*****************************************************************/
44 
tellListener(PL_Listener * pListener)45 bool pt_PieceTable::tellListener(PL_Listener* pListener)
46 {
47 	return _tellAndMaybeAddListener(pListener, 0, false);
48 }
49 
addListener(PL_Listener * pListener,PL_ListenerId listenerId)50 bool pt_PieceTable::addListener(PL_Listener* pListener,
51 								   PL_ListenerId listenerId)
52 {
53 	return _tellAndMaybeAddListener(pListener, listenerId, true);
54 }
55 
_tellAndMaybeAddListener(PL_Listener * pListener,PL_ListenerId listenerId,bool bAdd)56 bool pt_PieceTable::_tellAndMaybeAddListener(PL_Listener * pListener,
57 												PL_ListenerId listenerId,
58 												bool bAdd)
59 {
60 	// walk document and for each fragment, send a notification
61 	// to each layout.
62 
63 	fl_ContainerLayout* sfh = 0;
64 	PT_DocPosition sum = 0;
65 	UT_uint32 blockOffset = 0;
66 	pf_Frag_Strux * pfs2 = NULL;
67 	bool bListensOnly = (pListener->getType() >= PTL_CollabExport);
68 	for (pf_Frag * pf = m_fragments.getFirst(); (pf); pf=pf->getNext())
69 	{
70 		switch (pf->getType())
71 		{
72 		case pf_Frag::PFT_Text:
73 			{
74 			        if(bListensOnly)
75 				{
76 			                break;
77 				}
78 				pf_Frag_Text * pft = static_cast<pf_Frag_Text *> (pf);
79 				PX_ChangeRecord * pcr = NULL;
80 				bool bStatus1 = false;
81 				bool bAddOffset = true;
82 				if(sfh != NULL)
83 				{
84 					bStatus1 = pft->createSpecialChangeRecord(&pcr,sum,blockOffset);
85 					UT_return_val_if_fail (bStatus1, false);
86 				}
87 				else
88 				{
89 					PT_DocPosition pos = pf->getPos();
90 					getStruxOfTypeFromPosition(listenerId,pos,PTX_Block,&sfh);
91 
92 					getStruxOfTypeFromPosition(pos,PTX_Block,&pfs2);
93 					blockOffset = pos - pfs2->getPos() -1;
94 					bStatus1 = pft->createSpecialChangeRecord(&pcr,pos,blockOffset);
95 					UT_return_val_if_fail (bStatus1,false);
96 					// I do not understand at all why this was set to
97 					// false; if the doc contains a footnote section
98 					// followed by a text fragment and another
99 					// fragment, the text fragment and the fragment
100 					// after it are given identical block offsets !!!
101 					// Tomas, May, 12, 2003
102 					bAddOffset = true;
103 				}
104 				bool bStatus2 = pListener->populate(sfh,pcr);
105 				if (pcr)
106 					delete pcr;
107 				if (!bStatus2)
108 					return false;
109 				if(bAddOffset)
110 				{
111 					blockOffset += pf->getLength();
112 				}
113 			}
114 			break;
115 
116 		case pf_Frag::PFT_Strux:
117 			{
118 				pfs2 = static_cast<pf_Frag_Strux *> (pf);
119 				sfh = 0;
120 			        if(bListensOnly)
121 				{
122 					pfs2->setFmtHandle(listenerId,sfh);
123 			                break;
124 				}
125 				PX_ChangeRecord * pcr = NULL;
126 				bool bStatus1 = pfs2->createSpecialChangeRecord(&pcr,sum);
127 				UT_return_val_if_fail (bStatus1, false);
128 				bool bStatus2 = pListener->populateStrux(pfs2,pcr,&sfh);
129 
130 				// This can happen legally, for example when inserting a hdr/ftr strux
131 				// which was marked deleted in revisions mode -- such strux has no
132 				// corresponding layout element
133 				// UT_ASSERT_HARMLESS( sfh || !bAdd );
134 				if (bAdd && sfh)
135 				{
136 					pfs2->setFmtHandle(listenerId,sfh);
137 				}
138 
139 				if (pcr)
140 					delete pcr;
141 				if (!bStatus2)
142 					return false;
143 				blockOffset = 0;
144 				if(isEndFootnote(pfs2))
145 				{
146 					sfh = NULL;
147 				}
148 			}
149 			break;
150 
151 		case pf_Frag::PFT_Object:
152 			{
153 			        if(bListensOnly)
154 				{
155 			                break;
156 				}
157 				pf_Frag_Object * pfo = static_cast<pf_Frag_Object *> (pf);
158 				PX_ChangeRecord * pcr = NULL;
159 				bool bStatus1 = false;
160 				bool bAddOffset = true;
161 				if(sfh != NULL)
162 				{
163 					bStatus1 = pfo->createSpecialChangeRecord(&pcr,sum,blockOffset);
164 					UT_return_val_if_fail (bStatus1,false);
165 				}
166 				else
167 				{
168 					PT_DocPosition pos = pf->getPos();
169 					getStruxOfTypeFromPosition(listenerId,pos,PTX_Block,&sfh);
170 					pf_Frag_Strux* pfs = NULL;
171 					getStruxOfTypeFromPosition(pos,PTX_Block,&pfs);
172 					if(!pfs)
173 					  return false;
174 					blockOffset = pos - pfs->getPos() -1;
175 					bStatus1 = pfo->createSpecialChangeRecord(&pcr,pos,blockOffset);
176 					UT_return_val_if_fail (bStatus1, false);
177 					// I do not understand at all why this was set to
178 					// false; if the doc contains a footnote section
179 					// followed by a text fragment and another
180 					// fragment, the text fragment and the fragment
181 					// after it are given identical block offsets !!!
182 					// Tomas, May, 12, 2003
183 					bAddOffset = true;
184 				}
185 
186 				UT_return_val_if_fail (bStatus1,false);
187 				bool bStatus2 = pListener->populate(sfh,pcr);
188 				if (pcr)
189 					delete pcr;
190 				if (!bStatus2)
191 					return false;
192 				if(bAddOffset)
193 				{
194 					blockOffset += pf->getLength();
195 				}
196 			}
197 			break;
198 
199 		case pf_Frag::PFT_FmtMark:
200 			{
201 			        if(bListensOnly)
202 				{
203 			                break;
204 				}
205 				pf_Frag_FmtMark * pffm = static_cast<pf_Frag_FmtMark *> (pf);
206 				PX_ChangeRecord * pcr = NULL;
207 				bool bStatus1 = false;
208 				bool bAddOffset = true;
209 				if(sfh != NULL)
210 				{
211 					bStatus1 = pffm->createSpecialChangeRecord(&pcr,sum,blockOffset);
212 					UT_return_val_if_fail (bStatus1,false);
213 				}
214 				else
215 				{
216 					PT_DocPosition pos = pf->getPos();
217 					getStruxOfTypeFromPosition(listenerId,pos,PTX_Block,&sfh);
218 					getStruxOfTypeFromPosition(pos,PTX_Block,&pfs2);
219 					blockOffset = pos - pfs2->getPos() -1;
220 					bStatus1 = pffm->createSpecialChangeRecord(&pcr,pos,blockOffset);
221 					UT_return_val_if_fail (bStatus1, false);
222 					bAddOffset = false;
223 				}
224 				bool bStatus2 = pListener->populate(sfh,pcr);
225 				DELETEP(pcr);
226 				if (!bStatus2)
227 					return false;
228 				if(bAddOffset)
229 				{
230 					blockOffset += pf->getLength();
231 				}
232 			}
233 			break;
234 
235 		case pf_Frag::PFT_EndOfDoc:
236 			// they don't get to know about this.
237 			break;
238 
239 		default:
240 			UT_ASSERT_HARMLESS(0);
241 			return false;
242 		}
243 
244 		sum += pf->getLength();
245 	}
246 
247 	// TODO assert that sum == our cached value.
248 
249 	return true;
250 }
251 
252 #include "pl_ListenerCoupleCloser.h"
253 #include <set>
254 #include <boost/bind.hpp>
255 #include <boost/function.hpp>
256 typedef boost::function< bool (PT_DocPosition, PT_DocPosition, PT_DocPosition, PL_Listener*)> f_WalkRangeFinished_t;
257 
finishedFunctorEndOfRage(PT_DocPosition,PT_DocPosition rangeEndPos,PT_DocPosition curPos,PL_Listener *)258 static bool finishedFunctorEndOfRage( PT_DocPosition /*rangeStartPos*/,
259                                       PT_DocPosition rangeEndPos,
260                                       PT_DocPosition curPos,
261                                       PL_Listener* /*pListener*/ )
262 {
263     if( curPos >= rangeEndPos )
264         return true;
265     return false;
266 }
267 
finishedFunctorFinishingListener(PT_DocPosition,PT_DocPosition,PT_DocPosition,PL_Listener *,PL_FinishingListener * fl)268 static bool finishedFunctorFinishingListener( PT_DocPosition /*rangeStartPos*/,
269                                               PT_DocPosition /*rangeEndPos*/,
270                                               PT_DocPosition /*curPos*/,
271                                               PL_Listener* /*pListener*/,
272                                               PL_FinishingListener* fl )
273 {
274     if( fl->isFinished() )
275         return true;
276     return false;
277 }
278 
279 
280 typedef std::set< pf_Frag::PFType > m_fragtypecol_t;
_getTellListenerSubsetWalkRangeVisitAllFragments()281 static m_fragtypecol_t& _getTellListenerSubsetWalkRangeVisitAllFragments()
282 {
283     static m_fragtypecol_t col;
284     if( col.empty() )
285     {
286         col.insert( pf_Frag::PFT_Text );
287         col.insert( pf_Frag::PFT_Object );
288         col.insert( pf_Frag::PFT_Strux );
289         col.insert( pf_Frag::PFT_EndOfDoc );
290         col.insert( pf_Frag::PFT_FmtMark );
291     }
292     return col;
293 }
294 
295 
296 /**
297  * This is a static function instead of a member so that
298  * boost::functors can be passed in to this function but boost headers
299  * are not needed in pt_PieceTable.h
300  *
301  */
_tellListenerSubsetWalkRange(pt_PieceTable * pt,PL_Listener * pListener,PD_DocumentRange *,PT_DocPosition rangeStartPos,PT_DocPosition rangeEndPos,f_WalkRangeFinished_t finishedFunctor=finishedFunctorEndOfRage,m_fragtypecol_t & fragmentTypesToVisit=_getTellListenerSubsetWalkRangeVisitAllFragments (),bool walkForwards=true)302 static PT_DocPosition _tellListenerSubsetWalkRange(
303     pt_PieceTable* pt,
304     PL_Listener* pListener,
305     PD_DocumentRange*  /*pDocRange*/,
306     PT_DocPosition rangeStartPos,
307     PT_DocPosition rangeEndPos,
308     f_WalkRangeFinished_t finishedFunctor = finishedFunctorEndOfRage,
309     m_fragtypecol_t& fragmentTypesToVisit = _getTellListenerSubsetWalkRangeVisitAllFragments(),
310     bool walkForwards = true )
311 {
312     UT_DEBUGMSG(("_tellListenerSubsetWalkRange(top) listener %p startpos %d endpos %d\n",
313                  pListener, rangeStartPos, rangeEndPos ));
314 	fl_ContainerLayout* sfh = 0;
315 	UT_uint32 blockOffset = 0;
316 
317 	pf_Frag * pf1 = NULL;
318 	PT_BlockOffset fragOffset1 = 0;
319 	PT_BlockOffset endOffset = 0;
320     PT_DocPosition pfPos = rangeStartPos;
321     if( !walkForwards )
322         pfPos = rangeEndPos;
323 
324     if (!pt->getFragFromPosition( pfPos, &pf1, &fragOffset1 ))
325     {
326         UT_DEBUGMSG(("_tellListenerSubsetWalkRange(no frag!) listener %p startpos %d endpos %d\n",
327                      pListener, rangeStartPos, rangeEndPos ));
328         return true;
329     }
330 
331 	PT_DocPosition sum = rangeStartPos - fragOffset1;
332 
333     pf_Frag * pf = pf1;
334 	for ( ; (pf); )
335 	{
336         if( fragmentTypesToVisit.count(pf->getType()))
337         {
338             switch (pf->getType())
339             {
340                 case pf_Frag::PFT_Text:
341                 {
342                     pf_Frag_Text * pft = static_cast<pf_Frag_Text *> (pf);
343                     PX_ChangeRecord * pcr = NULL;
344                     if( rangeEndPos < sum+pf->getLength() )
345                         endOffset = (rangeEndPos - sum);
346                     else
347                         endOffset = pf->getLength();
348                     bool bStatus1 = pft->createSpecialChangeRecord(&pcr,sum,blockOffset,fragOffset1,endOffset);
349                     if(!bStatus1) {
350                         UT_DEBUGMSG(("_tellListenerSubsetWalkRange(st1) a\n" ));
351 		    }
352                     UT_return_val_if_fail (bStatus1,false);
353                     bool bStatus2 = pListener->populate(sfh,pcr);
354                     if (pcr)
355                         delete pcr;
356                     if (!bStatus2)
357                         return false;
358                     blockOffset += pf->getLength();
359                     fragOffset1 = 0;
360                 }
361                 break;
362 
363                 case pf_Frag::PFT_Strux:
364                 {
365                     pf_Frag_Strux * pfs = static_cast<pf_Frag_Strux *> (pf);
366                     pf_Frag_Strux* sdh = pfs;
367                     sfh = 0;
368                     PX_ChangeRecord * pcr = NULL;
369                     bool bStatus1 = pfs->createSpecialChangeRecord(&pcr,sum);
370                     if(!bStatus1) {
371                         UT_DEBUGMSG(("_tellListenerSubsetWalkRange(st1) b\n" ));
372 		    }
373                     UT_return_val_if_fail (bStatus1,false);
374                     bool bStatus2 = pListener->populateStrux(sdh,pcr,&sfh);
375                     if (pcr)
376                         delete pcr;
377                     if (!bStatus2)
378                         return false;
379                     blockOffset = 0;
380                 }
381                 break;
382 
383                 case pf_Frag::PFT_Object:
384                 {
385                     pf_Frag_Object * pfo = static_cast<pf_Frag_Object *> (pf);
386                     PX_ChangeRecord * pcr = NULL;
387                     bool bStatus1 = pfo->createSpecialChangeRecord(&pcr,sum,blockOffset);
388                     if(!bStatus1) {
389                         UT_DEBUGMSG(("_tellListenerSubsetWalkRange(st1) c\n" ));
390 		    }
391                     UT_return_val_if_fail (bStatus1,false);
392                     bool bStatus2 = pListener->populate(sfh,pcr);
393                     if (pcr)
394                         delete pcr;
395                     if (!bStatus2)
396                         return false;
397                     blockOffset += pf->getLength();
398                 }
399                 break;
400 
401                 case pf_Frag::PFT_FmtMark:
402                 {
403                     pf_Frag_FmtMark * pffm = static_cast<pf_Frag_FmtMark *> (pf);
404                     PX_ChangeRecord * pcr = NULL;
405                     bool bStatus1 = pffm->createSpecialChangeRecord(&pcr,sum,blockOffset);
406                     if(!bStatus1) {
407                         UT_DEBUGMSG(("_tellListenerSubsetWalkRange(st1) d\n" ));
408 		    }
409                     UT_return_val_if_fail (bStatus1,false);
410                     bool bStatus2 = pListener->populate(sfh,pcr);
411                     DELETEP(pcr);
412                     if (!bStatus2)
413                         return false;
414                     blockOffset += pf->getLength();
415                 }
416                 break;
417 
418                 case pf_Frag::PFT_EndOfDoc:
419                     // they don't get to know about this.
420                     break;
421 
422                 default:
423                     UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
424                     return false;
425             }
426         }
427 
428         UT_DEBUGMSG(("_tellListenerSubsetWalkRange(loop) listener %p sum %d startpos %d endpos %d\n",
429                      pListener, sum, rangeStartPos, rangeEndPos ));
430 
431 		sum += pf->getLength();
432         if( finishedFunctor( rangeStartPos, rangeEndPos, sum, pListener ) )
433 			break;
434 
435         if( walkForwards )
436         {
437             pf=pf->getNext();
438         }
439         else
440         {
441             pf=pf->getPrev();
442         }
443 	}
444 
445     UT_DEBUGMSG(("_tellListenerSubsetWalkRange(done) listener %p startpos %d endpos %d\n",
446                  pListener, rangeStartPos, rangeEndPos ));
447     return sum;
448 }
449 
450 
451 
tellListenerSubset(PL_Listener * pListener,PD_DocumentRange * pDocRange,PL_ListenerCoupleCloser * closer)452 bool pt_PieceTable::tellListenerSubset( PL_Listener * pListener,
453                                         PD_DocumentRange * pDocRange,
454                                         PL_ListenerCoupleCloser* closer )
455 {
456 	// walk the subset of the document in the given range
457 	// and send notifications.
458 
459     if( closer )
460     {
461         closer->setDocument( getDocument() );
462         closer->setDelegate( pListener );
463     }
464     m_fragtypecol_t closerFragmentTypesToVisit;
465     closerFragmentTypesToVisit.insert( pf_Frag::PFT_Object );
466     closerFragmentTypesToVisit.insert( pf_Frag::PFT_Strux );
467 
468 
469     //bool rc = 0;
470 
471     if( closer )
472     {
473         /*rc =*/ _tellListenerSubsetWalkRange( this,
474                                            closer,
475                                            pDocRange,
476                                            pDocRange->m_pos1,
477                                            pDocRange->m_pos2 );
478 
479         /**
480          * Emit the start tag for things that are closed in the
481          * selected range but are not opened in that range.
482          *
483          * Note that we walk backwards from the start of the range in
484          * order to find the matching open tags as quickly as possible
485          * even if the selection is at the end of a really large
486          * document.
487          *
488          * A null delegate is used while we are walking backwards. If
489          * we allowed the closer to emit to the real delegate then the
490          * calls to populate() on the delegate would happen in reverse
491          * document order. So we walk backwards to find the real
492          * startPos that the closer needs and then refresh the closer
493          * by walking the range again (in case it uses stacks which
494          * were erased during the reverse walk), and then use the real
495          * delegate and walk forwards from the correct startPos that
496          * we just found. This is admittedly a bit tricky, but for a
497          * 1000 page document we really really don't want to walk all
498          * the way from the start, so walking the range twice is
499          * likely to be a small trade off in performance.
500          */
501         if( PL_FinishingListener* cl = closer->getBeforeContentListener() )
502         {
503             bool walkForwards = false;
504             f_WalkRangeFinished_t f = boost::bind( finishedFunctorFinishingListener, _1, _2, _3, _4, cl );
505 
506             PL_FinishingListener* nullListener = closer->getNullContentListener();
507             closer->setDelegate( nullListener );
508             PT_DocPosition startPos = _tellListenerSubsetWalkRange( this, cl,
509                                                                     pDocRange, 0, pDocRange->m_pos1,
510                                                                     f, closerFragmentTypesToVisit, walkForwards );
511 
512             closer->setDelegate( pListener );
513             closer->reset();
514             /*rc =*/ _tellListenerSubsetWalkRange( this,
515                                                closer,
516                                                pDocRange,
517                                                pDocRange->m_pos1,
518                                                pDocRange->m_pos2 );
519 
520             /*rc =*/ _tellListenerSubsetWalkRange( this, cl,
521                                                pDocRange, startPos, pDocRange->m_pos1,
522                                                f, closerFragmentTypesToVisit, walkForwards );
523 
524         }
525     }
526 
527     /*rc =*/ _tellListenerSubsetWalkRange( this, pListener,
528                                        pDocRange, pDocRange->m_pos1, pDocRange->m_pos2 );
529 
530     if( closer )
531     {
532         /**
533          * emit the close tag for things that were left open in the range.
534          */
535         if( PL_FinishingListener* cl = closer->getAfterContentListener() )
536         {
537             f_WalkRangeFinished_t f = boost::bind( finishedFunctorFinishingListener, _1, _2, _3, _4, cl );
538             /*rc =*/ _tellListenerSubsetWalkRange( this, cl,
539                                                pDocRange, pDocRange->m_pos2, 0,
540                                                f, closerFragmentTypesToVisit );
541         }
542     }
543 
544 
545     // MIQ:2011, old code...
546     // move to using the above walker to allow mulitpass processing....
547 #if 0
548 
549 	fl_ContainerLayout* sfh = 0;
550 	UT_uint32 blockOffset = 0;
551 
552 	pf_Frag * pf1 = NULL;
553 	PT_BlockOffset fragOffset1 = 0;
554 	PT_BlockOffset endOffset = 0;
555 
556 	if (!getFragFromPosition(pDocRange->m_pos1, &pf1, &fragOffset1))
557 		return true;
558 
559 	PT_DocPosition sum = pDocRange->m_pos1 - fragOffset1;
560 
561     pf_Frag * pf = pf1;
562 	for ( ; (pf); pf=pf->getNext())
563 	{
564 		switch (pf->getType())
565 		{
566 		case pf_Frag::PFT_Text:
567 			{
568 				pf_Frag_Text * pft = static_cast<pf_Frag_Text *> (pf);
569 				PX_ChangeRecord * pcr = NULL;
570 				if (pDocRange->m_pos2 < sum+pf->getLength())
571 					endOffset = (pDocRange->m_pos2 - sum);
572 				else
573 					endOffset = pf->getLength();
574 				bool bStatus1 = pft->createSpecialChangeRecord(&pcr,sum,blockOffset,fragOffset1,endOffset);
575 				UT_return_val_if_fail (bStatus1,false);
576 				bool bStatus2 = pListener->populate(sfh,pcr);
577 				if (pcr)
578 					delete pcr;
579 				if (!bStatus2)
580 					return false;
581 				blockOffset += pf->getLength();
582 				fragOffset1 = 0;
583 			}
584 			break;
585 
586 		case pf_Frag::PFT_Strux:
587 			{
588 				pf_Frag_Strux * pfs = static_cast<pf_Frag_Strux *> (pf);
589 				pf_Frag_Strux* sdh = pf;
590 				sfh = 0;
591 				PX_ChangeRecord * pcr = NULL;
592 				bool bStatus1 = pfs->createSpecialChangeRecord(&pcr,sum);
593 				UT_return_val_if_fail (bStatus1,false);
594 				bool bStatus2 = pListener->populateStrux(sdh,pcr,&sfh);
595                 if( closer )
596                     closer->populateStrux(sdh,pcr,&sfh);
597 				if (pcr)
598 					delete pcr;
599 				if (!bStatus2)
600 					return false;
601 				blockOffset = 0;
602 			}
603 			break;
604 
605 		case pf_Frag::PFT_Object:
606 			{
607 				pf_Frag_Object * pfo = static_cast<pf_Frag_Object *> (pf);
608 				PX_ChangeRecord * pcr = NULL;
609 				bool bStatus1 = pfo->createSpecialChangeRecord(&pcr,sum,blockOffset);
610 				UT_return_val_if_fail (bStatus1,false);
611 				bool bStatus2 = pListener->populate(sfh,pcr);
612                 if( closer )
613                     closer->populate(sfh,pcr);
614 				if (pcr)
615 					delete pcr;
616 				if (!bStatus2)
617 					return false;
618 				blockOffset += pf->getLength();
619 			}
620 			break;
621 
622 		case pf_Frag::PFT_FmtMark:
623 			{
624 				pf_Frag_FmtMark * pffm = static_cast<pf_Frag_FmtMark *> (pf);
625 				PX_ChangeRecord * pcr = NULL;
626 				bool bStatus1 = pffm->createSpecialChangeRecord(&pcr,sum,blockOffset);
627 				UT_return_val_if_fail (bStatus1,false);
628 				bool bStatus2 = pListener->populate(sfh,pcr);
629 				DELETEP(pcr);
630 				if (!bStatus2)
631 					return false;
632 				blockOffset += pf->getLength();
633 			}
634 			break;
635 
636 		case pf_Frag::PFT_EndOfDoc:
637 			// they don't get to know about this.
638 			break;
639 
640 		default:
641 			UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
642 			return false;
643 		}
644 
645 		sum += pf->getLength();
646 		if (sum >= pDocRange->m_pos2)
647 			break;
648 	}
649 
650     // MIQ:2011
651     // The closer might want to inspect elements after the selected
652     // range to find end tags to emit as well. This allows things with
653     // a separate start and end element to have the end sent to the
654     // pListener although that end might not itself be in the range.
655     // Without this, we might have a bookmark-start but no matching
656     // bookmark-end, which will make a generated document invalid.
657     //
658     if( closer )
659     {
660         closer->setDelegate( pListener );
661         for ( ; (pf); pf=pf->getNext())
662         {
663             switch (pf->getType())
664             {
665                 case pf_Frag::PFT_Strux:
666                 {
667                     pf_Frag_Strux * pfs = static_cast<pf_Frag_Strux *> (pf);
668                     pf_Frag_Strux* sdh = pf;
669                     sfh = 0;
670                     PX_ChangeRecord * pcr = NULL;
671                     bool bStatus1 = pfs->createSpecialChangeRecord(&pcr,sum);
672                     UT_return_val_if_fail (bStatus1,false);
673                     closer->populateStruxClose(sdh,pcr,&sfh);
674                     if (pcr)
675                         delete pcr;
676                     blockOffset = 0;
677                 }
678                 break;
679 
680                 case pf_Frag::PFT_Object:
681                 {
682                     pf_Frag_Object * pfo = static_cast<pf_Frag_Object *> (pf);
683                     PX_ChangeRecord * pcr = NULL;
684                     bool bStatus1 = pfo->createSpecialChangeRecord(&pcr,sum,blockOffset);
685                     UT_return_val_if_fail (bStatus1,false);
686                     closer->populateClose(sfh,pcr);
687                     if (pcr)
688                         delete pcr;
689                     blockOffset += pf->getLength();
690                 }
691                 break;
692             }
693 
694             if( closer->isFinished() )
695                 break;
696         }
697     }
698 #endif
699 
700 	return true;
701 }
702