1 /* -*- mode: C++; tab-width: 4; c-basic-offset: 4; -*- */
2 
3 /* AbiWord
4  * Copyright (C) 1998 AbiSource, Inc.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19  * 02110-1301 USA.
20  */
21 
22 #include <list>
23 #include "ut_types.h"
24 #include "ut_misc.h"
25 #include "ut_assert.h"
26 #include "ut_debugmsg.h"
27 #include "ut_growbuf.h"
28 #include "ut_std_map.h"
29 #include "pt_PieceTable.h"
30 #include "pf_Frag.h"
31 #include "pf_Frag_FmtMark.h"
32 #include "pf_Frag_Strux.h"
33 #include "pf_Frag_Strux_Block.h"
34 #include "pf_Frag_Strux_Section.h"
35 #include "pf_Frag_Text.h"
36 #include "pf_Frag_Object.h"
37 #include "pf_Fragments.h"
38 #include "px_ChangeRecord.h"
39 #include "px_CR_Span.h"
40 #include "px_CR_SpanChange.h"
41 #include "px_CR_Strux.h"
42 #include "pd_Style.h"
43 #include "px_CR_Glob.h"
44 
45 // TODO: calculate this from pf_FRAG_STRUX_*_LENGTH instead?
46 #define pt_BOD_POSITION 2
47 
48 /*****************************************************************/
49 /*****************************************************************/
50 
pt_PieceTable(PD_Document * pDocument)51 pt_PieceTable::pt_PieceTable(PD_Document * pDocument)
52   : m_pts(PTS_Create),
53 	m_history(this),
54 	m_pDocument(pDocument),
55     m_atomicGlobCount(0),
56 	m_bDoingTheDo(false),
57 	m_bDoNotTweakPosition(false),
58 	m_iXID(0),
59 	m_iCurCRNumber(0)
60 {
61 
62 	setPieceTableState(PTS_Create);
63 	loading.m_indexCurrentInlineAP = 0;
64 }
65 
~pt_PieceTable()66 pt_PieceTable::~pt_PieceTable()
67 {
68 	m_fragments.purgeFrags();
69 	UT_map_delete_all_second(m_hashStyles);
70 }
71 
setPieceTableState(PTState pts)72 void pt_PieceTable::setPieceTableState(PTState pts)
73 {
74 	UT_return_if_fail (pts >= m_pts);
75 
76 	if ((m_pts==PTS_Create) && (pts==PTS_Loading))
77 	{
78 		// transition from create to loading.
79 		// populate the builtin styles
80 		_loadBuiltinStyles();
81 	}
82 
83 	if ((m_pts==PTS_Loading) && (pts==PTS_Editing))
84 	{
85 		// transition from loading to editing.
86 		// tack on an EOD fragment to the fragment list.
87 		// this allows us to safely go to the end of the document.
88 		pf_Frag * pfEOD = new pf_Frag(this,pf_Frag::PFT_EndOfDoc,0);
89 		m_fragments.appendFrag(pfEOD);
90 	}
91 
92 	m_pts = pts;
93 	m_varset.setPieceTableState(pts);
94 }
95 
96 /*!
97  * Use this for deleting unneeded strux during doc import. Particularly useful for importing
98  * RTF.
99  */
deleteStruxNoUpdate(pf_Frag_Strux * sdh)100 bool pt_PieceTable::deleteStruxNoUpdate(pf_Frag_Strux* sdh)
101 {
102 	const pf_Frag_Strux * pfs = sdh;
103 	UT_DEBUGMSG(("SEVIOR: deleting strux no update %p \n",sdh));
104 	pf_Frag * pf = pfs->getNext();
105 	if(pf != NULL && pf->getType() == pf_Frag::PFT_FmtMark)
106 	{
107 		getFragments().unlinkFrag(pf);
108 		delete pf;
109 	}
110 	getFragments().unlinkFrag(const_cast<pf_Frag_Strux*>(pfs));
111 	delete pfs;
112 	return true;
113 }
114 
115 
116 /*!
117  * Use this for deleting unused sections of the document during import.
118  * In Particular use this to remove unused headers/footers.
119  */
deleteFragNoUpdate(pf_Frag * pf)120 bool pt_PieceTable::deleteFragNoUpdate(pf_Frag * pf)
121 {
122 	UT_DEBUGMSG(("SEVIOR: deleting frag no update %p \n",pf));
123 	getFragments().unlinkFrag(pf);
124 	delete pf;
125 	return true;
126 }
127 
128 /*!
129  * Itterate through the document to calculate the document size
130  * Don't call this in production code. This is used only for recovery and
131  * testing purposes
132  */
calcDocsize(void)133 UT_sint32 pt_PieceTable::calcDocsize(void)
134 {
135 	UT_sint32 size = 0;
136 	pf_Frag * pf = getFragments().getFirst();
137 	while(pf && (pf->getType() !=  pf_Frag::PFT_EndOfDoc))
138 	{
139 		size += static_cast<UT_sint32>(pf->getLength());
140 		pf = pf->getNext();
141 	}
142 	UT_ASSERT(pf->getType() ==  pf_Frag::PFT_EndOfDoc);
143 	return size;
144 }
145 
createAndSendDocPropCR(const gchar ** pAtts,const gchar ** pProps)146 bool pt_PieceTable::createAndSendDocPropCR( const gchar ** pAtts, const gchar ** pProps)
147 {
148 	PT_AttrPropIndex indexAP = 0;
149 	PP_AttrProp * pAP = new PP_AttrProp();
150 	pAP->setAttributes(pAtts);
151 	pAP->setProperties(pProps);
152 	bool b = m_varset.addIfUniqueAP(pAP,&indexAP);
153 	PX_ChangeRecord * pcr= new PX_ChangeRecord(PX_ChangeRecord::PXT_ChangeDocProp,0,indexAP,0);
154 	const pf_Frag_Strux * pfStart = static_cast<pf_Frag_Strux *>(getFragments().getFirst());
155 	m_pDocument->notifyListeners(pfStart, pcr);
156 	delete pcr;
157 	return b;
158 }
159 
createAndSendCR(PT_DocPosition iPos,UT_sint32 iType,bool bSave,UT_Byte iGlob)160 bool pt_PieceTable::createAndSendCR(PT_DocPosition iPos, UT_sint32 iType,bool bSave,UT_Byte iGlob)
161 {
162 	PX_ChangeRecord::PXType cType = static_cast< PX_ChangeRecord::PXType>(iType);
163   switch(cType)
164     {
165     case PX_ChangeRecord::PXT_InsertSpan:
166     case PX_ChangeRecord::PXT_DeleteSpan:
167     case PX_ChangeRecord::PXT_ChangeSpan:
168     case PX_ChangeRecord::PXT_InsertStrux:
169     case PX_ChangeRecord::PXT_DeleteStrux:
170     case PX_ChangeRecord::PXT_ChangeStrux:
171     case PX_ChangeRecord::PXT_InsertObject:
172     case PX_ChangeRecord::PXT_ChangeObject:
173     case PX_ChangeRecord::PXT_InsertFmtMark:
174     case PX_ChangeRecord::PXT_DeleteFmtMark:
175     case PX_ChangeRecord::PXT_ChangeFmtMark:
176 		{
177 			UT_DEBUGMSG(("CR already implemented \n"));
178 			UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
179 			return false;
180 		}
181     case PX_ChangeRecord::PXT_GlobMarker:
182 	{
183 		PX_ChangeRecord * pcr= static_cast<PX_ChangeRecord *>(new PX_ChangeRecord_Glob(cType,iGlob));
184 		if(bSave)
185 		{
186 				m_history.addChangeRecord(pcr);
187 		}
188 		m_pDocument->notifyListeners(NULL, pcr);
189 		if(!bSave)
190 			delete pcr;
191 		return true;
192 
193 	}
194     case PX_ChangeRecord::PXT_ChangePoint:
195     case PX_ChangeRecord::PXT_ListUpdate:
196     case PX_ChangeRecord::PXT_StopList:
197     case PX_ChangeRecord::PXT_UpdateField:
198     case PX_ChangeRecord::PXT_RemoveList:
199     case PX_ChangeRecord::PXT_UpdateLayout:
200     {
201 		PX_ChangeRecord * pcr= new PX_ChangeRecord(cType,iPos, 0,0);
202 		if(bSave)
203 			{
204 				m_history.addChangeRecord(pcr);
205 			}
206 		m_pDocument->notifyListeners(NULL, pcr);
207 		if(!bSave)
208 			delete pcr;
209 		return true;
210      }
211     default:
212 		return false;
213     }
214 }
215 
216 
217 /*!
218  * Delete the single strux given in sdh and create and record a change record.
219  */
deleteStruxWithNotify(pf_Frag_Strux * sdh)220 bool pt_PieceTable::deleteStruxWithNotify(pf_Frag_Strux* sdh)
221 {
222 	pf_Frag_Strux * pfs = sdh;
223 	PT_DocPosition dpos = pfs->getPos();
224 	pf_Frag * pfEnd = NULL;
225 	UT_uint32 pfragOffsetEnd = 0;
226 	bool b = _deleteStruxWithNotify(dpos,pfs,&pfEnd,&pfragOffsetEnd,true);
227 	return b;
228 }
229 
230 /*!
231  * Delete The first FmtMark found at the position given.
232  */
deleteFmtMark(PT_DocPosition dpos)233 bool pt_PieceTable::deleteFmtMark(PT_DocPosition dpos)
234 {
235 	pf_Frag * pf = NULL;
236 	PT_BlockOffset pOffset= 0;
237 	getFragFromPosition(dpos,&pf,&pOffset);
238 	pf_Frag_FmtMark * pfm = NULL;
239 	if(pf->getType() == pf_Frag::PFT_FmtMark)
240 	{
241 		pfm = static_cast<pf_Frag_FmtMark *>(pf);
242 	}
243 	if(pf->getPrev() && pf->getPrev()->getType() == pf_Frag::PFT_FmtMark)
244 	{
245 		pfm = static_cast<pf_Frag_FmtMark *>(pf->getPrev());
246 	}
247 	if(pf->getNext() && pf->getNext()->getType() == pf_Frag::PFT_FmtMark)
248 	{
249 		pfm = static_cast<pf_Frag_FmtMark *>(pf->getNext());
250 	}
251 	if(pfm == NULL)
252 	{
253 		return false;
254 	}
255 	pf_Frag_Strux * pfs = NULL;
256 	if (!_getStruxFromFragSkip(pfm,&pfs))
257 		return false;
258 	pf_Frag * pfEnd = NULL;
259 	UT_uint32 fragOff = 0;
260 	bool b = _deleteFmtMarkWithNotify(dpos,pfm,pfs,&pfEnd,&fragOff);
261 	return b;
262 }
263 /*!
264  * This method inserts a strux of type pts immediately before the sdh given.
265  * Attributes of the strux can be optionally passed. This method does not throw
266  * a change record and should only be used under exceptional circumstances to
267  * repair the piecetable during loading. It was necessary to import RTF tables.
268  */
insertStruxNoUpdateBefore(pf_Frag_Strux * sdh,PTStruxType pts,const gchar ** attributes)269 bool pt_PieceTable::insertStruxNoUpdateBefore(pf_Frag_Strux* sdh, PTStruxType pts,const gchar ** attributes )
270 {
271 	const pf_Frag_Strux * pfs = sdh;
272 	UT_DEBUGMSG(("SEVIOR: Inserting strux of type %d no update %p \n",pts,sdh));
273 //
274 // Create an indexAP
275 //
276 	PT_AttrPropIndex indexAP = pfs->getIndexAP();
277 	if(attributes)
278 	{
279 		PT_AttrPropIndex pAPIold = indexAP;
280 		bool bMerged = m_varset.mergeAP(PTC_AddFmt,pAPIold,attributes,NULL,&indexAP,getDocument());
281 		UT_UNUSED(bMerged);
282 		UT_ASSERT_HARMLESS(bMerged);
283 	}
284 //
285 // create a strux
286 //
287 	pf_Frag_Strux * pNewStrux = NULL;
288 	_createStrux(pts,indexAP,&pNewStrux);
289 //
290 // Insert it.
291 //
292 	pf_Frag * pfPrev = pfs->getPrev();
293 	UT_return_val_if_fail (pfPrev,false);
294 
295 	m_fragments.insertFrag(pfPrev,pNewStrux);
296 	// insert frag in the embedded_strux list if needed
297 	if ((pts == PTX_EndFootnote) || (pts == PTX_EndEndnote) || (pts == PTX_EndAnnotation))
298 	{
299 		_insertNoteInEmbeddedStruxList(pNewStrux);
300 	}
301 
302 #if 0
303 	m_pDocument->miniDump(sdh,8);
304 #endif
305 	return true;
306 }
307 
_unlinkFrag(pf_Frag * pf,pf_Frag ** ppfEnd,UT_uint32 * pfragOffsetEnd)308 void pt_PieceTable::_unlinkFrag(pf_Frag * pf,
309 								pf_Frag ** ppfEnd, UT_uint32 * pfragOffsetEnd)
310 {
311 	// unlink the given fragment from the fragment list.
312 	// also, see if the adjacent fragments can be coalesced.
313 	// in (ppfEnd,pfragOffsetEnd) we return the position
314 	// immediately past the frag we're deleting.
315 	// the caller is responsible for deleting pf.
316 
317 	if (ppfEnd)
318 		*ppfEnd = pf->getNext();
319 	if (pfragOffsetEnd)
320 		*pfragOffsetEnd = 0;
321 
322 	pf_Frag * pp = pf->getPrev();
323 	xxx_UT_DEBUGMSG(("Unlink frag %x of type %d \n",pf,pf->getType()));
324 	m_fragments.unlinkFrag(pf);
325 
326 	if (   pp
327 		&& pp->getType()==pf_Frag::PFT_Text
328 		&& pp->getNext()
329 		&& pp->getNext()->getType()==pf_Frag::PFT_Text)
330 	{
331 		pf_Frag_Text * ppt = static_cast<pf_Frag_Text *> (pp);
332 		pf_Frag_Text * pnt = static_cast<pf_Frag_Text *> (pp->getNext());
333 		UT_uint32 prevLength = ppt->getLength();
334 		if (   ppt->getIndexAP() == pnt->getIndexAP()
335 			&& m_varset.isContiguous(ppt->getBufIndex(),prevLength,pnt->getBufIndex()))
336 		{
337 			if (ppfEnd)
338 				*ppfEnd = pp;
339 			if (pfragOffsetEnd)
340 				*pfragOffsetEnd = prevLength;
341 
342 			ppt->changeLength(prevLength+pnt->getLength());
343 			m_fragments.unlinkFrag(pnt);
344 			delete pnt;
345 		}
346 	}
347 	UT_ASSERT(pp && (pp->getNext() != pf));
348 }
349 
_struxHasContent(pf_Frag_Strux * pfs) const350 bool pt_PieceTable::_struxHasContent(pf_Frag_Strux * pfs) const
351 {
352 	// return true iff the paragraph has content (text).
353 
354 	return (pfs->getNext() && (pfs->getNext()->getType() == pf_Frag::PFT_Text));
355 }
356 
_struxIsEmpty(pf_Frag_Strux * pfs) const357 bool  pt_PieceTable::_struxIsEmpty(pf_Frag_Strux * pfs) const
358 {
359 	if(pfs->getNext() == NULL)
360 	{
361 		return true;
362 	}
363 	pf_Frag * pf = pfs->getNext();
364 	if(pf->getType() == pf_Frag::PFT_EndOfDoc)
365 	{
366 		return true;
367 	}
368 	if(pf->getType() != pf_Frag::PFT_Strux)
369 	{
370 		return false;
371 	}
372 	pf_Frag_Strux * pfsNext = static_cast<pf_Frag_Strux *>(pfs->getNext());
373 	if(isFootnote(pfsNext))
374 	{
375 		return false;
376 	}
377 	return true;
378 }
379 
getAttrProp(PT_AttrPropIndex indexAP,const PP_AttrProp ** ppAP) const380 bool pt_PieceTable::getAttrProp(PT_AttrPropIndex indexAP, const PP_AttrProp ** ppAP) const
381 {
382 	UT_return_val_if_fail (ppAP,false);
383 
384 	const PP_AttrProp * pAP = m_varset.getAP(indexAP);
385 	if (!pAP)
386 		return false;
387 
388 	*ppAP = pAP;
389 	return true;
390 }
391 
_getSpanAttrPropHelper(pf_Frag * pf,const PP_AttrProp ** ppAP) const392 bool pt_PieceTable::_getSpanAttrPropHelper(pf_Frag * pf, const PP_AttrProp ** ppAP) const
393 {
394 	switch (pf->getType())
395 	{
396 	case pf_Frag::PFT_FmtMark:
397 	case pf_Frag::PFT_Text:
398 	case pf_Frag::PFT_Object:
399 		{
400 			const PP_AttrProp * pAP = m_varset.getAP(pf->getIndexAP());
401 			if (pAP)
402 			{
403 				*ppAP = pAP;
404 				return true;
405 			}
406 			else
407 			{
408 				UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
409 				return false;
410 			}
411 		}
412 
413 	case pf_Frag::PFT_Strux:
414 	case pf_Frag::PFT_EndOfDoc:
415 	default:
416 		{
417 			*ppAP = NULL;
418 		}
419 	}
420 	return false;
421 }
422 
423 
getSpanAttrProp(pf_Frag_Strux * sdh,UT_uint32 offset,bool bLeftSide,const PP_AttrProp ** ppAP) const424 bool pt_PieceTable::getSpanAttrProp(pf_Frag_Strux* sdh, UT_uint32 offset, bool bLeftSide,
425 									   const PP_AttrProp ** ppAP) const
426 {
427 	// return the AP for the text at the given offset from the given strux.
428 	// offset zero now refers to the first character in the block, so adding
429 	// fl_BLOCK_STRUX_OFFSET to the offset in the call is no longer necessary.
430 
431 	UT_return_val_if_fail (sdh,false);
432 	UT_return_val_if_fail (ppAP,false);
433 
434 	const pf_Frag * pf = sdh;
435 	UT_return_val_if_fail (pf->getType() == pf_Frag::PFT_Strux,false);
436 	const pf_Frag_Strux * pfsBlock = sdh;
437 
438 	// This assert is incorrect; blocks that are inserted inside a TOC use sdh of the TOC section
439 	// UT_return_val_if_fail (pfsBlock->getStruxType() == PTX_Block,false);
440 	UT_return_val_if_fail (pfsBlock->getStruxType() == PTX_Block || pfsBlock->getStruxType() == PTX_SectionTOC,false);
441 
442 	UT_uint32 cumOffset = 0;
443 	UT_uint32 cumEndOffset = 0;
444 	pf_Frag * pfTemp = NULL;
445 	for (pfTemp=pfsBlock->getNext(); (pfTemp); cumOffset=cumEndOffset, pfTemp=pfTemp->getNext())
446 	{
447 		cumEndOffset = cumOffset+pfTemp->getLength();
448 
449 		if (offset > cumEndOffset)		// the place we want is way past the end of pfTemp,
450 			continue;					// so keep searching.
451 
452 		if (offset == cumOffset)		// there's a frag boundary exactly where we want. pfTemp is to our right.
453 		{
454 			// FmtMarks have length zero, so we have to see what side of the position the caller wants.
455 			if ((pfTemp->getType()==pf_Frag::PFT_FmtMark) && (!bLeftSide))
456 				continue;				// go round again and get thing to the right
457 
458 			return _getSpanAttrPropHelper(pfTemp,ppAP);
459 		}
460 
461 		UT_return_val_if_fail (offset > cumOffset,false);
462 
463 		if (offset == cumEndOffset)		// there's a frag boundary exactly where we want. pfTemp is to our left.
464 		{
465 			// FmtMarks have length zero, so we advance to put it to our left and then decide what to do
466 			if (!bLeftSide || (pfTemp->getNext() && (pfTemp->getNext()->getType()==pf_Frag::PFT_FmtMark)))
467 				continue;				// return the next one on the next iteration
468 			// If we are just after a footnote or an endnote, we move to the right fragment
469 			if (isEndFootnote(pfTemp) && pfTemp->getNext())
470 			{
471 				pfTemp = pfTemp->getNext();
472 			}
473 			return _getSpanAttrPropHelper(pfTemp,ppAP);
474 		}
475 
476 		UT_return_val_if_fail (offset < cumEndOffset,false);
477 
478 		// the place we want is inside of a fragment, so just use it.
479 		return _getSpanAttrPropHelper(pfTemp,ppAP);
480 	}
481 
482 	*ppAP = NULL;
483 	return false;
484 }
485 
486 #if 0
487 // I will leave the code here for now to aid in debugging any problems
488 // with the new iterator (should there be any, that is) Tomas, Nov 15, 2003
489 bool pt_PieceTable::getSpanPtr(pf_Frag_Strux* sdh, UT_uint32 offset,
490 								  const UT_UCSChar ** ppSpan, UT_uint32 * pLength) const
491 {
492 	// note: offset zero refers to the strux.  the first character is at
493 	// note: (0 + pfsBlock->getLength()).
494 
495 	*ppSpan = NULL;
496 	*pLength = 0;
497 
498 	const pf_Frag * pf = sdh;
499 	UT_return_val_if_fail (pf->getType() == pf_Frag::PFT_Strux,false);
500 	const pf_Frag_Strux * pfsBlock = sdh;
501 	UT_return_val_if_fail (pfsBlock->getStruxType() == PTX_Block,false);
502 	xxx_UT_DEBUGMSG(("getSpanPtr: Requested offset %d \n",offset));
503 
504 	UT_uint32 cumOffset = pf->getLength();
505 	for (pf_Frag * pfTemp=pfsBlock->getNext(); (pfTemp); pfTemp=pfTemp->getNext())
506 	{
507 		xxx_UT_DEBUGMSG(("getSpanPtr: offset %d cumOffset %d \n",offset,cumOffset));
508 		if (offset == cumOffset)
509 		{
510 			if (pfTemp->getType() == pf_Frag::PFT_FmtMark)
511 				continue;
512 			if(isFootnote(pfTemp) || isEndFootnote(pfTemp))
513 			{
514 				cumOffset += pfTemp->getLength();
515 				continue;
516 			}
517 			if (pfTemp->getType() != pf_Frag::PFT_Text)
518 			{
519 				xxx_UT_DEBUGMSG(("getSpanPtr: Error 1 offset %d cumOffset %d \n",offset,cumOffset));
520 //				UT_ASSERT_HARMLESS(0);
521 				return false;
522 			}
523 
524 			pf_Frag_Text * pfText = static_cast<pf_Frag_Text *> (pfTemp);
525 			*ppSpan = getPointer(pfText->getBufIndex());
526 			*pLength = pfText->getLength();
527 			return true;
528 		}
529 		if (offset < cumOffset+pfTemp->getLength())
530 		{
531 			if(isFootnote(pfTemp) || isEndFootnote(pfTemp))
532 			{
533 				cumOffset += pfTemp->getLength();
534 				continue;
535 			}
536 			if (pfTemp->getType() != pf_Frag::PFT_Text)
537 			{
538 				xxx_UT_DEBUGMSG(("getSpanPtr: Error 2 offset %d cumOffset %d \n",offset,cumOffset));
539 				return false;
540 			}
541 			pf_Frag_Text * pfText = static_cast<pf_Frag_Text *> (pfTemp);
542 			const UT_UCSChar * p = getPointer(pfText->getBufIndex());
543 			UT_uint32 delta = offset - cumOffset;
544 			*ppSpan = p + delta;
545 			*pLength = pfText->getLength() - delta;
546 			return true;
547 		}
548 
549 		cumOffset += pfTemp->getLength();
550 	}
551 	xxx_UT_DEBUGMSG(("getSpanPtr: Error 3 offset %d cumOffset %d \n",offset,cumOffset));
552 	return false;
553 }
554 #endif
555 
getDocument(void)556 PD_Document * pt_PieceTable::getDocument(void)
557 {
558         return m_pDocument;
559 }
560 
561 /*!
562   Copy paragraph (block) into buffer
563   \param sdh Paragraph to copy
564   \retval pgb Buffer where text should be copied to
565   \return Always returns true
566 
567   Copy the contents (unicode character data) of the paragraph (block)
568   into the growbuf given.  We append the content onto the growbuf.
569 */
getBlockBuf(pf_Frag_Strux * sdh,UT_GrowBuf * pgb) const570 bool pt_PieceTable::getBlockBuf(pf_Frag_Strux* sdh,
571                                    UT_GrowBuf * pgb) const
572 {
573     UT_return_val_if_fail (pgb,false);
574 
575     const pf_Frag * pf = sdh;
576     UT_return_val_if_fail(pf->getType() == pf_Frag::PFT_Strux, false);
577     const pf_Frag_Strux * pfsBlock = sdh;
578     UT_return_val_if_fail(pfsBlock->getStruxType() == PTX_Block, false);
579 
580     UT_uint32 bufferOffset = pgb->getLength();
581 
582     pf_Frag * pfTemp = pfsBlock->getNext();
583 	UT_sint32 countFoots = 0;
584     while (pfTemp)
585     {
586         switch (pfTemp->getType())
587         {
588         default:
589             UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
590         case pf_Frag::PFT_Strux:
591 //
592 // Have to deal with embedded section struxes like Footnotes. We do this by
593 // filling the block buffer with the content contained until we find an
594 // end of embedded container. By placing zero's and content as expected in
595 // the block buffer we match the situation in fl_BlockLayout.
596 //
597 		{
598 			UT_GrowBufElement valz = 0;
599             bool bAppended;
600 			//
601 			// Deal with TOC's. Since TOC's are always placed right before
602 			// the end of the paragraph we terminate the fill here.
603 			//
604 			pf_Frag_Strux * pfs = static_cast<pf_Frag_Strux *>(pfTemp);
605 			if(pfs->getStruxType() == PTX_SectionTOC)
606 			{
607 				pfTemp = NULL;
608 				break;
609 			}
610 			if(isFootnote(pfTemp))
611 			{
612 				countFoots++;
613 				bAppended = pgb->ins(bufferOffset,&valz,1);
614 				UT_return_val_if_fail (bAppended,false);
615 				bufferOffset++;
616 				pfTemp = pfTemp->getNext();
617 				break;
618 			}
619 			if(isEndFootnote(pfTemp))
620 			{
621 				countFoots--;
622 				if(countFoots < 0)
623 				{
624 					pfTemp = NULL;
625 					break;
626 				}
627 				bAppended = pgb->ins(bufferOffset,&valz,1);
628 				UT_return_val_if_fail (bAppended,false);
629 				bufferOffset++;
630 				pfTemp = pfTemp->getNext();
631 				break;
632 			}
633 			if(countFoots>0)
634 			{
635 				bAppended = pgb->ins(bufferOffset,&valz,1);
636 				UT_return_val_if_fail (bAppended,false);
637 				bufferOffset++;
638 				pfTemp = pfTemp->getNext();
639 				break;
640 			}
641 			pfTemp = NULL;
642 			break;
643 		}
644         case pf_Frag::PFT_EndOfDoc:
645             pfTemp = NULL;
646             break;
647 
648         case pf_Frag::PFT_FmtMark:
649             pfTemp = pfTemp->getNext();
650             break;
651 
652         case pf_Frag::PFT_Text:
653         {
654             pf_Frag_Text * pft = static_cast<pf_Frag_Text *>(pfTemp);
655             const UT_UCSChar * pSpan = getPointer(pft->getBufIndex());
656             UT_uint32 length = pft->getLength();
657 
658             bool bAppended;
659             bAppended = pgb->ins(bufferOffset,reinterpret_cast<const UT_GrowBufElement*>(pSpan),length);
660             UT_return_val_if_fail (bAppended,false);
661 
662             bufferOffset += length;
663         }
664         pfTemp = pfTemp->getNext();
665         break;
666 
667         case pf_Frag::PFT_Object:
668         {
669             /*
670               TODO investigate this....  Now *here* is a seriously
671               questionable fragment of code.  :-) We can't let
672               getBlockBuf halt on a block when it finds an inline
673               object.  However, we can't very well sensibly store an
674               inline object in a UNICODE character.  So, we dump
675               USC_BLOCK in its place, to preserve the integrity of the
676               buffer.  Obviously, those codes aren't useful, but at
677               least the app doesn't crash, and the rest of the text in
678               the block is safely stored in the buffer in the proper
679               location.
680 
681               The UCS_ABI_OBJECT used to be defined as a space, but
682               that caused selection code to fail for fields since the
683               code would look for the beginning of a word, ignoring
684               spaces. Now the UCS_ABI_OBJECT is instead defined as an
685               alpha character. Doesn't really matter since it'll never
686               be used for anything but limit checking anyway. See bug
687               #223 for details.
688 			*/
689 
690             UT_uint32 length = pfTemp->getLength();
691 
692             // TODO investigate appending the SPACES directly to
693             // TODO the pgb.  **or** investigate the cost of this
694             // TODO g_try_malloc and what happens when it fails....
695 
696             UT_UCSChar* pSpaces = new UT_UCSChar[length];
697             for (UT_uint32 i=0; i<length; i++)
698             {
699                 pSpaces[i] = UCS_ABI_OBJECT;
700             }
701             bool bAppended;
702             bAppended = pgb->ins(bufferOffset, reinterpret_cast<UT_GrowBufElement*>(pSpaces), length);
703             delete[] pSpaces;
704             UT_return_val_if_fail (bAppended,false);
705 
706             bufferOffset += length;
707         }
708         pfTemp = pfTemp->getNext();
709         break;
710         }
711     }
712 
713     UT_return_val_if_fail (bufferOffset == pgb->getLength(),false);
714     return true;
715 }
716 
getPosEnd()717 PT_DocPosition pt_PieceTable::getPosEnd()
718 {
719     PT_DocPosition ret = 0;
720     getBounds( true, ret );
721     return ret;
722 }
723 
724 
getBounds(bool bEnd,PT_DocPosition & docPos) const725 bool pt_PieceTable::getBounds(bool bEnd, PT_DocPosition & docPos) const
726 {
727 	// be optimistic
728 	bool res = true;
729 
730 	if (!bEnd)
731 	{
732 		docPos = pt_BOD_POSITION;
733 	}
734 	else
735 	{
736 		docPos = m_fragments.getLast()->getPos()+m_fragments.getLast()->getLength();
737 	}
738 	return res;
739 }
740 
getStruxPosition(pf_Frag_Strux * sdh) const741 PT_DocPosition pt_PieceTable::getStruxPosition(pf_Frag_Strux* sdh) const
742 {
743 	// return absolute document position of the given handle.
744 
745 	const pf_Frag * pfToFind = sdh;
746 
747 	return getFragPosition(pfToFind);
748 }
749 
deleteHdrFtrStrux(pf_Frag_Strux * pfs)750 void pt_PieceTable::deleteHdrFtrStrux(pf_Frag_Strux * pfs)
751 {
752 	UT_return_if_fail( pfs );
753 
754 	if(m_pDocument->isMarkRevisions())
755 	{
756 		const pf_Frag * pfFrag = pfs;
757 		PT_DocPosition dpos1 = getFragPosition(pfFrag);
758 
759 		pfFrag = pfFrag->getNext();
760 
761 		while(pfFrag)
762 		{
763 			if((pfFrag->getType() == pf_Frag::PFT_EndOfDoc) ||
764 			   (pfFrag->getType() == pf_Frag::PFT_Strux &&
765 				static_cast<const pf_Frag_Strux*>(pfFrag)->getStruxType() == PTX_SectionHdrFtr))
766 			{
767 				break;
768 			}
769 
770 			pfFrag = pfFrag->getNext();
771 		}
772 
773 		// there should always be at least EndOfDoc fragment !!!
774 		UT_return_if_fail( pfFrag );
775 
776 		PT_DocPosition dpos2 = getFragPosition(pfFrag);
777 
778 		UT_uint32 iRealDelete = 0;
779 		deleteSpan(dpos1, dpos2, NULL, iRealDelete, true /*delete table struxes*/, false /* don't glob */);
780 	}
781 	else
782 	{
783 		const PP_AttrProp * pAP = NULL;
784 		UT_return_if_fail(pfs->getStruxType()==PTX_SectionHdrFtr);
785 		pf_Frag_Strux_SectionHdrFtr * pfHdr = static_cast<pf_Frag_Strux_SectionHdrFtr *>(pfs);
786 
787 		if(!getAttrProp(pfHdr->getIndexAP(),&pAP) || !pAP)
788 			return;
789 
790 		const gchar * pszHdrId;
791 		if(!pAP->getAttribute("id", pszHdrId) || !pszHdrId)
792 			return;
793 
794 		const gchar * pszHdrType;
795 		if(!pAP->getAttribute("type", pszHdrType) || !pszHdrType)
796 			return;
797 
798 		_realDeleteHdrFtrStrux(pfs);
799 		_fixHdrFtrReferences(pszHdrType, pszHdrId);
800 	}
801 
802 }
803 
_realDeleteHdrFtrStrux(pf_Frag_Strux * pfs)804 void pt_PieceTable::_realDeleteHdrFtrStrux(pf_Frag_Strux * pfs)
805 {
806 	_deleteHdrFtrStruxWithNotify(pfs);
807 }
808 
getFragPosition(const pf_Frag * pfToFind) const809 PT_DocPosition pt_PieceTable::getFragPosition(const pf_Frag * pfToFind) const
810 {
811 	return  pfToFind->getPos();
812 }
813 
814 
getFragFromPosition(PT_DocPosition docPos,pf_Frag ** ppf,PT_BlockOffset * pFragOffset) const815 bool pt_PieceTable::getFragFromPosition(PT_DocPosition docPos,
816 										   pf_Frag ** ppf,
817 										   PT_BlockOffset * pFragOffset) const
818 {
819 	// return the frag at the given doc position.
820 //
821 // Sevior do a binary search here now
822 //
823 	pf_Frag * pfLast = m_fragments.findFirstFragBeforePos(docPos);
824 	if(pfLast)
825 	{
826 		xxx_UT_DEBUGMSG(("_findFrag: docPos %d pfLast->getPos %d pfLast->getLength %d \n",docPos,pfLast->getPos(),pfLast->getLength()));
827 		while(pfLast->getNext() && docPos >= (pfLast->getPos() + pfLast->getLength()))
828 		{
829 			xxx_UT_DEBUGMSG(("_findFrag: docPos %d pfLast->getPos %d pfLast->getLength %d \n",docPos,pfLast->getPos(),pfLast->getLength()));
830 			pfLast = pfLast->getNext();
831 		}
832 		xxx_UT_DEBUGMSG(("_findFrag: docPos %d pfLast->getPos %d pfLast->getLength %d offset %d Frag Type %d \n",docPos,pfLast->getPos(),pfLast->getLength(),docPos - pfLast->getPos(),pfLast->getType()));
833 		if (pFragOffset)
834 			*pFragOffset = docPos - pfLast->getPos();
835 		*ppf = pfLast;
836 		return true;
837 	}
838 
839 	UT_return_val_if_fail (pfLast,false);
840 	UT_return_val_if_fail (pfLast->getType() == pf_Frag::PFT_EndOfDoc,false);
841 
842 	return true;
843 }
844 	//  PT_DocPosition sum = 0;
845 //  	pfLast = NULL;
846 //  	pf_Frag * pf = NULL;
847 //  	for (pf = m_fragments.getFirst(); (pf); pf=pf->getNext())
848 //  	{
849 //  		if ((docPos >= sum) && (docPos < sum+pf->getLength()))
850 //  		{
851 //  			*ppf = pf;
852 //  			if (pFragOffset)
853 //  				*pFragOffset = docPos - sum;
854 
855 //  			// a FmtMark has length zero.  we don't want to find it
856 //  			// in this loop -- rather we want the thing just past it.
857 
858 //  			UT_ASSERT(pf->getType() != pf_Frag::PFT_FmtMark);
859 
860 //  			return true;
861 //  		}
862 
863 //  		sum += pf->getLength();
864 //  		pfLast = pf;
865 //  	}
866 
867 //  	// if we fall out of the loop, we didn't have a node
868 //  	// at or around the document position requested.
869 //  	// since we now have an EOD fragment, we should not
870 //  	// ever see this -- unless the caller sends a bogus
871 //  	// doc position.
872 
873 //  	UT_ASSERT(pfLast);
874 //  	UT_ASSERT(pfLast->getType() == pf_Frag::PFT_EndOfDoc);
875 
876 //  	// TODO if (docPos > sum) we should probably complain...
877 
878 //  	*ppf = pfLast;
879 //  	if (pFragOffset)
880 //  		*pFragOffset = docPos - sum;
881 
882 
883 //  	return true;
884 //  }
885 
getFragsFromPositions(PT_DocPosition dPos1,PT_DocPosition dPos2,pf_Frag ** ppf1,PT_BlockOffset * pOffset1,pf_Frag ** ppf2,PT_BlockOffset * pOffset2) const886 bool pt_PieceTable::getFragsFromPositions(PT_DocPosition dPos1, PT_DocPosition dPos2,
887 											 pf_Frag ** ppf1, PT_BlockOffset * pOffset1,
888 											 pf_Frag ** ppf2, PT_BlockOffset * pOffset2) const
889 {
890 	// compute the (fragment,offset) pairs for each position given.
891 
892 	UT_return_val_if_fail (dPos1 <= dPos2,false);
893 	UT_return_val_if_fail (ppf1,false);
894 	UT_return_val_if_fail (pOffset1,false);
895 
896 	// the first set has to be done the hard way.
897 
898 	if (!getFragFromPosition(dPos1,ppf1,pOffset1))
899 		return false;
900 
901 	// now get the second set relative to the first.
902 
903 	PT_DocPosition deltaPos = dPos2 - dPos1;
904 	PT_BlockOffset offset = *pOffset1;
905 	pf_Frag * pf = *ppf1;
906 	UT_uint32 length = pf->getLength();
907 	while (offset+deltaPos >= length)
908 	{
909 		deltaPos -= (length - offset);
910 		offset = 0;
911 		if (pf->getType() == pf_Frag::PFT_EndOfDoc)
912 			break;						// TODO if we haven't quite reached dPos2, we should probably complain...
913 		pf = pf->getNext();
914 		if(pf == NULL)
915 		{
916 			return false;
917 		}
918 		length = pf->getLength();
919 	}
920 
921 	// a FmtMark has length zero.  we don't want to find it here.
922 	// rather we want the thing to the right of it.
923 	UT_return_val_if_fail (pf->getType() != pf_Frag::PFT_FmtMark,false);
924 
925 	if (ppf2)
926 		*ppf2 = pf;
927 	if (pOffset2)
928 		*pOffset2 = offset+deltaPos;
929 	return true;
930 }
931 
getStruxFromPosition(PL_ListenerId listenerId,PT_DocPosition docPos,fl_ContainerLayout ** psfh) const932 bool pt_PieceTable::getStruxFromPosition(PL_ListenerId listenerId,
933 											PT_DocPosition docPos,
934 											fl_ContainerLayout* * psfh) const
935 {
936 	// return the SFH of the last strux immediately prior to
937 	// the given absolute document position.
938 
939 	pf_Frag_Strux * pfs = NULL;
940 	if (!_getStruxFromPosition(docPos,&pfs))
941 		return false;
942 
943 	*psfh = pfs->getFmtHandle(listenerId);
944 	return true;
945 }
946 
getStruxOfTypeFromPosition(PL_ListenerId listenerId,PT_DocPosition docPos,PTStruxType pts,fl_ContainerLayout ** psfh) const947 bool pt_PieceTable::getStruxOfTypeFromPosition(PL_ListenerId listenerId,
948 												  PT_DocPosition docPos,
949 												  PTStruxType pts,
950 												  fl_ContainerLayout* * psfh) const
951 {
952 	// return the SFH of the last strux of the given type
953 	// immediately prior to the given absolute document position.
954 
955 	pf_Frag_Strux * pfs = NULL;
956 	if (!_getStruxOfTypeFromPosition(docPos,pts,&pfs))
957 		return false;
958 
959 	*psfh = pfs->getFmtHandle(listenerId);
960 	return true;
961 }
962 ///
963 /// return the SDH of the last strux of the given type
964 /// immediately prior to the given absolute document position.
965 ///
getStruxOfTypeFromPosition(PT_DocPosition docPos,PTStruxType pts,pf_Frag_Strux ** sdh) const966 bool pt_PieceTable::getStruxOfTypeFromPosition( PT_DocPosition docPos,
967 						   PTStruxType pts,
968 						   pf_Frag_Strux* * sdh) const
969 {
970 	return _getStruxOfTypeFromPosition(docPos,pts,sdh);
971 }
972 
isEndFootnote(pf_Frag * pf) const973 bool pt_PieceTable::isEndFootnote(pf_Frag * pf) const
974 {
975 	if(pf && (pf->getType() == pf_Frag::PFT_Strux))
976 	{
977 		pf_Frag_Strux * pfs = static_cast<pf_Frag_Strux *>(pf);
978 		if((pfs->getStruxType() == PTX_EndFootnote) || (pfs->getStruxType() == PTX_EndEndnote) || (pfs->getStruxType() == PTX_EndTOC) || (pfs->getStruxType() == PTX_EndAnnotation))
979 		{
980 			return true;
981 		}
982 	}
983 	return false;
984 }
985 
986 
isFootnote(pf_Frag * pf) const987 bool pt_PieceTable::isFootnote(pf_Frag * pf) const
988 {
989 	if(pf && (pf->getType() == pf_Frag::PFT_Strux))
990 	{
991 		pf_Frag_Strux * pfs = static_cast<pf_Frag_Strux *>(pf);
992 		if((pfs->getStruxType() == PTX_SectionFootnote) || (pfs->getStruxType() == PTX_SectionEndnote) || (pfs->getStruxType() == PTX_SectionTOC) || (pfs->getStruxType() == PTX_SectionAnnotation))
993 		{
994 			return true;
995 		}
996 	}
997 	return false;
998 }
999 
1000 
isInsideFootnote(PT_DocPosition dpos,pf_Frag ** pfBegin) const1001 bool pt_PieceTable::isInsideFootnote(PT_DocPosition dpos, pf_Frag ** pfBegin) const
1002 {
1003 	// return true if pos is inside a footnote, an endnote or an annotation.
1004 	// the address of the footnote (endnote or annotation) pfs_strux is
1005 	// returned in pfBegin if pfBegin is not NULL
1006 	if(m_embeddedStrux.empty())
1007 	{
1008 		return false;
1009 	}
1010 
1011 	std::list<embeddedStrux>::const_iterator it;
1012 	it = m_embeddedStrux.begin();
1013 	for (it = m_embeddedStrux.begin(); it != m_embeddedStrux.end(); ++it)
1014 	{
1015 		if ((*it).endNote->getPos() > dpos)
1016 		{
1017 			if ((*it).beginNote->getPos() < dpos)
1018 			{
1019 				if (pfBegin)
1020 				{
1021 					*pfBegin = (*it).beginNote;
1022 				}
1023 				return true;
1024 			}
1025 			break;
1026 		}
1027 	}
1028 	return false;
1029 }
1030 
1031 
hasEmbedStruxOfTypeInRange(PT_DocPosition posStart,PT_DocPosition posEnd,PTStruxType iType) const1032 bool  pt_PieceTable::hasEmbedStruxOfTypeInRange(PT_DocPosition posStart, PT_DocPosition posEnd,
1033 												PTStruxType iType) const
1034 {
1035 	if(m_embeddedStrux.empty())
1036 	{
1037 		return false;
1038 	}
1039 
1040 	std::list<embeddedStrux>::const_iterator it;
1041 	it = m_embeddedStrux.begin();
1042 	for (it = m_embeddedStrux.begin(); it != m_embeddedStrux.end(); ++it)
1043 	{
1044 		if ((*it).type != iType)
1045 		{
1046 			continue;
1047 		}
1048 		if ((*it).beginNote->getPos() > posStart)
1049 		{
1050 			// if endNote->getPos() > posEnd, there are no notes inside the position range as
1051 			// m_embeddedStrux is ordered by position.
1052 			return ((*it).endNote->getPos() < posEnd);
1053 		}
1054 	}
1055 	return false;
1056 }
1057 
1058 
_getStruxFromPosition(PT_DocPosition docPos,pf_Frag_Strux ** ppfs,bool bSkipFootnotes) const1059 bool pt_PieceTable::_getStruxFromPosition(PT_DocPosition docPos,
1060 											 pf_Frag_Strux ** ppfs,
1061                                               bool bSkipFootnotes) const
1062 {
1063 	// return the strux fragment immediately prior (containing)
1064 	// the given absolute document position.  If bSkip set, skip past
1065     // Footnote struxes.
1066 	xxx_UT_DEBUGMSG(("_getStruxFromPosition bSkipFootnotes %d initial pos %d \n",bSkipFootnotes,docPos));
1067 	UT_sint32 countEndFootnotes = 0;
1068 	pf_Frag * pfFirst = m_fragments.findFirstFragBeforePos( docPos);
1069 	xxx_UT_DEBUGMSG(("Frag found %x pos %d \n",pfFirst,pfFirst->getPos()));
1070 	if(isEndFootnote(pfFirst))
1071 		countEndFootnotes++;
1072 	xxx_UT_DEBUGMSG(("Frag found, count endfootnotes %d  \n",countEndFootnotes));
1073 
1074 	while(pfFirst && pfFirst->getPrev() && pfFirst->getPos() >= docPos)
1075 	{
1076 		pfFirst = pfFirst->getPrev();
1077 		if (isFootnote(pfFirst))
1078 			countEndFootnotes--;
1079 		else if(isEndFootnote(pfFirst))
1080 			countEndFootnotes++;
1081 
1082 		xxx_UT_DEBUGMSG(("countEndNotes 1 %d \n",countEndFootnotes));
1083 	}
1084 	while(pfFirst && pfFirst->getPrev() &&
1085 		  (pfFirst->getType() != pf_Frag::PFT_Strux ||
1086 		   (bSkipFootnotes && ((countEndFootnotes > 0) || isFootnote(pfFirst) || isEndFootnote(pfFirst)))))
1087 	{
1088 		pfFirst = pfFirst->getPrev();
1089 		xxx_UT_DEBUGMSG(("Frag found %x pos %d \n",pfFirst,pfFirst->getPos()));
1090 		if(pfFirst == NULL)
1091 		{
1092 			break;
1093 		}
1094 		if(isFootnote(pfFirst))
1095 			countEndFootnotes--;
1096 		else if(isEndFootnote(pfFirst))
1097 			countEndFootnotes++;
1098 		xxx_UT_DEBUGMSG(("countEndNotes 2 %d \n",countEndFootnotes));
1099 	}
1100 	xxx_UT_DEBUGMSG(("countEndNotes final %d \n",countEndFootnotes));
1101   	pf_Frag_Strux * pfs = static_cast<pf_Frag_Strux *> (pfFirst);
1102   	*ppfs = pfs;
1103 	return pfs != NULL;
1104 }
1105 
1106 
getBlockFromPosition(PT_DocPosition pos) const1107 pf_Frag_Strux* pt_PieceTable::getBlockFromPosition(PT_DocPosition pos) const
1108 {
1109 	pf_Frag_Strux* pfs = _getBlockFromPosition(pos);
1110     return pfs;
1111 }
1112 
1113 
1114 /**
1115  * Get the PTX_Block that contains the given document position. Note that
1116  * pos might itself point right at a PTX_Block in which case that is the block
1117  * that is returned. This might return null if there is no containing block
1118  */
_getBlockFromPosition(PT_DocPosition pos) const1119 pf_Frag_Strux* pt_PieceTable::_getBlockFromPosition(PT_DocPosition pos) const
1120 {
1121     pf_Frag* pf;
1122     PT_BlockOffset offset;
1123     pf_Frag_Strux* ret = 0;
1124 
1125     if(!getFragFromPosition( pos, &pf, &offset ))
1126     {
1127         return ret;
1128     }
1129 
1130     // if the fragment right at pos is a block, return it.
1131     if( pf_Frag_Strux* pfs = tryDownCastStrux( pf, PTX_Block ))
1132     {
1133         return pfs;
1134     }
1135     // otherwise search backwards for the block.
1136     if(!_getStruxOfTypeFromPosition( pos, PTX_Block, &ret ))
1137     {
1138         return 0;
1139     }
1140     return ret;
1141 
1142 }
1143 
_getStruxOfTypeFromPosition(PT_DocPosition dpos,PTStruxType pts,pf_Frag_Strux ** ppfs) const1144 bool pt_PieceTable::_getStruxOfTypeFromPosition(PT_DocPosition dpos,
1145 												   PTStruxType pts,
1146 												   pf_Frag_Strux ** ppfs) const
1147 {
1148 	// return the strux fragment of the given type containing
1149 	// the given absolute document position.
1150 	UT_return_val_if_fail (ppfs, false);
1151 	xxx_UT_DEBUGMSG(("_getStruxOfTypeFromPosition: looking for type %d \n",pts));
1152 	*ppfs = NULL;
1153 
1154 	pf_Frag_Strux * pfs = NULL;
1155 	bool wantFootNoteType = (pts == PTX_EndFootnote || pts == PTX_SectionFootnote || pts == PTX_EndEndnote || pts == PTX_SectionEndnote || pts == PTX_SectionAnnotation || pts == PTX_EndAnnotation || pts == PTX_SectionTOC || pts == PTX_EndTOC);
1156 
1157 	if (!_getStruxFromPosition(dpos,&pfs, !wantFootNoteType))
1158 		return false;
1159 
1160 	PTStruxType pfsType = pfs->getStruxType();
1161 	xxx_UT_DEBUGMSG(("Got strux type %d \n",pfs->getStruxType()));
1162 	if (pfsType == pts || (pts == PTX_Section && pfsType == PTX_SectionHdrFtr)
1163 		|| (pts == PTX_SectionFootnote && pfsType == PTX_SectionFootnote)
1164 		|| (pts == PTX_SectionAnnotation && pfsType == PTX_SectionAnnotation)
1165 		|| (pts == PTX_SectionEndnote && pfsType == PTX_SectionEndnote)
1166 		|| (pts == PTX_SectionTable && pfsType == PTX_SectionTable)
1167 		|| (pts == PTX_SectionCell && pfsType == PTX_SectionCell)
1168 		|| (pts == PTX_EndTable && pfsType == PTX_EndTable)
1169 		|| (pts == PTX_EndCell && pfsType == PTX_EndCell)
1170 		|| (pts == PTX_SectionTOC && pfsType == PTX_SectionTOC)  )		// is it of the type we want
1171 	{
1172 		*ppfs = pfs;
1173 		return true;
1174 	}
1175 
1176 	// if not, we walk backwards thru the list and try to find it.
1177 	UT_sint32 numEndTable = 0;
1178 	for (pf_Frag * pf=pfs; (pf); pf=pf->getPrev())
1179 		if (pf->getType() == pf_Frag::PFT_Strux)
1180 		{
1181 			pf_Frag_Strux * pfsTemp = NULL;
1182 			if(!wantFootNoteType && isEndFootnote(pf))
1183 			{
1184 				_getStruxFromFragSkip(pf,&pfsTemp);
1185 			}
1186 			else
1187 			{
1188 				pfsTemp = static_cast<pf_Frag_Strux *>(pf);
1189 			}
1190 			UT_return_val_if_fail (pfsTemp, false);
1191 			if(pfsTemp->getStruxType() == PTX_EndTable)
1192 			{
1193 				numEndTable++;
1194 			}
1195 			else if(pfsTemp->getStruxType() == PTX_SectionTable)
1196 			{
1197 				numEndTable--;
1198 			}
1199 			if (pfsTemp->getStruxType() == pts || (pts == PTX_Section && pfsTemp->getStruxType() == PTX_SectionHdrFtr) || (pts == PTX_SectionFootnote && pfsTemp->getStruxType() == PTX_SectionFootnote) || (pts == PTX_EndFootnote && pfsTemp->getStruxType() == PTX_EndFootnote) || (pts == PTX_SectionEndnote && pfsTemp->getStruxType() == PTX_SectionEndnote) || (pts == PTX_EndEndnote && pfsTemp->getStruxType() == PTX_EndEndnote) || (pts == PTX_SectionTOC && pfsTemp->getStruxType() == PTX_SectionTOC) || (pts == PTX_EndTOC && pfsTemp->getStruxType() == PTX_EndTOC))	// did we find it
1200 			{
1201 				if(((numEndTable < 0) && (pfsTemp->getStruxType()==PTX_SectionTable)) || (numEndTable == 0 && (pfsTemp->getStruxType()!=PTX_SectionTable)))
1202 				{
1203 					*ppfs = pfsTemp;
1204 					return true;
1205 				}
1206 				else if(pfsTemp->getStruxType() != PTX_SectionTable &&
1207 						pfsTemp->getStruxType() != PTX_SectionCell &&
1208 						pfsTemp->getStruxType() != PTX_EndTable &&
1209 						pfsTemp->getStruxType() != PTX_EndCell)
1210 				{
1211 					*ppfs = pfsTemp;
1212 					return true;
1213 				}
1214 			}
1215 		}
1216 
1217 	// did not find it.
1218 
1219 	return false;
1220 }
1221 
1222 /*!
1223  * Scan backwards from the given frag until a start hyperlink is found.
1224  * This method is used to determine if a frag is inside a hyperlink span.
1225  * Returns NULL if:
1226  * (a) It encounters a strux first.
1227  * (b) It encounters an end hyperlink first
1228  * (c) It encounters the begin of document
1229  *
1230  * FIXME!! Should this code work for annotations too?
1231  */
_findPrevHyperlink(pf_Frag * pfStart)1232 pf_Frag *    pt_PieceTable::_findPrevHyperlink(pf_Frag * pfStart)
1233 {
1234 	pf_Frag * pf = pfStart;
1235 	pf_Frag_Object *pOb = NULL;
1236 	UT_sint32 iCountFootnotes = 0;
1237 	while(pf)
1238 	{
1239 		if(pf->getType() == pf_Frag::PFT_Strux)
1240 		{
1241 			if(isEndFootnote(pf))
1242 			{
1243 				iCountFootnotes++;
1244 			}
1245 			else if(isFootnote(pf))
1246 			{
1247 				iCountFootnotes--;
1248 			}
1249 			else if(iCountFootnotes == 0)
1250 			{
1251 				return NULL;
1252 			}
1253 		}
1254 		if(pf->getType() == pf_Frag::PFT_Object)
1255 		{
1256 			pOb = static_cast<pf_Frag_Object*>(pf);
1257 			if(pOb->getObjectType() == PTO_Hyperlink)
1258 			{
1259 				const PP_AttrProp * pAP = NULL;
1260 				pOb->getPieceTable()->getAttrProp(pOb->getIndexAP(),&pAP);
1261 				UT_return_val_if_fail (pAP, NULL);
1262 				const gchar* pszHref = NULL;
1263 				const gchar* pszHname  = NULL;
1264 				UT_uint32 k = 0;
1265 				while((pAP)->getNthAttribute(k++,pszHname, pszHref))
1266 				{
1267 					if(!strcmp(pszHname, "xlink:href"))
1268 				    {
1269 						return pf;
1270 					}
1271 				}
1272 				return NULL;
1273 			}
1274 
1275 		}
1276 		pf = pf->getPrev();
1277 	}
1278 	return NULL;
1279 }
1280 
1281 
1282 /*!
1283  * Scan backwards fromthe given frag until an end hyperlink is found.
1284  * This method is used to determine if a frag is inside a hyperlink span.
1285  * Returns NULL if:
1286  * (a) It encounters a strux first.
1287  * (b) It encounters a start hyperlink first
1288  * (c) It encounters the end of document
1289  */
_findNextHyperlink(pf_Frag * pfStart)1290 pf_Frag *    pt_PieceTable::_findNextHyperlink(pf_Frag * pfStart)
1291 {
1292 	pf_Frag * pf = pfStart;
1293 	pf_Frag_Object *pOb = NULL;
1294 	UT_sint32 iCountFootnotes = 0;
1295 	while(pf && pf != m_fragments.getLast())
1296 	{
1297 		if(pf->getType() == pf_Frag::PFT_Strux)
1298 		{
1299 			if(isFootnote(pf))
1300 			{
1301 				iCountFootnotes++;
1302 			}
1303 			else if(isEndFootnote(pf))
1304 			{
1305 				iCountFootnotes--;
1306 			}
1307 			else if(iCountFootnotes == 0)
1308 			{
1309 				return NULL;
1310 			}
1311 		}
1312 		if(pf->getType() == pf_Frag::PFT_Object)
1313 		{
1314 			pOb = static_cast<pf_Frag_Object*>(pf);
1315 			if(pOb->getObjectType() == PTO_Hyperlink)
1316 			{
1317 				const PP_AttrProp * pAP = NULL;
1318 				pOb->getPieceTable()->getAttrProp(pOb->getIndexAP(),&pAP);
1319 				UT_return_val_if_fail (pAP, NULL);
1320 				const gchar* pszHref = NULL;
1321 				const gchar* pszHname  = NULL;
1322 				UT_uint32 k = 0;
1323 				while((pAP)->getNthAttribute(k++,pszHname, pszHref))
1324 				{
1325 					if(!strcmp(pszHname, "xlink:href"))
1326 				    {
1327 						return NULL;
1328 					}
1329 				}
1330 				//
1331 				// No start marker => Must be end marker - GOT IT!
1332 				//
1333 				return pf;
1334 			}
1335 		}
1336 		pf = pf->getNext();
1337 	}
1338 	return NULL;
1339 }
1340 
_getStruxFromFrag(pf_Frag * pfStart,pf_Frag_Strux ** ppfs) const1341 bool pt_PieceTable::_getStruxFromFrag(pf_Frag * pfStart, pf_Frag_Strux ** ppfs) const
1342 {
1343 	// return the strux frag immediately prior to (containing)
1344 	// the given fragment.
1345 
1346 	*ppfs = NULL;
1347 
1348 	pf_Frag * pf;
1349 	for (pf=pfStart->getPrev(); (pf && (pf->getType() != pf_Frag::PFT_Strux)); pf=pf->getPrev())
1350 		;
1351 	if (!pf)
1352 		return false;
1353 
1354 	*ppfs = static_cast<pf_Frag_Strux *>(pf);
1355 	return true;
1356 }
1357 
1358 
_getNextStruxAfterFragSkip(pf_Frag * pfStart,pf_Frag_Strux ** ppfs)1359 bool pt_PieceTable::_getNextStruxAfterFragSkip(pf_Frag *pfStart, pf_Frag_Strux ** ppfs)
1360 {
1361 
1362 	*ppfs = NULL;
1363 
1364 	pf_Frag * pf;
1365 	UT_sint32 countFoots = 0;
1366 	if(isFootnote(pfStart))
1367 	{
1368 		countFoots++;
1369 	}
1370 	pf = pfStart->getNext();
1371 	if(pf && isFootnote(pf))
1372 	{
1373 		countFoots++;
1374 	}
1375 	xxx_UT_DEBUGMSG(("_getStruxFromFragStrux: 1 countFoots %d \n",countFoots));
1376 	while(pf && (pf->getType() != pf_Frag::PFT_EndOfDoc) && ((pf->getType() != pf_Frag::PFT_Strux) || (countFoots > 0)
1377 				 || isFootnote(pf) || isEndFootnote(pf)))
1378 	{
1379 		pf=pf->getNext();
1380 		if(isFootnote(pf))
1381 		{
1382 			countFoots++;
1383 		}
1384 		else if(isEndFootnote(pf))
1385 		{
1386 			countFoots--;
1387 		}
1388 		xxx_UT_DEBUGMSG(("_getStruxFromFragStrux: 2 countFoots %d \n",countFoots));
1389 	}
1390 		;
1391 	if (!pf)
1392 		return false;
1393 
1394 	*ppfs = static_cast<pf_Frag_Strux *>(pf);
1395 	return true;
1396 }
1397 
1398 
_getStruxFromFragSkip(pf_Frag * pfStart,pf_Frag_Strux ** ppfs) const1399 bool pt_PieceTable::_getStruxFromFragSkip(pf_Frag * pfStart, pf_Frag_Strux ** ppfs) const
1400 {
1401 	// return the strux frag immediately prior to (containing)
1402 	// the given fragment while skipping endFootnote/footnote stuff.
1403 
1404 	*ppfs = NULL;
1405 
1406 	pf_Frag * pf;
1407 	UT_sint32 countFoots = 0;
1408 	if(isEndFootnote(pfStart))
1409 	{
1410 		countFoots++;
1411 	}
1412 	pf = pfStart->getPrev();
1413 	if(isEndFootnote(pf))
1414 	{
1415 		countFoots++;
1416 	}
1417 	if(isFootnote(pf))
1418 	{
1419 		countFoots--;
1420 	}
1421 	xxx_UT_DEBUGMSG(("_getStruxFromFragStrux: 1 countFoots %d \n",countFoots));
1422 	while(pf && ((pf->getType() != pf_Frag::PFT_Strux) || (countFoots > 0)
1423 				 || isFootnote(pf) || isEndFootnote(pf)))
1424 	{
1425 		pf=pf->getPrev();
1426 		if(pf && isFootnote(pf))
1427 		{
1428 			countFoots--;
1429 		}
1430 		else if(pf && isEndFootnote(pf))
1431 		{
1432 			countFoots++;
1433 		}
1434 		xxx_UT_DEBUGMSG(("_getStruxFromFragStrux: 2 countFoots %d \n",countFoots));
1435 	}
1436 		;
1437 	if (!pf)
1438 		return false;
1439 
1440 	*ppfs = static_cast<pf_Frag_Strux *>(pf);
1441 	return true;
1442 }
1443 
_computeBlockOffset(pf_Frag_Strux * pfs,pf_Frag * pfTarget) const1444 UT_uint32 pt_PieceTable::_computeBlockOffset(pf_Frag_Strux * pfs,pf_Frag * pfTarget) const
1445 {
1446 	// return the block offset of the beginning of pfTarget from the end of pfs.
1447 
1448 	UT_uint32 sum;
1449 	pf_Frag * pf;
1450 
1451 	for (pf=pfs->getNext(), sum=0; (pf && (pf!=pfTarget)); sum+=pf->getLength(), pf=pf->getNext())
1452 		;
1453 	if(pf == NULL)
1454 	{
1455 		return 0;
1456 	}
1457 
1458 	return sum;
1459 }
1460 
1461 
clearIfAtFmtMark(PT_DocPosition dpos)1462 void pt_PieceTable::clearIfAtFmtMark(PT_DocPosition dpos)
1463 {
1464 	while (_lastUndoIsThisFmtMark(dpos))
1465 	{
1466 		UT_DEBUGMSG(("clearIfAtFmtMark doing something...\n"));
1467 		undoCmd();
1468 	}
1469 }
1470 
_changePointWithNotify(PT_DocPosition dpos)1471 bool pt_PieceTable::_changePointWithNotify(PT_DocPosition dpos)
1472 {
1473 	PX_ChangeRecord * pcr
1474 		= new PX_ChangeRecord(PX_ChangeRecord::PXT_ChangePoint,
1475 							  dpos, 0,0);
1476 	UT_return_val_if_fail (pcr,false);
1477 
1478 	m_history.addChangeRecord(pcr);
1479 	m_pDocument->notifyListeners(NULL, pcr);
1480 
1481 	return true;
1482 }
1483 
1484 /*!
1485     This function crawls the entire PT and assignes new xid to any fragment that should
1486     have one and does not. It is primarily to be used by exporters (accessed throught
1487     PD_Document wrapper)
1488 */
fixMissingXIDs()1489 void pt_PieceTable::fixMissingXIDs()
1490 {
1491 	for (pf_Frag * pf = m_fragments.getFirst(); (pf); pf=pf->getNext())
1492 	{
1493 		if(!pf->getXID() && pf->usesXID())
1494 			pf->setXID(getXID());
1495 	}
1496 }
1497 
getXID()1498 UT_uint32 pt_PieceTable::getXID()
1499 {
1500 	++m_iXID;
1501 	return m_iXID;
1502 }
1503 
1504 
1505 /* Return true if neither dpos1 nor dpos2 is within a note and the whole document is not selected*/
_checkSkipFootnote(PT_DocPosition dpos1,PT_DocPosition dpos2,pf_Frag * pf_End) const1506 bool pt_PieceTable::_checkSkipFootnote(PT_DocPosition dpos1, PT_DocPosition dpos2, pf_Frag * pf_End) const
1507 {
1508 	if(m_embeddedStrux.empty())
1509 	{
1510 		return true;
1511 	}
1512 
1513 	if (!pf_End)
1514 	{
1515 		PT_BlockOffset offset;
1516 		getFragFromPosition(dpos2,&pf_End,&offset);
1517 	}
1518 	if ((dpos1 == 1) && ((pf_End->getType() == pf_Frag::PFT_EndOfDoc) ||
1519 						 ((pf_End->getType() == pf_Frag::PFT_Strux) &&
1520 						  (static_cast<pf_Frag_Strux*>(pf_End)->getStruxType() == PTX_SectionHdrFtr))))
1521 	{
1522 		return false;
1523 	}
1524 
1525 	bool bSkipNote = true;
1526 	std::list<embeddedStrux>::const_reverse_iterator it;
1527 	for (it = m_embeddedStrux.rbegin(); it != m_embeddedStrux.rend(); ++it)
1528 	{
1529 		if ((*it).beginNote->getPos() < dpos2)
1530 		{
1531 			if ((*it).endNote->getPos() > dpos2)
1532 			{
1533 				bSkipNote = false;
1534 			}
1535 			break;
1536 		}
1537 	}
1538 	if (bSkipNote)
1539 	{
1540 		if (it != m_embeddedStrux.rbegin())
1541 		{
1542 			it--;
1543 		}
1544 		for (; it != m_embeddedStrux.rend(); ++it)
1545 		{
1546 			if ((*it).beginNote->getPos() < dpos1)
1547 			{
1548 				if ((*it).endNote->getPos() > dpos1)
1549 				{
1550 					bSkipNote = false;
1551 				}
1552 				break;
1553 			}
1554 		}
1555 	}
1556 	return bSkipNote;
1557 }
1558