1 /* -*- mode: C++; tab-width: 4; c-basic-offset: 4; -*- */
2 
3 /* AbiWord
4  * Copyright (C) 1998 AbiSource, Inc.
5  * Copyright (c) 2001,2002,2003 Tomas Frydrych
6  * Copyright (C) 2013 Hubert Figuiere
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21  * 02110-1301 USA.
22  */
23 
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include "ut_types.h"
28 #include "ut_string.h"
29 #include "ut_debugmsg.h"
30 #include "ut_assert.h"
31 #include "ut_bytebuf.h"
32 #include "ut_base64.h"
33 #include "ut_misc.h"
34 #include "ut_rand.h"
35 #include "ut_uuid.h"
36 #include "pd_Document.h"
37 #include "pd_DocumentRDF.h"
38 #include "xad_Document.h"
39 #include "xap_Strings.h"
40 #include "pt_PieceTable.h"
41 #include "pl_Listener.h"
42 #include "ie_imp.h"
43 #include "ie_exp.h"
44 #include "pf_Frag_Strux.h"
45 #include "pp_Property.h"
46 #include "pd_Style.h"
47 #include "pf_Frag_Object.h"
48 #include "pf_Frag_FmtMark.h"
49 #include "px_CR_Span.h"
50 #include "px_CR_SpanChange.h"
51 #include "px_CR_Strux.h"
52 #include "px_CR_StruxChange.h"
53 #include "pf_Frag.h"
54 #include "pd_Iterator.h"
55 #include "fd_Field.h"
56 #include "po_Bookmark.h"
57 #include "fl_AutoNum.h"
58 #include "xap_Frame.h"
59 #include "xap_App.h"
60 #include "xap_Prefs.h"
61 #include "ap_Prefs.h"
62 #include "ap_Strings.h"
63 #include "ut_units.h"
64 #include "ut_string_class.h"
65 #include "ut_sleep.h"
66 #include "ut_path.h"
67 #include "ut_locale.h"
68 #include "pp_Author.h"
69 
70 // these are needed because of the exportGetVisDirectionAtPosition() mechanism
71 #include "fp_Run.h"
72 #include "fl_BlockLayout.h"
73 #include "fl_DocListener.h"
74 #include "fl_DocLayout.h"
75 #include "fv_View.h"
76 #include "ap_StatusBar.h"
77 #include "ap_FrameData.h"
78 
79 #include "ut_go_file.h"
80 #include "ut_std_string.h"
81 
82 /*!
83  * Helper class to import Page Referenced Images
84  */
85 
ImagePage(UT_UTF8String & sImageId,UT_sint32 iPage,double xInch,double yInch,const char * pzProps)86 ImagePage::ImagePage(UT_UTF8String & sImageId, UT_sint32 iPage, double xInch, double yInch, const char * pzProps) : m_sImageId(sImageId), m_iPage(iPage),m_xInch(xInch),m_yInch(yInch)
87 {
88 	m_sProps = pzProps;
89 }
90 
~ImagePage(void)91 ImagePage::~ImagePage(void)
92 {
93 }
94 
getImageId(void) const95 const UT_UTF8String * ImagePage::getImageId(void) const
96 {
97 	return &m_sImageId;
98 }
99 
getPageNo(void) const100 UT_sint32 ImagePage::getPageNo(void) const
101 {
102 	return m_iPage;
103 }
104 
getXInch(void) const105 double ImagePage::getXInch(void) const
106 {
107 	return m_xInch;
108 }
109 
getYInch(void) const110 double ImagePage::getYInch(void) const
111 {
112 	return m_yInch;
113 }
114 
getProps(void) const115 const	UT_UTF8String * ImagePage::getProps(void) const
116 {
117 	return &m_sProps;
118 }
119 
120 
121 /*!
122  * Helpder class to import Page Referenced TextBoxes
123  */
TextboxPage(UT_sint32 iPage,double xInch,double yInch,const char * pzProps,UT_ByteBuf & sContent)124 TextboxPage::TextboxPage(UT_sint32 iPage, double xInch, double yInch,const char * pzProps, UT_ByteBuf & sContent) : m_iPage(iPage),m_xInch(xInch),m_yInch(yInch)
125 {
126 	m_sProps = pzProps;
127 	m_sContent.append(sContent.getPointer(0),sContent.getLength());
128 }
~TextboxPage(void)129 TextboxPage::~TextboxPage(void)
130 {
131 
132 }
getContent(void) const133 const UT_ByteBuf * TextboxPage::getContent(void) const
134 {
135 	return &m_sContent;
136 }
getPageNo(void) const137 UT_sint32 TextboxPage::getPageNo(void) const
138 {
139 	return m_iPage;
140 }
getXInch(void) const141 double TextboxPage::getXInch(void) const
142 {
143 	return m_xInch;
144 }
getYInch(void) const145 double TextboxPage::getYInch(void) const
146 {
147 	return m_yInch;
148 }
getProps(void) const149 const UT_UTF8String * TextboxPage::getProps(void) const
150 {
151 	return &m_sProps;
152 }
153 
154 
155 // our currently used DTD
156 #define ABIWORD_FILEFORMAT_VERSION "1.1"
157 
158 //////////////////////////////////////////////////////////////////
159 //////////////////////////////////////////////////////////////////
160 
161 struct _dataItemPair
162 {
163 	UT_ByteBuf* pBuf;
164 	const void*	pToken;
165 };
166 
167 //////////////////////////////////////////////////////////////////
168 //////////////////////////////////////////////////////////////////
169 
170 // perhaps this should be a magic "unknown" or "NULL" value,
171 // but now we just depend on save() never being called without
172 // a previous saveAs() (which specifies a type)
PD_Document()173 PD_Document::PD_Document()
174 	: AD_Document(),
175 	  m_docPageSize("A4"),
176 	  m_ballowListUpdates(false),
177 	  m_pPieceTable(0),
178       m_hDocumentRDF( new PD_DocumentRDF( this )),
179 	  m_lastOpenedType(IEFT_Bogus), // used to be: IE_Imp::fileTypeForSuffix(".abw"))
180 	  m_lastSavedAsType(IEFT_Bogus), // used to be: IE_Exp::fileTypeForSuffix(".abw")
181 	  m_bDoingPaste(false),
182 	  m_bAllowInsertPointChange(true),
183 	  m_bRedrawHappenning(false),
184 	  m_bLoading(false),
185 	  m_bLockedStyles(false),        // same as lockStyles(false)
186 	  m_indexAP(0xffffffff),
187 	  m_bDontImmediatelyLayout(false),
188 	  m_iLastDirMarker(0),
189 	  m_pVDBl(NULL),
190 	  m_pVDRun(NULL),
191 	  m_iVDLastPos(0xffffffff),
192 	  m_iNewHdrHeight(0),
193 	  m_iNewFtrHeight(0),
194 	  m_bMarginChangeOnly(false),
195 	  m_bVDND(false),
196 	  m_iCRCounter(0),
197 	  m_iUpdateCount(0),
198 	  m_bIgnoreSignals(false),
199 	  m_bCoalescingMask(false),
200 	  m_bShowAuthors(true),
201 	  m_bExportAuthorAtts(false), //should be false by default. Set true to test
202 	  m_iMyAuthorInt(-1),
203 	  m_iLastAuthorInt(-1),
204 	  m_iStruxCount(0)
205 {
206 	XAP_App::getApp()->getPrefs()->getPrefsValueBool(AP_PREF_KEY_LockStyles,&m_bLockedStyles);
207 	UT_ASSERT(isOrigUUID());
208 #ifdef PT_TEST
209 	m_pDoc = this;
210 #endif
211 	UT_UTF8String sDoc;
212 	getOrigDocUUID()->toString(sDoc);
213 
214 	const gchar *name = g_get_real_name();
215 	if(strcmp(name, "Unknown") == 0)
216 		name = g_get_user_name();
217 	gchar *utf8name = g_locale_to_utf8(name, -1, NULL, NULL, NULL);
218 	if (utf8name != NULL)
219 	{
220 		m_sUserName = utf8name;
221 		g_free(utf8name);
222 	}
223 	else
224 	{
225 		m_sUserName = "Unknown";
226 	}
227 }
228 
~PD_Document()229 PD_Document::~PD_Document()
230 {
231 	// ideally all connections would have been removed BEFORE
232 	// we ever reach this destructor (for example by disconnecting
233 	// listeners in the frame before deleting the document); this
234 	// will do for now though
235 	removeConnections();
236 
237 	if (m_pPieceTable)
238 		delete m_pPieceTable;
239 
240 	_destroyDataItemData();
241 
242 	UT_VECTOR_PURGEALL(fl_AutoNum*, m_vecLists);
243 	m_mailMergeMap.purgeData();
244 	//UT_HASH_PURGEDATA(UT_UTF8String*, &m_mailMergeMap, delete) ;
245 
246 	UT_VECTOR_PURGEALL(pp_Author *, m_vecAuthors);
247 	UT_VECTOR_PURGEALL(ImagePage *, m_pPendingImagePage);
248 	UT_VECTOR_PURGEALL(TextboxPage *, m_pPendingTextboxPage);
249 	// we do not purge the contents of m_vecListeners
250 	// since these are not owned by us.
251 
252 	// TODO: delete the key/data pairs
253 }
254 
255 
256 //////////////////////////////////////////////////////////////////
257 //////////////////////////////////////////////////////////////////
258 
setMetaDataProp(const std::string & key,const std::string & value)259 void PD_Document::setMetaDataProp ( const std::string & key,
260 									const std::string & value )
261 {
262 	m_metaDataMap[key] = value;
263 
264 	const gchar * atts[3] = { PT_DOCPROP_ATTRIBUTE_NAME,"metadata",NULL};
265 	const gchar * props[3] = {NULL,NULL,NULL};
266 	props[0] = key.c_str();
267 	props[1] = value.c_str();
268 	createAndSendDocPropCR(atts,props);
269 }
270 
getNextCRNumber(void)271 UT_sint32  PD_Document::getNextCRNumber(void)
272 {
273 	m_iCRCounter++;
274 	return m_iCRCounter;
275 }
276 
getMetaDataProp(const std::string & key,std::string & outProp) const277 bool PD_Document::getMetaDataProp (const std::string & key, std::string & outProp) const
278 {
279   std::map<std::string,std::string>::const_iterator iter = m_metaDataMap.find(key);
280   bool found = (iter != m_metaDataMap.end());
281 
282   if (found && !iter->second.empty()) {
283 	  outProp = iter->second;
284   }
285   else {
286 	  outProp = "";
287   }
288 
289   return found;
290 }
291 
292 // RIVERA TODO not working and may not be needed
setAnnotationProp(const std::string &,const std::string &)293 void PD_Document::setAnnotationProp ( const std::string & /*key*/,
294 									  const std::string & /*value*/ )
295 {
296 	UT_ASSERT(UT_NOT_IMPLEMENTED);
297 	return; // TODO something!
298 }
getAnnotationProp(const std::string &,std::string & outProp) const299 bool PD_Document::getAnnotationProp (const std::string & /*key*/, std::string & outProp) const
300 {
301 	UT_ASSERT(UT_NOT_IMPLEMENTED);
302 	bool found = true;//false;
303 	outProp = "Dummy value";
304 
305 	return found;
306 }
307 
308 
getMailMergeField(const UT_String & key) const309 UT_UTF8String PD_Document::getMailMergeField(const UT_String & key) const
310 {
311   const UT_UTF8String * val = m_mailMergeMap.pick ( key );
312   if (val)
313     return *val;
314   return "";
315 }
316 
mailMergeFieldExists(const UT_String & key) const317 bool PD_Document::mailMergeFieldExists(const UT_String & key) const
318 {
319     const UT_UTF8String * val = m_mailMergeMap.pick ( key );
320     return (val != NULL);
321 }
322 
setMailMergeField(const UT_String & key,const UT_UTF8String & value)323 void PD_Document::setMailMergeField(const UT_String & key,
324 									const UT_UTF8String & value)
325 {
326 	UT_UTF8String * old = m_mailMergeMap.pick ( key );
327 	DELETEP(old);
328 
329 	UT_UTF8String * ptrvalue = new UT_UTF8String ( value ) ;
330 	m_mailMergeMap.set ( key, ptrvalue ) ;
331 }
332 
clearMailMergeMap()333 void PD_Document::clearMailMergeMap()
334 {
335 	m_mailMergeMap.clear();
336 }
337 
setMarginChangeOnly(bool b)338 void PD_Document::setMarginChangeOnly(bool b)
339 {
340 	m_bMarginChangeOnly = b;
341 }
342 
isMarginChangeOnly(void) const343 bool PD_Document::isMarginChangeOnly(void) const
344 {
345 	return m_bMarginChangeOnly;
346 }
347 
348 //////////////////////////////////////////////////////////////////
349 //////////////////////////////////////////////////////////////////
350 
351 
removeCaret(const std::string & sCaretID)352 void PD_Document::removeCaret(const std::string& sCaretID)
353 {
354 	UT_GenericVector<AV_View *> vecViews;
355 	getAllViews(&vecViews);
356 	UT_sint32 i = 0;
357 	for(i = 0; i<vecViews.getItemCount(); i++)
358 	{
359 		FV_View * pView = static_cast<FV_View *>(vecViews.getNthItem(i));
360 		pView->removeCaret(sCaretID);
361 	}
362 }
363 
364 /////////////////////////////////////////////////////////////////
365 
addPageReferencedImage(UT_UTF8String & sImageId,UT_sint32 iPage,double xInch,double yInch,const char * pzProps)366 void PD_Document::addPageReferencedImage(UT_UTF8String & sImageId, UT_sint32 iPage, double xInch, double yInch, const char * pzProps)
367 {
368 	m_pPendingImagePage.addItem(new ImagePage(sImageId, iPage, xInch, yInch, pzProps));
369 }
370 
addPageReferencedTextbox(UT_ByteBuf & sContent,UT_sint32 iPage,double xInch,double yInch,const char * pzProps)371 void PD_Document::addPageReferencedTextbox(UT_ByteBuf & sContent,UT_sint32 iPage, double xInch, double yInch,const char * pzProps)
372 {
373 	m_pPendingTextboxPage.addItem(new TextboxPage(iPage, xInch,yInch,pzProps, sContent));
374 }
375 
getNthImagePage(UT_sint32 iImagePage)376 ImagePage * PD_Document::getNthImagePage(UT_sint32 iImagePage)
377 {
378 	if(iImagePage < m_pPendingImagePage.getItemCount())
379 	{
380 		return m_pPendingImagePage.getNthItem(iImagePage);
381 	}
382 	return NULL;
383 }
384 
getNthTextboxPage(UT_sint32 iTextboxPage)385 TextboxPage * PD_Document::getNthTextboxPage(UT_sint32 iTextboxPage)
386 {
387 	if(iTextboxPage < m_pPendingTextboxPage.getItemCount())
388 	{
389 		return m_pPendingTextboxPage.getNthItem(iTextboxPage);
390 	}
391 	return NULL;
392 }
393 
clearAllPendingObjects(void)394 void PD_Document::clearAllPendingObjects(void)
395 {
396 	UT_VECTOR_PURGEALL(ImagePage *, m_pPendingImagePage);
397 	UT_VECTOR_PURGEALL(TextboxPage *, m_pPendingTextboxPage);
398 	m_pPendingImagePage.clear();
399 	m_pPendingTextboxPage.clear();
400 }
401 
402 
getNumAuthors() const403 UT_sint32 PD_Document::getNumAuthors() const
404 {
405 	return m_vecAuthors.getItemCount();
406 }
407 
getNthAuthor(UT_sint32 i) const408 pp_Author *  PD_Document::getNthAuthor(UT_sint32 i) const
409 {
410 	return m_vecAuthors.getNthItem(i);
411 }
412 
addAuthor(UT_sint32 iAuthor)413 pp_Author *  PD_Document::addAuthor(UT_sint32 iAuthor)
414 {
415 	UT_DEBUGMSG(("creating author with int %d \n",iAuthor));
416 	m_vecAuthors.addItem(new pp_Author(this, iAuthor));
417 	return 	m_vecAuthors.getNthItem(m_vecAuthors.getItemCount()-1);
418 }
419 
sendAddAuthorCR(pp_Author * pAuthor)420 bool PD_Document::sendAddAuthorCR(pp_Author * pAuthor)
421 {
422 	UT_return_val_if_fail(pAuthor, false);
423 	const gchar * szAtts[3] = {PT_DOCPROP_ATTRIBUTE_NAME,"addauthor",NULL};
424 	const gchar ** szProps = NULL;
425 	std::string storage;
426 	_buildAuthorProps(pAuthor, szProps, storage);
427 	UT_return_val_if_fail(szProps, false);
428 	bool b = createAndSendDocPropCR(szAtts,szProps);
429 	DELETEPV(szProps);
430 	return b;
431 }
432 
433 
sendChangeAuthorCR(pp_Author * pAuthor)434 bool PD_Document::sendChangeAuthorCR(pp_Author * pAuthor)
435 {
436 	const gchar * szAtts[3] = {PT_DOCPROP_ATTRIBUTE_NAME,"changeauthor",NULL};
437 	const gchar ** szProps = NULL;
438 	std::string storage;
439 	_buildAuthorProps(pAuthor, szProps, storage);
440 	UT_return_val_if_fail(szProps, false);
441 	bool b = createAndSendDocPropCR(szAtts,szProps);
442 	DELETEPV(szProps);
443 	return b;
444 }
445 
_buildAuthorProps(pp_Author * pAuthor,const gchar ** & szProps,std::string & storage)446 bool PD_Document::_buildAuthorProps(pp_Author * pAuthor, const gchar **& szProps, std::string & storage)
447 {
448 	const PP_AttrProp * pAP = pAuthor->getAttrProp();
449 	UT_uint32 iCnt= pAP->getPropertyCount();
450 	szProps = new const gchar * [2*iCnt + 3];
451 	UT_DEBUGMSG(("_buildAuthorProps getAuthorInt %d \n",pAuthor->getAuthorInt()));
452 	storage = UT_std_string_sprintf("%d",pAuthor->getAuthorInt());
453 	szProps[0] = "id";
454 	szProps[1] = storage.c_str();
455 	UT_uint32 i = 0;
456 	const gchar * szName = NULL;
457 	const gchar * szValue = NULL;
458 	UT_uint32 j = 2;
459 	for(i=0;i<iCnt;i++)
460 	{
461 		pAP->getNthProperty(i,szName,szValue);
462 		if(*szValue)
463 		{
464 			szProps[j++] = szName;
465 			szProps[j++] = szValue;
466 		}
467 	}
468 	szProps[j] = NULL;
469 	return true;
470 }
471 
findFirstFreeAuthorInt(void) const472 UT_sint32 PD_Document::findFirstFreeAuthorInt(void) const
473 {
474 	UT_sint32 i= 0;
475 	for(i=0;i<1000;i++)
476 	{
477 		if(getAuthorByInt(i) == NULL)
478 			break;
479 	}
480 	return i;
481 }
getAuthorByInt(UT_sint32 i) const482 pp_Author * PD_Document::getAuthorByInt(UT_sint32 i) const
483 {
484 	UT_sint32 j = 0;
485 	for(j=0; j< m_vecAuthors.getItemCount(); j++)
486 	{
487 		if(m_vecAuthors.getNthItem(j)->getAuthorInt() == i)
488 			return m_vecAuthors.getNthItem(j);
489 	}
490 	return NULL;
491 }
492 /*!
493  * True if the Author attributes should be exported.
494  */
isExportAuthorAtts(void) const495 bool  PD_Document::isExportAuthorAtts(void) const
496 {
497 	return m_bExportAuthorAtts;
498 }
499 
setExportAuthorAtts(bool bAuthor)500 void  PD_Document::setExportAuthorAtts(bool bAuthor)
501 {
502 	m_bExportAuthorAtts = bAuthor;
503 }
504 /*!
505  * Returns the integer mapping for this session
506  */
getMyAuthorInt(void) const507 UT_sint32 PD_Document::getMyAuthorInt(void) const
508 {
509 	return m_iMyAuthorInt;
510 }
511 
setMyAuthorInt(UT_sint32 i)512 void PD_Document::setMyAuthorInt(UT_sint32 i)
513 {
514 	m_iMyAuthorInt = i;
515 }
516 /*!
517  * Returns the most recently received author int
518  */
getLastAuthorInt(void) const519 UT_sint32 PD_Document::getLastAuthorInt(void) const
520 {
521 	return m_iLastAuthorInt;
522 }
523 
524 /*!
525  * Add the current documents UUID as the author to the attribute list if
526  * the author attribute is not present.
527  * The caller must delete [] the szAttsOut after use.
528  * @param storage a std::string storage for until the properties are copied.
529  * Returns true if author attribute is present.
530 */
addAuthorAttributeIfBlank(const gchar ** szAttsIn,const gchar ** & szAttsOut,std::string & storage)531 bool  PD_Document::addAuthorAttributeIfBlank(const gchar ** szAttsIn, const gchar **& szAttsOut, std::string &storage)
532 {
533 	// Set Author attribute
534 	UT_sint32 icnt =  0;
535 	bool bFound = false;
536 	const gchar * sz = NULL;
537 	if(szAttsIn)
538 	{
539 		sz = szAttsIn[0];
540 		while(sz != NULL)
541 		{
542 			sz = szAttsIn[icnt];
543 			if(sz && (strcmp(sz,PT_AUTHOR_NAME) == 0))
544 			{
545 				bFound = true;
546 				const gchar * sz1 = szAttsIn[icnt+1];
547 				if(sz1 && *sz1)
548 					m_iLastAuthorInt = atoi(sz1);
549 			}
550 			icnt++;
551 		}
552 	}
553 	if(bFound)
554 		szAttsOut = new const gchar * [icnt+1];
555 	else
556 		szAttsOut = new const gchar * [icnt+3];
557 	UT_sint32 i = 0;
558 	for(i = 0; i< icnt; i++)
559 		szAttsOut[i] =  szAttsIn[i];
560 	if(bFound)
561 	{
562 		szAttsOut[icnt] = NULL;
563 		return bFound;
564 	}
565 	szAttsOut[icnt] = PT_AUTHOR_NAME;
566 	if(getMyAuthorInt() == -1)
567 	{
568 		UT_sint32 k = findFirstFreeAuthorInt();
569 		setMyAuthorInt(k);
570 		m_iLastAuthorInt = k;
571 		pp_Author * pA = addAuthor(k);
572 		sendAddAuthorCR(pA);
573 	}
574 	storage = UT_std_string_sprintf("%d",getMyAuthorInt());
575 	m_iLastAuthorInt = getMyAuthorInt();
576 	szAttsOut[icnt+1] = storage.c_str();
577 	xxx_UT_DEBUGMSG(("Attribute %s set to %s \n",szAttsOut[icnt],szAttsOut[icnt+1]));
578 	szAttsOut[icnt+2] = NULL;
579 	return false;
580 }
581 
setShowAuthors(bool bAuthors)582 void PD_Document::setShowAuthors(bool bAuthors)
583 {
584 	bool bChanged = (bAuthors != m_bShowAuthors);
585 	m_bShowAuthors = bAuthors;
586 	//
587 	// Could do this with a listner but that might screw up other stuff
588 	//
589 	if(bChanged)
590 	{
591 		UT_GenericVector<AV_View *> vecViews;
592 		getAllViews(&vecViews);
593 		UT_sint32 i = 0;
594 		for(i = 0; i<vecViews.getItemCount(); i++)
595 		{
596 			FV_View * pView = static_cast<FV_View *>(vecViews.getNthItem(i));
597 			FL_DocLayout * pL = pView->getLayout();
598 			pL->refreshRunProperties();
599 			pView->updateScreen(false ); // redraw the whole thing
600 		}
601 	}
602 
603 }
604 
605 /*!
606  * Add the current documents UUID as the author to the ppAttrProps
607  * if the attribute is not set.
608  * returns true if author is set
609  */
addAuthorAttributeIfBlank(PP_AttrProp * & p_AttrProp)610 bool PD_Document::addAuthorAttributeIfBlank( PP_AttrProp *&p_AttrProp)
611 {
612 	xxx_UT_DEBUGMSG(("Doing addAuthorAttributeIfBlank PAP \n"));
613 	std::string sNum;
614 	if(getMyAuthorInt() == -1)
615 	{
616 		UT_sint32 k = findFirstFreeAuthorInt();
617 		setMyAuthorInt(k);
618 		pp_Author * pA = addAuthor(k);
619 		sendAddAuthorCR(pA);
620 	}
621 	sNum = UT_std_string_sprintf("%d",getMyAuthorInt());
622 	m_iLastAuthorInt = getMyAuthorInt();
623 	if(!p_AttrProp)
624 	{
625 		static PP_AttrProp p;
626 		p.setAttribute(PT_AUTHOR_NAME,sNum.c_str());
627 		p_AttrProp = &p;
628 		return false;
629 	}
630 	const gchar * sz = NULL;
631 	if(p_AttrProp->getAttribute(PT_AUTHOR_NAME,sz))
632 	{
633 		xxx_UT_DEBUGMSG(("Found athor att %s \n",sz));
634 		if(sz)
635 		{
636 			m_iLastAuthorInt = atoi(sz);
637 			return true;
638 		}
639 	}
640 	p_AttrProp->setAttribute(PT_AUTHOR_NAME,sNum.c_str());
641 	return false;
642 }
643 //////////////////////////////////////////////////////////////////
644 //////////////////////////////////////////////////////////////////
645 
UT_filenameToUri(const std::string & rhs)646 static std::string UT_filenameToUri(const std::string & rhs) {
647 	char * uri = UT_go_filename_to_uri (rhs.c_str());
648 	if(!uri) {
649 		return "";
650 	}
651 	std::string res(uri);
652 	g_free (uri);
653 
654 	return res;
655 }
656 
buildTemplateList(std::string * template_list,const std::string & base)657 static void buildTemplateList(std::string *template_list, const std::string & base)
658 {
659 	UT_LocaleInfo locale(UT_LocaleInfo::system());
660 
661 	std::string lang (locale.getLanguage());
662 	std::string terr (locale.getTerritory());
663 
664 	/* try *6* combinations of the form:
665 	   1) /templates/normal.awt-en_US
666 	   2) /templates/normal.awt-en
667 	   3) /templates/normal.awt
668 	   4) /templates/normal.awt-en_US
669 	   5) /templates/normal.awt-en
670 	   6) /templates/normal.awt
671 	*/
672 
673 	std::string user_template_base (XAP_App::getApp()->getUserPrivateDirectory());
674 #if defined(WIN32)
675 	user_template_base += UT_std_string_sprintf("\\templates\\%s", base.c_str());
676 #else
677 	user_template_base += UT_std_string_sprintf("/templates/%s", base.c_str());
678 #endif
679 	std::string global_template_base (XAP_App::getApp()->getAbiSuiteLibDir());
680 #if defined(WIN32)
681 	global_template_base += UT_std_string_sprintf("\\templates\\%s", base.c_str());
682 #else
683 	global_template_base += UT_std_string_sprintf("/templates/%s", base.c_str());
684 #endif
685 
686 	template_list[0] = UT_std_string_sprintf ("%s-%s_%s", user_template_base.c_str(), lang.c_str(), terr.c_str());
687 	template_list[1] = UT_std_string_sprintf ("%s-%s", user_template_base.c_str(), lang.c_str());
688 	template_list[2] = user_template_base;
689 
690 	if (!XAP_App::getApp()->findAbiSuiteLibFile(template_list[5],base.c_str(),"templates"))
691 		template_list[5] = global_template_base; // always try to load global normal.awt last
692 
693 	std::string xbase = base;
694 
695 	xbase += "-";
696 	xbase += lang;
697 
698 	if (!XAP_App::getApp()->findAbiSuiteLibFile(template_list[4],xbase.c_str(),"templates"))
699 		template_list[4] = UT_std_string_sprintf ("%s-%s", global_template_base.c_str(), lang.c_str());
700 
701 	xbase += "_";
702 	xbase += terr;
703 
704 	if (!XAP_App::getApp()->findAbiSuiteLibFile(template_list[3],xbase.c_str(),"templates"))
705 		template_list[3] = UT_std_string_sprintf ("%s-%s_%s", global_template_base.c_str(), lang.c_str(), terr.c_str());
706 
707 	for(int i = 0; i < 6; i++) {
708 		template_list[i] = UT_filenameToUri(template_list[i]);
709 	}
710 }
711 
importFile(const char * szFilename,int ieft,bool markClean,bool bImportStylesFirst,const char * impProps)712 UT_Error PD_Document::importFile(const char * szFilename, int ieft,
713 								 bool markClean, bool bImportStylesFirst,
714 								 const char* impProps)
715 {
716 	return _importFile(szFilename, ieft, markClean, bImportStylesFirst, true, impProps);
717 }
718 
importFile(GsfInput * input,int ieft,bool markClean,bool bImportStylesFirst,const char * impProps)719 UT_Error PD_Document::importFile(GsfInput * input, int ieft,
720 								 bool markClean, bool bImportStylesFirst,
721 								 const char* impProps)
722 {
723 	return _importFile(input, ieft, markClean, bImportStylesFirst, true, impProps);
724 }
725 
_importFile(const char * szFilename,int ieft,bool markClean,bool bImportStylesFirst,bool bIsImportFile,const char * impProps)726 UT_Error PD_Document::_importFile(const char * szFilename, int ieft,
727 								  bool markClean, bool bImportStylesFirst,
728 								  bool bIsImportFile, const char* impProps)
729 {
730 	GsfInput * input;
731 
732 	input = UT_go_file_open(szFilename, NULL);
733 	if (!input)
734 	{
735 		UT_DEBUGMSG(("PD_Document::importFile -- invalid filename\n"));
736 		return UT_INVALIDFILENAME;
737 	}
738 
739 	UT_Error result = _importFile(input, ieft, markClean, bImportStylesFirst, bIsImportFile, impProps);
740 
741 	g_object_unref (G_OBJECT (input));
742 
743 	return result;
744 }
745 
updateStatus(void)746 void PD_Document::updateStatus(void)
747 {
748 	m_iStruxCount++;
749 	UT_sint32 updateRate =100;
750 	UT_sint32 iStruxDiv = m_iStruxCount/updateRate;
751 	if(iStruxDiv*updateRate == m_iStruxCount)
752 	{
753 		xxx_UT_DEBUGMSG(("UpdateStatus StruxCount %d \n",m_iStruxCount));
754 		XAP_Frame * pFrame = XAP_App::getApp()->getLastFocussedFrame();
755 		if(pFrame)
756 			pFrame->nullUpdate();
757 		AP_StatusBar * pBar = getStatusBar();
758 		if(pFrame && pBar)
759 		{
760 			const XAP_StringSet * pSS = XAP_App::getApp()->getStringSet();
761 			UT_UTF8String msg (pSS->getValue(XAP_STRING_ID_MSG_ParagraphsImported));
762 			UT_UTF8String msg2;
763 			UT_UTF8String_sprintf(msg2," %d",m_iStruxCount);
764 			msg += msg2;
765 			pBar->setStatusMessage(static_cast<const gchar *>(msg.utf8_str()));
766 			pBar->setStatusProgressValue(m_iStruxCount);
767 		}
768 	}
769 }
770 
_importFile(GsfInput * input,int ieft,bool markClean,bool bImportStylesFirst,bool bIsImportFile,const char * impProps)771 UT_Error PD_Document::_importFile(GsfInput * input, int ieft,
772 								  bool markClean, bool bImportStylesFirst,
773 								  bool bIsImportFile, const char* impProps)
774 {
775 	if (!input)
776 	{
777 		UT_DEBUGMSG(("PD_Document::importFile -- invalid filename\n"));
778 		return UT_INVALIDFILENAME;
779 	}
780 
781 	const char * szFilename = gsf_input_name (input);
782 	XAP_Frame * pFrame = XAP_App::getApp()->getLastFocussedFrame();
783 	if(pFrame)
784 	{
785 		pFrame->nullUpdate();
786 	}
787     AP_StatusBar * pStatusBar = getStatusBar();
788 	if(pFrame && pStatusBar)
789 	{
790 		//
791 		// Show a pulsing status bar since we don't know how big the document
792 		// is.
793 		//
794 		pStatusBar->setStatusProgressType(0,100,PROGRESS_INDEFINATE);
795 		pStatusBar->showProgressBar();
796 		pFrame->nullUpdate();
797 	}
798 
799 	m_pPieceTable = new pt_PieceTable(this);
800 	if (!m_pPieceTable)
801 	{
802 		UT_DEBUGMSG(("PD_Document::importFile -- could not construct piece table\n"));
803 		return UT_NOPIECETABLE;
804 	}
805 
806 	m_bLoading = true;
807 	m_pPieceTable->setPieceTableState(PTS_Loading);
808 
809 	UT_Error errorCode;
810 
811     UT_DEBUGMSG(("PD_Document::_importFile... name:%s\n",szFilename));
812     errorCode = m_hDocumentRDF->setupWithPieceTable();
813     if( errorCode != UT_OK )
814     {
815         return errorCode;
816     }
817     // m_hDocumentRDF->runMilestone2Test();
818 
819 	if (bImportStylesFirst) {
820 		std::string template_list[6];
821 
822 		buildTemplateList (template_list, "normal.awt");
823 
824 		bool success = false;
825 		for (UT_uint32 i = 0; i < 6 && !success; i++)
826 			success = (importStyles(template_list[i].c_str(), ieft, true) == UT_OK);
827 
828 		// don't worry if this fails
829 	}
830 
831 
832 	// set standard document properties and attributes, such as dtd,
833 	// lang, dom-dir, etc., which the importer can then overwite this
834 	// also initializes m_indexAP
835 	m_indexAP = 0xffffffff;
836 	setAttrProp(NULL);
837 
838 	if(bIsImportFile)
839 		{
840 			IEFileType savedAsType;
841 			errorCode = IE_Imp::loadFile (this, input, static_cast<IEFileType>(ieft), impProps, &savedAsType);
842 		}
843 	else
844 		{
845 			errorCode = IE_Imp::loadFile(this, input, static_cast<IEFileType>(ieft), impProps, &m_lastOpenedType);
846 			_syncFileTypes(false);
847 
848 			if (!getFilename())
849 				_setFilename(g_strdup(szFilename));
850 		}
851 
852 	if (!UT_IS_IE_SUCCESS(errorCode))
853 	{
854 		UT_DEBUGMSG(("PD_Document::importFile -- could not import file\n"));
855 		DELETEP(m_pPieceTable);
856 		return errorCode;
857 	}
858 	repairDoc();
859 
860 	m_bLoading = false;
861 
862 	setLastOpenedTime(time(NULL));
863 
864 	// get document-wide settings from the AP
865 	const PP_AttrProp * pAP = getAttrProp();
866 
867 	if(pAP)
868 	{
869 		const gchar * pA = NULL;
870 
871 		// TODO this should probably be stored as an attribute of the
872 		// styles section rather then the whole doc ...
873 		if(pAP->getAttribute("styles", pA))
874 		{
875 			m_bLockedStyles = !(strcmp(pA, "locked"));
876 		}
877 
878 		if(pAP->getAttribute("xid-max", pA))
879 		{
880 			UT_uint32 i = (UT_uint32)atoi(pA);
881 			m_pPieceTable->setXIDThreshold(i);
882 		}
883 	}
884 
885 	m_pPieceTable->setPieceTableState(PTS_Editing);
886 	updateFields();
887 
888 	if(markClean)
889 		_setClean();
890 	else
891 	  	_setForceDirty(true); // force this to be dirty
892 	//	m_pPieceTable->getFragments().verifyDoc();
893 
894 
895 	// show warning if document contains revisions and they are hidden
896 	// from view ...
897 	bool bHidden = (isMarkRevisions() && (getHighestRevisionId() <= getShowRevisionId()));
898 	bHidden |= (!isMarkRevisions() && !isShowRevisions() && getRevisions().getItemCount());
899 
900 	// note: the GsfInput could be a memory stream, and thus we have could have no filename yet
901 	if(pFrame && szFilename && (strstr(szFilename, "normal.awt") == NULL))
902 		XAP_App::getApp()->getPrefs()->addRecent(szFilename);
903 
904 	if(pFrame && bHidden)
905 	{
906 		pFrame->showMessageBox(AP_STRING_ID_MSG_HiddenRevisions,
907 						       XAP_Dialog_MessageBox::b_O,
908 							   XAP_Dialog_MessageBox::a_OK);
909 	}
910 	UT_ASSERT(isOrigUUID());
911 	if(pFrame && pStatusBar)
912 	{
913 		pStatusBar->hideProgressBar();
914 		pFrame->nullUpdate();
915 	}
916 
917 	return errorCode;
918 }
919 
getStatusBar(void)920 AP_StatusBar *  PD_Document::getStatusBar(void)
921 {
922 	XAP_Frame * pFrame = XAP_App::getApp()->getLastFocussedFrame();
923 	if(pFrame)
924 	{
925 		AP_FrameData * pData =  static_cast<AP_FrameData *>(pFrame->getFrameData());
926 		if(pData)
927 			return static_cast<AP_StatusBar *>(pData->m_pStatusBar);
928 	}
929 	return NULL;
930 }
931 
createRawDocument(void)932 UT_Error PD_Document::createRawDocument(void)
933 {
934 	m_pPieceTable = new pt_PieceTable(this);
935 	if (!m_pPieceTable)
936 	{
937 		UT_DEBUGMSG(("PD_Document::readFromFile -- could not construct piece table\n"));
938 		return UT_NOPIECETABLE;
939 	}
940 
941 	m_pPieceTable->setPieceTableState(PTS_Loading);
942 
943 	{
944 		std::string template_list[6];
945 
946 		buildTemplateList (template_list, "normal.awt");
947 
948 		bool success = false;
949 		int ieft = IEFT_Unknown;
950 		for (UT_uint32 i = 0; i < 6 && !success; i++)
951 			success = (importStyles(template_list[i].c_str(), ieft, true) == UT_OK);
952 
953 		// don't worry if this fails
954 	}
955 
956 	// set standard document properties and attributes, such as dtd, lang,
957 	// dom-dir, etc., which the importer can then overwite
958 	// this also initializes m_indexAP
959 	m_indexAP = 0xffffffff;
960 	setAttrProp(NULL);
961 	UT_ASSERT(isOrigUUID());
962 
963     UT_Error errorCode = m_hDocumentRDF->setupWithPieceTable();
964     if( errorCode != UT_OK )
965     {
966         return errorCode;
967     }
968 
969 	return UT_OK;
970 }
971 
finishRawCreation(void)972 void PD_Document::finishRawCreation(void)
973 {
974 	repairDoc();
975 	m_pPieceTable->setPieceTableState(PTS_Editing);
976 	updateFields();
977 	_setClean();							// mark the document as not-dirty
978 	//	m_pPieceTable->getFragments().verifyDoc();
979 }
980 
readFromFile(const char * szFilename,int ieft,const char * impProps)981 UT_Error PD_Document::readFromFile(const char * szFilename, int ieft,
982 								   const char * impProps)
983 {
984 	return _importFile(szFilename, ieft, true, true, false, impProps);
985 }
986 
readFromFile(GsfInput * input,int ieft,const char * impProps)987 UT_Error PD_Document::readFromFile(GsfInput *input, int ieft,
988 								   const char * impProps)
989 {
990 	return _importFile(input, ieft, true, true, false, impProps);
991 }
992 
importStyles(const char * szFilename,int ieft,bool bDocProps)993 UT_Error PD_Document::importStyles(const char * szFilename, int ieft, bool bDocProps)
994 {
995 	if (!szFilename || !*szFilename)
996 	{
997 		UT_DEBUGMSG(("PD_Document::importStyles -- invalid filename\n"));
998 		return UT_INVALIDFILENAME;
999 	}
1000 
1001 	if ( !UT_isRegularFile(szFilename) )
1002 	{
1003 	  UT_DEBUGMSG (("PD_Document::importStyles -- file is not plain file\n"));
1004 	  return UT_INVALIDFILENAME;
1005 	}
1006 
1007 	if (!m_pPieceTable)
1008 	{
1009 		UT_DEBUGMSG(("PD_Document::importStyles -- could not construct piece table\n"));
1010 		return UT_NOPIECETABLE;
1011 	}
1012 
1013 	IE_Imp * pie = NULL;
1014 	UT_Error errorCode;
1015 
1016 	// don't use IE_Imp::loadFile () here, because of the setLoadStylesOnly below. it doesn't do us much good anyway
1017 	errorCode = IE_Imp::constructImporter(this, szFilename, static_cast<IEFileType>(ieft), &pie);
1018 	if (errorCode)
1019 	{
1020 		UT_DEBUGMSG(("PD_Document::importStyles -- could not construct importer\n"));
1021 		return errorCode;
1022 	}
1023 
1024 	if(!pie->supportsLoadStylesOnly())
1025 	{
1026 		UT_DEBUGMSG(("PD_Document::importStyles -- import of styles-only not supported\n"));
1027 		return UT_IE_IMPSTYLEUNSUPPORTED;
1028 	}
1029 
1030 	pie->setLoadStylesOnly(true);
1031 	pie->setLoadDocProps(bDocProps);
1032 	errorCode = pie->importFile(szFilename);
1033 	delete pie;
1034 
1035 	if (errorCode)
1036 	{
1037 		UT_DEBUGMSG(("PD_Document::importStyles -- could not import file\n"));
1038 		return errorCode;
1039 	}
1040 
1041 	// need to update anything that uses styles ...
1042 	//
1043 	// this is rather cumbersome, but did not see a simpler way of
1044 	// doing this (perhaps we should consider some way of invalidating
1045 	// styles: a style could carry a time stamp and each element would
1046 	// also carry a timestamp reflecting when its atributes were last
1047 	// refreshed; in this case if style stamp > element stamp, element
1048 	// would reformat) Tomas, June 7, 2003
1049 
1050 	UT_GenericVector<PD_Style*> vStyles;
1051 	getAllUsedStyles(&vStyles);
1052 	for(UT_sint32 i = 0; i < vStyles.getItemCount();i++)
1053 	{
1054 		PD_Style * pStyle = vStyles.getNthItem(i);
1055 
1056 		if(pStyle)
1057 			updateDocForStyleChange(pStyle->getName(),!pStyle->isCharStyle());
1058 	}
1059 
1060 	return UT_OK;
1061 }
1062 
newDocument(void)1063 UT_Error PD_Document::newDocument(void)
1064 {
1065 	std::string template_list[6];
1066 
1067 	buildTemplateList(template_list, "normal.awt");
1068 
1069 	bool success = false;
1070 
1071 	for (UT_uint32 i = 0; i < 6 && !success; i++)
1072 		success = (importFile (template_list[i].c_str(), IEFT_Unknown, true, false) == UT_OK);
1073 
1074 	if (!success) {
1075 			m_pPieceTable = new pt_PieceTable(this);
1076 			if (!m_pPieceTable)
1077 				return UT_NOPIECETABLE;
1078 
1079 			m_pPieceTable->setPieceTableState(PTS_Loading);
1080 
1081 			// add just enough structure to empty document so we can edit
1082 			appendStrux(PTX_Section,NULL);
1083 			appendStrux(PTX_Block, NULL);
1084 
1085 			// set standard document properties, such as dtd, lang,
1086 			// dom-dir, etc. (some of the code that used to be here is
1087 			// now in the setAttrProp() function, since it is shared
1088 			// both by new documents and documents being loaded from disk
1089 			// this also initializes m_indexAP
1090 			m_indexAP = 0xffffffff;
1091 			setAttrProp(NULL);
1092 
1093 			m_pPieceTable->setPieceTableState(PTS_Editing);
1094 	}
1095 	//	m_pPieceTable->getFragments().verifyDoc();
1096 
1097 	setDocVersion(0);
1098 	setEditTime(0);
1099 	setLastOpenedTime(time(NULL));
1100 
1101 	// set document metadata from context
1102 	setMetaDataProp(PD_META_KEY_CREATOR, m_sUserName);
1103 
1104 	// mark the document as not-dirty
1105 	_setClean();
1106 	UT_ASSERT(isOrigUUID());
1107 
1108 	return UT_OK;
1109 }
1110 
saveAs(GsfOutput * output,int ieft,bool cpy,const char * expProps)1111 UT_Error PD_Document::saveAs(GsfOutput *output, int ieft, bool cpy, const char * expProps)
1112 {
1113 	return _saveAs(output, ieft, cpy, expProps);
1114 }
1115 
_saveAs(const char * szFilename,int ieft,const char * expProps)1116 UT_Error PD_Document::_saveAs(const char * szFilename, int ieft,
1117 							 const char * expProps)
1118 {
1119   return _saveAs(szFilename, ieft, true, expProps);
1120 }
1121 
_saveAs(const char * szFilename,int ieft,bool cpy,const char * expProps)1122 UT_Error PD_Document::_saveAs(const char * szFilename, int ieft, bool cpy,
1123 							  const char * expProps)
1124 {
1125 	IE_Exp * pie = NULL;
1126 	UT_Error errorCode;
1127 	IEFileType newFileType;
1128 
1129 	errorCode = IE_Exp::constructExporter(this, szFilename, static_cast<IEFileType>(ieft), &pie, &newFileType);
1130 	if (errorCode)
1131 	{
1132 		UT_DEBUGMSG(("PD_Document::Save -- could not construct exporter\n"));
1133 		return UT_SAVE_EXPORTERROR;
1134 	}
1135 	if (expProps && strlen(expProps))
1136 		pie->setProps (expProps);
1137 
1138 	if (cpy && !XAP_App::getApp()->getPrefs()->isIgnoreRecent())
1139 	{
1140 		m_lastSavedAsType = newFileType;
1141 		_syncFileTypes(true);
1142 	}
1143 	if(!XAP_App::getApp()->getPrefs()->isIgnoreRecent())
1144 	{
1145 		// order of these calls matters
1146 		_adjustHistoryOnSave();
1147 
1148 		// see if revisions table is still needed ...
1149 		purgeRevisionTable();
1150 	}
1151 
1152 	errorCode = pie->writeFile(szFilename);
1153 	delete pie;
1154 
1155 	if (errorCode)
1156 	{
1157 		UT_DEBUGMSG(("PD_Document::Save -- could not write file\n"));
1158 		return (errorCode == UT_SAVE_CANCELLED) ? UT_SAVE_CANCELLED : UT_SAVE_WRITEERROR;
1159 	}
1160 
1161 	if (cpy && !XAP_App::getApp()->getPrefs()->isIgnoreRecent()) // we want to make the current settings persistent
1162 	{
1163 	    // no file name currently set - make this filename the filename
1164 	    // stored for the doc
1165 	    char * szFilenameCopy = NULL;
1166 	    if (!(szFilenameCopy = g_strdup(szFilename)))
1167 			return UT_SAVE_OTHERERROR;
1168 	    _setFilename(szFilenameCopy);
1169 	    _setClean(); // only mark as clean if we're saving under a new name
1170 		signalListeners(PD_SIGNAL_DOCNAME_CHANGED);
1171 	}
1172 
1173 	if (szFilename)
1174 		XAP_App::getApp()->getPrefs()->addRecent(szFilename);
1175 
1176 	return UT_OK;
1177 }
1178 
_saveAs(GsfOutput * output,int ieft,bool cpy,const char * expProps)1179 UT_Error PD_Document::_saveAs(GsfOutput *output, int ieft, bool cpy, const char * expProps)
1180 {
1181 	UT_return_val_if_fail(output, UT_SAVE_NAMEERROR);
1182 
1183 	const char * szFilename = gsf_output_name(output);
1184 
1185 	IE_Exp * pie = NULL;
1186 	UT_Error errorCode;
1187 	IEFileType newFileType;
1188 
1189 	errorCode = IE_Exp::constructExporter(this, output, static_cast<IEFileType>(ieft), &pie, &newFileType);
1190 	if (errorCode)
1191 	{
1192 		UT_DEBUGMSG(("PD_Document::Save -- could not construct exporter\n"));
1193 		return UT_SAVE_EXPORTERROR;
1194 	}
1195 	if (expProps && strlen(expProps))
1196 		pie->setProps (expProps);
1197 
1198 	if (cpy && !XAP_App::getApp()->getPrefs()->isIgnoreRecent())
1199 	{
1200 		m_lastSavedAsType = newFileType;
1201 		_syncFileTypes(true);
1202 	}
1203 	if(!XAP_App::getApp()->getPrefs()->isIgnoreRecent())
1204 	{
1205 		// order of these calls matters
1206 		_adjustHistoryOnSave();
1207 
1208 		// see if revisions table is still needed ...
1209 		purgeRevisionTable();
1210 	}
1211 
1212 	errorCode = pie->writeFile(output);
1213 	delete pie;
1214 
1215 	if (errorCode)
1216 	{
1217 		UT_DEBUGMSG(("PD_Document::Save -- could not write file\n"));
1218 		return (errorCode == UT_SAVE_CANCELLED) ? UT_SAVE_CANCELLED : UT_SAVE_WRITEERROR;
1219 	}
1220 
1221 	if (cpy && !XAP_App::getApp()->getPrefs()->isIgnoreRecent()) // we want to make the current settings persistent
1222 	{
1223 	    // no file name currently set - make this filename the filename
1224 	    // stored for the doc
1225 	    char * szFilenameCopy = NULL;
1226 	    if (!(szFilenameCopy = g_strdup(szFilename)))
1227 			return UT_SAVE_OTHERERROR;
1228 	    _setFilename(szFilenameCopy);
1229 	    _setClean(); // only mark as clean if we're saving under a new name
1230 		signalListeners(PD_SIGNAL_DOCNAME_CHANGED);
1231 	}
1232 
1233 	if (szFilename)
1234 		XAP_App::getApp()->getPrefs()->addRecent(szFilename);
1235 
1236 	return UT_OK;
1237 }
1238 
_save(void)1239 UT_Error PD_Document::_save(void)
1240 {
1241 	if (!getFilename() || !*getFilename())
1242 		return UT_SAVE_NAMEERROR;
1243 	if (m_lastSavedAsType == IEFT_Unknown)
1244 		return UT_EXTENSIONERROR;
1245 
1246 	IE_Exp * pie = NULL;
1247 	UT_Error errorCode;
1248 
1249 	errorCode = IE_Exp::constructExporter(this,getFilename(),m_lastSavedAsType,&pie);
1250 	if (errorCode)
1251 	{
1252 		UT_DEBUGMSG(("PD_Document::Save -- could not construct exporter\n"));
1253 		return UT_SAVE_EXPORTERROR;
1254 	}
1255 
1256 	_syncFileTypes(true);
1257 
1258 	_adjustHistoryOnSave();
1259 
1260 	// see if revisions table is still needed ...
1261 	purgeRevisionTable();
1262 
1263 	errorCode = pie->writeFile(getFilename());
1264 	delete pie;
1265 	if (errorCode)
1266 	{
1267 		UT_DEBUGMSG(("PD_Document::Save -- could not write file\n"));
1268 		return (errorCode == UT_SAVE_CANCELLED) ? UT_SAVE_CANCELLED : UT_SAVE_WRITEERROR;
1269 	}
1270 
1271 	_setClean();
1272 	return UT_OK;
1273 }
1274 
1275 //////////////////////////////////////////////////////////////////
1276 //////////////////////////////////////////////////////////////////
1277 
isDirty(void) const1278 bool PD_Document::isDirty(void) const
1279 {
1280 	return m_pPieceTable->isDirty() || isForcedDirty();
1281 }
1282 
_setClean(void)1283 void PD_Document::_setClean(void)
1284 {
1285 	m_pPieceTable->setClean();
1286 	_setForceDirty(false);
1287 }
1288 
1289 //////////////////////////////////////////////////////////////////
1290 //////////////////////////////////////////////////////////////////
1291 
insertObject(PT_DocPosition dpos,PTObjectType pto,const gchar ** attributes,const gchar ** properties)1292 bool	PD_Document::insertObject(PT_DocPosition dpos,
1293 								  PTObjectType pto,
1294 								  const gchar ** attributes,
1295 								  const gchar ** properties)
1296 {
1297 	if(isDoingTheDo())
1298 	{
1299 		return false;
1300 	}
1301 	const gchar ** szAttsAuthor = NULL;
1302 	std::string storage;
1303 	addAuthorAttributeIfBlank(attributes,szAttsAuthor,storage);
1304 	bool b = m_pPieceTable->insertObject(dpos, pto, szAttsAuthor, properties);
1305 	delete [] szAttsAuthor;
1306 	return b;
1307 }
1308 
insertObject(PT_DocPosition dpos,PTObjectType pto,const gchar ** attributes,const gchar ** properties,fd_Field ** pField)1309 bool	PD_Document::insertObject(PT_DocPosition dpos,
1310 								  PTObjectType pto,
1311 								  const gchar ** attributes,
1312 								  const gchar ** properties, fd_Field ** pField)
1313 {
1314 	if(isDoingTheDo())
1315 	{
1316 		return false;
1317 	}
1318 	pf_Frag_Object * pfo = NULL;
1319 	const gchar ** szAttsAuthor = NULL;
1320 	std::string storage;
1321 	addAuthorAttributeIfBlank(attributes,szAttsAuthor,storage);
1322 	bool b = m_pPieceTable->insertObject(dpos, pto, szAttsAuthor, properties, &pfo);
1323 	delete [] szAttsAuthor;
1324 	*pField = pfo->getField();
1325 	return b;
1326 }
1327 
insertSpan(PT_DocPosition dpos,const std::string & s,PP_AttrProp * p_AttrProp)1328 bool PD_Document::insertSpan( PT_DocPosition dpos,
1329                               const std::string& s,
1330                               PP_AttrProp *p_AttrProp )
1331 {
1332 	UT_UCS4String t( s );
1333     return insertSpan( dpos, t.ucs4_str(), t.length(), p_AttrProp );
1334 }
1335 
1336 
1337 /*!
1338  * Note that the text will be set to exactly the properties of given by
1339  *  p_AttrProp.
1340  * If pAttrProp is set to NULL, the text will be set to exactly
1341  * the properties of the style of the current paragraph.
1342  */
insertSpan(PT_DocPosition dpos,const UT_UCSChar * pbuf,UT_uint32 length,PP_AttrProp * p_AttrProp,UT_uint32 * insertedSpanLength)1343 bool PD_Document::insertSpan(PT_DocPosition dpos, const UT_UCSChar * pbuf,
1344 							 UT_uint32 length, PP_AttrProp *p_AttrProp,
1345 							 UT_uint32 *insertedSpanLength)
1346 {
1347 	if(isDoingTheDo())
1348 	{
1349 		return false;
1350 	}
1351 	addAuthorAttributeIfBlank(p_AttrProp);
1352 	if(p_AttrProp)
1353 	{
1354 		m_pPieceTable->insertFmtMark(PTC_SetExactly, dpos, p_AttrProp);
1355 	}
1356 #if DEBUG
1357 #if 1
1358 	UT_uint32 ii = 0;
1359 	std::string sStr;
1360 	for(ii=0; ii<length;ii++)
1361 	{
1362 		sStr += static_cast<const char>(pbuf[ii]);
1363 	}
1364 	UT_DEBUGMSG(("PD_Document Insert span |%s| pos %d \n",sStr.c_str(),dpos));
1365 #endif
1366 #endif
1367 	// REMOVE UNDESIRABLE CHARACTERS ...
1368 	// we will remove all LRO, RLO, LRE, RLE, and PDF characters
1369 	// * at the moment we do not handle LRE/RLE
1370 	// * we replace LRO/RLO with our dir-override property
1371 
1372 	PT_DocPosition cur_pos = dpos;
1373 	PP_AttrProp AP;
1374 
1375 	// we want to reset m_iLastDirMarker (which is in a state left
1376 	// over from the last insert/append operation)
1377 	m_iLastDirMarker = 0;
1378 
1379 	bool result = true;
1380 	const UT_UCS4Char * pStart = pbuf;
1381 	UT_sint32 newLength = length;
1382 
1383 	for(const UT_UCS4Char * p = pbuf; p < pbuf + length; p++)
1384 	{
1385 		switch(*p)
1386 		{
1387 			case UCS_LRO:
1388 				if((p - pStart) > 0)
1389 				{
1390 					result &= m_pPieceTable->insertSpan(cur_pos, pStart, p - pStart);
1391 					cur_pos += p - pStart;
1392 				}
1393 
1394 				AP.setProperty("dir-override", "ltr");
1395 				result &= m_pPieceTable->insertFmtMark(PTC_AddFmt, cur_pos, &AP);
1396 				pStart = p + 1;
1397 				m_iLastDirMarker = *p;
1398 				newLength--;
1399 				break;
1400 
1401 			case UCS_RLO:
1402 				if((p - pStart) > 0)
1403 				{
1404 					result &= m_pPieceTable->insertSpan(cur_pos, pStart, p - pStart);
1405 					cur_pos += p - pStart;
1406 				}
1407 
1408 				AP.setProperty("dir-override", "rtl");
1409 				result &= m_pPieceTable->insertFmtMark(PTC_AddFmt, cur_pos, &AP);
1410 				pStart = p + 1;
1411 				m_iLastDirMarker = *p;
1412 				newLength--;
1413 				break;
1414 
1415 			case UCS_PDF:
1416 				if((p - pStart) > 0)
1417 				{
1418 					result &= m_pPieceTable->insertSpan(cur_pos, pStart, p - pStart);
1419 					cur_pos += p - pStart;
1420 				}
1421 
1422 				if((m_iLastDirMarker == UCS_RLO) || (m_iLastDirMarker == UCS_LRO))
1423 				{
1424 					AP.setProperty("dir-override", "");
1425 					result &= m_pPieceTable->insertFmtMark(PTC_RemoveFmt, cur_pos, &AP);
1426 				}
1427 
1428 				pStart = p + 1;
1429 				m_iLastDirMarker = *p;
1430 				newLength--;
1431 				break;
1432 
1433 			case UCS_LRE:
1434 			case UCS_RLE:
1435 				if((p - pStart) > 0)
1436 				{
1437 					result &= m_pPieceTable->insertSpan(cur_pos, pStart, p - pStart);
1438 					cur_pos += p - pStart;
1439 				}
1440 
1441 				pStart = p + 1;
1442 				m_iLastDirMarker = *p;
1443 				newLength--;
1444 				break;
1445 		}
1446 	}
1447 
1448 	// A length of zero can occur if one of the already-handled characters
1449 	// in the above switch comprises the entire span.
1450 	if((length - (pStart - pbuf)) > 0)
1451 		result &= m_pPieceTable->insertSpan(cur_pos, pStart, length - (pStart - pbuf));
1452 
1453 	if (insertedSpanLength)
1454 	{
1455 		*insertedSpanLength = (newLength >= 0) ? newLength:0;
1456 	}
1457 	return result;
1458 }
1459 
deleteSpan(PT_DocPosition dpos1,PT_DocPosition dpos2,PP_AttrProp * p_AttrProp_Before,UT_uint32 & iRealDeleteCount,bool bDeleteTableStruxes)1460 bool PD_Document::deleteSpan(PT_DocPosition dpos1,
1461 							 PT_DocPosition dpos2,
1462 							 PP_AttrProp *p_AttrProp_Before,
1463 							 UT_uint32 &iRealDeleteCount,
1464 							 bool bDeleteTableStruxes)
1465 {
1466 	if(isDoingTheDo())
1467 	{
1468 		return false;
1469 	}
1470 	return m_pPieceTable->deleteSpanWithTable(dpos1, dpos2, p_AttrProp_Before,iRealDeleteCount, bDeleteTableStruxes );
1471 }
1472 
changeSpanFmt(PTChangeFmt ptc,PT_DocPosition dpos1,PT_DocPosition dpos2,const gchar ** attributes,const gchar ** properties)1473 bool PD_Document::changeSpanFmt(PTChangeFmt ptc,
1474 								PT_DocPosition dpos1,
1475 								PT_DocPosition dpos2,
1476 								const gchar ** attributes,
1477 								const gchar ** properties)
1478 {
1479 	if(isDoingTheDo())
1480 	{
1481 		return false;
1482 	}
1483 	bool f;
1484 	deferNotifications();
1485 	const gchar ** szAttsAuthor = NULL;
1486 	std::string storage;
1487 	addAuthorAttributeIfBlank(attributes,szAttsAuthor,storage);
1488 	f = m_pPieceTable->changeSpanFmt(ptc,dpos1,dpos2,szAttsAuthor,properties);
1489 	delete [] szAttsAuthor;
1490 	processDeferredNotifications();
1491 	return f;
1492 }
1493 
1494 
1495 
insertStrux(PT_DocPosition dpos,PTStruxType pts,pf_Frag_Strux ** ppfs_ret)1496 bool PD_Document::insertStrux(PT_DocPosition dpos,
1497 							  PTStruxType pts, pf_Frag_Strux ** ppfs_ret)
1498 {
1499 	if(isDoingTheDo())
1500 	{
1501 		return false;
1502 	}
1503 	return m_pPieceTable->insertStrux(dpos,pts,ppfs_ret);
1504 }
1505 
1506 
insertStrux(PT_DocPosition dpos,PTStruxType pts,const gchar ** attributes,const gchar ** properties,pf_Frag_Strux ** ppfs_ret)1507 bool PD_Document::insertStrux(PT_DocPosition dpos,
1508 							  PTStruxType pts,
1509 							  const gchar ** attributes,
1510 							  const gchar ** properties, pf_Frag_Strux ** ppfs_ret)
1511 {
1512 	if(isDoingTheDo())
1513 	{
1514 		return false;
1515 	}
1516 	const gchar ** szAttsAuthor = NULL;
1517 	std::string storage;
1518 	addAuthorAttributeIfBlank(attributes,szAttsAuthor,storage);
1519 	bool b =  m_pPieceTable->insertStrux(dpos,pts,szAttsAuthor,properties,ppfs_ret);
1520 	delete [] szAttsAuthor;
1521 	return b;
1522 }
1523 
1524 
1525 /*!
1526  * This method deletes the HdrFtr strux pointed to by sdh
1527  */
deleteHdrFtrStrux(pf_Frag_Strux * sdh)1528 void PD_Document::deleteHdrFtrStrux(pf_Frag_Strux* sdh)
1529 {
1530 	pf_Frag_Strux * pfs_hdrftr = sdh;
1531 	UT_return_if_fail (pfs_hdrftr->getType()  == pf_Frag::PFT_Strux);
1532 	m_pPieceTable->deleteHdrFtrStrux(pfs_hdrftr);
1533 }
1534 
changeStruxFmt(PTChangeFmt ptc,PT_DocPosition dpos1,PT_DocPosition dpos2,const gchar ** attributes,const gchar ** properties,PTStruxType pts)1535 bool PD_Document::changeStruxFmt(PTChangeFmt ptc,
1536 								 PT_DocPosition dpos1,
1537 								 PT_DocPosition dpos2,
1538 								 const gchar ** attributes,
1539 								 const gchar ** properties,
1540 								 PTStruxType pts)
1541 {
1542 	if(isDoingTheDo())
1543 	{
1544 		return false;
1545 	}
1546 	return m_pPieceTable->changeStruxFmt(ptc,dpos1,dpos2,attributes,properties,pts);
1547 }
1548 
1549 
changeStruxFmtNoUndo(PTChangeFmt ptc,pf_Frag_Strux * sdh,const gchar ** attributes,const gchar ** properties)1550 bool PD_Document::changeStruxFmtNoUndo(PTChangeFmt ptc,
1551 								 pf_Frag_Strux* sdh,
1552 								 const gchar ** attributes,
1553 								 const gchar ** properties)
1554 {
1555 	pf_Frag_Strux * pfs = sdh;
1556 	UT_return_val_if_fail (pfs->getType() == pf_Frag::PFT_Strux,false);
1557 	return m_pPieceTable->changeStruxFmtNoUndo(ptc,pfs,attributes,properties);
1558 }
1559 
1560 
1561 /*!
1562  * This method changes *all* the strux fragments in the specified region.
1563  */
changeStruxFmt(PTChangeFmt ptc,PT_DocPosition dpos1,PT_DocPosition dpos2,const gchar ** attributes,const gchar ** properties)1564 bool PD_Document::changeStruxFmt(PTChangeFmt ptc,
1565 								 PT_DocPosition dpos1,
1566 								 PT_DocPosition dpos2,
1567 								 const gchar ** attributes,
1568 								 const gchar ** properties)
1569 {
1570 	if(isDoingTheDo())
1571 	{
1572 		return false;
1573 	}
1574 	return m_pPieceTable->changeStruxFmt(ptc,dpos1,dpos2,attributes,properties);
1575 }
1576 
1577 /*!
1578  * This Method is used to change just the parentID of each strux in a list
1579  * without updating the fl_Layouts.
1580  */
changeStruxForLists(pf_Frag_Strux * sdh,const char * pszParentID)1581 bool PD_Document::changeStruxForLists(pf_Frag_Strux* sdh, const char * pszParentID)
1582 {
1583 	return m_pPieceTable->changeStruxForLists(sdh, pszParentID);
1584 }
1585 
insertFmtMark(PTChangeFmt ptc,PT_DocPosition dpos,PP_AttrProp * p_AttrProp)1586 bool PD_Document::insertFmtMark(PTChangeFmt ptc, PT_DocPosition dpos, PP_AttrProp *p_AttrProp)
1587 {
1588 	if(isDoingTheDo())
1589 	{
1590 		return false;
1591 	}
1592 	return m_pPieceTable->insertFmtMark(ptc, dpos, p_AttrProp);
1593 }
1594 
deleteFmtMark(PT_DocPosition dpos)1595 bool  PD_Document::deleteFmtMark( PT_DocPosition dpos)
1596 {
1597 	return m_pPieceTable->deleteFmtMark(dpos);
1598 }
1599 //////////////////////////////////////////////////////////////////
1600 //////////////////////////////////////////////////////////////////
1601 
appendStrux(PTStruxType pts,const gchar ** attributes,pf_Frag_Strux ** ppfs_ret)1602 bool PD_Document::appendStrux(PTStruxType pts, const gchar ** attributes, pf_Frag_Strux ** ppfs_ret)
1603 {
1604 	UT_return_val_if_fail (m_pPieceTable, false);
1605 
1606 	// can only be used while loading the document
1607 //
1608 // Update frames during load.
1609 //
1610 	if(pts == PTX_EndCell)
1611 	{
1612 		checkForSuspect();
1613 	}
1614 	else if(pts == PTX_Section)
1615 	{
1616 		checkForSuspect();
1617 	}
1618 	updateStatus();
1619 	return m_pPieceTable->appendStrux(pts,attributes,ppfs_ret);
1620 }
1621 
1622 /*!
1623     appends given fmt to the last strux in document
1624 */
appendLastStruxFmt(PTStruxType pts,const gchar ** attributes,const gchar ** props,bool bSkipEmbededSections)1625 bool PD_Document::appendLastStruxFmt(PTStruxType pts, const gchar ** attributes, const gchar ** props,
1626 									 bool bSkipEmbededSections)
1627 {
1628 	UT_return_val_if_fail (m_pPieceTable, false);
1629 	updateStatus();
1630 	return m_pPieceTable->appendLastStruxFmt(pts,attributes,props,bSkipEmbededSections);
1631 }
1632 
appendLastStruxFmt(PTStruxType pts,const gchar ** attributes,const gchar * props,bool bSkipEmbededSections)1633 bool PD_Document::appendLastStruxFmt(PTStruxType pts, const gchar ** attributes, const gchar * props,
1634 									 bool bSkipEmbededSections)
1635 {
1636 	UT_return_val_if_fail (m_pPieceTable, false);
1637 	updateStatus();
1638 	return m_pPieceTable->appendLastStruxFmt(pts,attributes,props,bSkipEmbededSections);
1639 }
1640 
changeLastStruxFmtNoUndo(PT_DocPosition dpos,PTStruxType pts,const gchar ** attributes,const gchar ** props,bool bSkipEmbededSections)1641 bool PD_Document::changeLastStruxFmtNoUndo(PT_DocPosition dpos, PTStruxType pts,
1642 									 const gchar ** attributes, const gchar ** props,
1643 									 bool bSkipEmbededSections)
1644 {
1645 	UT_return_val_if_fail (m_pPieceTable, false);
1646 
1647 	return m_pPieceTable->changeLastStruxFmtNoUndo(dpos, pts,attributes,props,bSkipEmbededSections);
1648 }
1649 
changeLastStruxFmtNoUndo(PT_DocPosition dpos,PTStruxType pts,const gchar ** attributes,const gchar * props,bool bSkipEmbededSections)1650 bool PD_Document::changeLastStruxFmtNoUndo(PT_DocPosition dpos, PTStruxType pts,
1651 										   const gchar ** attributes, const gchar * props,
1652 									 bool bSkipEmbededSections)
1653 {
1654 	UT_return_val_if_fail (m_pPieceTable, false);
1655 
1656 	return m_pPieceTable->changeLastStruxFmtNoUndo(dpos, pts,attributes,props,bSkipEmbededSections);
1657 }
1658 
appendStruxFmt(pf_Frag_Strux * pfs,const gchar ** attributes)1659 bool PD_Document::appendStruxFmt(pf_Frag_Strux * pfs, const gchar ** attributes)
1660 {
1661 	UT_return_val_if_fail (m_pPieceTable, false);
1662 	updateStatus();
1663 	return m_pPieceTable->appendStruxFmt(pfs,attributes);
1664 }
1665 
1666 /*!
1667  * Scan the vector of suspect frags and add blocks if they're needed.
1668  * Returns true if there are changes to the document.
1669  */
repairDoc(void)1670 bool PD_Document::repairDoc(void)
1671 {
1672 	pf_Frag * pf = NULL;
1673 	pf_Frag_Strux * pfs = NULL;
1674 	bool bRepaired = false;
1675 	//
1676 	// First check there is *some* content.
1677 	//
1678 	pf = m_pPieceTable->getFragments().getFirst();
1679 	if(!pf)
1680 	{
1681 		appendStrux(PTX_Section, NULL);
1682 		appendStrux(PTX_Block,NULL);
1683 		return true;
1684 	}
1685 	// Now check if the document starts with a section
1686 	pf = m_pPieceTable->getFragments().getFirst();
1687 	if(pf->getType() != pf_Frag::PFT_Strux)
1688 	{
1689 		insertStruxBeforeFrag(pf, PTX_Section,NULL);
1690 		insertStruxBeforeFrag(pf, PTX_Block,NULL);
1691 		bRepaired = true;
1692 	}
1693 	pf = m_pPieceTable->getFragments().getFirst();
1694 	pfs = static_cast<pf_Frag_Strux *>(pf);
1695 	if(pfs->getStruxType() != PTX_Section)
1696 	{
1697 		insertStruxBeforeFrag(pf, PTX_Section,NULL);
1698 		insertStruxBeforeFrag(pf, PTX_Block,NULL);
1699 		bRepaired = true;
1700 	}
1701 
1702 	checkForSuspect(); // Look at last frag. If it's an endtable we need a block
1703 	UT_sint32 i = 0;
1704 	for(i=0; i< m_vecSuspectFrags.getItemCount(); i++)
1705 	{
1706 		pf = m_vecSuspectFrags.getNthItem(i);
1707 		UT_DEBUGMSG(("Suspect frag %d pointer %p \n",i,pf));
1708 		if(pf->getType() == pf_Frag::PFT_Strux)
1709 		{
1710 			pfs = static_cast<pf_Frag_Strux *>(pf);
1711 			if((pfs->getStruxType() != PTX_Block) && (pfs->getStruxType() != PTX_EndFootnote) && (pfs->getStruxType() != PTX_EndEndnote)  && (pfs->getStruxType() != PTX_EndAnnotation) )
1712 			{
1713 				pf_Frag * pfNext = pf->getNext();
1714 				if(pfNext && ((pfNext->getType() ==  pf_Frag::PFT_Text) || (pfNext->getType() ==  pf_Frag::PFT_Object) || (pfNext->getType() ==  pf_Frag::PFT_FmtMark)))
1715 				{
1716 					//
1717 					// Insert a block afterwards!
1718 					//
1719 					insertStruxBeforeFrag(pfNext, PTX_Block,NULL);
1720 					bRepaired = true;
1721 				}
1722 				else if(pfNext && (pfs->getStruxType() == PTX_SectionCell) && (pfNext->getType() == pf_Frag::PFT_Strux) )
1723 				{
1724 					pf_Frag_Strux * pfNexts = static_cast<pf_Frag_Strux *>(pfNext);
1725 					if(pfNexts->getStruxType() == PTX_EndCell)
1726 					{
1727 					//
1728 					// Insert a block afterwards!
1729 					//
1730 						insertStruxBeforeFrag(pfNext, PTX_Block,NULL);
1731 						bRepaired = true;
1732 					}
1733 				}
1734 				else if(pfNext && (pfs->getStruxType() == PTX_EndTable) && ((pfNext->getType() == pf_Frag::PFT_Strux) || (pfNext == m_pPieceTable->getFragments().getLast())))
1735 			    {
1736 					if(pfNext == m_pPieceTable->getFragments().getLast())
1737 					{
1738 					//
1739 					// Insert a block afterwards!
1740 					//
1741 						insertStruxBeforeFrag(pfNext, PTX_Block,NULL);
1742 						bRepaired = true;
1743 					}
1744 					else
1745 					{
1746 						pf_Frag_Strux * pfNexts = static_cast<pf_Frag_Strux *>(pfNext);
1747 						if(pfNexts->getStruxType() == PTX_Section)
1748 						{
1749 							//
1750 							// Insert a block afterwards!
1751 							//
1752 							insertStruxBeforeFrag(pfNext, PTX_Block,NULL);
1753 							bRepaired = true;
1754 						}
1755 
1756 					}
1757 				}
1758 				else if(pfs->getStruxType() == PTX_EndTable && pfNext == NULL)
1759 				{
1760 					appendStrux(PTX_Block, NULL);
1761 				}
1762 			}
1763 		}
1764 	}
1765 	//
1766 	// Now Obtain a list of sections/headers and footers.
1767 	// remove unreferenced headers/footer
1768 	// Remove references to headers and footers in sections that are not
1769 	// Present.
1770 	// Remove repeated HdrFtr's
1771 	//
1772 	UT_GenericVector<pf_Frag_Strux *> vecSections;
1773 	UT_GenericVector<pf_Frag_Strux *> vecHdrFtrs;
1774 	UT_GenericVector<pf_Frag_Strux *> vecTables;
1775 	pf = m_pPieceTable->getFragments().getFirst();
1776 	while(pf)
1777 	{
1778 		if(pf->getType() == pf_Frag::PFT_Strux)
1779 		{
1780 			pfs = static_cast<pf_Frag_Strux *>(pf);
1781 			if(pfs->getStruxType() == PTX_Section)
1782 			{
1783 				vecSections.addItem(pfs);
1784 			}
1785 			else if(pfs->getStruxType() == PTX_SectionHdrFtr)
1786 			{
1787 				vecHdrFtrs.addItem(pfs);
1788 			}
1789 			else if(pfs->getStruxType() == PTX_SectionTable)
1790 			{
1791 				vecTables.addItem(pfs);
1792 			}
1793 			else if(pfs->getStruxType() == PTX_EndTable)
1794 			{
1795 				vecTables.addItem(pfs);
1796 			}
1797 		}
1798 		pf = pf->getNext();
1799 	}
1800 	//
1801 	// Look for bare tables struxes. Delete them if we find one
1802 	//
1803 	for(i=0; i< vecTables.getItemCount(); i++)
1804 	{
1805 		pfs = vecTables.getNthItem(i);
1806 		bRepaired = bRepaired | _checkAndFixTable(pfs);
1807 	}
1808 	//
1809 	// Fix section matching of HdrFtrs
1810 	//
1811 	for(i = 0; i< vecSections.getItemCount(); i++)
1812 	{
1813 		pfs = vecSections.getNthItem(i);
1814 		bRepaired = bRepaired | _pruneSectionAPI(pfs,"header",&vecHdrFtrs);
1815 		bRepaired = bRepaired | _pruneSectionAPI(pfs,"header-even",&vecHdrFtrs);
1816 		bRepaired = bRepaired | _pruneSectionAPI(pfs,"header-first",&vecHdrFtrs);
1817 		bRepaired = bRepaired | _pruneSectionAPI(pfs,"header-last",&vecHdrFtrs);
1818 		bRepaired = bRepaired | _pruneSectionAPI(pfs,"footer",&vecHdrFtrs);
1819 		bRepaired = bRepaired | _pruneSectionAPI(pfs,"footer-even",&vecHdrFtrs);
1820 		bRepaired = bRepaired | _pruneSectionAPI(pfs,"footer-first",&vecHdrFtrs);
1821 		bRepaired = bRepaired | _pruneSectionAPI(pfs,"footer-last",&vecHdrFtrs);
1822 	}
1823 	for(i = 0; i< vecHdrFtrs.getItemCount(); i++)
1824 	{
1825 		pfs = vecHdrFtrs.getNthItem(i);
1826 		if(!_matchSection(pfs,&vecSections))
1827 		{
1828 			//
1829 			// Now we have to delete this whole Header/Footer
1830 			//
1831 			_removeHdrFtr(pfs);
1832 			bRepaired = true;
1833 			vecHdrFtrs.deleteNthItem(i);
1834 			i--;
1835 		}
1836 	}
1837 	//
1838 	// Now remove repeated HdrFtr's ie Header/Footers with identical ID's
1839 	//
1840 	for(i = 0; i< vecHdrFtrs.getItemCount(); i++)
1841 	{
1842 		pfs = vecHdrFtrs.getNthItem(i);
1843 		if(!_removeRepeatedHdrFtr(pfs,&vecHdrFtrs,i+1))
1844 		{
1845 			bRepaired = true;
1846 		}
1847 	}
1848 	//
1849 	// Check that no section is empty. Add block if necessary
1850 	//
1851 	for(i = 0; i < vecSections.getItemCount(); i++)
1852 	{
1853 		pfs = vecSections.getNthItem(i);
1854 		pf_Frag * pfsNext = pfs->getNext();
1855 		if (!pfsNext)
1856 		{
1857 			appendStrux(PTX_Block,NULL);
1858 			bRepaired = true;
1859 		}
1860 		else if ((pfsNext->getType() == pf_Frag::PFT_Strux) &&
1861 				 (static_cast<pf_Frag_Strux *>(pfsNext)->getStruxType() != PTX_Block) &&
1862 				 (static_cast<pf_Frag_Strux *>(pfsNext)->getStruxType() != PTX_SectionTable))
1863 		{
1864 			insertStruxBeforeFrag(pfsNext, PTX_Block,NULL);
1865 			bRepaired = true;
1866 		}
1867 	}
1868 
1869 	for(i = 0; i < vecHdrFtrs.getItemCount(); i++)
1870 	{
1871 		pfs = vecHdrFtrs.getNthItem(i);
1872 		pf_Frag * pfsNext = pfs->getNext();
1873 		if (!pfsNext)
1874 		{
1875 			appendStrux(PTX_Block,NULL);
1876 			bRepaired = true;
1877 		}
1878 		else if ((pfsNext->getType() == pf_Frag::PFT_Strux) &&
1879 				 (static_cast<pf_Frag_Strux *>(pfsNext)->getStruxType() != PTX_Block) &&
1880 				 (static_cast<pf_Frag_Strux *>(pfsNext)->getStruxType() != PTX_SectionTable))
1881 		{
1882 			insertStruxBeforeFrag(pfsNext, PTX_Block,NULL);
1883 			bRepaired = true;
1884 		}
1885 	}
1886 
1887 	//
1888 	// Now repair text and objects which aren't enclosed in a paragraph
1889 	//
1890 	pf = m_pPieceTable->getFragments().getFirst();
1891 	bool bGotBlock = false;
1892 	while(pf)
1893 	{
1894 		if(pf->getType() == pf_Frag::PFT_Strux)
1895 		{
1896 			pfs = static_cast<pf_Frag_Strux *>(pf);
1897 			if((pfs->getStruxType() == PTX_Block) || (m_pPieceTable->isEndFootnote(pfs)))
1898 			{
1899 				bGotBlock = true;
1900 			}
1901 			else
1902 			{
1903 				bGotBlock = false;
1904 			}
1905 		}
1906 		else if(!bGotBlock && (pf->getType() !=  pf_Frag::PFT_EndOfDoc))
1907 		{
1908 			// BUG! Content not in a block. Insert one now
1909 			insertStruxBeforeFrag(pf, PTX_Block,NULL);
1910 			bGotBlock = true;
1911 			bRepaired = true;
1912 		}
1913 		pf = pf->getNext();
1914 	}
1915 	return !bRepaired;
1916 }
1917 
1918 /*!
1919  * Look for a Hdr/Ftr with exactly the same identification as that of the
1920  * input strux.
1921  * If we find a match delete the HdrFtr
1922  */
_removeRepeatedHdrFtr(pf_Frag_Strux * pfs,UT_GenericVector<pf_Frag_Strux * > * vecHdrFtrs,UT_sint32 iStart)1923 bool PD_Document::_removeRepeatedHdrFtr(pf_Frag_Strux * pfs ,UT_GenericVector<pf_Frag_Strux *> * vecHdrFtrs,UT_sint32 iStart)
1924 {
1925 	const char * pszMyHdrFtr = NULL;
1926 	const char * pszMyID = NULL;
1927 	const char * pszThisID = NULL;
1928 	const char * pszThisHdrFtr = NULL;
1929 	UT_sint32 i=0;
1930 	pf_Frag_Strux * pfsS = NULL;
1931 	getAttributeFromSDH(pfs,false,0,"type",&pszMyHdrFtr);
1932 	getAttributeFromSDH(pfs,false,0,"id",&pszMyID);
1933 	if(pszMyHdrFtr && *pszMyHdrFtr && pszMyID && *pszMyID)
1934 	{
1935 		for(i = iStart; i<vecHdrFtrs->getItemCount(); i++)
1936 		{
1937 			pfsS = vecHdrFtrs->getNthItem(i);
1938 			getAttributeFromSDH(pfsS,false,0,"type",&pszThisHdrFtr);
1939 			getAttributeFromSDH(pfsS,false,0,"id",&pszThisID);
1940 			if(pszThisHdrFtr && *pszThisHdrFtr && pszThisID && *pszThisID)
1941 			{
1942 				if((strcmp(pszMyHdrFtr,pszThisHdrFtr) == 0) &&
1943 				   (strcmp(pszMyID,pszThisID) == 0))
1944 				{
1945 					_removeHdrFtr(pfsS);
1946 					vecHdrFtrs->deleteNthItem(i);
1947 				}
1948 			}
1949 
1950 		}
1951 	}
1952 	return false;
1953 }
1954 
1955 
1956 /*!
1957  * This method looks to see if we have a table strux without a matching cell
1958  * or an endtable without a matching endcell preceding it
1959  * If we do it deletes the table/endtable.
1960  */
_checkAndFixTable(pf_Frag_Strux * pfs)1961 bool PD_Document::_checkAndFixTable(pf_Frag_Strux * pfs)
1962 {
1963 	pf_Frag * pf =NULL;
1964 	pf_Frag_Strux * pfsn = NULL;
1965 	if(pfs->getStruxType() == PTX_SectionTable)
1966 	{
1967 		pf = pfs->getNext();
1968 		if(!pf)
1969 		{
1970 			m_pPieceTable->deleteFragNoUpdate(pfs);
1971 			return true;
1972 		}
1973 		else if(pf->getType() != pf_Frag::PFT_Strux)
1974 		{
1975 			m_pPieceTable->deleteFragNoUpdate(pfs);
1976 			return true;
1977 		}
1978 		pfsn = static_cast<pf_Frag_Strux *>(pf);
1979 		if(pfsn->getStruxType() !=  PTX_SectionCell)
1980 		{
1981 			m_pPieceTable->deleteFragNoUpdate(pfs);
1982 			return true;
1983 		}
1984 	}
1985 	else if(pfs->getStruxType() == PTX_EndTable)
1986 	{
1987 		pf = pfs->getPrev();
1988 		if(!pf)
1989 		{
1990 			m_pPieceTable->deleteFragNoUpdate(pfs);
1991 			return true;
1992 		}
1993 		else if(pf->getType() != pf_Frag::PFT_Strux)
1994 		{
1995 			m_pPieceTable->deleteFragNoUpdate(pfs);
1996 			return true;
1997 		}
1998 		pfsn = static_cast<pf_Frag_Strux *>(pf);
1999 		if(pfsn->getStruxType() !=  PTX_EndCell)
2000 		{
2001 			m_pPieceTable->deleteFragNoUpdate(pfs);
2002 			return true;
2003 		}
2004 	}
2005 	return false;
2006 }
2007 
2008 
2009 /*!
2010  * This scans the HdrFtrs to see if there is a match for the header/footer type
2011  * in the section strux pfs.
2012  * Return true of a prune happened
2013  */
_pruneSectionAPI(pf_Frag_Strux * pfs,const char * szHType,UT_GenericVector<pf_Frag_Strux * > * vecHdrFtrs)2014 bool PD_Document::_pruneSectionAPI(pf_Frag_Strux * pfs,const char * szHType, UT_GenericVector<pf_Frag_Strux *> *vecHdrFtrs)
2015 {
2016 	const char * pszHdrFtr = NULL;
2017 	const char * pszHFID = NULL;
2018 	const char * pszID = NULL;
2019 	UT_sint32 i = 0;
2020 	getAttributeFromSDH(pfs,false,0,szHType,&pszID);
2021 	if(!pszID)
2022 		return false;
2023 	if(!(*pszID))
2024 		return false;
2025 	for(i= 0; i< vecHdrFtrs->getItemCount(); i++)
2026 	{
2027 		pf_Frag_Strux * pfsS = vecHdrFtrs->getNthItem(i);
2028 		getAttributeFromSDH(pfsS,false,0,"type",&pszHdrFtr);
2029 		if(pszHdrFtr && *pszHdrFtr)
2030 		{
2031 			if(strcmp(szHType,pszHdrFtr) == 0)
2032 			{
2033 				getAttributeFromSDH(pfsS,false,0,"id",&pszHFID);
2034 				if(pszHFID && *pszHFID)
2035 				{
2036 					if(strcmp(pszHFID,pszID) == 0)
2037 					{
2038 						return false;
2039 					}
2040 				}
2041 			}
2042 		}
2043 	}
2044 	//
2045 	// No matching HdrFtr was found. Remove the property.
2046 	//
2047 	const char * atts[3] = {szHType,pszID,NULL};
2048 	UT_DEBUGMSG(("Pruning HdrFtr %s ID %s From section \n",szHType,pszID));
2049 	m_pPieceTable->changeStruxFormatNoUpdate(PTC_RemoveFmt ,pfs,atts);
2050 	return true;
2051 }
2052 
2053 /*!
2054  * Remove the HdrFtr starting at pfs. No changeRecords are cre created.
2055  * Only used for document repair during import.
2056  */
_removeHdrFtr(pf_Frag_Strux * pfs)2057 bool PD_Document::_removeHdrFtr(pf_Frag_Strux * pfs)
2058 {
2059 	UT_DEBUGMSG(("Removing HdrFtr %p \n",pfs));
2060 	pf_Frag * pf = NULL;
2061 	pf_Frag * pfNext = NULL;
2062 	pfNext = pfs->getNext();
2063 	pf = static_cast<pf_Frag *>(pfs);
2064 	while(pf )
2065 	{
2066 		m_pPieceTable->deleteFragNoUpdate(pf);
2067 		pf = pfNext;
2068 		if(pf)
2069 		{
2070 			pfNext = pf->getNext();
2071 			if(pf->getType() == pf_Frag::PFT_Strux)
2072 			{
2073 				pfs = static_cast<pf_Frag_Strux *>(pf);
2074 				if(pfs->getStruxType() == PTX_SectionHdrFtr)
2075 					break;
2076 			}
2077 		}
2078 	}
2079 	return true;
2080 }
2081 
2082 /*!
2083  * pfs point to a header footer section. This Method returns true if there
2084  * is a section that has a reference to it's HdrFtr type and id
2085  */
_matchSection(pf_Frag_Strux * pfs,UT_GenericVector<pf_Frag_Strux * > * vecSections)2086 bool PD_Document::_matchSection(pf_Frag_Strux * pfs, UT_GenericVector<pf_Frag_Strux *> *vecSections)
2087 {
2088 	const char * pszHdrFtr = NULL;
2089 	const char * pszHFID = NULL;
2090 	const char * pszID = NULL;
2091 	UT_sint32 i = 0;
2092 	getAttributeFromSDH(pfs,false,0,"type",&pszHdrFtr);
2093 	if(!pszHdrFtr)
2094 		return false;
2095 	if(!(*pszHdrFtr))
2096 		return false;
2097 	getAttributeFromSDH(pfs,false,0,"id",&pszHFID);
2098 	if(!pszHFID)
2099 		return false;
2100 	if(!(*pszHFID))
2101 		return false;
2102 	for(i= 0; i< vecSections->getItemCount(); i++)
2103 	{
2104 		pf_Frag_Strux * pfsS = vecSections->getNthItem(i);
2105 		getAttributeFromSDH(pfsS,false,0,pszHdrFtr,&pszID);
2106 		if(pszID && *pszID)
2107 		{
2108 			if(strcmp(pszID,pszHFID) == 0)
2109 			{
2110 				return true;
2111 			}
2112 		}
2113 	}
2114 	return false;
2115 }
2116 
2117 /*!
2118  * This method is called after appendspan, appendObject, appendfmtMark and
2119  * checks to see if there is an invalid strux just before. If it see one, it
2120  * marks the strux as suspect for verification after the load is over.
2121  * Really useful for importers.
2122  */
checkForSuspect(void)2123 bool PD_Document::checkForSuspect(void)
2124 {
2125 	pf_Frag * pf = getLastFrag();
2126 	if(pf == NULL)
2127 	{
2128 		return true;
2129 	}
2130 	if(pf->getType() == pf_Frag::PFT_Strux)
2131 	{
2132 		pf_Frag_Strux * pfs = static_cast<pf_Frag_Strux *>(pf);
2133 		if((pfs->getStruxType() != PTX_Block) && (pfs->getStruxType() != PTX_EndFootnote) && (pfs->getStruxType() != PTX_EndEndnote) && (pfs->getStruxType() != PTX_EndAnnotation) )
2134 		{
2135 			//
2136 			// Append a block!
2137 			//
2138 			m_vecSuspectFrags.addItem(pf);
2139 			return true;
2140 		}
2141 
2142 	}
2143 	return true;
2144 }
2145 
appendFmt(const gchar ** attributes)2146 bool PD_Document::appendFmt(const gchar ** attributes)
2147 {
2148 	UT_return_val_if_fail (m_pPieceTable, false);
2149 	checkForSuspect();
2150 	// can only be used while loading the document
2151 	return m_pPieceTable->appendFmt(attributes);
2152 }
2153 
appendFmt(const UT_GenericVector<const gchar * > * pVecAttributes)2154 bool PD_Document::appendFmt(const UT_GenericVector<const gchar*> * pVecAttributes)
2155 {
2156 	UT_return_val_if_fail (m_pPieceTable, false);
2157 	checkForSuspect();
2158 
2159 	// can only be used while loading the document
2160 
2161 	return m_pPieceTable->appendFmt(pVecAttributes);
2162 }
2163 
appendSpan(const UT_UCSChar * pbuf,UT_uint32 length)2164 bool PD_Document::appendSpan(const UT_UCSChar * pbuf, UT_uint32 length)
2165 {
2166 	UT_return_val_if_fail (m_pPieceTable, false);
2167 	checkForSuspect();
2168 
2169 	// can only be used while loading the document
2170 
2171 	// REMOVE UNDESIRABLE CHARACTERS ...
2172 	// we will remove all LRO, RLO, LRE, RLE, and PDF characters
2173 	// * at the moment we do not handle LRE/RLE
2174 	// * we replace LRO/RLO with our dir-override property
2175 
2176 	const gchar * attrs[] = {"props", NULL, NULL};
2177 	UT_String s;
2178 
2179 	bool result = true;
2180 	const UT_UCS4Char * pStart = pbuf;
2181 
2182 	for(const UT_UCS4Char * p = pbuf; p < pbuf + length; p++)
2183 	{
2184 		switch(*p)
2185 		{
2186 			case UCS_LRO:
2187 				if((p - pStart) > 0)
2188 					result &= m_pPieceTable->appendSpan(pStart,p - pStart);
2189 
2190 				s = "dir-override:ltr";
2191 				attrs[1] = s.c_str();
2192 				result &= m_pPieceTable->appendFmt(&attrs[0]);
2193 				pStart = p + 1;
2194 				m_iLastDirMarker = *p;
2195 				break;
2196 
2197 			case UCS_RLO:
2198 				if((p - pStart) > 0)
2199 					result &= m_pPieceTable->appendSpan(pStart,p - pStart);
2200 
2201 				s = "dir-override:rtl";
2202 				attrs[1] = s.c_str();
2203 				result &= m_pPieceTable->appendFmt(&attrs[0]);
2204 
2205 				pStart = p + 1;
2206 				m_iLastDirMarker = *p;
2207 				break;
2208 
2209 			case UCS_PDF:
2210 				if((p - pStart) > 0)
2211 					result &= m_pPieceTable->appendSpan(pStart,p - pStart);
2212 
2213 				if((m_iLastDirMarker == UCS_RLO) || (m_iLastDirMarker == UCS_LRO))
2214 				{
2215 					s = "dir-override:";
2216 					attrs[1] = s.c_str();
2217 					result &= m_pPieceTable->appendFmt(&attrs[0]);
2218 				}
2219 
2220 				pStart = p + 1;
2221 				m_iLastDirMarker = *p;
2222 				break;
2223 
2224 			case UCS_LRE:
2225 			case UCS_RLE:
2226 				if((p - pStart) > 0)
2227 					result &= m_pPieceTable->appendSpan(pStart,p - pStart);
2228 
2229 				pStart = p + 1;
2230 				m_iLastDirMarker = *p;
2231 				break;
2232 		}
2233 	}
2234 
2235 	if(length - (pStart-pbuf))
2236 		{
2237 #if DEBUG
2238 #if 0
2239 	UT_uint32 ii = 0;
2240 	std::string sStr;
2241 	for(ii=0; ii<(length -(pStart-pbuf));ii++)
2242 	{
2243 		sStr += static_cast<const char>(pStart[ii]);
2244 	}
2245 	UT_DEBUGMSG(("Append span %s \n",sStr.c_str()));
2246 #endif
2247 #endif
2248 
2249 			result &= m_pPieceTable->appendSpan(pStart,length - (pStart-pbuf));
2250 		}
2251 	return result;
2252 }
2253 
appendObject(PTObjectType pto,const gchar ** attributes)2254 bool PD_Document::appendObject(PTObjectType pto, const gchar ** attributes)
2255 {
2256 	UT_return_val_if_fail (m_pPieceTable, false);
2257 	checkForSuspect();
2258 
2259 	// can only be used while loading the document
2260 
2261 	return m_pPieceTable->appendObject(pto,attributes);
2262 }
2263 
appendFmtMark(void)2264 bool PD_Document::appendFmtMark(void)
2265 {
2266 	UT_return_val_if_fail (m_pPieceTable, false);
2267 	checkForSuspect();
2268 
2269 	// can only be used while loading the document
2270 
2271 	return m_pPieceTable->appendFmtMark();
2272 }
2273 
2274 /*!
2275  * This method returns the value associated with attribute szAttribute
2276  * at picetable strux given by sdh.
2277  * NB: attributes and props are view-specific because of revision attributes
2278  *
2279  \param  pf_Frag_Strux* sdh (pf_Frag_Strux) where we want to find the value
2280  \param  bool bShowRevisions -- revisions setting for the view (FV_View::isShowRevisions())
2281  \param  UT_uint32 iRevisionLevel -- the revision level of the view (FV_View::getRevisionLevel())
2282  \param const char * szAttribute the attribute we're looking for.
2283  \param const char ** pszValue the value of the attribute.
2284  \returns true if the attribute was present at the sdh
2285 
2286  Don't FREEP *pszRetValue!!!
2287 */
getAttributeFromSDH(pf_Frag_Strux * sdh,bool bShowRevisions,UT_uint32 iRevisionLevel,const char * szAttribute,const char ** pszRetValue)2288 bool PD_Document::getAttributeFromSDH(pf_Frag_Strux* sdh, bool bShowRevisions, UT_uint32 iRevisionLevel,
2289 									  const char * szAttribute, const char ** pszRetValue)
2290 {
2291 	const pf_Frag_Strux * pfStrux = static_cast<const pf_Frag_Strux *>(sdh);
2292 	PT_AttrPropIndex indexAP = pfStrux->getIndexAP();
2293 	const PP_AttrProp * pAP = NULL;
2294 	const gchar * pszValue = NULL;
2295 
2296 	bool bHiddenRevision = false;
2297 	getAttrProp(indexAP, &pAP,NULL,bShowRevisions,iRevisionLevel,bHiddenRevision);
2298 
2299 	UT_return_val_if_fail (pAP, false);
2300 	(pAP)->getAttribute(szAttribute, pszValue);
2301 	if(pszValue == NULL)
2302 	{
2303 		*pszRetValue = NULL;
2304 		return false;
2305 	}
2306 	*pszRetValue = pszValue;
2307 	return true;
2308 }
2309 
2310 /*!
2311  * Get API fromthe supplied StruxDocHandle
2312  *
2313  * NB: this method does not take into account revisions settings; you either have to further process
2314  *     the AP at the index using the explodeRevisions() methods or you can retrieve specific props
2315  *     and attrs using getPropertyFromSDH() and getAttributeFromSDH().
2316  */
getAPIFromSDH(pf_Frag_Strux * sdh)2317 PT_AttrPropIndex PD_Document::getAPIFromSDH( pf_Frag_Strux* sdh)
2318 {
2319 	const pf_Frag_Strux * pfStrux = sdh;
2320 	return pfStrux->getIndexAP();
2321 }
2322 
2323 /*!
2324  * This method returns the value associated with attribute szProperty
2325  * at picetable strux given by sdh.
2326  * NB: attributes and props are view-specific because of revision attributes
2327  *
2328  \param  pf_Frag_Strux* sdh (pf_Frag_Strux) where we want to find the value
2329  \param  bool bShowRevisions -- revisions setting for the view (FV_View::isShowRevisions())
2330  \param  UT_uint32 iRevisionLevel -- the revision level of the view (FV_View::getRevisionLevel())
2331  \param const char * szProperty the Property we're looking for.
2332  \param const char ** pszValue the value of the property.
2333  \returns true if the property was present at the sdh
2334 
2335  Don't FREEP *pszRetValue!!!
2336 */
getPropertyFromSDH(const pf_Frag_Strux * sdh,bool bShowRevisions,UT_uint32 iRevisionLevel,const char * szProperty,const char ** pszRetValue) const2337 bool PD_Document::getPropertyFromSDH(const pf_Frag_Strux* sdh, bool bShowRevisions, UT_uint32 iRevisionLevel,
2338 									 const char * szProperty, const char ** pszRetValue) const
2339 {
2340 	const pf_Frag_Strux * pfStrux = static_cast<const pf_Frag_Strux *>(sdh);
2341 	PT_AttrPropIndex indexAP = pfStrux->getIndexAP();
2342 	const PP_AttrProp * pAP = NULL;
2343 	const gchar * pszValue = NULL;
2344 
2345 	bool bHiddenRevision = false;
2346 
2347 	getAttrProp(indexAP, &pAP,NULL,bShowRevisions,iRevisionLevel,bHiddenRevision);
2348 
2349 	UT_return_val_if_fail (pAP, false);
2350 	(pAP)->getProperty(szProperty, pszValue);
2351 
2352 	if(pszValue == NULL)
2353 	{
2354 		*pszRetValue = NULL;
2355 		return false;
2356 	}
2357 	*pszRetValue = pszValue;
2358 	return true;
2359 }
2360 
2361 /*!
2362  * This medthod modifies the attributes of a section strux without
2363  * generating a change record. Use with extreme care!!
2364  */
changeStruxAttsNoUpdate(pf_Frag_Strux * sdh,const char * attr,const char * attvalue)2365 bool  PD_Document::changeStruxAttsNoUpdate(pf_Frag_Strux* sdh, const char * attr, const char * attvalue)
2366 {
2367 	pf_Frag_Strux * pfStrux = sdh;
2368 	UT_return_val_if_fail (pfStrux, false);
2369 	return m_pPieceTable->changeSectionAttsNoUpdate(pfStrux, attr, attvalue);
2370 }
2371 
2372 
2373 /*!
2374  * This method inserts a strux of type pts immediately before the sdh given.
2375  * Attributes of the strux can be optionally passed. This method does not throw
2376  * a change record and should only be used under exceptional circumstances to
2377  * repair the piecetable during loading. It was necessary to import RTF tables.
2378  */
insertStruxNoUpdateBefore(pf_Frag_Strux * sdh,PTStruxType pts,const gchar ** attributes)2379 bool PD_Document::insertStruxNoUpdateBefore(pf_Frag_Strux* sdh, PTStruxType pts,const gchar ** attributes )
2380 {
2381 #if 0
2382 	pf_Frag_Strux * pfStrux = sdh;
2383 	T_ASSERT(pfStrux->getStruxType() != PTX_Section);
2384 #endif
2385 	return m_pPieceTable->insertStruxNoUpdateBefore(sdh, pts, attributes );
2386 }
2387 
2388 /*!
2389  * This method examines the frag immediately before the given sdh and decides
2390  * if it matches the strux type given.
2391  */
isStruxBeforeThis(pf_Frag_Strux * sdh,PTStruxType pts)2392 bool PD_Document::isStruxBeforeThis(pf_Frag_Strux* sdh,  PTStruxType pts)
2393 {
2394 	const pf_Frag_Strux * pfs = static_cast<const pf_Frag_Strux *>(sdh);
2395 	pf_Frag * pfb = pfs->getPrev();
2396 	if(pfb->getType() != pf_Frag::PFT_Strux)
2397 		return false;
2398 	pf_Frag_Strux * pfsb = static_cast<pf_Frag_Strux *>(pfb);
2399 	if(pfsb->getStruxType() == pts)
2400 		return true;
2401 	return false;
2402 }
2403 
2404  /*!
2405  * Create a changerecord object and broadcast it to all the listeners.
2406  * If bsave is true save the CR in th eunod stack.
2407  */
createAndSendCR(PT_DocPosition dpos,UT_sint32 iType,bool bSave,UT_Byte iGlob)2408 bool PD_Document::createAndSendCR(PT_DocPosition dpos, UT_sint32 iType,bool bSave,UT_Byte iGlob)
2409 {
2410 	return m_pPieceTable->createAndSendCR(dpos,iType,bSave,iGlob);
2411 }
2412 
2413 /*!
2414  * method used to import document property changes
2415  */
changeDocPropeties(const gchar ** pAtts,const gchar ** pProps)2416 bool PD_Document::changeDocPropeties(const gchar ** pAtts,const gchar ** pProps)
2417 {
2418 	PP_AttrProp  AP;
2419 	if(pAtts)
2420 		AP.setAttributes(pAtts);
2421 	if(pProps)
2422 		AP.setProperties(pProps);
2423 	const gchar * szValue=NULL;
2424 	bool b= AP.getAttribute( PT_DOCPROP_ATTRIBUTE_NAME,szValue);
2425 	if(!b || (szValue == NULL))
2426 		return false;
2427 	gchar * szLCValue = g_utf8_strdown (szValue, -1);
2428 	if(strcmp(szLCValue,"revision") == 0)
2429     {
2430 		const gchar * szID=NULL;
2431 		const gchar * szDesc=NULL;
2432 		const gchar * szTime=NULL;
2433 		const gchar * szVersion=NULL;
2434 		AP.getAttribute(PT_REVISION_ATTRIBUTE_NAME,szID);
2435 		AP.getAttribute(PT_REVISION_DESC_ATTRIBUTE_NAME,szDesc);
2436 		AP.getAttribute(PT_REVISION_TIME_ATTRIBUTE_NAME,szTime);
2437 		AP.getAttribute(PT_REVISION_VERSION_ATTRIBUTE_NAME,szVersion);
2438 		UT_DEBUGMSG(("Received revision ID %s szDesc %s time %s ver %s \n",szID,szDesc,szTime,szVersion));
2439 		UT_uint32 id = atoi(szID);
2440 		UT_UTF8String sDesc = szDesc;
2441 		time_t iTime = atoi(szTime);
2442 		UT_uint32 iVer = atoi(szVersion);
2443 		UT_UCS4Char * pD = NULL;
2444 		UT_uint32 iLen = sDesc.ucs4_str().size();
2445 		pD = new UT_UCS4Char [iLen+1];
2446 		UT_UCS4_strncpy(pD,sDesc.ucs4_str().ucs4_str(),iLen);
2447 		pD[iLen] = 0;
2448 		AD_Document::addRevision(id,pD,iTime,iVer, false);
2449 	}
2450 	else if(strcmp(szLCValue,"pagesize") == 0)
2451     {
2452 		UT_sint32 i = 0;
2453 		UT_DEBUGMSG(("pagesize docprop received \n"));
2454 		const gchar * szP = pProps[i];
2455 		while(szP != NULL)
2456 		{
2457 			UT_DEBUGMSG(("property %s value %s \n",pProps[i],pProps[i+1]));
2458 			i += 2;
2459 			szP = pProps[i];
2460 		}
2461 		setPageSizeFromFile(pProps);
2462 	}
2463 	else if(strcmp(szLCValue,"metadata") == 0)
2464     {
2465 		UT_sint32 i = 0;
2466 		UT_DEBUGMSG(("metadata docprop received \n"));
2467 		const gchar * szName = pProps[i];
2468 		while(szName != NULL)
2469 		{
2470 			szValue = pProps[i+1];
2471 			UT_DEBUGMSG(("property %s value %s \n",szName,szValue));
2472 			const std::string sName = szName;
2473 			const std::string sValue = szValue;
2474 			setMetaDataProp(sName,sValue);
2475 			i += 2;
2476 			szName = pProps[i];
2477 		}
2478 
2479 	}
2480 	else if(strcmp(szLCValue,"addauthor") == 0)
2481 	{
2482 		const gchar * szInt=NULL;
2483 		AP.getProperty("id",szInt);
2484 		UT_DEBUGMSG(("addauthor docprop CR received int %s \n",szInt));
2485 		if(szInt)
2486 		{
2487 			UT_sint32 iAuthor = atoi(szInt);
2488 			pp_Author * pA = addAuthor(iAuthor);
2489 			UT_uint32 j = 0;
2490 			const gchar * szName = NULL;
2491 			szValue = NULL;
2492 			PP_AttrProp * pAP = pA->getAttrProp();
2493 			while(AP.getNthProperty(j++,szName,szValue))
2494 			{
2495 				if(strcmp(szName,"id") == 0)
2496 					continue;
2497 				if(*szValue)
2498 					pAP->setProperty(szName,szValue);
2499 			}
2500 			sendAddAuthorCR(pA);
2501 		}
2502 	}
2503 	else if(strcmp(szLCValue,"changeauthor") == 0)
2504 	{
2505 		const gchar * szInt=NULL;
2506 		pp_Author * pA = NULL;
2507 		if(AP.getProperty("id",szInt) && szInt && *szInt)
2508 	    {
2509 			UT_sint32 iAuthor = atoi(szInt);
2510 			pA = getAuthorByInt(iAuthor);
2511 		}
2512 		if(pA)
2513 		{
2514 			PP_AttrProp * pAP = pA->getAttrProp();
2515 			UT_uint32 j = 0;
2516 			const gchar * szName = NULL;
2517 			while(AP.getNthProperty(j++,szName,szValue))
2518 			{
2519 				if(strcmp(szName,"id") == 0)
2520 					continue;
2521 				if(*szValue)
2522 					pAP->setProperty(szName,szValue);
2523 			}
2524 			sendChangeAuthorCR(pA);
2525 		}
2526 	}
2527 	g_free (szLCValue);
2528 	return true;
2529 }
2530 
2531 /*!
2532  * This method creates DocProp Change Record and broadcasts it to the listeners
2533  */
createAndSendDocPropCR(const gchar ** pAtts,const gchar ** pProps)2534 bool PD_Document::createAndSendDocPropCR( const gchar ** pAtts,const gchar ** pProps )
2535 {
2536 	return m_pPieceTable->createAndSendDocPropCR(pAtts,pProps);
2537 }
2538 
2539 /*!
2540  * This method deletes a strux of the type specified at the position
2541  * requested.
2542  * if bRecordChange is fale no change record is recorded.
2543  * This method was created soled for the use of AbiCollab.
2544   * Use with extreme care. Should only be needed by AbiCollab
2545  */
deleteStrux(PT_DocPosition dpos,PTStruxType,bool bRecordChange)2546 bool PD_Document::deleteStrux(PT_DocPosition dpos,
2547 							  PTStruxType /*pts*/,
2548 							  bool bRecordChange)
2549 {
2550 	PT_BlockOffset pOffset;
2551 	pf_Frag * pf = NULL;
2552 	m_pPieceTable->getFragFromPosition(dpos,&pf,&pOffset);
2553 	while(pf && pf->getLength() == 0)
2554 		pf = pf->getPrev();
2555 	if(pf == NULL)
2556 		return false;
2557 	pf_Frag_Strux* sdh = NULL;
2558 	if(pf->getType() == pf_Frag::PFT_Strux)
2559 	{
2560 		pf_Frag_Strux * pfs = static_cast<pf_Frag_Strux *>(pf);
2561 		sdh = static_cast<pf_Frag_Strux*>(pfs);
2562 	}
2563 	else
2564 	{
2565 		return false;
2566 	}
2567 	if(!bRecordChange)
2568 	{
2569 		return m_pPieceTable->deleteStruxNoUpdate(sdh);
2570 	}
2571 	if(getStruxPosition(sdh) != dpos)
2572 		return false;
2573 	return m_pPieceTable->deleteStruxWithNotify(sdh);
2574 }
2575 
2576 /*!
2577  * This method deletes a strux without throwing a change record.
2578  * sdh is the StruxDocHandle that gets deleted..
2579  * Use with extreme care. Should only be used for document import.
2580  */
deleteStruxNoUpdate(pf_Frag_Strux * sdh)2581 bool PD_Document::deleteStruxNoUpdate(pf_Frag_Strux* sdh)
2582 {
2583 	return m_pPieceTable->deleteStruxNoUpdate(sdh);
2584 }
2585 
2586 /*!
2587  * This method deletes a frag without throwing a change record.
2588  * pf is the frag that gets deleted..
2589  * Use with extreme care. Should only be used for document import.
2590  */
deleteFragNoUpdate(pf_Frag * pf)2591 bool PD_Document::deleteFragNoUpdate(pf_Frag * pf)
2592 {
2593 	return m_pPieceTable->deleteFragNoUpdate(pf);
2594 }
2595 
2596 /*!
2597  * Returns true if it is legal to insert a hperlink at this position. Looks to see if there is
2598  * An open hyperlink or annotation upstream.
2599  */
isInsertHyperLinkValid(PT_DocPosition pos) const2600 bool   PD_Document::isInsertHyperLinkValid(PT_DocPosition pos) const
2601 {
2602 	PT_BlockOffset pOffset;
2603 	pf_Frag * pf = NULL;
2604 	m_pPieceTable->getFragFromPosition(pos,&pf,&pOffset);
2605 	while(pf && (pf->getType() != pf_Frag::PFT_Strux) )
2606 	{
2607 		if(pf->getType() == pf_Frag::PFT_Object)
2608 		{
2609 			pf_Frag_Object * pfo = static_cast<pf_Frag_Object *>(pf);
2610 			if((pfo->getObjectType() != PTO_Hyperlink)
2611                && (pfo->getObjectType() != PTO_Annotation)
2612                && (pfo->getObjectType() != PTO_RDFAnchor) )
2613 			{
2614 				pf = pf->getPrev();
2615 			}
2616 			else
2617 			{
2618 				PT_AttrPropIndex iAP = pf->	getIndexAP();
2619 				const PP_AttrProp * pAP = NULL;
2620 				m_pPieceTable->getAttrProp(iAP,&pAP);
2621 				UT_return_val_if_fail (pAP, false);
2622 				const gchar * pszXlink = NULL;
2623 				(pAP)->getAttribute(PT_HYPERLINK_TARGET_NAME,pszXlink);
2624 				if(pszXlink)
2625 				{
2626 					return false;
2627 				}
2628 				(pAP)->getAttribute(PT_ANNOTATION_NUMBER,pszXlink);
2629 				if(pszXlink)
2630 				{
2631 					return false;
2632 				}
2633 				(pAP)->getAttribute(PT_RDF_XMLID,pszXlink);
2634 				if(pszXlink)
2635 				{
2636 					return false;
2637 				}
2638 				return true;
2639 			}
2640 		}
2641 		else
2642 		{
2643 			pf = pf->getPrev();
2644 		}
2645 	}
2646 	if(!pf)
2647 		return false;
2648 	if(pf->getType() == pf_Frag::PFT_Strux)
2649 	{
2650 		pf_Frag_Strux * pfs = static_cast<pf_Frag_Strux *>(pf);
2651 		if(pfs->getStruxType() == PTX_Block)
2652 		{
2653 			return true;
2654 		}
2655 	}
2656 	return false;
2657 }
2658 /*!
2659  * This method returns the last pf_Frag_Strux as a pf_Frag_Strux* before the end of the piecetable.
2660  */
getLastSectionSDH(void) const2661 const pf_Frag_Strux*  PD_Document::getLastSectionSDH(void) const
2662 {
2663 	const pf_Frag * currentFrag = m_pPieceTable->getFragments().getFirst();
2664 	const pf_Frag_Strux * pfSecLast = NULL;
2665 	while (currentFrag!=m_pPieceTable->getFragments().getLast())
2666 	{
2667 		UT_return_val_if_fail (currentFrag,0);
2668 		if(currentFrag->getType()  == pf_Frag::PFT_Strux)
2669 		{
2670 		     const pf_Frag_Strux * pfSec = static_cast<const pf_Frag_Strux *>(currentFrag);
2671 		     if(pfSec->getStruxType() == PTX_Section)
2672 		     {
2673 				 pfSecLast = pfSec;
2674 			 }
2675 		}
2676 		currentFrag = currentFrag->getNext();
2677 	}
2678 	return pfSecLast;
2679 }
2680 
2681 /*!
2682  * This method returns the last pf_Frag_Strux as a pf_Frag_Strux* before the end of the piecetable.
2683  */
getLastSectionMutableSDH(void)2684 pf_Frag_Strux*  PD_Document::getLastSectionMutableSDH(void)
2685 {
2686 	pf_Frag * currentFrag = m_pPieceTable->getFragments().getFirst();
2687 	pf_Frag_Strux * pfSecLast = NULL;
2688 	while (currentFrag!=m_pPieceTable->getFragments().getLast())
2689 	{
2690 		UT_return_val_if_fail (currentFrag,0);
2691 		if(currentFrag->getType()  == pf_Frag::PFT_Strux)
2692 		{
2693 		     pf_Frag_Strux * pfSec = static_cast<pf_Frag_Strux *>(currentFrag);
2694 		     if(pfSec->getStruxType() == PTX_Section)
2695 		     {
2696 				 pfSecLast = pfSec;
2697 			 }
2698 		}
2699 		currentFrag = currentFrag->getNext();
2700 	}
2701 	return pfSecLast;
2702 }
2703 
2704 
2705 /*!
2706  * This method returns the last pf_Frag_Strux as a pf_Frag_Strux*
2707  * before the end of the piecetable.
2708  */
getLastStruxOfType(PTStruxType pts)2709 pf_Frag_Strux*  PD_Document::getLastStruxOfType(PTStruxType pts )
2710 {
2711 	pf_Frag * currentFrag = m_pPieceTable->getFragments().getLast();
2712 	pf_Frag_Strux * pfSecLast = NULL;
2713 	bool bFound = false;
2714 	UT_sint32 nest = 0;
2715 	pf_Frag_Strux * pfSec = NULL;
2716 	if(pts == PTX_SectionTable)
2717 		nest = 1;
2718 	if(currentFrag->getType()  == pf_Frag::PFT_Strux)
2719 	{
2720 		pfSec = static_cast<pf_Frag_Strux *>(currentFrag);
2721 		if(pfSec->getStruxType() == PTX_EndTable)
2722 			nest--;
2723 	}
2724 	while (!bFound && currentFrag!=m_pPieceTable->getFragments().getFirst())
2725 	{
2726 		UT_return_val_if_fail (currentFrag,0);
2727 		if(currentFrag->getType()  == pf_Frag::PFT_Strux)
2728 		{
2729 		     pfSec = static_cast<pf_Frag_Strux *>(currentFrag);
2730 			 if(pts != PTX_EndTable)
2731 			 {
2732 				 if(pfSec->getStruxType() == PTX_EndTable)
2733 					 nest++;
2734 				 if(pfSec->getStruxType() == PTX_SectionTable)
2735  					 nest--;
2736 			 }
2737 		     if((pfSec->getStruxType() == pts) && (nest == 0))
2738 		     {
2739 				 pfSecLast = pfSec;
2740 				 bFound = true;
2741 			 }
2742 		}
2743 		currentFrag = currentFrag->getPrev();
2744 	}
2745 	return pfSecLast;
2746 }
2747 
2748 
2749 /*!
2750  * This method scans the document to check that the id of a header/footer
2751  *  section actually exists in a section somewhere in the document.
2752  */
verifySectionID(const gchar * pszId)2753 bool PD_Document::verifySectionID(const gchar * pszId)
2754 {
2755 	pf_Frag * currentFrag = m_pPieceTable->getFragments().getFirst();
2756 	while (currentFrag!=m_pPieceTable->getFragments().getLast())
2757 	{
2758 		UT_return_val_if_fail (currentFrag,0);
2759 		PT_AttrPropIndex indexAP = 0;
2760 		if(currentFrag->getType()  == pf_Frag::PFT_Strux)
2761 		{
2762 		     pf_Frag_Strux * pfSec = static_cast<pf_Frag_Strux *>(currentFrag);
2763 		     if(pfSec->getStruxType() == PTX_Section)
2764 		     {
2765 				 indexAP = static_cast<pf_Frag_Text *>(currentFrag)->getIndexAP();
2766 				 const PP_AttrProp * pAP = NULL;
2767 				 m_pPieceTable->getAttrProp(indexAP,&pAP);
2768 				 UT_return_val_if_fail (pAP,false);
2769 				 const gchar * pszIDName = NULL;
2770 				 (pAP)->getAttribute("header", pszIDName);
2771 				 if(pszIDName && strcmp(pszIDName,pszId) == 0)
2772 					 return true;
2773 				 (pAP)->getAttribute("header-first", pszIDName);
2774 				 if(pszIDName && strcmp(pszIDName,pszId) == 0)
2775 					 return true;
2776 				 (pAP)->getAttribute("header-last", pszIDName);
2777 				 if(pszIDName && strcmp(pszIDName,pszId) == 0)
2778 					 return true;
2779 				 (pAP)->getAttribute("header-even", pszIDName);
2780 				 if(pszIDName && strcmp(pszIDName,pszId) == 0)
2781 					 return true;
2782 				 (pAP)->getAttribute("footer", pszIDName);
2783 				 if(pszIDName && strcmp(pszIDName,pszId) == 0)
2784 					 return true;
2785 				 (pAP)->getAttribute("footer-first", pszIDName);
2786 				 if(pszIDName && strcmp(pszIDName,pszId) == 0)
2787 					 return true;
2788 				 (pAP)->getAttribute("footer-last", pszIDName);
2789 				 if(pszIDName && strcmp(pszIDName,pszId) == 0)
2790 					 return true;
2791 				 (pAP)->getAttribute("footer-even", pszIDName);
2792 				 if(pszIDName && strcmp(pszIDName,pszId) == 0)
2793 					 return true;
2794 
2795 				 // the id could also be hidden in a revision attribute ...
2796 				 const gchar * pszRevisionAttr = NULL;
2797 
2798 				 if((pAP)->getAttribute("revision", pszRevisionAttr))
2799 				 {
2800 					 PP_RevisionAttr RA(pszRevisionAttr);
2801 
2802 					 for(UT_uint32 i = 0; i < RA.getRevisionsCount(); ++i)
2803 					 {
2804 						 const PP_Revision * pRev = RA.getNthRevision(i);
2805 						 if(!pRev)
2806 						 {
2807 							 UT_ASSERT_HARMLESS( UT_SHOULD_NOT_HAPPEN );
2808 							 continue;
2809 						 }
2810 
2811 						 (pRev)->getAttribute("header", pszIDName);
2812 						 if(pszIDName && strcmp(pszIDName,pszId) == 0)
2813 							 return true;
2814 						 (pRev)->getAttribute("header-first", pszIDName);
2815 						 if(pszIDName && strcmp(pszIDName,pszId) == 0)
2816 							 return true;
2817 						 (pRev)->getAttribute("header-last", pszIDName);
2818 						 if(pszIDName && strcmp(pszIDName,pszId) == 0)
2819 							 return true;
2820 						 (pRev)->getAttribute("header-even", pszIDName);
2821 						 if(pszIDName && strcmp(pszIDName,pszId) == 0)
2822 							 return true;
2823 						 (pRev)->getAttribute("footer", pszIDName);
2824 						 if(pszIDName && strcmp(pszIDName,pszId) == 0)
2825 							 return true;
2826 						 (pRev)->getAttribute("footer-first", pszIDName);
2827 						 if(pszIDName && strcmp(pszIDName,pszId) == 0)
2828 							 return true;
2829 						 (pRev)->getAttribute("footer-last", pszIDName);
2830 						 if(pszIDName && strcmp(pszIDName,pszId) == 0)
2831 							 return true;
2832 						 (pRev)->getAttribute("footer-even", pszIDName);
2833 						 if(pszIDName && strcmp(pszIDName,pszId) == 0)
2834 							 return true;
2835 					 }
2836 				 }
2837 		     }
2838 		}
2839 //
2840 // Get Next frag in the table.
2841 //
2842 		currentFrag = currentFrag->getNext();
2843 	}
2844 	return false;
2845 }
2846 
2847 
2848 
2849 /*!
2850  * This method scans the document to look for a HdrFtr strux.
2851 \param const char * pszHdrFtr The particular attribute that identifies the
2852                                strux as "header" "footer" "header-even" etc.
2853 \param const char * pszHdrFtrID the unique string to match with Docsection.
2854 \returns a pf_Frag_Strux* of the matching frag or NULL if none found.
2855  */
findHdrFtrStrux(const gchar * pszHdrFtr,const gchar * pszHdrFtrID)2856 pf_Frag_Strux* PD_Document::findHdrFtrStrux(const gchar * pszHdrFtr,
2857 											const gchar * pszHdrFtrID)
2858 {
2859 	pf_Frag * currentFrag = m_pPieceTable->getFragments().getFirst();
2860 	while (currentFrag!=m_pPieceTable->getFragments().getLast())
2861 	{
2862 		UT_return_val_if_fail (currentFrag,0);
2863 		PT_AttrPropIndex indexAP = 0;
2864 		if(currentFrag->getType()  == pf_Frag::PFT_Strux)
2865 		{
2866 		     pf_Frag_Strux * pfSec = static_cast<pf_Frag_Strux *>(currentFrag);
2867 		     if(pfSec->getStruxType() == PTX_SectionHdrFtr)
2868 		     {
2869 				 indexAP = pfSec->getIndexAP();
2870 				 const PP_AttrProp * pAP = NULL;
2871 				 m_pPieceTable->getAttrProp(indexAP,&pAP);
2872 				 UT_return_val_if_fail (pAP, NULL);
2873 				 const gchar * pszIDName = NULL;
2874 				 const gchar * pszHeaderName = NULL;
2875 				 (pAP)->getAttribute(PT_TYPE_ATTRIBUTE_NAME, pszHeaderName);
2876 				 (pAP)->getAttribute(PT_ID_ATTRIBUTE_NAME, pszIDName);
2877 				 if(pszIDName && pszHeaderName && (strcmp(pszIDName,pszHdrFtrID) == 0) && (strcmp(pszHeaderName,pszHdrFtr) == 0))
2878 					 return static_cast<pf_Frag_Strux*>(pfSec) ;
2879 			 }
2880 		}
2881 //
2882 // Get Next frag in the table.
2883 //
2884 		currentFrag = currentFrag->getNext();
2885 	}
2886 	return NULL;
2887 }
2888 
2889 
2890 /*!
2891  * This method returns the offset to a an embedded strux
2892  * And a pointer to the embedded strux found.
2893  * If no emebedded strux is found in the block we return -1 ans NULL
2894  */
getEmbeddedOffset(pf_Frag_Strux * sdh,PT_DocPosition posoff,pf_Frag_Strux * & sdhEmbedded)2895 UT_sint32 PD_Document::getEmbeddedOffset(pf_Frag_Strux* sdh, PT_DocPosition posoff, pf_Frag_Strux* & sdhEmbedded)
2896 {
2897 	pf_Frag_Strux * pfs = sdh;
2898 	UT_return_val_if_fail (pfs->getStruxType() == PTX_Block,-1);
2899 	pf_Frag * pf = pfs;
2900 	pf = pf->getNext();
2901 	PT_DocPosition pos = m_pPieceTable->getStruxPosition(sdh) + posoff;
2902 	while(pf && m_pPieceTable->getFragPosition(pf) + pf->getLength() <= pos)
2903 	{
2904 		pf = pf->getNext();
2905 	}
2906 	if(pf == NULL)
2907 	{
2908 		sdhEmbedded = NULL;
2909 		return -1;
2910 	}
2911 	while(pf && pf->getType() != pf_Frag::PFT_Strux)
2912 	{
2913 		pf = pf ->getNext();
2914 	}
2915 	if(pf == NULL)
2916 	{
2917 		sdhEmbedded = NULL;
2918 		return -1;
2919 	}
2920 	if(!m_pPieceTable->isFootnote(pf))
2921     {
2922 		sdhEmbedded = NULL;
2923 		return -1;
2924 	}
2925 	pf_Frag_Strux * pfsNew = static_cast<pf_Frag_Strux *>(pf);
2926 	pos  = m_pPieceTable->getFragPosition(pf);
2927 	UT_sint32 diff = static_cast<UT_sint32>(pos) - static_cast<UT_sint32>(m_pPieceTable->getFragPosition(pfs));
2928 	sdhEmbedded = pfsNew;
2929 	return diff;
2930 }
2931 
hasEmbedStruxOfTypeInRange(PT_DocPosition posStart,PT_DocPosition posEnd,PTStruxType iType) const2932 bool PD_Document::hasEmbedStruxOfTypeInRange(PT_DocPosition posStart, PT_DocPosition posEnd,
2933 											 PTStruxType iType) const
2934 {
2935 	UT_return_val_if_fail(posStart < posEnd,false);
2936 	if ((iType != PTX_SectionFootnote) && (iType != PTX_SectionEndnote) &&
2937 		(iType != PTX_SectionAnnotation))
2938 	{
2939 		UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
2940 		return false;
2941 	}
2942 	return m_pPieceTable->hasEmbedStruxOfTypeInRange(posStart,posEnd,iType);
2943 }
2944 
2945 
2946 /*!
2947  * This method returns true if there is a Footnote strux at exactly this
2948  * position.
2949  */
isFootnoteAtPos(PT_DocPosition pos)2950 bool PD_Document::isFootnoteAtPos(PT_DocPosition pos)
2951 {
2952 	PT_BlockOffset pOffset;
2953 	pf_Frag * pf = NULL;
2954 	m_pPieceTable->getFragFromPosition(pos,&pf,&pOffset);
2955 	while(pf && (pf->getLength() == 0))
2956     {
2957 		pf = pf->getPrev();
2958     }
2959 	bool b = m_pPieceTable->isFootnote(pf);
2960 	if(b)
2961 	{
2962 		pf_Frag_Strux * pfs = static_cast<pf_Frag_Strux *>(pf);
2963 		if(pfs->getStruxType() == PTX_SectionTOC)
2964 		{
2965 			return false;
2966 		}
2967 	}
2968 	return b;
2969 }
2970 
2971 
2972 /*!
2973  * This method returns true if there is a TOC or endTOC strux at exactly this
2974  * position.
2975  */
isTOCAtPos(PT_DocPosition pos)2976 bool PD_Document::isTOCAtPos(PT_DocPosition pos)
2977 {
2978 	PT_BlockOffset pOffset;
2979 	pf_Frag * pf = NULL;
2980 	m_pPieceTable->getFragFromPosition(pos,&pf,&pOffset);
2981 	while(pf && (pf->getLength() == 0))
2982     {
2983 		pf = pf->getPrev();
2984     }
2985 	bool b = (pf && (pf->getType() == pf_Frag::PFT_Strux));
2986 	if(b)
2987 	{
2988 		pf_Frag_Strux * pfs = static_cast<pf_Frag_Strux *>(pf);
2989 		if(pfs->getStruxType() == PTX_SectionTOC)
2990 		{
2991 			return true;
2992 		}
2993 	}
2994 	if(b)
2995 	{
2996 		pf_Frag_Strux * pfs = static_cast<pf_Frag_Strux *>(pf);
2997 		if(pfs->getStruxType() == PTX_EndTOC)
2998 		{
2999 			return true;
3000 		}
3001 	}
3002 	return false;
3003 }
3004 
3005 
3006 /*!
3007  * This method returns true if there is an EndFootnote strux at exactly this
3008  * position.
3009  */
isEndFootnoteAtPos(PT_DocPosition pos)3010 bool PD_Document::isEndFootnoteAtPos(PT_DocPosition pos)
3011 {
3012 	PT_BlockOffset pOffset;
3013 	pf_Frag * pf = NULL;
3014 	/*bool bRes = */m_pPieceTable->getFragFromPosition(pos,&pf,&pOffset);
3015 	while(pf && (pf->getLength() == 0))
3016 	{
3017 		pf = pf->getPrev();
3018 	}
3019 	if(!pf || pf->getPos() < pos)
3020 	{
3021 		return false;
3022 	}
3023 	bool b = m_pPieceTable->isEndFootnote(pf);
3024 	if(b)
3025 	{
3026 		pf_Frag_Strux * pfs = static_cast<pf_Frag_Strux *>(pf);
3027 		if(pfs->getStruxType() == PTX_EndTOC)
3028 		{
3029 			return false;
3030 		}
3031 	}
3032 	return b;
3033 }
3034 
3035 
3036 /*!
3037  * This method returns true if there is a frame strux at exactly this
3038  * position.
3039  */
isFrameAtPos(PT_DocPosition pos)3040 bool PD_Document::isFrameAtPos(PT_DocPosition pos)
3041 {
3042 	PT_BlockOffset pOffset;
3043 	pf_Frag * pf = NULL;
3044 	/*bool bRes = */m_pPieceTable->getFragFromPosition(pos,&pf,&pOffset);
3045 	if(!pf)
3046 		return false;
3047 	while(pf && pf->getLength() == 0)
3048 		pf = pf->getPrev();
3049 	if(pf && pf->getType() == pf_Frag::PFT_Strux)
3050 	{
3051 		pf_Frag_Strux * pfs = static_cast<pf_Frag_Strux *>(pf);
3052 		if(pfs->getStruxType() == PTX_SectionFrame)
3053 		{
3054 			return true;
3055 		}
3056 	}
3057 	return false;
3058 }
3059 
3060 
3061 
3062 /*!
3063  * This method returns true if there is an endFrame strux at exactly this
3064  * position.
3065  */
isEndFrameAtPos(PT_DocPosition pos)3066 bool PD_Document::isEndFrameAtPos(PT_DocPosition pos)
3067 {
3068 	PT_BlockOffset pOffset;
3069 	pf_Frag * pf = NULL;
3070 	/*bool bRes = */m_pPieceTable->getFragFromPosition(pos,&pf,&pOffset);
3071 	if(!pf)
3072 		return false;
3073 	while(pf && pf->getLength() == 0)
3074 		pf = pf->getPrev();
3075 	if(pf && pf->getType() == pf_Frag::PFT_Strux)
3076 	{
3077 		pf_Frag_Strux * pfs = static_cast<pf_Frag_Strux *>(pf);
3078 		if(pfs->getStruxType() == PTX_EndFrame)
3079 		{
3080 			return true;
3081 		}
3082 	}
3083 	return false;
3084 }
3085 
3086 
3087 /*!
3088  * This method returns true if there is a HdrFtr strux at exactly this
3089  * position.
3090  */
isHdrFtrAtPos(PT_DocPosition pos)3091 bool PD_Document::isHdrFtrAtPos(PT_DocPosition pos)
3092 {
3093 	PT_BlockOffset pOffset;
3094 	pf_Frag * pf = NULL;
3095 	/*bool bRes = */m_pPieceTable->getFragFromPosition(pos,&pf,&pOffset);
3096 	if(!pf)
3097 		return false;
3098 	while(pf && pf->getLength() == 0)
3099 		pf = pf->getPrev();
3100 	if(pf && pf->getType() == pf_Frag::PFT_Strux)
3101 	{
3102 		pf_Frag_Strux * pfs = static_cast<pf_Frag_Strux *>(pf);
3103 		if(pfs->getStruxType() == PTX_SectionHdrFtr)
3104 		{
3105 			return true;
3106 		}
3107 	}
3108 	return false;
3109 }
3110 
3111 
3112 
3113 /*!
3114  * This method returns true if there is a Section strux at exactly this
3115  * position.
3116  */
isSectionAtPos(PT_DocPosition pos)3117 bool PD_Document::isSectionAtPos(PT_DocPosition pos)
3118 {
3119 	PT_BlockOffset pOffset;
3120 	pf_Frag * pf = NULL;
3121 	/*bool bRes = */m_pPieceTable->getFragFromPosition(pos,&pf,&pOffset);
3122 	if(!pf)
3123 		return false;
3124 	while(pf && pf->getLength() == 0)
3125 		pf = pf->getPrev();
3126 	if(pf && pf->getType() == pf_Frag::PFT_Strux)
3127 	{
3128 		pf_Frag_Strux * pfs = static_cast<pf_Frag_Strux *>(pf);
3129 		if(pfs->getStruxType() == PTX_Section)
3130 		{
3131 			return true;
3132 		}
3133 	}
3134 	return false;
3135 }
3136 
3137 
3138 /*!
3139  * This method returns true if there is a Block strux at exactly this
3140  * position.
3141  */
isBlockAtPos(PT_DocPosition pos)3142 bool PD_Document::isBlockAtPos(PT_DocPosition pos)
3143 {
3144 	PT_BlockOffset pOffset;
3145 	pf_Frag * pf = NULL;
3146 	/*bool bRes = */m_pPieceTable->getFragFromPosition(pos,&pf,&pOffset);
3147 	if(!pf)
3148 		return false;
3149 	while(pf && pf->getLength() == 0)
3150 		pf = pf->getPrev();
3151 	if(pf && pf->getType() == pf_Frag::PFT_Strux)
3152 	{
3153 		pf_Frag_Strux * pfs = static_cast<pf_Frag_Strux *>(pf);
3154 		if(pfs->getStruxType() == PTX_Block)
3155 		{
3156 			return true;
3157 		}
3158 	}
3159 	return false;
3160 }
3161 
3162 
3163 //============================================================================
3164 // Table Medthods
3165 //===========================================================================
3166 
3167 /*!
3168  * This method returns true if there is a table strux at exactly this
3169  * position.
3170  */
isTableAtPos(PT_DocPosition pos)3171 bool PD_Document::isTableAtPos(PT_DocPosition pos)
3172 {
3173 	PT_BlockOffset pOffset;
3174 	pf_Frag * pf = NULL;
3175 	/*bool bRes = */m_pPieceTable->getFragFromPosition(pos,&pf,&pOffset);
3176 	if(!pf)
3177 		return false;
3178 	while(pf && pf->getLength() == 0)
3179 		pf = pf->getPrev();
3180 	if(pf && pf->getType() == pf_Frag::PFT_Strux)
3181 	{
3182 		pf_Frag_Strux * pfs = static_cast<pf_Frag_Strux *>(pf);
3183 		if(pfs->getStruxType() == PTX_SectionTable)
3184 		{
3185 			return true;
3186 		}
3187 	}
3188 	return false;
3189 }
3190 
3191 
3192 /*!
3193  * This method returns true if there is an EndTable strux at exactly this
3194  * position.
3195  */
isEndTableAtPos(PT_DocPosition pos)3196 bool PD_Document::isEndTableAtPos(PT_DocPosition pos)
3197 {
3198 	PT_BlockOffset pOffset;
3199 	pf_Frag * pf = NULL;
3200 	/*bool bRes = */m_pPieceTable->getFragFromPosition(pos,&pf,&pOffset);
3201 	if(!pf)
3202 		return false;
3203 	while(pf && pf->getLength() == 0)
3204 		pf = pf->getPrev();
3205 	if(pf && pf->getType() == pf_Frag::PFT_Strux)
3206 	{
3207 		pf_Frag_Strux * pfs = static_cast<pf_Frag_Strux *>(pf);
3208 		if(pfs->getStruxType() == PTX_EndTable)
3209 		{
3210 			return true;
3211 		}
3212 	}
3213 	return false;
3214 }
3215 
3216 
3217 /*!
3218  * This method returns true if there is a cell strux at exactly this
3219  * position.
3220  */
isCellAtPos(PT_DocPosition pos)3221 bool PD_Document::isCellAtPos(PT_DocPosition pos)
3222 {
3223 	PT_BlockOffset pOffset;
3224 	pf_Frag * pf = NULL;
3225 	/*bool bRes = */m_pPieceTable->getFragFromPosition(pos,&pf,&pOffset);
3226 	if(!pf)
3227 		return false;
3228 	while(pf && pf->getLength() == 0)
3229 		pf = pf->getPrev();
3230 	if(pf && pf->getType() == pf_Frag::PFT_Strux)
3231 	{
3232 		pf_Frag_Strux * pfs = static_cast<pf_Frag_Strux *>(pf);
3233 		if(pfs->getStruxType() == PTX_SectionCell)
3234 		{
3235 			return true;
3236 		}
3237 	}
3238 	return false;
3239 }
3240 
3241 /*!
3242  * This method returns the end table strux associated with the table strux tableSDH
3243  * Returns NULL on failure to find it.
3244  */
getEndTableStruxFromTableSDH(pf_Frag_Strux * tableSDH)3245 pf_Frag_Strux* PD_Document::getEndTableStruxFromTableSDH(pf_Frag_Strux* tableSDH)
3246 {
3247 	pf_Frag * currentFrag = tableSDH;
3248 	currentFrag = currentFrag->getNext();
3249 	UT_sint32 depth =0;
3250 	while (currentFrag!=m_pPieceTable->getFragments().getLast())
3251 	{
3252 		UT_return_val_if_fail (currentFrag,0);
3253 		if(currentFrag->getType()  == pf_Frag::PFT_Strux)
3254 		{
3255 			pf_Frag_Strux * pfSec = static_cast<pf_Frag_Strux *>(currentFrag);
3256 			if(pfSec->getStruxType() == PTX_SectionTable)
3257 				depth++;
3258 			else if(pfSec->getStruxType() == PTX_EndTable)
3259 			{
3260 				if(depth == 0)
3261 				{
3262 					return pfSec;
3263 				}
3264 				else
3265 					depth--;
3266 			}
3267 		}
3268 //
3269 // Get Next frag in the table.
3270 //
3271 		currentFrag = currentFrag->getNext();
3272 	}
3273 	return NULL;
3274 }
3275 /*!
3276  * This method returns the end cell strux associated with the cell strux cellSDH
3277  * Returns NULL on failure to find it.
3278  */
getEndCellStruxFromCellSDH(pf_Frag_Strux * cellSDH)3279 pf_Frag_Strux* PD_Document::getEndCellStruxFromCellSDH(pf_Frag_Strux* cellSDH)
3280 {
3281 	pf_Frag * currentFrag = cellSDH;
3282 	currentFrag = currentFrag->getNext();
3283 	while (currentFrag && currentFrag!=m_pPieceTable->getFragments().getLast())
3284 	{
3285 		UT_return_val_if_fail (currentFrag,0);
3286 		if(currentFrag->getType()  == pf_Frag::PFT_Strux)
3287 		{
3288 			pf_Frag_Strux * pfSec = static_cast<pf_Frag_Strux*>(currentFrag);
3289 			if(pfSec->getStruxType() == PTX_SectionTable)
3290 			{
3291 				pf_Frag_Strux* endTab = getEndTableStruxFromTableSDH(pfSec);
3292 				currentFrag = endTab;
3293 			}
3294 			else if(pfSec->getStruxType() == PTX_EndCell )
3295 			{
3296 				return pfSec;
3297 			}
3298 			else if(pfSec->getStruxType() == PTX_SectionCell)
3299 			{
3300 				return NULL;
3301 			}
3302 			else if(pfSec->getStruxType() == PTX_EndTable)
3303 			{
3304 				return NULL;
3305 			}
3306 		}
3307 //
3308 // Get Next frag in the table.
3309 //
3310 		if(currentFrag)
3311 		{
3312 			currentFrag = currentFrag->getNext();
3313 		}
3314 	}
3315 	return NULL;
3316 }
3317 
3318 /*!
3319  * This method returns the end table strux associated with the table strux tableSDH
3320  * Returns NULL on failure to find it.
3321  */
getEndTableStruxFromTablePos(PT_DocPosition tablePos)3322 pf_Frag_Strux* PD_Document::getEndTableStruxFromTablePos(PT_DocPosition tablePos)
3323 {
3324 	pf_Frag_Strux* tableSDH = NULL;
3325 	pf_Frag_Strux* EndTableSDH = NULL;
3326 	bool bRes =	getStruxOfTypeFromPosition(tablePos, PTX_SectionTable, &tableSDH);
3327 	if(!bRes)
3328 		return NULL;
3329 	EndTableSDH = getEndTableStruxFromTableSDH(tableSDH);
3330 	return EndTableSDH;
3331 }
3332 
3333 /*!
3334  * The method returns the number of rows and columns in table pointed to by tableSDH
3335 \param pf_Frag_Strux* tableSDH SDH of the table in question
3336 \param UT_sint32 * numRows pointer to the number of rows returned
3337 \param UT_sint32 * numCols pointer to the number of cols returned
3338 */
getRowsColsFromTableSDH(pf_Frag_Strux * tableSDH,bool bShowRevisions,UT_uint32 iRevisionLevel,UT_sint32 * numRows,UT_sint32 * numCols)3339 bool PD_Document::getRowsColsFromTableSDH(pf_Frag_Strux* tableSDH, bool bShowRevisions, UT_uint32 iRevisionLevel,
3340 										  UT_sint32 * numRows, UT_sint32 * numCols)
3341 {
3342 	UT_sint32 iRight = 0;
3343     UT_sint32 iBot = 0;
3344 	const char * szRight = NULL;
3345 	const char * szBot = NULL;
3346 	pf_Frag_Strux* cellSDH;
3347 	*numRows = 0;
3348 	*numCols = 0;
3349 //
3350 // Do the scan
3351 //
3352 	pf_Frag * currentFrag = tableSDH;
3353 	currentFrag = currentFrag->getNext();
3354 	while (currentFrag && currentFrag!=m_pPieceTable->getFragments().getLast())
3355 	{
3356 		UT_return_val_if_fail (currentFrag,0);
3357 		if(currentFrag->getType()  == pf_Frag::PFT_Strux)
3358 		{
3359 			pf_Frag_Strux * pfSec = static_cast<pf_Frag_Strux *>(currentFrag);
3360 			if(pfSec->getStruxType() == PTX_SectionTable)
3361 			{
3362 //
3363 // skip to the end of this nested table
3364 //
3365 				pf_Frag_Strux* endSDH = getEndTableStruxFromTableSDH(pfSec);
3366 				pfSec = endSDH;
3367 			}
3368 			else if(pfSec->getStruxType() == PTX_EndTable)
3369 				return true;
3370 			else if(pfSec->getStruxType() == PTX_SectionCell)
3371 			{
3372 				cellSDH = pfSec;
3373 				UT_DebugOnly<bool> bres = getPropertyFromSDH(cellSDH,bShowRevisions, iRevisionLevel,"right-attach",&szRight);
3374 				UT_ASSERT(bres);
3375 				if(szRight && *szRight)
3376 					iRight = atoi(szRight);
3377 				bres = getPropertyFromSDH(cellSDH,bShowRevisions, iRevisionLevel,"bot-attach",&szBot);
3378 				UT_ASSERT(bres);
3379 				if(szBot && *szBot)
3380 					iBot = atoi(szBot);
3381 
3382 				if(*numCols < iRight)
3383 					*numCols = iRight;
3384 				if(*numRows < iBot)
3385 					*numRows = iBot;
3386 			}
3387 			currentFrag = pfSec;
3388 		}
3389 		if(currentFrag)
3390 			currentFrag = currentFrag->getNext();
3391 	}
3392 	return false;
3393 }
3394 
miniDump(pf_Frag_Strux * sdh,UT_sint32 nstruxes)3395 void  PD_Document::miniDump(pf_Frag_Strux* sdh, UT_sint32 nstruxes)
3396 {
3397 #ifdef DEBUG
3398 	UT_sint32 i=0;
3399 	const pf_Frag_Strux * pfs = sdh;
3400 	const pf_Frag * pf = pfs;
3401 	for(i=0; pfs && (i< nstruxes); i++)
3402 	{
3403 		pf = pf->getPrev();
3404 		while(pf && pf->getType() != pf_Frag::PFT_Strux)
3405 			pf = pf->getPrev();
3406 		pfs = static_cast<const pf_Frag_Strux *>(pf);
3407 	}
3408 	if(pfs == NULL)
3409 	{
3410 		pf =  m_pPieceTable->getFragments().getFirst();
3411 		while(pf && (pf->getType() != pf_Frag::PFT_Strux))
3412 			pf = pf->getNext();
3413 		if(pf)
3414 			pfs = static_cast<const pf_Frag_Strux *>(pf);
3415 	}
3416 	for(i=0; pfs && (i< 2*nstruxes); i++)
3417 	{
3418 		pf = pfs;
3419 		pfs = static_cast<const pf_Frag_Strux *>(pf);
3420 		const char * szStrux = NULL;
3421 		if(pfs->getStruxType() == PTX_Block)
3422 			szStrux = "Block";
3423 		else if(pfs->getStruxType() == PTX_SectionTable)
3424 			szStrux = "Table";
3425 		else if(pfs->getStruxType() == PTX_SectionCell)
3426 			szStrux = "Cell";
3427 		else if(pfs->getStruxType() == PTX_EndTable)
3428 			szStrux = "End Table";
3429 		else if(pfs->getStruxType() == PTX_EndCell)
3430 			szStrux = "End Cell";
3431 		else if(pfs->getStruxType() == PTX_SectionFootnote)
3432 			szStrux = "Footnote";
3433 		else if(pfs->getStruxType() == PTX_EndFootnote)
3434 			szStrux = "End Footnote";
3435 		else if(pfs->getStruxType() == PTX_SectionAnnotation)
3436 			szStrux = "Annotation";
3437 		else if(pfs->getStruxType() == PTX_EndAnnotation)
3438 			szStrux = "End Annotation";
3439 		else if(pfs->getStruxType() == PTX_SectionEndnote)
3440 			szStrux = "Endnote";
3441 		else if(pfs->getStruxType() == PTX_EndEndnote)
3442 			szStrux = "End Endnote";
3443 		else if(pfs->getStruxType() == PTX_Section)
3444 			szStrux = "Section";
3445 		else if(pfs->getStruxType() == PTX_SectionFrame)
3446 			szStrux = "Frame";
3447 		else if(pfs->getStruxType() == PTX_EndFrame)
3448 			szStrux = "EndFrame";
3449 		else
3450 			szStrux = "Other Strux";
3451 		if(i< nstruxes)
3452 		{
3453 			UT_DEBUGMSG(("MiniDump Before Frag %p Type %s \n",pfs,szStrux));
3454 		}
3455 		else if(i > nstruxes)
3456 		{
3457 			UT_DEBUGMSG(("MiniDump After Frag %p Type %s \n",pfs,szStrux));
3458 		}
3459 		if(pfs == static_cast<const pf_Frag_Strux *>(sdh))
3460 		{
3461 			UT_DEBUGMSG(("MiniDump Actual Frag %p Type %s \n",pfs,szStrux));
3462 		}
3463 		const char * szLeft=NULL;
3464 		const char * szRight=NULL;
3465 		const char * szTop=NULL;
3466 		const char * szBot = NULL;
3467 		getPropertyFromSDH(pfs,true, PD_MAX_REVISION,"left-attach",&szLeft);
3468 		getPropertyFromSDH(pfs,true, PD_MAX_REVISION,"right-attach",&szRight);
3469 		getPropertyFromSDH(pfs,true, PD_MAX_REVISION,"top-attach",&szTop);
3470 		getPropertyFromSDH(pfs,true, PD_MAX_REVISION,"bot-attach",&szBot);
3471 		if(szLeft != NULL)
3472 		{
3473 			UT_DEBUGMSG(("left-attach %s right-attach %s top-attach %s bot-attach %s \n",szLeft,szRight,szTop,szBot));
3474 		}
3475 		pf = pf->getNext();
3476 		while(pf && pf->getType() != pf_Frag::PFT_Strux)
3477 		{
3478 			UT_DEBUGMSG(("MiniDump: Other Frag %p of Type %d \n",pf,pf->getType()));
3479 			pf = pf->getNext();
3480 		}
3481 		if(pf)
3482 			pfs= static_cast<const pf_Frag_Strux *>(pf);
3483 	}
3484 #else
3485 	UT_UNUSED(sdh);
3486 	UT_UNUSED(nstruxes);
3487 #endif
3488 }
3489 
3490 bool
dumpDoc(const char * msg,PT_DocPosition currentpos,PT_DocPosition endpos)3491 PD_Document::dumpDoc( const char* msg, PT_DocPosition currentpos, PT_DocPosition endpos )
3492 {
3493     return m_pPieceTable->dumpDoc( msg, currentpos, endpos );
3494 }
3495 
3496 /*!
3497  * The method returns the SDH of the cell at the location given by (rows,columns) in table
3498  * pointed to by tableSDH. Returns NULL if the requested location is not contained in the
3499  * cell.
3500 \param pf_Frag_Strux* tableSDH SDH of the table in question
3501 \param UT_sint32 row row location.
3502 \param UT_sint32 col column location
3503 */
3504 
getCellSDHFromRowCol(pf_Frag_Strux * tableSDH,bool bShowRevisions,UT_uint32 iRevisionLevel,UT_sint32 row,UT_sint32 col)3505 pf_Frag_Strux* PD_Document::getCellSDHFromRowCol(pf_Frag_Strux* tableSDH,
3506 													bool bShowRevisions, UT_uint32 iRevisionLevel,
3507 													UT_sint32 row,
3508 													UT_sint32 col)
3509 {
3510 	UT_sint32 Top,Left,Bot,Right;
3511 	const char * szLeft = NULL;
3512 	const char * szTop = NULL;
3513 	const char * szRight = NULL;
3514 	const char * szBot = NULL;
3515 	pf_Frag_Strux* cellSDH;
3516 //
3517 // Do the scan
3518 //
3519 	pf_Frag * currentFrag = tableSDH;
3520 
3521 	UT_return_val_if_fail(currentFrag != NULL, NULL);
3522 
3523 	currentFrag = currentFrag->getNext();
3524 	while (currentFrag && currentFrag!=m_pPieceTable->getFragments().getLast())
3525 	{
3526 		UT_return_val_if_fail (currentFrag,0);
3527 		if(currentFrag->getType() == pf_Frag::PFT_Strux)
3528 		{
3529 			pf_Frag_Strux * pfSec = static_cast<pf_Frag_Strux *>(currentFrag);
3530 			if(pfSec->getStruxType() == PTX_SectionTable)
3531 			{
3532 //
3533 // skip to the end of this nested table
3534 //
3535 				pfSec = getEndTableStruxFromTableSDH(pfSec);
3536 			}
3537 			else if(pfSec->getStruxType() == PTX_EndTable)
3538 			{
3539 //
3540 // end of table before the requested cell was found. Return NULL
3541 //
3542 				return NULL;
3543 			}
3544 			else if(pfSec->getStruxType() == PTX_SectionCell)
3545 			{
3546 				cellSDH = pfSec;
3547 				Left = -1;
3548 				Top = -1;
3549 				Right = -1;
3550 				Bot = -1;
3551 				UT_DebugOnly<bool> bres = getPropertyFromSDH(cellSDH,bShowRevisions,iRevisionLevel,"left-attach",&szLeft);
3552 				UT_ASSERT(bres);
3553 				if(szLeft && *szLeft)
3554 					Left = atoi(szLeft);
3555 				bres = getPropertyFromSDH(cellSDH,bShowRevisions,iRevisionLevel,"top-attach",&szTop);
3556 				UT_ASSERT(bres);
3557 				if(szTop && *szTop)
3558 					Top = atoi(szTop);
3559 				bres = getPropertyFromSDH(cellSDH,bShowRevisions,iRevisionLevel,"right-attach",&szRight);
3560 				UT_ASSERT(bres);
3561 				if(szRight && *szRight)
3562 					Right = atoi(szRight);
3563 				bres = getPropertyFromSDH(cellSDH,bShowRevisions,iRevisionLevel,"bot-attach",&szBot);
3564 				UT_ASSERT(bres);
3565 				if(szBot && *szBot)
3566 					Bot = atoi(szBot);
3567 				if( (Top <= row) && (row < Bot) && (Left <= col) && (Right > col))
3568 				{
3569 					return pfSec;
3570 				}
3571 			}
3572 			currentFrag = pfSec;
3573 		}
3574 		if(currentFrag)
3575 			currentFrag = currentFrag->getNext();
3576 	}
3577 	return NULL;
3578 }
3579 
3580 //===================================================================================
3581 /*!
3582  * This method scans the document for all styles used in the document, including
3583  * styles in the basedon heiracy and the followedby list
3584  *
3585  */
getAllUsedStyles(UT_GenericVector<PD_Style * > * pVecStyles)3586 void PD_Document::getAllUsedStyles(UT_GenericVector <PD_Style*>* pVecStyles)
3587 {
3588 	UT_sint32 i = 0;
3589 	pf_Frag * currentFrag = m_pPieceTable->getFragments().getFirst();
3590 	PD_Style * pStyle = NULL;
3591 	while (currentFrag!=m_pPieceTable->getFragments().getLast())
3592 	{
3593 		UT_return_if_fail (currentFrag);
3594 //
3595 // get indexAP
3596 // get PT_STYLE_ATTRIBUTE_NAME
3597 // if it matches style name or is contained in a basedon name or followedby
3598 //
3599 //
3600 // All this code is used to find if this frag has a style in it.
3601 //
3602 		PT_AttrPropIndex indexAP = 0;
3603 		if(currentFrag->getType()  == pf_Frag::PFT_Strux)
3604 			indexAP = static_cast<pf_Frag_Strux *>(currentFrag)->getIndexAP();
3605 		else if(currentFrag->getType()  == pf_Frag::PFT_Text)
3606 			indexAP = static_cast<pf_Frag_Text *>(currentFrag)->getIndexAP();
3607 		else if(currentFrag->getType()  == pf_Frag::PFT_Object)
3608 			indexAP = static_cast<pf_Frag_Object *>(currentFrag)->getIndexAP();
3609 		else if(currentFrag->getType()  == pf_Frag::PFT_FmtMark)
3610 			indexAP = static_cast<pf_Frag_FmtMark *>(currentFrag)->getIndexAP();
3611 		const PP_AttrProp * pAP = NULL;
3612 		m_pPieceTable->getAttrProp(indexAP,&pAP);
3613 		UT_return_if_fail (pAP);
3614 		const gchar * pszStyleName = NULL;
3615 		(pAP)->getAttribute(PT_STYLE_ATTRIBUTE_NAME, pszStyleName);
3616 //
3617 // We've found a style...
3618 //
3619 		if(pszStyleName != NULL)
3620 		{
3621 			m_pPieceTable->getStyle(pszStyleName,&pStyle);
3622 			UT_return_if_fail (pStyle);
3623 			if(pStyle)
3624 			{
3625 				if(pVecStyles->findItem(pStyle) < 0)
3626 					pVecStyles->addItem(pStyle);
3627 				PD_Style * pBasedOn = pStyle->getBasedOn();
3628 				i = 0;
3629 				while(pBasedOn != NULL && i <  pp_BASEDON_DEPTH_LIMIT)
3630 				{
3631 					if(pVecStyles->findItem(pBasedOn) < 0)
3632 						pVecStyles->addItem(pBasedOn);
3633 					i++;
3634 					pBasedOn = pBasedOn->getBasedOn();
3635 				}
3636 				PD_Style * pFollowedBy = pStyle->getFollowedBy();
3637 				if(pFollowedBy && (pVecStyles->findItem(pFollowedBy) < 0))
3638 					pVecStyles->addItem(pFollowedBy);
3639 			}
3640 		}
3641 //
3642 // Get Next frag in the table.
3643 //
3644 		currentFrag = currentFrag->getNext();
3645 	}
3646 //
3647 // Done!
3648 //
3649 }
3650 
3651 
3652 struct prevStuff
3653 {
3654 private:
3655 	pf_Frag::PFType fragType;
3656 	pf_Frag_Strux * lastFragStrux;
3657 	PT_AttrPropIndex indexAPFrag;
3658 	pf_Frag * thisFrag;
3659 	PT_DocPosition thisPos;
3660 	PT_DocPosition thisStruxPos;
3661 	UT_uint32 fragLength;
3662 	bool bChangeIndexAP;
3663 	friend bool PD_Document::removeStyle(const gchar * pszName);
3664 };
3665 
3666 /*!
3667  * This method removes the style of name pszName from the styles definition and removes
3668  * all instances of it from the document including the basedon heiracy and the
3669  * followed-by sequences.
3670  */
removeStyle(const gchar * pszName)3671 bool PD_Document::removeStyle(const gchar * pszName)
3672 {
3673 	UT_return_val_if_fail (m_pPieceTable, false);
3674 //
3675 // First replace all occurances of pszName with "Normal"
3676 //
3677 	PD_Style * pNormal = NULL;
3678 	PD_Style * pNuke = NULL;
3679 	m_pPieceTable->getStyle(pszName,&pNuke);
3680 	UT_return_val_if_fail (pNuke, false);
3681 	pNormal = pNuke->getBasedOn();
3682 	const gchar * szBack = NULL;
3683 	if(pNormal == NULL)
3684 	{
3685 		m_pPieceTable->getStyle("Normal",&pNormal);
3686 		szBack = "None";
3687 	}
3688 	else
3689 	{
3690 //
3691 // The name of the style is stored in the PT_NAME_ATTRIBUTE_NAME attribute within the
3692 // style
3693 //
3694 		pNormal->getAttribute(PT_NAME_ATTRIBUTE_NAME, szBack);
3695 	}
3696 	UT_return_val_if_fail (szBack, false);
3697 	UT_return_val_if_fail (pNormal, false);
3698 	PT_AttrPropIndex indexNormal = pNormal->getIndexAP();
3699 
3700 //
3701 // Now scan through the document finding all instances of pszName as either
3702 // the style or the basedon style or the followed by style. Replace these with
3703 // "normal"
3704 //
3705 	UT_GenericVector<prevStuff *> vFrag;
3706 
3707 	PT_DocPosition pos = 0;
3708 	pf_Frag_Strux * pfs = NULL;
3709 	pf_Frag * currentFrag = m_pPieceTable->getFragments().getFirst();
3710 	UT_return_val_if_fail (currentFrag,false);
3711 	while (currentFrag!=m_pPieceTable->getFragments().getLast())
3712 	{
3713 //
3714 // get indexAP
3715 // get PT_STYLE_ATTRIBUTE_NAME
3716 // if it matches style name or is contained in a basedon name or followedby
3717 //
3718 //
3719 // All this code is used to find if this strux has our style in it
3720 //
3721 		PT_AttrPropIndex indexAP = 0;
3722 		if(currentFrag->getType()  == pf_Frag::PFT_Strux)
3723 		{
3724 			pfs = static_cast<pf_Frag_Strux *>(currentFrag);
3725 			indexAP = static_cast<pf_Frag_Strux *>(currentFrag)->getIndexAP();
3726 		}
3727 		else if(currentFrag->getType()  == pf_Frag::PFT_Text)
3728 		{
3729 			indexAP = static_cast<pf_Frag_Text *>(currentFrag)->getIndexAP();
3730 		}
3731 		else if(currentFrag->getType()  == pf_Frag::PFT_Object)
3732 		{
3733 			indexAP = static_cast<pf_Frag_Object *>(currentFrag)->getIndexAP();
3734 		}
3735 		else if(currentFrag->getType()  == pf_Frag::PFT_FmtMark)
3736 		{
3737 			indexAP = static_cast<pf_Frag_FmtMark *>(currentFrag)->getIndexAP();
3738 		}
3739 		const PP_AttrProp * pAP = NULL;
3740 		m_pPieceTable->getAttrProp(indexAP,&pAP);
3741 		UT_return_val_if_fail (pAP, false);
3742 		const gchar * pszStyleName = NULL;
3743 		(pAP)->getAttribute(PT_STYLE_ATTRIBUTE_NAME, pszStyleName);
3744 //
3745 // It does so remember this frag and set the old indexAP to Normal
3746 //
3747 		if(pszStyleName != NULL && strcmp(pszStyleName,pszName)==0)
3748 		{
3749 			prevStuff *  pStuff = new prevStuff;
3750 			pf_Frag::PFType cType = currentFrag->getType();
3751 			pStuff->fragType = cType;
3752 			pStuff->thisFrag = currentFrag;
3753 			pStuff->indexAPFrag = indexAP;
3754 			pStuff->lastFragStrux = pfs;
3755 			pStuff->thisPos = pos;
3756 			pStuff->thisStruxPos = pos;
3757 			pStuff->fragLength = currentFrag->getLength();
3758 			pStuff->bChangeIndexAP = true;
3759 			vFrag.addItem(pStuff);
3760 //
3761 // OK set this frag's indexAP to that of basedon of our deleted style or
3762 // Normal.
3763 //
3764 			if(pf_Frag::PFT_Strux == cType)
3765 				static_cast<pf_Frag_Strux *>(currentFrag)->setIndexAP(indexNormal);
3766 			else if(pf_Frag::PFT_Text == cType)
3767 				static_cast<pf_Frag_Text *>(currentFrag)->setIndexAP(indexNormal);
3768 			else if(pf_Frag::PFT_Object == cType)
3769 				static_cast<pf_Frag_Object *>(currentFrag)->setIndexAP(indexNormal);
3770 			else if(pf_Frag::PFT_FmtMark == cType)
3771 				static_cast<pf_Frag_FmtMark *>(currentFrag)->setIndexAP(indexNormal);
3772 		}
3773 //
3774 // Now recursively search to see if has our style in the basedon list
3775 //
3776 		else if(pszStyleName != NULL)
3777 		{
3778 			PD_Style * cStyle = NULL;
3779 			m_pPieceTable->getStyle(pszStyleName,&cStyle);
3780 			UT_ASSERT_HARMLESS(cStyle);
3781 			if(!cStyle)
3782 				break;
3783 			PD_Style * pBasedOn = cStyle->getBasedOn();
3784 			PD_Style * pFollowedBy = cStyle->getFollowedBy();
3785 			UT_uint32 i =0;
3786 			for(i=0; (i < pp_BASEDON_DEPTH_LIMIT) && (pBasedOn != NULL) && (pBasedOn!= pNuke); i++)
3787 			{
3788 				pBasedOn = pBasedOn->getBasedOn();
3789 			}
3790 			if(pBasedOn == pNuke)
3791 			{
3792 				prevStuff *  pStuff = new prevStuff;
3793 				pStuff->fragType = currentFrag->getType();
3794 				pStuff->thisFrag = currentFrag;
3795 				pStuff->indexAPFrag = indexAP;
3796 				pStuff->lastFragStrux = pfs;
3797 				pStuff->thisPos = pos;
3798 				pStuff->thisStruxPos = pos;
3799 				pStuff->fragLength = currentFrag->getLength();
3800 				pStuff->bChangeIndexAP = false;
3801 				vFrag.addItem(pStuff);
3802 			}
3803 //
3804 // Look if followedBy points to our style
3805 //
3806 			else if(pFollowedBy == pNuke)
3807 			{
3808 				prevStuff *  pStuff = new prevStuff;
3809 				pStuff->fragType = currentFrag->getType();
3810 				pStuff->thisFrag = currentFrag;
3811 				pStuff->indexAPFrag = indexAP;
3812 				pStuff->lastFragStrux = pfs;
3813 				pStuff->thisPos = pos;
3814 				pStuff->thisStruxPos = pos;
3815 				pStuff->fragLength = currentFrag->getLength();
3816 				pStuff->bChangeIndexAP = false;
3817 				vFrag.addItem(pStuff);
3818 			}
3819 		}
3820 		pos = pos + currentFrag->getLength();
3821 		currentFrag = currentFrag->getNext();
3822 	}
3823 //
3824 // Now replace all pointers to this style in basedon or followedby
3825 // with Normal
3826 //
3827 	UT_uint32 nstyles = getStyleCount();
3828 	UT_uint32 i;
3829 	UT_GenericVector<PD_Style*> * pStyles = NULL;
3830 	enumStyles(pStyles);
3831 	UT_return_val_if_fail( pStyles, false );
3832 
3833 	for(i=0; i< nstyles;i++)
3834 	{
3835 		// enumStyles(i, &szCstyle,&cStyle);
3836 		const PD_Style * pStyle = pStyles->getNthItem(i);
3837 		UT_return_val_if_fail( pStyle, false );
3838 
3839 		bool bDoBasedOn = false;
3840 		bool bDoFollowedby = false;
3841 		if(pStyle->getBasedOn() == pNuke)
3842 		{
3843 			bDoBasedOn = true;
3844 		}
3845 		if(pStyle->getFollowedBy() == pNuke)
3846 		{
3847 			bDoFollowedby = true;
3848 		}
3849 		const gchar * nAtts[5] ={NULL,NULL,NULL,NULL,NULL};
3850 		if( bDoBasedOn && bDoFollowedby)
3851 		{
3852 			nAtts[0] = "basedon"; nAtts[1] =  szBack;
3853 			nAtts[2]= "followedby";	nAtts[3] = "Current Settings";
3854 			nAtts[4] = NULL;
3855 		}
3856 		else if ( bDoBasedOn && ! bDoFollowedby)
3857 		{
3858 			nAtts[0] = "basedon"; nAtts[1] =  szBack;
3859 			nAtts[2] = NULL;
3860 		}
3861 		else if ( !bDoBasedOn && bDoFollowedby)
3862 		{
3863 			nAtts[0]= "followedby";	nAtts[1] = "Current Settings";
3864 			nAtts[2] = NULL;
3865 		}
3866 		if( bDoBasedOn || bDoFollowedby)
3867 		{
3868 #if 0
3869 			UT_uint32 j =0;
3870 			for(j=0; nAtts[j] != NULL; j+=2)
3871 			{
3872 				xxx_UT_DEBUGMSG(("SEVIOR New Style Name %s, Value %s \n",nAtts[j],nAtts[j+1]));
3873 			}
3874 #endif
3875 			const_cast<PD_Style *>(pStyle)->addAttributes( static_cast<const gchar **>(&nAtts[0]));
3876 		}
3877 	}
3878 
3879 	delete pStyles;
3880 //
3881 // OK Now remove the style
3882 //
3883 	m_pPieceTable->removeStyle(pszName);
3884 //
3885 // Alright now we replace all the instances of fragSrux using the style to be
3886 // deleted.
3887 //
3888 	UT_sint32 countChanges = vFrag.getItemCount();
3889 	UT_sint32 j;
3890 	pf_Frag * pfsLast = NULL;
3891 	PX_ChangeRecord * pcr = NULL;
3892 	for(j = 0; j<countChanges; j++)
3893 	{
3894 		prevStuff * pStuff = static_cast<prevStuff *>(vFrag.getNthItem(j));
3895 		if(pStuff->fragType == pf_Frag::PFT_Strux)
3896 		{
3897 			pfsLast = pStuff->thisFrag;
3898 			if(pStuff->bChangeIndexAP)
3899 			{
3900 				pcr = new PX_ChangeRecord(PX_ChangeRecord::PXT_ChangeStrux,pStuff->thisPos,indexNormal,
3901 										  pfsLast->getXID());
3902 				notifyListeners(pStuff->lastFragStrux, pcr);
3903 				delete pcr;
3904 			}
3905 			else
3906 			{
3907 				pcr = new PX_ChangeRecord(PX_ChangeRecord::PXT_ChangeStrux,pStuff->thisPos,pStuff->indexAPFrag,
3908 										  pfsLast->getXID());
3909 				notifyListeners(pStuff->lastFragStrux, pcr);
3910 				delete pcr;
3911 			}
3912 		}
3913 		else
3914 		{
3915 			if(pStuff->lastFragStrux != pfsLast)
3916 			{
3917 				pfsLast = pStuff->lastFragStrux;
3918 				if(pStuff->bChangeIndexAP)
3919 				{
3920 					pcr = new PX_ChangeRecord(PX_ChangeRecord::PXT_ChangeStrux,pStuff->thisPos,indexNormal,
3921 											  pfsLast->getXID());
3922 					notifyListeners(pStuff->lastFragStrux, pcr);
3923 					delete pcr;
3924 				}
3925 				else
3926 				{
3927 					PT_AttrPropIndex indexLastAP = static_cast<pf_Frag_Strux *>(pfsLast)->getIndexAP();
3928 					pcr = new PX_ChangeRecord(PX_ChangeRecord::PXT_ChangeStrux,pStuff->thisPos,indexLastAP,
3929 											  pfsLast->getXID());
3930 					notifyListeners(pStuff->lastFragStrux, pcr);
3931 					delete pcr;
3932 				}
3933 			}
3934 		}
3935 	}
3936 //  		else if(bisCharStyle)
3937 //  		{
3938 //  			UT_uint32 blockoffset = (UT_uint32) (pStuff->thisPos - pStuff->thisStruxPos -1);
3939 //  			pf_Frag_Text * pft = static_cast<pf_Frag_Text *>(pStuff->thisFrag);
3940 //  			PX_ChangeRecord_SpanChange * pcr =
3941 //  				new PX_ChangeRecord_SpanChange(PX_ChangeRecord::PXT_ChangeSpan,
3942 //  											   pStuff->thisPos,
3943 //  											   pStuff->indexAPFrag,
3944 //  											   indexNormal,
3945 //  											   m_pPieceTable->getVarSet().getBufIndex(pft->getBufIndex(),0),
3946 //  											   pStuff->fragLength,
3947 //  											   blockoffset);
3948 //  			notifyListeners(pStuff->lastFragStrux, pcr);
3949 //  			delete pcr;
3950 //  		}
3951 //  	}
3952 	if(countChanges > 0)
3953 	{
3954 		UT_VECTOR_PURGEALL(prevStuff *,vFrag);
3955 	}
3956 //
3957 // Now reformat the entire document
3958 //
3959 //	signalListeners(PD_SIGNAL_REFORMAT_LAYOUT);
3960 	return true;
3961 }
3962 
appendStyle(const gchar ** attributes)3963 bool PD_Document::appendStyle(const gchar ** attributes)
3964 {
3965 	UT_return_val_if_fail (m_pPieceTable, false);
3966 
3967 	// can only be used while loading the document
3968 
3969 	return m_pPieceTable->appendStyle(attributes);
3970 }
3971 
getStyleCount(void)3972 size_t PD_Document::getStyleCount(void)
3973 {
3974   UT_return_val_if_fail (m_pPieceTable, false);
3975 
3976   return m_pPieceTable->getStyleCount();
3977 }
3978 
3979 //////////////////////////////////////////////////////////////////
3980 //////////////////////////////////////////////////////////////////
3981 
tellListener(PL_Listener * pListener)3982 bool PD_Document::tellListener(PL_Listener* pListener)
3983 {
3984 	UT_return_val_if_fail (pListener,false);
3985 	UT_return_val_if_fail (m_pPieceTable,false);
3986 
3987 	return m_pPieceTable->tellListener(pListener);
3988 }
3989 
tellListenerSubset(PL_Listener * pListener,PD_DocumentRange * pDocRange,PL_ListenerCoupleCloser * closer)3990 bool PD_Document::tellListenerSubset( PL_Listener* pListener,
3991                                       PD_DocumentRange * pDocRange,
3992                                       PL_ListenerCoupleCloser* closer )
3993 {
3994 	UT_return_val_if_fail (pListener, false);
3995 	UT_return_val_if_fail (m_pPieceTable, false);
3996 	UT_return_val_if_fail (pDocRange && pDocRange->m_pDoc==this, false);
3997 
3998 	return m_pPieceTable->tellListenerSubset(pListener,pDocRange,closer);
3999 }
4000 
addListener(PL_Listener * pListener,PL_ListenerId * pListenerId)4001 bool PD_Document::addListener(PL_Listener * pListener,
4002 								 PL_ListenerId * pListenerId)
4003 {
4004 	UT_sint32 kLimit = m_vecListeners.getItemCount();
4005 	UT_sint32 k=0;
4006 
4007 	// see if we can recycle a cell in the vector.
4008 
4009 	for (k=0; k<kLimit; k++)
4010 		if (m_vecListeners.getNthItem(k) == 0)
4011 		{
4012 			m_vecListeners.setNthItem(k,pListener,NULL);
4013 			goto ClaimThisK;
4014 		}
4015 
4016 	// otherwise, extend the vector for it.
4017 
4018 	if (m_vecListeners.addItem(pListener,&k) != 0)
4019 	{
4020 		UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
4021 		return false;				// could not add item to vector
4022 	}
4023   ClaimThisK:
4024 
4025 	// propagate the listener to the PieceTable and
4026 	// let it do its thing.
4027 	UT_return_val_if_fail (m_pPieceTable, false);
4028 
4029 	// give our vector index back to the caller as a "Listener Id".
4030 
4031 	*pListenerId = k;
4032 	UT_return_val_if_fail (pListener, false);
4033 	m_pPieceTable->addListener(pListener,k);
4034 	return true;
4035 }
4036 
removeListener(PL_ListenerId listenerId)4037 bool PD_Document::removeListener(PL_ListenerId listenerId)
4038 {
4039 	xxx_UT_DEBUGMSG(("Removing lid %d from document %x \n",listenerId,this));
4040 	bool res = (m_vecListeners.setNthItem(listenerId,NULL,NULL) == 0);
4041 
4042 	// clear out all format handles that this listener has created
4043 	pf_Frag* pFrag = m_pPieceTable->getFragments().getFirst();
4044 	for (; pFrag; pFrag = pFrag->getNext())
4045 	{
4046 		if (pFrag->getType() == pf_Frag::PFT_Strux)
4047 		{
4048 			pf_Frag_Strux* pFS = static_cast<pf_Frag_Strux*>(pFrag);
4049 			pFS->setFmtHandle(listenerId, NULL);
4050 		}
4051 	}
4052 
4053 	return res;
4054 }
4055 
signalListeners(UT_uint32 iSignal) const4056 bool PD_Document::signalListeners(UT_uint32 iSignal) const
4057 {
4058 	if(m_bIgnoreSignals)
4059 		return true;
4060 	if(iSignal == PD_SIGNAL_UPDATE_LAYOUT)
4061 	{
4062 			m_iUpdateCount++;
4063 	}
4064 	else
4065 	{
4066 			m_iUpdateCount = 0;
4067 	}
4068 	if(m_iUpdateCount > 1)
4069   	{
4070 			return true;
4071 	}
4072 	PL_ListenerId lid;
4073 	PL_ListenerId lidCount = m_vecListeners.getItemCount();
4074 
4075 	// for each listener in our vector, we send a notification.
4076 	// we step over null listners (for listeners which have been
4077 	// removed (views that went away)).
4078 
4079 	for (lid=0; lid<lidCount; lid++)
4080 	{
4081 		PL_Listener * pListener = m_vecListeners.getNthItem(lid);
4082 		if (pListener)
4083 		{
4084 			pListener->signal(iSignal);
4085 		}
4086 	}
4087 
4088 	return true;
4089 }
4090 
4091 /*!
4092  * Remove all AbiCollab connections. We should do this before the document is destructed.
4093  */
removeConnections(void)4094 void PD_Document::removeConnections(void)
4095 {
4096 	PL_ListenerId lid;
4097 	PL_ListenerId lidCount = m_vecListeners.getItemCount();
4098 	for (lid=0; lid<lidCount; lid++)
4099 	{
4100 		PL_Listener * pListener = static_cast<PL_Listener *>(m_vecListeners.getNthItem(lid));
4101 		if (pListener)
4102 		{
4103 			if(pListener->getType() >= PTL_CollabExport)
4104 			{
4105 				static_cast<PL_DocChangeListener *>(pListener)->removeDocument();
4106 				removeListener(lid);
4107 			}
4108 		}
4109 	}
4110 }
4111 
4112 
4113 /*!
4114  * Change all AbiCollab connections to point to the new document.
4115  */
changeConnectedDocument(PD_Document * pDoc)4116 void PD_Document::changeConnectedDocument(PD_Document * pDoc)
4117 {
4118 	PL_ListenerId lid;
4119 	PL_ListenerId lidCount = m_vecListeners.getItemCount();
4120 	for (lid=0; lid<lidCount; lid++)
4121 	{
4122 		PL_Listener * pListener = static_cast<PL_Listener *>(m_vecListeners.getNthItem(lid));
4123 		if (pListener)
4124 		{
4125 			if(pListener->getType() >= PTL_CollabExport )
4126 			{
4127 				static_cast<PL_DocChangeListener *>(pListener)->setNewDocument(pDoc);
4128 				removeListener(lid);
4129 			}
4130 		}
4131 	}
4132 }
4133 
getAllViews() const4134 std::list<AV_View*> PD_Document::getAllViews() const
4135 {
4136     UT_GenericVector<AV_View *> t;
4137     getAllViews( &t );
4138     std::list<AV_View*> ret;
4139     for( int i=0; i < t.size(); ++i )
4140         ret.push_back( (AV_View*)t[i] );
4141     return ret;
4142 }
4143 
4144 
4145 /*!
4146  * return a vector of all the views attached to this document.
4147  */
getAllViews(UT_GenericVector<AV_View * > * vecViews) const4148 void PD_Document::getAllViews(UT_GenericVector<AV_View *> * vecViews) const
4149 {
4150 	PL_ListenerId lid;
4151 	PL_ListenerId lidCount = m_vecListeners.getItemCount();
4152 
4153 	// for each listener in our vector, we send a notification.
4154 	// we step over null listners (for listeners which have been
4155 	// removed (views that went away)).
4156 
4157 	for (lid=0; lid<lidCount; lid++)
4158 	{
4159 		PL_Listener * pListener = static_cast<PL_Listener *>(m_vecListeners.getNthItem(lid));
4160 		if (pListener)
4161 		{
4162 			if(pListener->getType() == PTL_DocLayout)
4163 				{
4164 					fl_DocListener * pLayoutList = static_cast<fl_DocListener *>(pListener);
4165 					const FL_DocLayout * pLayout = pLayoutList->getLayout();
4166 					if(pLayout != NULL)
4167 					{
4168 						AV_View * pView = reinterpret_cast<AV_View *>(pLayout->getView());
4169 						if(pView != NULL)
4170 						 {
4171 							 vecViews->addItem(pView);
4172 						 }
4173 					}
4174 				}
4175 		}
4176 	}
4177 }
4178 
notifyListeners(const pf_Frag_Strux * pfs,const PX_ChangeRecord * pcr) const4179 bool PD_Document::notifyListeners(const pf_Frag_Strux * pfs, const PX_ChangeRecord * pcr) const
4180 {
4181 	// notify listeners of a change.
4182 
4183 #ifdef PT_TEST
4184 	//pcr->__dump();
4185 #endif
4186 	m_iUpdateCount = 0;
4187 	if(pcr->getDocument() == NULL)
4188 	{
4189 	        pcr->setDocument(this);
4190 			pcr->setCRNumber();
4191 	}
4192 	else if(pcr->getCRNumber() == 0)
4193 	{
4194 			pcr->setCRNumber();
4195 	}
4196 	PL_ListenerId lid;
4197 	PL_ListenerId lidCount = m_vecListeners.getItemCount();
4198 
4199 	// for each listener in our vector, we send a notification.
4200 	// we step over null listners (for listeners which have been
4201 	// removed (views that went away)).
4202 
4203 	for (lid=0; lid<lidCount; lid++)
4204 	{
4205 		PL_Listener * pListener = static_cast<PL_Listener *>(m_vecListeners.getNthItem(lid));
4206 		if (pListener)
4207 		{
4208 			fl_ContainerLayout* sfh = 0;
4209 			if (pfs && (pListener->getType() < PTL_CollabExport))
4210 				sfh = pfs->getFmtHandle(lid);
4211 
4212 			// some pt elements have no corresponding layout elements (for example a
4213 			// hdr/ftr section that was deleted in revisions mode)
4214 			if(sfh && (pListener->getType() < PTL_CollabExport ))
4215 				pListener->change(sfh,pcr);
4216 			else if(pListener->getType() >= PTL_CollabExport)
4217 				pListener->change(NULL,pcr);
4218 		}
4219 	}
4220 
4221 	return true;
4222 }
4223 
deferNotifications(void)4224 void PD_Document::deferNotifications(void)
4225 {
4226 	// notify listeners to defer notifications.
4227 
4228 #ifdef PT_TEST
4229 	//pcr->__dump();
4230 #endif
4231 
4232 	PL_ListenerId lid;
4233 	PL_ListenerId lidCount = m_vecListeners.getItemCount();
4234 
4235 	// for each listener in our vector, we send a notification.
4236 	// we step over null listeners (for listeners which have been
4237 	// removed (views that went away)).
4238 
4239 	for (lid=0; lid<lidCount; lid++)
4240 	{
4241 		PL_Listener * pListener = static_cast<PL_Listener *>(m_vecListeners.getNthItem(lid));
4242 		if (pListener)
4243 		{
4244 			pListener->deferNotifications();
4245 		}
4246 	}
4247 }
4248 
getAdjustmentForCR(const PX_ChangeRecord * pcr) const4249 UT_sint32 PD_Document::getAdjustmentForCR(const PX_ChangeRecord * pcr) const
4250 {
4251 	UT_sint32 iAdj = 0;
4252 	switch(pcr->getType())
4253 	{
4254 		case PX_ChangeRecord::PXT_GlobMarker:
4255 			break;
4256 		case PX_ChangeRecord::PXT_InsertSpan:
4257 			{
4258 				const PX_ChangeRecord_SpanChange * pcrc = static_cast<const PX_ChangeRecord_SpanChange *> (pcr);
4259 				UT_uint32 iLen = pcrc->getLength();
4260 				iAdj = iLen;
4261 			}
4262 			break;
4263 		case PX_ChangeRecord::PXT_DeleteSpan:
4264 			{
4265 				const PX_ChangeRecord_SpanChange * pcrc = static_cast<const PX_ChangeRecord_SpanChange *> (pcr);
4266 				UT_uint32 iLen = pcrc->getLength();
4267 				iAdj = -iLen;
4268 			}
4269 			break;
4270 		case PX_ChangeRecord::PXT_ChangeSpan:
4271 			break;
4272 		case PX_ChangeRecord::PXT_InsertStrux:
4273 			iAdj = 1;
4274 			break;
4275 		case PX_ChangeRecord::PXT_DeleteStrux:
4276 			iAdj = -1;
4277 			break;
4278 		case PX_ChangeRecord::PXT_ChangeStrux:
4279 			break;
4280 		case PX_ChangeRecord::PXT_InsertObject:
4281 			iAdj =  1;
4282 			break;
4283 		case PX_ChangeRecord::PXT_DeleteObject:
4284 			iAdj = -1;
4285 			break;
4286 		case PX_ChangeRecord::PXT_ChangeObject:
4287 			break;
4288 		case PX_ChangeRecord::PXT_InsertFmtMark:
4289 			break;
4290 		case PX_ChangeRecord::PXT_DeleteFmtMark:
4291 			break;
4292 		case PX_ChangeRecord::PXT_ChangeFmtMark:
4293 			break;
4294 		case PX_ChangeRecord::PXT_ChangePoint:
4295 			break;
4296 		case PX_ChangeRecord::PXT_ListUpdate:
4297 			break;
4298 		case PX_ChangeRecord::PXT_StopList:
4299 			break;
4300 		case PX_ChangeRecord::PXT_UpdateField:
4301 			break;
4302 		case PX_ChangeRecord::PXT_RemoveList:
4303 			break;
4304 		case PX_ChangeRecord::PXT_UpdateLayout:
4305 			break;
4306 		case PX_ChangeRecord::PXT_AddStyle:
4307 			break;
4308 		case PX_ChangeRecord::PXT_RemoveStyle:
4309 			break;
4310 		case PX_ChangeRecord::PXT_CreateDataItem:
4311 			break;
4312 		case PX_ChangeRecord::PXT_ChangeDocProp:
4313 			break;
4314 		default:
4315 			break;
4316 	}
4317 	return iAdj;
4318 }
4319 
processDeferredNotifications(void)4320 void PD_Document::processDeferredNotifications(void)
4321 {
4322 	// notify listeners to process any deferred notifications.
4323 
4324 #ifdef PT_TEST
4325 	//pcr->__dump();
4326 #endif
4327 
4328 	PL_ListenerId lid;
4329 	PL_ListenerId lidCount = m_vecListeners.getItemCount();
4330 
4331 	// for each listener in our vector, we send a notification.
4332 	// we step over null listeners (for listeners which have been
4333 	// removed (views that went away)).
4334 
4335 	for (lid=0; lid<lidCount; lid++)
4336 	{
4337 		PL_Listener * pListener = m_vecListeners.getNthItem(lid);
4338 		if (pListener)
4339 		{
4340 			pListener->processDeferredNotifications();
4341 		}
4342 	}
4343 }
4344 
4345 
4346 
4347 
getNthFmtHandle(pf_Frag_Strux * sdh,UT_uint32 n)4348 fl_ContainerLayout* PD_Document::getNthFmtHandle(pf_Frag_Strux* sdh, UT_uint32 n)
4349 {
4350 	const pf_Frag_Strux * pfs = static_cast<const pf_Frag_Strux *>(sdh);
4351 	UT_uint32 nListen = m_vecListeners.getItemCount();
4352 	if(n >= nListen)
4353 		return NULL;
4354 	PL_ListenerId lid = static_cast<PL_ListenerId>(n);
4355 	fl_ContainerLayout* sfh = pfs->getFmtHandle(lid);
4356 	return sfh;
4357 }
4358 
s_BindHandles(pf_Frag_Strux * sdhNew,PL_ListenerId lid,fl_ContainerLayout * sfhNew)4359 static void s_BindHandles(pf_Frag_Strux* sdhNew,
4360 						  PL_ListenerId lid,
4361 						  fl_ContainerLayout* sfhNew)
4362 {
4363 	UT_return_if_fail (sdhNew);
4364 	UT_return_if_fail (sfhNew);
4365 
4366 	pf_Frag_Strux * pfsNew = sdhNew;
4367 	UT_DEBUGMSG(("Set Format handle number %d of strux %p to format %p \n",lid,pfsNew,sfhNew));
4368 	pfsNew->setFmtHandle(lid,sfhNew);
4369 }
4370 
notifyListeners(const pf_Frag_Strux * pfs,pf_Frag_Strux * pfsNew,const PX_ChangeRecord * pcr) const4371 bool PD_Document::notifyListeners(const pf_Frag_Strux * pfs,
4372 									 pf_Frag_Strux * pfsNew,
4373 									 const PX_ChangeRecord * pcr) const
4374 {
4375 	// notify listeners of a new strux.  this is slightly
4376 	// different from the other one because we need to exchange
4377 	// handles with the listener for the new strux.
4378 
4379 #ifdef PT_TEST
4380 	//pcr->__dump();
4381 #endif
4382 	m_iUpdateCount = 0;
4383 	if(pcr->getDocument() == NULL)
4384 	{
4385 	        pcr->setDocument(this);
4386 			pcr->setCRNumber();
4387 	}
4388 	else if(pcr->getCRNumber() == 0)
4389 	{
4390 			pcr->setCRNumber();
4391 	}
4392 
4393 	PL_ListenerId lid;
4394 	PL_ListenerId lidCount = m_vecListeners.getItemCount();
4395 
4396 	// for each listener in our vector, we send a notification.
4397 	// we step over null listeners (for listeners which have been
4398 	// removed (views that went away)).
4399 
4400 	for (lid=0; lid<lidCount; lid++)
4401 	{
4402 		PL_Listener * pListener = m_vecListeners.getNthItem(lid);
4403 		if (pListener)
4404 		{
4405 			pf_Frag_Strux* sdhNew = static_cast<pf_Frag_Strux*>(pfsNew);
4406 			fl_ContainerLayout* sfh = NULL;
4407 			if(pListener->getType() < PTL_CollabExport)
4408 				sfh = pfs->getFmtHandle(lid);
4409 			if (pListener->insertStrux(sfh,pcr,sdhNew,lid,s_BindHandles))
4410 			{
4411 				// verify that the listener used our callback
4412 				if(pListener->getType() < PTL_CollabExport)
4413 				{
4414 					UT_ASSERT_HARMLESS(pfsNew->getFmtHandle(lid));
4415 				}
4416 			}
4417 		}
4418 	}
4419 
4420 	return true;
4421 }
4422 
4423 /*!
4424  * Return true if the document has an abicollab connection
4425  */
isConnected(void)4426 bool PD_Document::isConnected(void)
4427 {
4428 	PL_ListenerId lid;
4429 	PL_ListenerId lidCount = m_vecListeners.getItemCount();
4430 	for (lid=0; lid<lidCount; lid++)
4431 	{
4432 		PL_Listener * pListener = m_vecListeners.getNthItem(lid);
4433 		if (pListener)
4434 		{
4435 			if(pListener->getType() >= PTL_CollabExport)
4436 			{
4437 				return true;
4438 			}
4439 		}
4440 	}
4441 	return false;
4442 
4443 }
4444 //////////////////////////////////////////////////////////////////
4445 //////////////////////////////////////////////////////////////////
4446 /*!
4447     If the input pAP contains revision attribute, this function
4448     returns AP in which the revision attribute has been inflated into
4449     actual properties and attributes (the return AP lives in the PT varset,
4450     so it is not to be modified, deleted, etc., but the caller)
4451 
4452     bShow indicates whether revisions are shown or hidden (view - dependent)
4453     iId is the id of revision to be shown (view - dependent)
4454 
4455     on return bHiddenRevision indicates if the element associated with
4456     pAP is to be hidden or visible
4457 */
explodeRevisions(PP_RevisionAttr * & pRevisions,const PP_AttrProp * pAP,bool bShow,UT_uint32 iId,bool & bHiddenRevision) const4458 const PP_AttrProp * PD_Document::explodeRevisions(PP_RevisionAttr *& pRevisions, const PP_AttrProp * pAP,
4459 												  bool bShow, UT_uint32 iId, bool &bHiddenRevision) const
4460 {
4461 	PP_AttrProp * pNewAP = NULL;
4462 	const gchar* pRevision = NULL;
4463 	bHiddenRevision = false;
4464 
4465 	bool bMark = isMarkRevisions();
4466 
4467 	if(pAP && pAP->getAttribute("revision", pRevision))
4468 	{
4469 		if(!pRevisions)
4470 			pRevisions = new PP_RevisionAttr(pRevision);
4471 
4472 		UT_return_val_if_fail(pRevisions, NULL);
4473 
4474 		//first we need to ascertain if this revision is visible
4475 		bool bDeleted = false;
4476 
4477 		const PP_Revision * pRev;
4478 		UT_uint32 i = 0;
4479 		UT_uint32 iMinId;
4480 
4481 		pRev = pRevisions->getLastRevision();
4482 		UT_return_val_if_fail(pRev, NULL);
4483 
4484 		UT_uint32 iMaxId = pRev->getId();
4485 
4486 		if(!bMark && !bShow && iId == 0)
4487 		{
4488 			// revisions are not to be shown, and the document to be
4489 			// shown in the state before the first revision, i.e.,
4490 			// additions are to be hidden, fmt changes ignored, and
4491 			// deletions will be visible
4492 
4493 			// see if the first revision is an addition ...
4494 			i = 1;
4495 			do
4496 			{
4497 				pRev = pRevisions->getRevisionWithId(i, iMinId);
4498 
4499 				if(!pRev)
4500 				{
4501 					UT_DEBUGMSG(("PD_Document::inflateRevisions: iMinId %d\n", iMinId));
4502 
4503 					if(iMinId == PD_MAX_REVISION)
4504 					{
4505 						UT_ASSERT_HARMLESS( UT_SHOULD_NOT_HAPPEN );
4506 						return NULL;
4507 					}
4508 
4509 					// jump directly to the first revision ...
4510 					i = iMinId;
4511 				}
4512 			}
4513 			while(!pRev && i <= iMaxId);
4514 
4515 
4516 			if(  (pRev->getType() == PP_REVISION_ADDITION)
4517 			   ||(pRev->getType() == PP_REVISION_ADDITION_AND_FMT))
4518 			{
4519 				bHiddenRevision = true;
4520 				return NULL;
4521 			}
4522 
4523 			bHiddenRevision = false;
4524 			return NULL;
4525 		}
4526 
4527 		if((bMark || !bShow) && iId != 0)
4528 		{
4529 			// revisions not to be shown, but document to be presented
4530 			// as it looks after the revision iId
4531 			// UT_ASSERT( bMark || iId == PD_MAX_REVISION );
4532 
4533 			UT_uint32 iMyMaxId = bMark ? UT_MIN(iId,iMaxId) : iMaxId;
4534 
4535 			// we need to loop through subsequent revisions,
4536 			// working out the their cumulative effect
4537 			i = 1;
4538 
4539 			for(i = 1; i <= iMyMaxId; i++)
4540 			{
4541 				pRev = pRevisions->getRevisionWithId(i,iMinId);
4542 
4543 				if(!pRev)
4544 				{
4545 					if(iMinId == PD_MAX_REVISION)
4546 					{
4547 						UT_ASSERT_HARMLESS( UT_SHOULD_NOT_HAPPEN );
4548 						break;
4549 					}
4550 
4551 					// advance i so that we do not waste our time, -1
4552 					// because of i++ in loop
4553 					i = iMinId - 1;
4554 					continue;
4555 				}
4556 
4557 
4558 				if(  (pRev->getType() == PP_REVISION_FMT_CHANGE && !bDeleted)
4559 					 ||(pRev->getType() == PP_REVISION_ADDITION_AND_FMT))
4560 				{
4561 					// create copy of span AP and then set all props contained
4562 					// in our revision;
4563 					if(!pNewAP)
4564 					{
4565 						pNewAP = new PP_AttrProp;
4566 						UT_return_val_if_fail(pNewAP,NULL);
4567 
4568 						(*pNewAP) = *pAP;
4569 						(*pNewAP) = *pRev;
4570 					}
4571 					else
4572 					{
4573 						// add fmt to our AP
4574 						pNewAP->setAttributes(pRev->getAttributes());
4575 						pNewAP->setProperties(pRev->getProperties());
4576 					}
4577 				}
4578 				else if(pRev->getType() == PP_REVISION_DELETION)
4579 				{
4580 					// deletion means resetting all previous fmt
4581 					// changes
4582 					if(pNewAP)
4583 					{
4584 						delete pNewAP;
4585 						pNewAP = NULL;
4586 					}
4587 
4588 					bDeleted = true;
4589 				}
4590 				else if(pRev->getType() == PP_REVISION_ADDITION)
4591 				{
4592 					bDeleted = false;
4593 				}
4594 			} // for
4595 
4596 			if(bDeleted)
4597 			{
4598 				bHiddenRevision = true;
4599 			}
4600 			else
4601 			{
4602 				bHiddenRevision = false;
4603 			}
4604 
4605 			if(!bMark || iId == PD_MAX_REVISION)
4606 			{
4607 				if(pNewAP)
4608 				{
4609 					// explode style, prune and store the AP
4610 					pNewAP->explodeStyle(this);
4611 					pNewAP->prune();
4612 					pNewAP->markReadOnly();
4613 
4614 					PT_AttrPropIndex api;
4615 					UT_return_val_if_fail(getPieceTable()->getVarSet().addIfUniqueAP(pNewAP, &api), NULL);
4616 					pAP->setRevisedIndex(api,iId,bShow,bMark,bHiddenRevision);
4617 
4618 					// the above might have resulted in the deletion
4619 					// of pNewAP -- retrieve it by the index
4620 					getAttrProp(api, const_cast<const PP_AttrProp **>(&pNewAP));
4621 				}
4622 
4623 				return pNewAP;
4624 			}
4625 
4626 			// if we are in Mark mode, we need to process the last
4627 			// revision ...
4628 		}
4629 		else if(!pRevisions->isVisible(iId))
4630 		{
4631 			// we are to show revisions with id <= iId
4632 			bHiddenRevision = true;
4633 			UT_ASSERT_HARMLESS(!pNewAP);
4634 			return NULL;
4635 		}
4636 
4637 		//next step is to find any fmt changes, layering them as
4638 		//subsequent revisions come
4639 		if(bMark && iId != 0)
4640 		{
4641 			// we are in Mark mode and only interested in the last
4642 			// revision; the loop below will run only once
4643 			i = UT_MIN(iId+1,iMaxId);
4644 		}
4645 		else
4646 		{
4647 			i = 1;
4648 		}
4649 
4650 
4651 		for(i = 1; i <= iMaxId; i++)
4652 		{
4653 			pRev = pRevisions->getRevisionWithId(i,iMinId);
4654 
4655 			if(!pRev)
4656 			{
4657 				if(iMinId == PD_MAX_REVISION)
4658 				{
4659 					UT_ASSERT_HARMLESS( UT_SHOULD_NOT_HAPPEN );
4660 					break;
4661 				}
4662 
4663 				// advance i so that we do not waste our time, -1
4664 				// because of i++ in loop
4665 				i = iMinId - 1;
4666 				continue;
4667 			}
4668 
4669 
4670 			if(  (pRev->getType() == PP_REVISION_FMT_CHANGE && !bDeleted)
4671 				 ||(pRev->getType() == PP_REVISION_ADDITION_AND_FMT))
4672 			{
4673 				// create copy of span AP and then set all props contained
4674 				// in our revision;
4675 				if(!pNewAP)
4676 				{
4677 					pNewAP = new PP_AttrProp;
4678 					UT_return_val_if_fail(pNewAP, NULL);
4679 
4680 					(*pNewAP) = *pAP;
4681 					(*pNewAP) = *pRev;
4682 					bDeleted = false;
4683 				}
4684 				else
4685 				{
4686 					// add fmt to our AP
4687 					pNewAP->setAttributes(pRev->getAttributes());
4688 					pNewAP->setProperties(pRev->getProperties());
4689 					bDeleted = false;
4690 				}
4691 			}
4692 		} // for
4693 	} // if "revision"
4694 
4695 	if(pNewAP)
4696 	{
4697 		// explode style, prune and store the AP
4698 		pNewAP->explodeStyle(this);
4699 		pNewAP->prune();
4700 		pNewAP->markReadOnly();
4701 
4702 		PT_AttrPropIndex api;
4703 		UT_return_val_if_fail(getPieceTable()->getVarSet().addIfUniqueAP(pNewAP, &api), NULL);
4704 		pAP->setRevisedIndex(api,iId,bShow,bMark,bHiddenRevision);
4705 
4706 		// the above might have resulted in the deletion
4707 		// of pNewAP -- retrieve it by the index
4708 		getAttrProp(api, const_cast<const PP_AttrProp**>(&pNewAP));
4709 	}
4710 
4711 	return pNewAP;
4712 }
4713 
getAttrProp(PT_AttrPropIndex indexAP,const PP_AttrProp ** ppAP) const4714 bool PD_Document::getAttrProp(PT_AttrPropIndex indexAP, const PP_AttrProp ** ppAP) const
4715 {
4716 	return m_pPieceTable->getAttrProp(indexAP,ppAP);
4717 }
4718 
getPointer(PT_BufIndex bi) const4719 const UT_UCSChar * PD_Document::getPointer(PT_BufIndex bi) const
4720 {
4721 	// the pointer that we return is NOT a zero-terminated
4722 	// string.  the caller is responsible for knowing how
4723 	// long the data is within the span/fragment.
4724 
4725 	return m_pPieceTable->getPointer(bi);
4726 }
4727 
getBlockBuf(pf_Frag_Strux * sdh,UT_GrowBuf * pgb) const4728 bool PD_Document::getBlockBuf(pf_Frag_Strux* sdh, UT_GrowBuf * pgb) const
4729 {
4730 	return m_pPieceTable->getBlockBuf(sdh,pgb);
4731 }
4732 
getBounds(bool bEnd,PT_DocPosition & docPos) const4733 bool PD_Document::getBounds(bool bEnd, PT_DocPosition & docPos) const
4734 {
4735 	return m_pPieceTable->getBounds(bEnd,docPos);
4736 }
4737 
getStruxPosition(pf_Frag_Strux * sdh) const4738 PT_DocPosition PD_Document::getStruxPosition(pf_Frag_Strux* sdh) const
4739 {
4740 	return m_pPieceTable->getStruxPosition(sdh);
4741 }
4742 
getSpanAttrProp(pf_Frag_Strux * sdh,UT_uint32 offset,bool bLeftSide,const PP_AttrProp ** ppAP) const4743 bool PD_Document::getSpanAttrProp(pf_Frag_Strux* sdh, UT_uint32 offset, bool bLeftSide,
4744 									 const PP_AttrProp ** ppAP) const
4745 {
4746 	return m_pPieceTable->getSpanAttrProp(sdh,offset,bLeftSide,ppAP);
4747 }
4748 
4749 /*!
4750  * Return strux type of the StruxDocHandle
4751  */
getStruxType(pf_Frag_Strux * sdh) const4752 PTStruxType PD_Document::getStruxType(pf_Frag_Strux* sdh) const
4753 {
4754 	UT_return_val_if_fail( sdh,(PTStruxType)0 );
4755 	const pf_Frag * pf = static_cast<const pf_Frag *>(sdh);
4756 	UT_return_val_if_fail (pf->getType() == pf_Frag::PFT_Strux,(PTStruxType)0);
4757 	const pf_Frag_Strux * pfs = static_cast<const pf_Frag_Strux *> (pf);
4758 	return pfs->getStruxType();
4759 }
4760 
4761 /*!
4762  * Returns true if the document as any math or SVG runs within it.
4763  */
hasMath(void)4764 bool PD_Document::hasMath(void)
4765 {
4766 	pf_Frag *  pf = getPieceTable()->getFragments().getFirst();
4767 	while(pf)
4768 	{
4769 		if(pf->getType() == pf_Frag::PFT_Object)
4770 		{
4771 			pf_Frag_Object * po = (pf_Frag_Object*) pf;
4772 			if(po->getObjectType() == PTO_Math)
4773 			{
4774 				return true;
4775 			}
4776 		}
4777 		pf = pf->getNext();
4778 	}
4779 	return false;
4780 }
4781 
findBookmark(const char * pName,bool bEnd,pf_Frag * pfStart)4782 pf_Frag * PD_Document::findBookmark(const char * pName, bool bEnd, pf_Frag * pfStart)
4783 {
4784 	if(!pfStart)
4785 	{
4786 		pfStart = getPieceTable()->getFragments().getFirst();
4787 	}
4788 
4789 	UT_return_val_if_fail(pfStart, NULL);
4790 
4791 	pf_Frag * pf = pfStart;
4792 	while(pf)
4793 	{
4794 		if(pf->getType() == pf_Frag::PFT_Object)
4795 		{
4796 			pf_Frag_Object * po = (pf_Frag_Object*) pf;
4797 			if(po->getObjectType() == PTO_Bookmark)
4798 			{
4799 				po_Bookmark * pb = po->getBookmark();
4800 				if(!pb)
4801 				{
4802 					UT_ASSERT_HARMLESS( UT_SHOULD_NOT_HAPPEN );
4803 					pf = pf->getNext();
4804 					continue;
4805 				}
4806 
4807 				if(  (!bEnd && pb->getBookmarkType() == po_Bookmark::POBOOKMARK_START)
4808 				   ||( bEnd && pb->getBookmarkType() == po_Bookmark::POBOOKMARK_END))
4809 				{
4810 					if(0 == strcmp(pName, pb->getName()))
4811 					   return pf;
4812 				}
4813 			}
4814 		}
4815 
4816 		pf = pf->getNext();
4817 	}
4818 
4819 	return NULL;
4820 }
4821 
4822 
getBookmark(pf_Frag_Strux * sdh,UT_uint32 offset)4823 po_Bookmark * PD_Document::getBookmark(pf_Frag_Strux* sdh, UT_uint32 offset)
4824 {
4825 	const pf_Frag * pf = static_cast<const pf_Frag *>(sdh);
4826 	UT_return_val_if_fail (pf->getType() == pf_Frag::PFT_Strux, NULL);
4827 	const pf_Frag_Strux * pfsBlock = static_cast<const pf_Frag_Strux *> (pf);
4828 	UT_return_val_if_fail (pfsBlock->getStruxType() == PTX_Block, NULL);
4829 
4830 	UT_uint32 cumOffset = 0;
4831 	pf_Frag_Object * pfo = NULL;
4832 	for (pf_Frag * pfTemp=pfsBlock->getNext(); (pfTemp); pfTemp=pfTemp->getNext())
4833 	{
4834 		cumOffset += pfTemp->getLength();
4835 		if (offset < cumOffset)
4836 		{
4837 			switch (pfTemp->getType())
4838 			{
4839 				case pf_Frag::PFT_Object:
4840 					pfo = static_cast<pf_Frag_Object *> (pfTemp);
4841 					return pfo->getBookmark();
4842 				default:
4843 					return NULL;
4844 			}
4845 		}
4846 
4847 	}
4848 	return NULL;
4849 }
4850 
getField(pf_Frag_Strux * sdh,UT_uint32 offset,fd_Field * & pField)4851 bool PD_Document::getField(pf_Frag_Strux* sdh, UT_uint32 offset,
4852                                fd_Field * & pField)
4853 {
4854 
4855 	const pf_Frag * pf = static_cast<const pf_Frag *>(sdh);
4856 	UT_return_val_if_fail (pf->getType() == pf_Frag::PFT_Strux, false);
4857 	const pf_Frag_Strux * pfsBlock = static_cast<const pf_Frag_Strux *> (pf);
4858 	UT_return_val_if_fail (pfsBlock->getStruxType() == PTX_Block, false);
4859 
4860 	UT_uint32 cumOffset = 0;
4861 	pf_Frag_Text * pft = NULL;
4862 	for (pf_Frag * pfTemp=pfsBlock->getNext(); (pfTemp); pfTemp=pfTemp->getNext())
4863 	{
4864 		cumOffset += pfTemp->getLength();
4865 		if (offset < cumOffset)
4866 		{
4867 			switch (pfTemp->getType())
4868 			{
4869 			case pf_Frag::PFT_Text:
4870 			case pf_Frag::PFT_Object:
4871 				pft = static_cast<pf_Frag_Text *> (pfTemp);
4872 				pField = pft->getField();
4873 				return true; // break out of loop
4874 				break;
4875 			default:
4876 				return false;
4877 				break;
4878 			}
4879 		}
4880 
4881 	}
4882 	return false;
4883 }
4884 
getStruxFromPosition(PL_ListenerId listenerId,PT_DocPosition docPos,fl_ContainerLayout ** psfh) const4885 bool PD_Document::getStruxFromPosition(PL_ListenerId listenerId,
4886 										  PT_DocPosition docPos,
4887 										  fl_ContainerLayout* * psfh) const
4888 {
4889 	return m_pPieceTable->getStruxFromPosition(listenerId,docPos,psfh);
4890 }
4891 
getStruxOfTypeFromPosition(PL_ListenerId listenerId,PT_DocPosition docPos,PTStruxType pts,fl_ContainerLayout ** psfh) const4892 bool PD_Document::getStruxOfTypeFromPosition(PL_ListenerId listenerId,
4893 												PT_DocPosition docPos,
4894 												PTStruxType pts,
4895 												fl_ContainerLayout* * psfh) const
4896 {
4897 	return m_pPieceTable->getStruxOfTypeFromPosition(listenerId,docPos,pts,psfh);
4898 }
4899 
getBlockFromPosition(PT_DocPosition pos) const4900 pf_Frag_Strux* PD_Document::getBlockFromPosition( PT_DocPosition pos ) const
4901 {
4902     return m_pPieceTable->getBlockFromPosition( pos );
4903 }
4904 
4905 
4906 ///
4907 ///  return the SDH of the last strux of the given type
4908 /// immediately prior to the given absolute document position.
4909 /// This sdh is actually a (void *) pointer to a pf_Frag_Strux
4910 ///
getStruxOfTypeFromPosition(PT_DocPosition docPos,PTStruxType pts,pf_Frag_Strux ** sdh) const4911 bool PD_Document::getStruxOfTypeFromPosition(PT_DocPosition docPos,
4912 												PTStruxType pts,
4913 												pf_Frag_Strux* * sdh) const
4914 {
4915 	return m_pPieceTable->getStruxOfTypeFromPosition(docPos,pts, sdh);
4916 }
4917 
4918 ///
4919 /// Return the sdh of type pts immediately prior to sdh
4920 ///
getPrevStruxOfType(pf_Frag_Strux * sdh,PTStruxType pts,pf_Frag_Strux ** prevsdh)4921 bool PD_Document::getPrevStruxOfType(pf_Frag_Strux* sdh,PTStruxType pts,
4922 					pf_Frag_Strux* * prevsdh)
4923 {
4924 	pf_Frag* pfs = sdh;
4925 	UT_return_val_if_fail (pfs, false);
4926 	pfs = pfs->getPrev();
4927 	for (pf_Frag * pf=pfs; (pf); pf=pf->getPrev())
4928 		if (pf->getType() == pf_Frag::PFT_Strux)
4929 		{
4930 			pf_Frag_Strux * pfsTemp = static_cast<pf_Frag_Strux *>(pf);
4931 			if (pfsTemp->getStruxType() == pts)	// did we find it
4932 			{
4933 				*prevsdh = pfsTemp;
4934 				return true;
4935 			}
4936 		}
4937 
4938 	// did not find it.
4939 
4940 	return false;
4941 }
4942 
4943 
4944 ///
4945 ///get the next strux after the strux given. Skip embedded strux's
4946 ///
getNextStrux(pf_Frag_Strux * sdh,pf_Frag_Strux ** nextsdh)4947 bool PD_Document::getNextStrux(pf_Frag_Strux* sdh,
4948 							   pf_Frag_Strux* * nextsdh)
4949 {
4950 	pf_Frag_Strux * pfs = sdh;
4951 	UT_return_val_if_fail (pfs, false);
4952 	pfs = static_cast<pf_Frag_Strux *>(pfs->getNext());
4953 	UT_sint32 iEmbedDepth = 0;
4954 	for (pf_Frag * pf=pfs; (pf); pf=pf->getNext())
4955 	{
4956 		if (pf->getType() == pf_Frag::PFT_Strux)
4957 		{
4958 			pf_Frag_Strux * pfsTemp = static_cast<pf_Frag_Strux *>(pf);
4959 			if(iEmbedDepth <= 0 && !m_pPieceTable->isFootnote(pf) &&
4960 									 !m_pPieceTable->isEndFootnote(pf))
4961 			{
4962 				*nextsdh = pfsTemp;
4963 				return true;
4964 			}
4965 			else if(m_pPieceTable->isFootnote(pf))
4966 			{
4967 				iEmbedDepth++;
4968 			}
4969 			else if(m_pPieceTable->isEndFootnote(pf))
4970 			{
4971 				iEmbedDepth--;
4972 			}
4973 		}
4974 	}
4975 	// did not find it.
4976 
4977 	return false;
4978 }
4979 
getFragFromPosition(PT_DocPosition docPos) const4980 pf_Frag * PD_Document::getFragFromPosition(PT_DocPosition docPos) const
4981 {
4982 	pf_Frag * pf = 0;
4983 	m_pPieceTable->getFragFromPosition(docPos,&pf,0);
4984 	return pf;
4985 }
4986 
4987 ///
4988 /// Return the sdh of type pts immediately after sdh
4989 ///
getNextStruxOfType(pf_Frag_Strux * sdh,PTStruxType pts,pf_Frag_Strux ** nextsdh)4990 bool PD_Document::getNextStruxOfType(pf_Frag_Strux* sdh,PTStruxType pts,
4991 					pf_Frag_Strux* * nextsdh)
4992 {
4993 	pf_Frag_Strux * pfs = sdh;
4994 	UT_return_val_if_fail (pfs, false);
4995 	pfs = static_cast<pf_Frag_Strux *>(pfs->getNext());
4996 	UT_sint32 iNest = 0;
4997 	for (pf_Frag * pf=pfs; (pf); pf=pf->getNext())
4998 		if (pf->getType() == pf_Frag::PFT_Strux)
4999 		{
5000 			pf_Frag_Strux * pfsTemp = static_cast<pf_Frag_Strux *>(pf);
5001 			if((pfsTemp->getStruxType() == PTX_SectionTable) && (pts != PTX_SectionTable))
5002 			{
5003 				iNest++;
5004 				continue;
5005 			}
5006 			if((iNest > 0) && (pfsTemp->getStruxType() == PTX_EndTable))
5007 			{
5008 				iNest--;
5009 				continue;
5010 			}
5011 			if(iNest > 0)
5012 			{
5013 				continue;
5014 			}
5015 			if (pfsTemp->getStruxType() == pts)	// did we find it
5016 			{
5017 				*nextsdh = pfsTemp;
5018 				return true;
5019 			}
5020 		}
5021 
5022 	// did not find it.
5023 
5024 	return false;
5025 }
5026 
5027 
5028 //////////////////////////////////////////////////////////////////
5029 //////////////////////////////////////////////////////////////////
5030 
beginUserAtomicGlob(void)5031 void PD_Document::beginUserAtomicGlob(void)
5032 {
5033 	m_pPieceTable->beginUserAtomicGlob();
5034 }
5035 
endUserAtomicGlob(void)5036 void PD_Document::endUserAtomicGlob(void)
5037 {
5038 	m_pPieceTable->endUserAtomicGlob();
5039 }
5040 
undoCount(bool bUndo) const5041 UT_uint32 PD_Document::undoCount(bool bUndo) const
5042 {
5043   return m_pPieceTable->undoCount(bUndo);
5044 }
5045 
canDo(bool bUndo) const5046 bool PD_Document::canDo(bool bUndo) const
5047 {
5048 	return m_pPieceTable->canDo(bUndo);
5049 }
5050 
undoCmd(UT_uint32 repeatCount)5051 bool PD_Document::undoCmd(UT_uint32 repeatCount)
5052 {
5053 	UT_sint32 sRepeatCount = static_cast<UT_uint32>(repeatCount);
5054 	while (sRepeatCount > 0)
5055 	{
5056 		UT_uint32 inCount = undoCount(true);
5057 		if (!m_pPieceTable->undoCmd())
5058 			return false;
5059 		sRepeatCount -= inCount - undoCount(true);
5060 	}
5061 	return true;
5062 }
5063 
redoCmd(UT_uint32 repeatCount)5064 bool PD_Document::redoCmd(UT_uint32 repeatCount)
5065 {
5066 	while (repeatCount--)
5067 		if (!m_pPieceTable->redoCmd())
5068 			return false;
5069 	return true;
5070 }
5071 
5072 
isDoingTheDo(void) const5073 bool  PD_Document::isDoingTheDo(void) const
5074 {
5075 	return m_pPieceTable->isDoingTheDo();
5076 }
5077 
5078 ///////////////////////////////////////////////////////////////////
5079 // DataItems represent opaque (and probably binary) data found in
5080 // the data-section of the document.  These are used, for example,
5081 // to store the actual data of an image.  The inline image tag has
5082 // a reference to a DataItem.
5083 // mime_type is the associated mime type for the blob.
createDataItem(const char * szName,bool bBase64,const UT_ByteBuf * pByteBuf,const std::string & mime_type,PD_DataItemHandle * ppHandle)5084 bool PD_Document::createDataItem(const char * szName, bool bBase64,
5085                                  const UT_ByteBuf * pByteBuf,
5086                                  const std::string & mime_type,
5087                                  PD_DataItemHandle* ppHandle)
5088 {
5089 	PD_DataItemHandle pPair = NULL;
5090     UT_ByteBuf * pNew = NULL;
5091 
5092 	UT_return_val_if_fail (pByteBuf, false);
5093 
5094 	// verify unique name
5095 	UT_DEBUGMSG(("Create data item name %s \n",szName));
5096 	if (getDataItemDataByName(szName,NULL,NULL,NULL) == true)
5097     {
5098         UT_DEBUGMSG(("Data item %s already exists! \n",szName));
5099         goto Failed;
5100     }
5101 	// set the actual DataItem's data using the contents of the ByteBuf.
5102 	// we must copy it if we want to keep it.  bBase64 is TRUE if the
5103 	// data is Base64 encoded.
5104 
5105 	pNew = new UT_ByteBuf();
5106 	if (!pNew) {
5107 		goto Failed;
5108     }
5109 
5110 	if (bBase64)
5111 	{
5112 		if (!UT_Base64Decode(pNew,pByteBuf))
5113 			goto Failed;
5114 	}
5115 	else
5116 	{
5117 		if (!pNew->ins(0,pByteBuf->getPointer(0),pByteBuf->getLength()))
5118 			goto Failed;
5119 	}
5120 
5121 	pPair = new _dataItemPair();
5122 	if (!pPair)
5123 	{
5124 		goto Failed;
5125 	}
5126 
5127 	pPair->pBuf = pNew;
5128 	pPair->pToken = g_strdup(mime_type.c_str());
5129 	m_hashDataItems.insert(std::make_pair(szName, pPair));
5130 
5131 	// give them back a handle if they want one
5132 
5133 	if (ppHandle)
5134 	{
5135 		hash_data_items_t::iterator iter = m_hashDataItems.find(szName);
5136 		UT_return_val_if_fail (iter != m_hashDataItems.end(), false);
5137 		*ppHandle = iter->second;
5138 	}
5139 	{
5140 		const gchar * szAttributes[3] = {PT_DATAITEM_ATTRIBUTE_NAME,szName,NULL};
5141 		PT_AttrPropIndex iAP= 0;
5142 		m_pPieceTable->getVarSet().storeAP(szAttributes, &iAP);
5143 		PX_ChangeRecord * pcr =  new PX_ChangeRecord(PX_ChangeRecord::PXT_CreateDataItem,0,iAP,getXID());
5144 		UT_DEBUGMSG(("indexAP %d \n",iAP));
5145 		notifyListeners(NULL, pcr);
5146 		delete pcr;
5147 	}
5148 	return true;
5149 
5150 Failed:
5151 	if (pNew)
5152 	{
5153 		delete pNew;
5154 	}
5155 
5156 	return false;
5157 }
5158 
5159 /*!
5160  * Replace the contents of the pre-existing data item with this new
5161  * data item (pByteBuf). Used when updating a preview of an embedded object.
5162  */
replaceDataItem(const char * szName,const UT_ByteBuf * pByteBuf)5163 bool PD_Document::replaceDataItem(const char * szName, const UT_ByteBuf * pByteBuf)
5164 {
5165 	// verify data item exists
5166 
5167 	hash_data_items_t::iterator iter = m_hashDataItems.find(szName);
5168 	if (iter == m_hashDataItems.end()) {
5169 		return false;
5170 	}
5171 
5172 	_dataItemPair* pPair = iter->second;
5173 	UT_return_val_if_fail (pPair, false);
5174 
5175 	UT_return_val_if_fail (pByteBuf, false);
5176 
5177 	UT_ByteBuf * pOldBuf =  pPair->pBuf;
5178 	pOldBuf->truncate(0);
5179 	if (!pOldBuf->ins(0,pByteBuf->getPointer(0),pByteBuf->getLength()))
5180 		return false;
5181 
5182 	return true;
5183 }
5184 
getDataItemDataByName(const char * szName,const UT_ByteBuf ** ppByteBuf,std::string * pMimeType,PD_DataItemHandle * ppHandle) const5185 bool PD_Document::getDataItemDataByName(const char * szName,
5186 										   const UT_ByteBuf ** ppByteBuf,
5187                                         std::string * pMimeType,
5188 										   PD_DataItemHandle* ppHandle) const
5189 {
5190 	UT_DEBUGMSG(("Look for %s \n",szName));
5191 	UT_return_val_if_fail (szName && *szName, false);
5192 
5193 
5194 	hash_data_items_t::const_iterator iter = m_hashDataItems.find(szName);
5195 	if (iter == m_hashDataItems.end()) {
5196 		return false;
5197 	}
5198 
5199 	_dataItemPair* pPair = iter->second;
5200 	UT_DEBUGMSG(("Found data item name %s buf %p \n",szName,pPair->pBuf));
5201 
5202 	if (ppByteBuf)
5203 	{
5204 		*ppByteBuf = pPair->pBuf;
5205 	}
5206 
5207 	if (pMimeType)
5208 	{
5209 		*pMimeType = (const char *)pPair->pToken;
5210 	}
5211 
5212 	if (ppHandle)
5213 	{
5214 		*ppHandle = pPair;
5215 	}
5216 
5217 	return true;
5218 }
5219 
5220 /*! This function accepts a data ID and assigns the file extension of the corresponding data item.
5221 	 \param szDataID The incoming data ID to look up
5222 	 \param sExt The extension string that is populated on success
5223 	 \param bDot A boolean to determine whether the extension string will be prefixed with a dot ('.').
5224 	 Defaults to true.
5225 	 \return Returns true only if the data item is found _and_ an extension is assigned.
5226 */
5227 
getDataItemFileExtension(const char * szDataID,std::string & sExt,bool bDot) const5228 bool PD_Document::getDataItemFileExtension(const char *szDataID, std::string &sExt, bool bDot) const
5229 {
5230 	UT_return_val_if_fail(szDataID && *szDataID, false);
5231 
5232     std::string mimeType;
5233 
5234 	if(getDataItemDataByName(szDataID, NULL, &mimeType, NULL))
5235 	{
5236 		if(mimeType.empty())
5237 			return false;
5238 
5239 		if(mimeType == "image/png")
5240 		{
5241 			sExt = (bDot ? "." : "");
5242 			sExt += "png";
5243 			return true;
5244 		}
5245 		if(mimeType == "image/jpeg")
5246 		{
5247 			sExt = (bDot ? "." : "");
5248 			sExt += "jpg";
5249 			return true;
5250 		}
5251 		else if(mimeType ==  "image/svg+xml")
5252 		{
5253 			sExt = (bDot ? "." : "");
5254 			sExt += "svg";
5255 			return true;
5256 		}
5257 		else
5258 		{
5259 			UT_DEBUGMSG(("getDataItemFileExtension(): unhandled/ignored mime type: %s\n", mimeType.c_str()));
5260 		}
5261 	}
5262 
5263 	return false;
5264 }
5265 
5266 
setDataItemToken(PD_DataItemHandle pHandle,void * pToken) const5267 bool PD_Document::setDataItemToken(PD_DataItemHandle pHandle,
5268 									  void* pToken) const
5269 {
5270 	UT_return_val_if_fail (pHandle, false);
5271 
5272 	_dataItemPair* pPair = pHandle;
5273 	UT_return_val_if_fail (pPair, false);
5274 
5275 	pPair->pToken = pToken;
5276 
5277 	return true;
5278 }
5279 
getDataItemData(PD_DataItemHandle pHandle,const char ** pszName,const UT_ByteBuf ** ppByteBuf,const void ** ppToken) const5280 bool PD_Document::getDataItemData(PD_DataItemHandle pHandle,
5281 									 const char ** pszName,
5282 									 const UT_ByteBuf ** ppByteBuf,
5283 									 const void** ppToken) const
5284 {
5285 	UT_return_val_if_fail (pHandle,false);
5286 
5287 	_dataItemPair* pPair = pHandle;
5288 
5289 	if (ppByteBuf)
5290 	{
5291 		*ppByteBuf = pPair->pBuf;
5292 	}
5293 
5294 	if (ppToken)
5295 	{
5296 		*ppToken = pPair->pToken;
5297 	}
5298 
5299 	if (pszName)
5300 	{
5301 		UT_ASSERT_HARMLESS(UT_TODO);
5302 		*pszName = 0;
5303 		//*pszName = pHashEntry->pszLeft;
5304 	}
5305 
5306 	return true;
5307 }
5308 
enumDataItems(UT_uint32 k,PD_DataItemHandle * ppHandle,const char ** pszName,const UT_ByteBuf ** ppByteBuf,std::string * pMimeType) const5309 bool PD_Document::enumDataItems(UT_uint32 k,
5310                                 PD_DataItemHandle* ppHandle, const char ** pszName,
5311 								const UT_ByteBuf ** ppByteBuf, std::string * pMimeType) const
5312 {
5313 	// return the kth data item.
5314 
5315 	UT_uint32 kLimit = m_hashDataItems.size();
5316 	if (k >= kLimit)
5317 		return false;
5318 
5319 	UT_uint32 i = 0;
5320 	hash_data_items_t::const_iterator iter;
5321 	for (iter = m_hashDataItems.begin();
5322 		 iter != m_hashDataItems.end() && i < k; ++i, ++iter) {
5323 
5324 		// noop
5325 	}
5326 
5327 	if (ppHandle && iter != m_hashDataItems.end()) {
5328 		*ppHandle = iter->second;
5329 	}
5330 
5331 	const struct _dataItemPair* pPair = iter->second;
5332 	UT_return_val_if_fail (pPair, false);
5333 
5334 	if (ppByteBuf)
5335 	{
5336 		*ppByteBuf = pPair->pBuf;
5337 	}
5338 
5339 	if (pMimeType)
5340 	{
5341 		*pMimeType = (const char *)pPair->pToken;
5342 	}
5343 
5344 	if (pszName)
5345 	{
5346 		*pszName = iter->first.c_str();
5347 	}
5348 
5349 	return true;
5350 }
5351 
_destroyDataItemData(void)5352 void PD_Document::_destroyDataItemData(void)
5353 {
5354 	if (m_hashDataItems.empty())
5355 		return;
5356 
5357 	hash_data_items_t::iterator iter;
5358 
5359 	for (iter = m_hashDataItems.begin(); iter != m_hashDataItems.end(); ++iter) {
5360 		xxx_UT_DEBUGMSG(("DOM: destroying data item\n"));
5361 		_dataItemPair* pPair = iter->second;
5362 		UT_return_if_fail (pPair);
5363 		delete pPair->pBuf;
5364 		FREEP(pPair->pToken);
5365 		delete pPair;
5366 	}
5367 	m_hashDataItems.clear();
5368 }
5369 
5370 /*!
5371   Synchronize the last opened/last saves filetypes.
5372  \param bReadLastSavedAsType True to write last opened and read last
5373            saved type; otherwise, write last saved type from last opened type.
5374 
5375  There are actually two filetypes - one for importers and one for
5376  exporters.  This function tries to synchronize the one to the other.
5377 */
_syncFileTypes(bool bReadSaveWriteOpen)5378 bool PD_Document::_syncFileTypes(bool bReadSaveWriteOpen)
5379 {
5380 	const char *szSuffixes;
5381 
5382 	// used to operate on description. now operates on suffixes
5383 
5384 	if (bReadSaveWriteOpen)
5385 	  szSuffixes = IE_Exp::suffixesForFileType(m_lastSavedAsType);
5386 	else
5387 	  szSuffixes = IE_Imp::suffixesForFileType(m_lastOpenedType);
5388 
5389 	if (!szSuffixes)
5390 	  return false;
5391 
5392 	IEFileType ieft;
5393 	if (bReadSaveWriteOpen)
5394 	{
5395 		ieft = IE_Imp::fileTypeForSuffixes(szSuffixes);
5396 		m_lastOpenedType = ieft;
5397 	}
5398 	else
5399 	{
5400 		ieft = IE_Exp::fileTypeForSuffixes(szSuffixes);
5401 		m_lastSavedAsType = ieft;
5402 	}
5403 
5404 	if (ieft == IEFT_Unknown || ieft == IEFT_Bogus)
5405 	{
5406 		//UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
5407 		return false;
5408 	}
5409 
5410 	return true;
5411 }
5412 
5413 ///////////////////////////////////////////////////////////////////
5414 // Styles represent named collections of formatting properties.
5415 
getDefaultStyle() const5416 const char * PD_Document::getDefaultStyle() const
5417 {
5418     return "Normal";
5419 }
5420 
getStyle(const char * szName,PD_Style ** ppStyle) const5421 bool PD_Document::getStyle(const char * szName, PD_Style ** ppStyle) const
5422 {
5423 	return m_pPieceTable->getStyle(szName, ppStyle);
5424 }
5425 
enumStyles(UT_uint32 k,const char ** pszName,const PD_Style ** ppStyle) const5426 bool PD_Document::enumStyles(UT_uint32 k,
5427 								const char ** pszName, const PD_Style ** ppStyle) const
5428 {
5429 	return m_pPieceTable->enumStyles(k, pszName, ppStyle);
5430 }
5431 
enumStyles(UT_GenericVector<PD_Style * > * & pStyles) const5432 bool PD_Document::enumStyles(UT_GenericVector<PD_Style*> * & pStyles) const
5433 {
5434 	return m_pPieceTable->enumStyles(pStyles);
5435 }
5436 
getStyleProperty(const char * szStyleName,const char * szPropertyName,const char * & szPropertyValue)5437 bool PD_Document::getStyleProperty(const char * szStyleName, const char * szPropertyName, const char *& szPropertyValue)
5438 {
5439 	PD_Style * pS;
5440 	PD_Style ** ppS = &pS;
5441 	if(!m_pPieceTable->getStyle(szStyleName, ppS))
5442 		return false;
5443 
5444 	return (*ppS)->getProperty(szPropertyName, szPropertyValue);
5445 }
5446 
addStyleProperty(const char * szStyleName,const char * szPropertyName,const char * szPropertyValue)5447 bool	PD_Document::addStyleProperty(const char * szStyleName, const char * szPropertyName, const char * szPropertyValue)
5448 {
5449 	PD_Style * pS;
5450 	PD_Style ** ppS = &pS;
5451 	if(!m_pPieceTable->getStyle(szStyleName, ppS))
5452 		return false;
5453 
5454 	return (*ppS)->addProperty(szPropertyName, szPropertyValue);
5455 }
5456 
addStyleProperties(const gchar * szStyleName,const gchar ** pProperties)5457 bool	PD_Document::addStyleProperties(const gchar * szStyleName, const gchar ** pProperties)
5458 {
5459 	PD_Style * pS;
5460 	PD_Style ** ppS = &pS;
5461 	if(!m_pPieceTable->getStyle(szStyleName, ppS))
5462 		return false;
5463 	if(!(*ppS)->addProperties(pProperties))
5464 		return false;
5465 	return updateDocForStyleChange(szStyleName,!(*ppS)->isCharStyle());
5466 }
5467 
5468 /*!
5469  * This methods changes the attributes of a style (basedon,followedby)
5470  *
5471 \param szStyleName the const gchar * name of the style
5472 \param pAttribs The list of attributes of the updated style.
5473 */
addStyleAttributes(const gchar * szStyleName,const gchar ** pAttribs)5474 bool	PD_Document::addStyleAttributes(const gchar * szStyleName, const gchar ** pAttribs)
5475 {
5476 	PD_Style * pS;
5477 	PD_Style ** ppS = &pS;
5478 	if(!m_pPieceTable->getStyle(szStyleName, ppS))
5479 		return false;
5480 	if(!(*ppS)->addAttributes(pAttribs))
5481 		return false;
5482 //
5483 // These functions just set the new member variable pointers in the class
5484 //
5485 	(*ppS)->getBasedOn();
5486 	(*ppS)->getFollowedBy();
5487 	return updateDocForStyleChange(szStyleName,!(*ppS)->isCharStyle());
5488 }
5489 
5490 /*!
5491  * The method returns the style defined in a sdh. If there is no style it returns
5492  * NULL
5493  */
getStyleFromSDH(pf_Frag_Strux * sdh)5494 PD_Style * PD_Document::getStyleFromSDH( pf_Frag_Strux* sdh)
5495 {
5496 	const pf_Frag_Strux * pfs = sdh;
5497 	PT_AttrPropIndex indexAP = pfs->getIndexAP();
5498 	const PP_AttrProp * pAP = NULL;
5499 	m_pPieceTable->getAttrProp(indexAP,&pAP);
5500 	UT_return_val_if_fail (pAP, NULL);
5501 	const gchar * pszStyleName = NULL;
5502 	(pAP)->getAttribute(PT_STYLE_ATTRIBUTE_NAME, pszStyleName);
5503 	if(pszStyleName == NULL  || strcmp(pszStyleName,"Current Settings") == 0 || strcmp(pszStyleName,"None") == 0)
5504 	{
5505 		return NULL;
5506 	}
5507 	PD_Style * pStyle = NULL;
5508 	if(!m_pPieceTable->getStyle(pszStyleName, &pStyle))
5509 	{
5510 		return NULL;
5511 	}
5512 	return pStyle;
5513 }
5514 
5515 /*!
5516  * Find previous style of type numbered heading or basedon numbered heading
5517 \param sdh The StruxDocHandle of the fragment where we start to look from.
5518 \returns PD_Style of the first Numbered Heading, otherwise NULL
5519 */
getPrevNumberedHeadingStyle(pf_Frag_Strux * sdh)5520 pf_Frag_Strux* PD_Document::getPrevNumberedHeadingStyle(pf_Frag_Strux* sdh)
5521 {
5522 	pf_Frag * pf = sdh;
5523 	bool bFound = false;
5524 	pf = pf->getPrev();
5525 	PD_Style * pStyle = NULL;
5526 	pf_Frag_Strux* foundSDH = NULL;
5527 	PD_Style * pBasedOn = NULL;
5528 	const char * szStyleName = NULL;
5529 	while(pf && !bFound)
5530 	{
5531 		if(pf->getType() == pf_Frag::PFT_Strux)
5532 		{
5533 			foundSDH = static_cast<pf_Frag_Strux*>(pf);
5534 			pStyle = getStyleFromSDH(foundSDH);
5535 			if(pStyle != NULL)
5536 			{
5537 				szStyleName = pStyle->getName();
5538 				if(strstr(szStyleName,"Numbered Heading") != 0)
5539 				{
5540 					bFound = true;
5541 					break;
5542 				}
5543 				pBasedOn  = pStyle->getBasedOn();
5544 				UT_uint32 i = 0;
5545 				while(pBasedOn != NULL && i < 10 && !bFound)
5546 				{
5547 					if(strstr(pBasedOn->getName(),"Numbered Heading") != 0)
5548 					{
5549 						bFound = true;
5550 					}
5551 					else
5552 					{
5553 						pBasedOn = pBasedOn->getBasedOn();
5554 					}
5555 				}
5556 				if(bFound)
5557 				{
5558 					break;
5559 				}
5560 			}
5561 		}
5562 //
5563 // Should not need the if. It's in for defensive programming.
5564 //
5565 		if(!bFound)
5566 		{
5567 			pf = pf->getPrev();
5568 		}
5569 	}
5570 	if(!bFound)
5571 	{
5572 		return NULL;
5573 	}
5574 	return foundSDH;
5575 }
5576 
5577 
5578 
5579 //
5580 /*!
5581  * This methods changes the attributes /properties of a style (basedon,followedby)
5582  * plus the properties. We have to save the indexAP of the pre-existing style
5583  * and broadcast it out witht e change records.
5584  *
5585 \param szStyleName the const gchar * name of the style
5586 \param pAttribs The list of attributes/properties of the updated style.
5587 */
setAllStyleAttributes(const gchar * szStyleName,const gchar ** pAttribs)5588 bool	PD_Document::setAllStyleAttributes(const gchar * szStyleName, const gchar ** pAttribs)
5589 {
5590 	PD_Style * pS;
5591 	PD_Style ** ppS = &pS;
5592 	if(!m_pPieceTable->getStyle(szStyleName, ppS))
5593 		return false;
5594 //
5595 // Sevior May need this code
5596 //	PT_AttrPropIndex oldindexAp = (*pss)->getIndexAP();
5597 	if(!(*ppS)->setAllAttributes(pAttribs))
5598 		return false;
5599 //
5600 // These functions just set the new member variable pointers in the class
5601 //
5602 	(*ppS)->getBasedOn();
5603 	(*ppS)->getFollowedBy();
5604 	return updateDocForStyleChange(szStyleName,!(*ppS)->isCharStyle());
5605 }
5606 
5607 /*!
5608  * This method scans the document backwards for a struc with the style name szStyle in it.
5609 \param pStyle a pointer to style to be scanned for.
5610 \param pos the document position to start from.
5611 \return the sdh of the strux found.
5612 */
findPreviousStyleStrux(const gchar * szStyle,PT_DocPosition pos)5613 pf_Frag_Strux* PD_Document::findPreviousStyleStrux(const gchar * szStyle, PT_DocPosition pos)
5614 {
5615 	pf_Frag_Strux* sdh = NULL;
5616 	getStruxOfTypeFromPosition(pos,PTX_Block, &sdh);
5617 	pf_Frag_Strux * pfs = NULL;
5618 	pf_Frag * currentFrag = sdh;
5619 	bool bFound = false;
5620     while (currentFrag && currentFrag != m_pPieceTable->getFragments().getFirst() && !bFound)
5621 	{
5622 		if (currentFrag->getType()==pf_Frag::PFT_Strux)
5623 		{
5624 //
5625 // All this code is used to find if this strux has our style in it
5626 //
5627 			pfs = static_cast<pf_Frag_Strux *> (currentFrag);
5628 			PT_AttrPropIndex indexAP = pfs->getIndexAP();
5629 			const PP_AttrProp * pAP = NULL;
5630 			m_pPieceTable->getAttrProp(indexAP,&pAP);
5631 			UT_return_val_if_fail (pAP,0);
5632 			const gchar * pszStyleName = NULL;
5633 			(pAP)->getAttribute(PT_STYLE_ATTRIBUTE_NAME, pszStyleName);
5634 			if(pszStyleName != NULL && strcmp(pszStyleName,szStyle)==0)
5635 			{
5636 				bFound = true;
5637 			}
5638 		}
5639 		if(!bFound)
5640 		{
5641 			currentFrag = currentFrag->getPrev();
5642 		}
5643 	}
5644 	if(bFound)
5645 	{
5646 		sdh = static_cast<pf_Frag_Strux*>(currentFrag);
5647 	}
5648 	else
5649 	{
5650 		sdh = NULL;
5651 	}
5652 	return sdh;
5653 }
5654 
5655 /*!
5656  * This method scans the document forwards for a strux with the style name
5657  * szStyle in it.
5658 \param pStyle a pointer to style to be scanned for.
5659 \param pos the document position to start from.
5660 \return the sdh of the strux found.
5661 */
findForwardStyleStrux(const gchar * szStyle,PT_DocPosition pos)5662 pf_Frag_Strux* PD_Document::findForwardStyleStrux(const gchar * szStyle, PT_DocPosition pos)
5663 {
5664 	pf_Frag_Strux* sdh = NULL;
5665 	getStruxOfTypeFromPosition(pos,PTX_Block, &sdh);
5666 	pf_Frag_Strux * pfs = NULL;
5667 	pf_Frag * currentFrag = sdh;
5668 	bool bFound = false;
5669     while (currentFrag != m_pPieceTable->getFragments().getLast() && !bFound)
5670 	{
5671 		if (currentFrag->getType()==pf_Frag::PFT_Strux)
5672 		{
5673 //
5674 // All this code is used to find if this strux has our style in it
5675 //
5676 			pfs = static_cast<pf_Frag_Strux *> (currentFrag);
5677 			PT_AttrPropIndex indexAP = pfs->getIndexAP();
5678 			const PP_AttrProp * pAP = NULL;
5679 			m_pPieceTable->getAttrProp(indexAP,&pAP);
5680 			UT_return_val_if_fail (pAP, 0);
5681 			const gchar * pszStyleName = NULL;
5682 			(pAP)->getAttribute(PT_STYLE_ATTRIBUTE_NAME, pszStyleName);
5683 			if(pszStyleName != NULL && strcmp(pszStyleName,szStyle)==0)
5684 			{
5685 				bFound = true;
5686 			}
5687 		}
5688 		if(!bFound)
5689 		{
5690 			currentFrag = currentFrag->getNext();
5691 		}
5692 	}
5693 	if(bFound)
5694 	{
5695 		sdh = static_cast<pf_Frag_Strux*>(currentFrag);
5696 	}
5697 	else
5698 	{
5699 		sdh = NULL;
5700 	}
5701 	return sdh;
5702 }
5703 
5704 
5705 /*!
5706  * This method loops through the entire document updating each location
5707  * where the style exists.
5708 \param szStyle the name of style that has changed.
5709 \param isParaStyle true if the style is a paragraph type.
5710 */
updateDocForStyleChange(const gchar * szStyle,bool isParaStyle)5711 bool   PD_Document::updateDocForStyleChange(const gchar * szStyle,
5712 											bool isParaStyle)
5713 {
5714 	PT_DocPosition pos = 0;
5715 	PT_DocPosition posLastStrux = 0;
5716 	pf_Frag_Strux * pfs = NULL;
5717 	PD_Style * pStyle = NULL;
5718 	m_pPieceTable->getStyle(szStyle,&pStyle);
5719 	UT_return_val_if_fail (pStyle, false);
5720 	pf_Frag * currentFrag = m_pPieceTable->getFragments().getFirst();
5721 	UT_return_val_if_fail (currentFrag, false);
5722 	while (currentFrag!=m_pPieceTable->getFragments().getLast())
5723 	{
5724 //
5725 // get indexAP
5726 // get PT_STYLE_ATTRIBUTE_NAME
5727 // if it matches style name do a notify listeners call.
5728 		if(isParaStyle)
5729 		{
5730 			if (currentFrag->getType()==pf_Frag::PFT_Strux)
5731 			{
5732 //
5733 // All this code is used to find if this strux has our style in it
5734 //
5735 				pfs = static_cast<pf_Frag_Strux *> (currentFrag);
5736 				PT_AttrPropIndex indexAP = pfs->getIndexAP();
5737 				const PP_AttrProp * pAP = NULL;
5738 				m_pPieceTable->getAttrProp(indexAP,&pAP);
5739 				UT_return_val_if_fail (pAP, false);
5740 				const gchar * pszStyleName = NULL;
5741 				(pAP)->getAttribute(PT_STYLE_ATTRIBUTE_NAME, pszStyleName);
5742 				bool bUpdate = false;
5743 //
5744 // It does so signal all the layouts to update themselves for the new definition
5745 // of the style.
5746 //
5747 				if(pszStyleName != NULL && strcmp(pszStyleName,szStyle)==0)
5748 				{
5749 					bUpdate = true;
5750 				}
5751 				else if(pfs->getStruxType() == 	PTX_SectionTOC)
5752 				{
5753 					bUpdate = true; // FIXME should be more fine grained.
5754 				}
5755 //
5756 // Look if the style in the basedon ancestory is our style
5757 //
5758 				else if (pszStyleName != NULL)
5759 				{
5760 					PD_Style * cStyle = NULL;
5761 					m_pPieceTable->getStyle(pszStyleName,&cStyle);
5762 					UT_ASSERT_HARMLESS(cStyle);
5763 					if(cStyle)
5764 					{
5765 						PD_Style * pBasedOn = cStyle->getBasedOn();
5766 						UT_uint32 i =0;
5767 						for(i=0; (i < pp_BASEDON_DEPTH_LIMIT) && (pBasedOn != NULL) && (pBasedOn!= pStyle); i++)
5768 						{
5769 							pBasedOn = pBasedOn->getBasedOn();
5770 						}
5771 						if(pBasedOn == pStyle)
5772 						{
5773 							bUpdate = true;
5774 						}
5775 					}
5776 				}
5777 				if(bUpdate)
5778 				{
5779 					PX_ChangeRecord * pcr = new PX_ChangeRecord_StruxChange(PX_ChangeRecord::PXT_ChangeStrux,
5780 					                                      pos, indexAP, indexAP, pfs->getStruxType(), false);
5781 					notifyListeners(pfs, pcr);
5782 					delete pcr;
5783 				}
5784 			}
5785 		}
5786 //
5787 // Character type
5788 //
5789 		else
5790 		{
5791 //
5792 // Need the most recent frag_strux to find the block containing our text span
5793 //
5794 			if (currentFrag->getType()==pf_Frag::PFT_Strux)
5795 			{
5796 				pfs = static_cast<pf_Frag_Strux *> (currentFrag);
5797 				posLastStrux = pos;
5798 			}
5799 			if (currentFrag->getType()==pf_Frag::PFT_Text)
5800 			{
5801 //
5802 // All this code is used to find if this Text Frag has our style in it
5803 //
5804 				pf_Frag_Text * pft = static_cast<pf_Frag_Text *> (currentFrag);
5805 				PT_AttrPropIndex indexAP = pft->getIndexAP();
5806 				const PP_AttrProp * pAP = NULL;
5807 				m_pPieceTable->getAttrProp(indexAP,&pAP);
5808 				UT_return_val_if_fail (pAP, false);
5809 				const gchar * pszStyleName = NULL;
5810 				(pAP)->getAttribute(PT_STYLE_ATTRIBUTE_NAME, pszStyleName);
5811 
5812 //
5813 // It does so signal all the layouts to update themselves for the new definition
5814 // of the style.
5815 //
5816 				if(pszStyleName != NULL && strcmp(pszStyleName,szStyle)==0)
5817 				{
5818 					UT_uint32 blockoffset = (UT_uint32) (pos - posLastStrux -1);
5819 					PX_ChangeRecord_SpanChange * pcr = new PX_ChangeRecord_SpanChange(PX_ChangeRecord::PXT_ChangeSpan,
5820 																					  pos,indexAP,indexAP,
5821 																					  m_pPieceTable->getVarSet().getBufIndex(pft->getBufIndex(),0) ,
5822 																					  currentFrag->getLength(),
5823 																					  blockoffset, false);
5824 					notifyListeners(pfs, pcr);
5825 					delete pcr;
5826 				}
5827 			}
5828 		}
5829 		pos += currentFrag->getLength();
5830 		currentFrag = currentFrag->getNext();
5831 	}
5832 	return true;
5833 }
5834 
5835 
5836 /*!
5837  * This method updates all the layouts associated with the document.
5838 */
updateAllLayoutsInDoc(pf_Frag_Strux * sdh)5839 void  PD_Document::updateAllLayoutsInDoc( pf_Frag_Strux* sdh)
5840 {
5841 	const pf_Frag_Strux * pfs = sdh;
5842 	PT_AttrPropIndex indexAP = pfs->getIndexAP();
5843 	PT_DocPosition pos = getStruxPosition(sdh);
5844 	PX_ChangeRecord * pcr = new PX_ChangeRecord(PX_ChangeRecord::PXT_ChangeStrux,
5845 												pos,indexAP,pfs->getXID());
5846 	notifyListeners(pfs, pcr);
5847 	delete pcr;
5848 }
5849 
5850 //////////////////////////////////////////////////////////////////
5851 
clearIfAtFmtMark(PT_DocPosition dpos)5852 void PD_Document::clearIfAtFmtMark (PT_DocPosition dpos)
5853 {
5854 	m_pPieceTable->clearIfAtFmtMark(dpos);
5855 }
5856 
updateFields(void)5857 bool PD_Document::updateFields(void)
5858 {
5859 	//
5860 	// Turn off Insertion point motion during this general update
5861 	//
5862 	setDontChangeInsPoint();
5863 	pf_Frag * currentFrag = m_pPieceTable->getFragments().getFirst();
5864 	UT_return_val_if_fail (currentFrag,false);
5865 	while (currentFrag!=m_pPieceTable->getFragments().getLast())
5866 	{
5867 		if (currentFrag->getType()==pf_Frag::PFT_Object)
5868 		{
5869 			pf_Frag_Object * pfo = static_cast<pf_Frag_Object *>
5870 				(currentFrag);
5871 			if (pfo->getObjectType()==PTO_Field)
5872 			{
5873 				UT_return_val_if_fail (pfo->getField(), false);
5874 				pfo->getField()->update();
5875 			}
5876 		}
5877 		currentFrag = currentFrag->getNext();
5878 	}
5879 	//
5880 	// Restore insertion point motion
5881 	//
5882 	allowChangeInsPoint();
5883 	return true;
5884 }
5885 
setDontChangeInsPoint(void)5886 void PD_Document::setDontChangeInsPoint(void)
5887 {
5888 	if(m_bLoading)
5889 	{
5890 		UT_DEBUGMSG(("Illegal request to not change insertion Point!!! \n"));
5891         m_bAllowInsertPointChange = true;
5892 		return;
5893 	}
5894 	m_bAllowInsertPointChange = false;
5895 }
5896 
allowChangeInsPoint(void)5897 void PD_Document::allowChangeInsPoint(void)
5898 {
5899         m_bAllowInsertPointChange = true;
5900 }
5901 
getAllowChangeInsPoint(void) const5902 bool PD_Document::getAllowChangeInsPoint(void) const
5903 {
5904         return m_bAllowInsertPointChange;
5905 }
5906 
5907 ////////////////////////////////////////////////////////////////////////////////
5908 // Step towards full thread safety
5909 
notifyPieceTableChangeStart(void)5910 void PD_Document::notifyPieceTableChangeStart(void)
5911 {
5912 //
5913 // Wait for all redraws to finish before starting.
5914 //
5915 	UT_uint32 i = 0;
5916 	while(m_bRedrawHappenning && i < 10000)
5917 	{
5918 		UT_usleep(100); // wait 100 microseonds
5919 		i++;
5920 	}
5921 	if(i>0)
5922 	{
5923 		UT_DEBUGMSG(("!!!!Waited %d microseconds for redraw to finish \n",i*100));
5924 	}
5925 	m_bRedrawHappenning = false;
5926 	_setPieceTableChanging(true);
5927 //
5928 // Invalidate visible direction cache variables. PieceTable manipulations of
5929 // any sort can screw these.
5930 //
5931 	m_pVDBl = NULL;
5932 	m_pVDRun = NULL;
5933 	m_iVDLastPos = 0;
5934 }
5935 
notifyPieceTableChangeEnd(void)5936 void PD_Document::notifyPieceTableChangeEnd(void)
5937 {
5938         _setPieceTableChanging(false);
5939 }
5940 
invalidateCache(void)5941 void PD_Document::invalidateCache(void)
5942 {
5943 	m_pVDBl = NULL;
5944 	m_pVDRun = NULL;
5945 	m_iVDLastPos = 0;
5946 }
5947 
5948 ////////////////////////////////////////////////////////////////
5949 // List Vector Functions
5950 
5951 
5952 
getListByID(UT_uint32 id) const5953 fl_AutoNum * PD_Document::getListByID(UT_uint32 id) const
5954 {
5955 	UT_uint16 i = 0;
5956 	UT_sint32 cnt = 0;
5957 	fl_AutoNum * pAutoNum;
5958 
5959 	cnt = m_vecLists.getItemCount();
5960 	if ( cnt <= 0)
5961 		return static_cast<fl_AutoNum *>(NULL);
5962 	UT_return_val_if_fail (m_vecLists.getFirstItem(), NULL);
5963 
5964 	while (i<cnt)
5965 	{
5966 		pAutoNum = m_vecLists[i];
5967 		if (pAutoNum->getID() == id)
5968 			return pAutoNum;
5969 		i++;
5970 	}
5971 
5972 	return static_cast<fl_AutoNum *>(NULL);
5973 }
5974 
enumLists(UT_uint32 k,fl_AutoNum ** pAutoNum)5975 bool PD_Document::enumLists(UT_uint32 k, fl_AutoNum ** pAutoNum)
5976 {
5977 	UT_uint32 kLimit = m_vecLists.getItemCount();
5978 	if (k >= kLimit)
5979 		return false;
5980 
5981 	if (pAutoNum)
5982 		*pAutoNum = m_vecLists[k];
5983 
5984 	return true;
5985 }
5986 
getNthList(UT_uint32 i) const5987 fl_AutoNum * PD_Document::getNthList(UT_uint32 i) const
5988 {
5989 	return m_vecLists[i];
5990 }
5991 
getListsCount(void) const5992 UT_uint32 PD_Document::getListsCount(void) const
5993 {
5994 	return m_vecLists.getItemCount();
5995 }
5996 
addList(fl_AutoNum * pAutoNum)5997 void PD_Document::addList(fl_AutoNum * pAutoNum)
5998 {
5999 	UT_uint32 id = pAutoNum->getID();
6000 	UT_uint32 i;
6001 	UT_uint32 numlists = m_vecLists.getItemCount();
6002 	for(i=0; i < numlists; i++)
6003 	{
6004 		fl_AutoNum * pAuto = m_vecLists.getNthItem(i);
6005 		if(pAuto->getID() == id)
6006 			break;
6007 	}
6008 	if(i >= numlists)
6009 		m_vecLists.addItem(pAutoNum);
6010 }
6011 
listUpdate(pf_Frag_Strux * sdh)6012 void PD_Document::listUpdate(pf_Frag_Strux* sdh )
6013 {
6014 	//
6015 	// Notify all views of a listupdate
6016 	//
6017 	UT_return_if_fail (sdh);
6018 	const pf_Frag_Strux * pfs = sdh;
6019 	PT_AttrPropIndex pAppIndex = pfs->getIndexAP();
6020 	PT_DocPosition pos = getStruxPosition(sdh);
6021 	const PX_ChangeRecord * pcr = new PX_ChangeRecord(PX_ChangeRecord::PXT_ListUpdate,pos,pAppIndex,pfs->getXID());
6022 	notifyListeners(pfs, pcr);
6023 	delete pcr;
6024 }
6025 
6026 
StopList(pf_Frag_Strux * sdh)6027 void PD_Document::StopList(pf_Frag_Strux* sdh )
6028 {
6029 	//
6030 	// Notify all views of a stoplist
6031 	//
6032 	setHasListStopped(false);
6033 	const pf_Frag_Strux * pfs = sdh;
6034 	PT_AttrPropIndex pAppIndex = pfs->getIndexAP();
6035 	PT_DocPosition pos = getStruxPosition(sdh);
6036 	const PX_ChangeRecord * pcr = new PX_ChangeRecord(PX_ChangeRecord::PXT_StopList,pos,pAppIndex,pfs->getXID());
6037 	notifyListeners(pfs, pcr);
6038 	delete pcr;
6039 	setHasListStopped(false);
6040 }
6041 
6042 
appendList(const gchar ** attributes)6043 bool PD_Document::appendList(const gchar ** attributes)
6044 {
6045 	const gchar * szID=NULL, * szPid=NULL, * szType=NULL, * szStart=NULL, * szDelim=NULL, *szDec=NULL;
6046 	UT_uint32 id, parent_id, start;
6047 	FL_ListType type;
6048 
6049 	for (const gchar ** a = attributes; (*a); a++)
6050 	{
6051 		if (strcmp(a[0],"id") == 0)
6052 			szID = a[1];
6053 		else if (strcmp(a[0], "parentid") == 0)
6054 			szPid = a[1];
6055 		else if (strcmp(a[0], "type") == 0)
6056 			szType = a[1];
6057 		else if (strcmp(a[0], "start-value") == 0)
6058 			szStart = a[1];
6059 		else if (strcmp(a[0], "list-delim") == 0)
6060 			szDelim = a[1];
6061 		else if (strcmp(a[0], "list-decimal") == 0)
6062 			szDec = a[1];
6063 	}
6064 
6065 	if(!szID)
6066 		return false;
6067 	if(!szPid)
6068 		return false;
6069 	if(!szType)
6070 		return false;
6071 	if(!szStart)
6072 		return false;
6073 	if(!szDelim)
6074 		return false;
6075 	if(!szDec)
6076 		szDec = static_cast<const gchar *>(".");
6077 	id = atoi(szID);
6078 	UT_uint32 i;
6079 	UT_uint32 numlists = m_vecLists.getItemCount();
6080 	for(i=0; i < numlists; i++)
6081 	{
6082 		fl_AutoNum * pAuto = m_vecLists.getNthItem(i);
6083 		if(pAuto->getID() == id)
6084 			break;
6085 	}
6086 	if(i < numlists)
6087 		return true; // List is already present
6088 	parent_id = atoi(szPid);
6089 	type = static_cast<FL_ListType>(atoi(szType));
6090 	start = atoi(szStart);
6091 
6092 	// this is bad design -- layout items should not be created by the document, only by the view
6093 	// (the props and attrs of layout items are view-specific due to possible revisions settings !!!)
6094 	fl_AutoNum * pAutoNum = new fl_AutoNum(id, parent_id, type, start, szDelim,szDec,this,NULL);
6095 	addList(pAutoNum);
6096 
6097 	return true;
6098 }
6099 
areListUpdatesAllowed(void)6100 bool PD_Document::areListUpdatesAllowed(void)
6101 {
6102         return m_ballowListUpdates;
6103 }
6104 
disableListUpdates(void)6105 void PD_Document::disableListUpdates(void)
6106 {
6107         m_ballowListUpdates = false;
6108 }
6109 
enableListUpdates(void)6110 void PD_Document::enableListUpdates(void)
6111 {
6112         m_ballowListUpdates = true;
6113 }
6114 
6115 
updateDirtyLists(void)6116 void PD_Document::updateDirtyLists(void)
6117 {
6118 	UT_uint32 iNumLists = m_vecLists.getItemCount();
6119 	UT_uint32 i;
6120 	fl_AutoNum * pAutoNum;
6121 	bool bDirtyList = false;
6122 	for(i=0; i< iNumLists; i++)
6123 	{
6124 		pAutoNum = m_vecLists.getNthItem(i);
6125 		if(pAutoNum->isEmpty() || (pAutoNum->getDoc() != this))
6126 		{
6127 			delete pAutoNum;
6128 			m_vecLists.deleteNthItem(i);
6129 			iNumLists--;
6130 			i--;
6131 		}
6132 	}
6133 	for(i=0; i< iNumLists; i++)
6134 	{
6135 		pAutoNum = m_vecLists.getNthItem(i);
6136 		if(pAutoNum->isDirty() == true)
6137 		{
6138 			pAutoNum->update(0);
6139 			bDirtyList = true;
6140 		}
6141 	}
6142 	if(bDirtyList)
6143 	{
6144 		for(i=0; i< iNumLists; i++)
6145 		{
6146 			pAutoNum = m_vecLists.getNthItem(i);
6147 			pAutoNum->fixHierarchy();
6148 			pAutoNum->findAndSetParentItem();
6149 		}
6150 	}
6151 }
6152 
6153 
fixListHierarchy(void)6154 bool PD_Document::fixListHierarchy(void)
6155 {
6156 	UT_uint32 iNumLists = m_vecLists.getItemCount();
6157 	fl_AutoNum * pAutoNum;
6158 
6159 	if (iNumLists == 0)
6160 	{
6161 		return false;
6162 	}
6163 	else
6164 	{
6165             // Some documents may contain empty lists
6166             // that appear as a result of importing ODT file which contains
6167             // nested lists without paragraphs. To get rid of them we should
6168             // delete all lists that are defined but doesn't contain any items
6169             std::vector<unsigned int> itemsToRemove;
6170             for (UT_uint32 i = 0; i < iNumLists; i++)
6171             {
6172                     pAutoNum = m_vecLists.getNthItem(i);
6173                     if (pAutoNum->getFirstItem() == NULL)
6174                     {
6175                         itemsToRemove.push_back(i);
6176                     }
6177                     else
6178                     {
6179                         pAutoNum->fixHierarchy();
6180                     }
6181             }
6182             while(!itemsToRemove.empty())
6183             {
6184                 m_vecLists.deleteNthItem(itemsToRemove.back());
6185                 itemsToRemove.pop_back();
6186             }
6187 
6188             return true;
6189 	}
6190 }
6191 
removeList(fl_AutoNum * pAutoNum,pf_Frag_Strux * sdh)6192 void PD_Document::removeList(fl_AutoNum * pAutoNum, pf_Frag_Strux* sdh )
6193 {
6194 	UT_return_if_fail (pAutoNum);
6195 	UT_sint32 ndx = m_vecLists.findItem(pAutoNum);
6196 	UT_return_if_fail (ndx >= 0);
6197 	//
6198 	// Notify all views of a remove List
6199 	//
6200 	const pf_Frag_Strux * pfs = sdh;
6201 	PT_AttrPropIndex pAppIndex = pfs->getIndexAP();
6202 	PT_DocPosition pos = getStruxPosition(sdh);
6203 	const PX_ChangeRecord * pcr = new PX_ChangeRecord(PX_ChangeRecord::PXT_RemoveList,pos,pAppIndex,pfs->getXID());
6204 	notifyListeners(pfs, pcr);
6205 	delete pcr;
6206 	m_vecLists.deleteNthItem(ndx);
6207 }
6208 
setDoingPaste(void)6209 void  PD_Document::setDoingPaste(void)
6210 {
6211          m_bDoingPaste = true;
6212 }
6213 
6214 
clearDoingPaste(void)6215 void  PD_Document::clearDoingPaste(void)
6216 {
6217          m_bDoingPaste = false;
6218 }
6219 
isDoingPaste(void)6220 bool  PD_Document::isDoingPaste(void)
6221 {
6222          return m_bDoingPaste;
6223 }
6224 
convertPercentToInches(const char * szPercent,UT_UTF8String & sInches)6225 bool PD_Document::convertPercentToInches(const char * szPercent, UT_UTF8String & sInches)
6226 {
6227 	double width = m_docPageSize.Width(DIM_IN);
6228 	const pf_Frag_Strux* sdhSec = getLastSectionSDH();
6229 	const char * szLeftMargin = NULL;
6230 	const char * szRightMargin = NULL;
6231 
6232 	// TODO -- probably needs to get revision settings from some view ...
6233 	getPropertyFromSDH(sdhSec,true,PD_MAX_REVISION,"page-margin-left",&szLeftMargin);
6234 	getPropertyFromSDH(sdhSec,true,PD_MAX_REVISION,"page-margin-right",&szRightMargin);
6235 	if(szLeftMargin == NULL)
6236 	{
6237 		szLeftMargin = "0.5in";
6238 	}
6239 	if(szRightMargin == NULL)
6240 	{
6241 		szRightMargin = "0.5in";
6242 	}
6243 	double dLeft = UT_convertToInches(szLeftMargin);
6244 	double dRight = UT_convertToInches(szRightMargin);
6245 	width = width - dLeft - dRight;
6246 	UT_String sVal = szPercent;
6247 	sInches = UT_convertInchesToDimensionString(DIM_IN,width);
6248 	return true;
6249 }
6250 
6251 
setPageSizeFromFile(const gchar ** attributes)6252 bool PD_Document:: setPageSizeFromFile(const gchar ** attributes)
6253 {
6254 
6255 	bool b =  m_docPageSize.Set(attributes);
6256 	UT_DEBUGMSG(("SetPageSize m_bLoading %d \n",m_bLoading));
6257 	if(!m_bLoading)
6258 	{
6259 		const gchar * szAtts[] = {PT_DOCPROP_ATTRIBUTE_NAME,"pagesize",
6260 								  NULL,NULL};
6261 		UT_DEBUGMSG(("Sending page size CR \n"));
6262 		createAndSendDocPropCR(szAtts,attributes);
6263 	}
6264 	return b;
6265 }
6266 
addBookmark(const gchar * pName)6267 void PD_Document::addBookmark(const gchar * pName)
6268 {
6269 	m_vBookmarkNames.push_back(pName);
6270 }
6271 
removeBookmark(const gchar * pName)6272 void PD_Document::removeBookmark(const gchar * pName)
6273 {
6274 	std::vector<std::string>::iterator iter = m_vBookmarkNames.begin();
6275 	for( ; iter != m_vBookmarkNames.end() ; ++iter)
6276 	{
6277 		if(*iter == pName)
6278 		{
6279 			m_vBookmarkNames.erase(iter);
6280 			break;
6281 		}
6282 	}
6283 }
6284 
6285 /*! Returns true if pName doesn't correspond to a
6286  *  currently existing bookmark. */
isBookmarkUnique(const gchar * pName) const6287 bool PD_Document::isBookmarkUnique(const gchar * pName) const
6288 {
6289 	std::vector<std::string>::const_iterator iter = m_vBookmarkNames.begin();
6290 	for( ; iter != m_vBookmarkNames.end() ; ++iter)
6291 	{
6292 		if(*iter == pName)
6293 		{
6294 			return false;
6295 		}
6296 	}
6297 
6298 	return true;
6299 }
6300 
6301 /*! Returns true if pName looks like a relative link, rather than a
6302  *  bookmark.
6303 
6304  *  Current heuristic: if pName contains a ., then it's a rel link;
6305  * otherwise it's a bookmark. */
isBookmarkRelativeLink(const gchar * pName) const6306 bool PD_Document::isBookmarkRelativeLink(const gchar * pName) const
6307 {
6308 	UT_ASSERT_HARMLESS(sizeof(char) == sizeof(gchar));
6309 	return strchr(static_cast<const char *>(pName), '.') != NULL;
6310 }
6311 
6312 //////////////////////////////////////////////////////////////////
6313 // document-level properties
6314 
6315 #define VARSET m_pPieceTable->getVarSet()
6316 
getAttrProp() const6317 const PP_AttrProp * PD_Document::getAttrProp() const
6318 {
6319 	return VARSET.getAP(m_indexAP);
6320 }
6321 
6322 /*!
6323     Sets document attributes and properties
6324     can only be used while loading documents
6325 
6326     \param const gchar ** ppAttr: array of attribute/value pairs
6327 
6328 	    if ppAttr == NULL and m_indexAP == 0xffffffff, the function
6329     	creates a new AP and sets it to the default values hardcoded
6330     	in it
6331 
6332         if ppAttr == NULL and m_indexAP != 0xffffffff, the function
6333         does nothing
6334 
6335         if ppAttr != NULL the function overlays passed attributes over
6336         the existing attributes (creating a new AP first if necessary)
6337 
6338     When initialising document attributes and props, we need to set
6339     m_indexAP to 0xffffffff and then call setAttributes(NULL).
6340 
6341     Importers should just call setAttributes(NULL) in the
6342     initialisation stage, this ensures that default values are set
6343     without overwriting existing values if those were set by the
6344     caller of the importer.
6345 
6346     Tomas, Dec 6, 2003
6347 */
setAttrProp(const gchar ** ppAttr)6348 bool PD_Document::setAttrProp(const gchar ** ppAttr)
6349 {
6350 	// this method can only be used while loading  ...
6351 	if(m_pPieceTable->getPieceTableState() != PTS_Loading)
6352 	{
6353 		UT_return_val_if_fail(0,false);
6354 	}
6355 
6356 	bool bRet = true;
6357 
6358 	if(m_indexAP == 0xffffffff)
6359 	{
6360 		// AP not initialised, do so and set standard document attrs
6361 		// and properties
6362 
6363 		// first create an empty AP by passing NULL to storeAP
6364 		// cast needed to disambiguate function signature
6365 		bRet = VARSET.storeAP(static_cast<const gchar **>(0), &m_indexAP);
6366 
6367 		if(!bRet)
6368 			return false;
6369 
6370 		// now set standard attributes
6371 		UT_uint32 i = 0;
6372 		const UT_uint32 iSize = 23;
6373 		const gchar * attr[iSize];
6374 
6375 		attr[i++] = "xmlns";
6376 		attr[i++] = "http://www.abisource.com/awml.dtd";
6377 
6378 		attr[i++] = "xml:space";
6379 		attr[i++] = "preserve";
6380 
6381 		attr[i++] = "xmlns:awml";
6382 		attr[i++] = "http://www.abisource.com/awml.dtd";
6383 
6384 		attr[i++] = "xmlns:xlink";
6385 		attr[i++] = "http://www.w3.org/1999/xlink";
6386 
6387 		attr[i++] = "xmlns:svg";
6388 		attr[i++] = "http://www.w3.org/2000/svg";
6389 
6390 		attr[i++] = "xmlns:fo";
6391 		attr[i++] = "http://www.w3.org/1999/XSL/Format";
6392 
6393 		attr[i++] = "xmlns:math";
6394 		attr[i++] = "http://www.w3.org/1998/Math/MathML";
6395 
6396 		attr[i++] = "xmlns:dc";
6397 		attr[i++] = "http://purl.org/dc/elements/1.1/";
6398 
6399 		attr[i++] = "xmlns:ct";
6400 		attr[i++] = "http://www.abisource.com/changetracking.dtd";
6401 
6402 		attr[i++] = "fileformat";
6403 		attr[i++] = ABIWORD_FILEFORMAT_VERSION;
6404 
6405 		if (XAP_App::s_szBuild_Version && XAP_App::s_szBuild_Version[0])
6406 		{
6407 			attr[i++] = "version";
6408 			attr[i++] = XAP_App::s_szBuild_Version;
6409 		}
6410 
6411 		attr[i] = NULL;
6412 		UT_return_val_if_fail(i < iSize, false);
6413 
6414 		bRet =  setAttributes(attr);
6415 
6416 		if(!bRet)
6417 			return false;
6418 
6419 		// now set default properties, starting with dominant
6420 		// direction
6421 		const gchar r[] = "rtl";
6422 		const gchar l[] = "ltr";
6423 		const gchar p[] = "dom-dir";
6424 		const gchar * props[3] = {p,l,NULL};
6425 
6426 		bool bRTL = false;
6427 		XAP_App::getApp()->getPrefs()->getPrefsValueBool(AP_PREF_KEY_DefaultDirectionRtl,&bRTL);
6428 
6429 		if(bRTL)
6430 			props[1] = r;
6431 
6432 		UT_DEBUGMSG(( "pd_Document::setAttrProp: setting dom-dir to %s\n", props[1]));
6433 		bRet = setProperties(props);
6434 
6435 		if(!bRet)
6436 			return false;
6437 
6438 		// if there is a default language in the preferences, set it
6439 		UT_LocaleInfo locale;
6440 
6441 		UT_UTF8String lang(locale.getLanguage());
6442 		if (locale.getTerritory().size()) {
6443 			lang += "-";
6444 			lang += locale.getTerritory();
6445 		}
6446 
6447 		props[0] = "lang";
6448 		props[1] = lang.utf8_str();
6449 		props[2] = 0;
6450 		bRet = setProperties(props);
6451 
6452 		if(!bRet)
6453 			return false;
6454 
6455 		// Yes, we have to set default properties for all document-level items, because
6456 		// some piece of code (exporter, plugin) may want to get the value of that default,
6457 		// not unitialized memory.  When a hashing solution is factored out of the PT,
6458 		// it may be tempting to return NULLs.  Not good enough either.
6459 		// I'm going to ask Dom the preferred way to make this rather more concise. -MG
6460 		//
6461 		// Actually, we do not set these because of uninitialised memory; you never get a
6462 		// uninitialised memory from the the PP_AttrProp chain; nor do we set these
6463 		// because we cannot return NULLs. We set these, because without them we cannot
6464 		// lay the document out, and it is much better to have the defaults gathered in
6465 		// one place than having all kinds of fallback values hardcoded all over the
6466 		// place. Tomas
6467 
6468 		// Update: Surely there is a way to make the getProperty mechanisms smarter, to
6469 		// provide valid and accurate information on request (lazy-evaluation /
6470 		// late-binding), because this superfluous storage sucks, and actually (in
6471 		// concept) adds ambiguity by virtue of the fact that the means by which these
6472 		// were set is not known or stored, and hence other pieces of code while capable
6473 		// of following WYSIWYG, are not able to do otherwise with knowledge of whether
6474 		// the user explicitly requested these properties to be set to these values or
6475 		// they're just this way by virtue of AbiWord insisting on setting the default
6476 		// upon initialization of any and every pd_Document.  This is bad for external
6477 		// document storage and processing solutions, not to mention plugins that AbiWord
6478 		// may ship.  Keep in mind, this is NOT the only place we have to do this.  Even
6479 		// individual struxes within the document have to have their properties
6480 		// initialized as it stands now. -MG
6481 
6482 		// This storage is not superfluous, I have already explained that. Also, the attrs
6483 		// and props in here fall into two separate groups. The document-only stuff (like
6484 		// the various xml attributes), and attributes and properties that are part of the
6485 		// resolution mechanism: when looking for property value, it is resolved through a
6486 		// chain: spanAP - blockAP - sectionAP - documentAP - hardcoded defaults (the
6487 		// hardcodes defaults are in PP_Property.cpp).  Struxes, etc., do not have any
6488 		// properties as such, and do not have to have them initialised; they simply have
6489 		// a reference to an PP_AttrProp instance, which can contain any number of
6490 		// attributes/props, or none. If you use the getProperty() mechanism, you are
6491 		// simply asking about resolution of a given property; if you want to know where
6492 		// that property came from, it can be achieved by stepping down the chain (and the
6493 		// PP_EvaluateProperty() function could easily be extended to return this info if
6494 		// you really need it).
6495 		//
6496 		// There might be some value in knowing which properties were set manually by the
6497 		// user, but I am not sure it is at all necessary. As the chain is, each level
6498 		// should only contain attributes and properties set manually, since everything
6499 		// else is inherited from the level below. There is currently a problem with some
6500 		// code that sets individual attributes and properties without asking about their
6501 		// relationship to the lower levels of the chain -- properties that resolve to the
6502 		// same values as the chain below should be removed, not explicitely set. This
6503 		// should be fixed up, but that has nothing to do with this code. Tomas
6504 
6505 			// Endnotes
6506 		props[0] = "document-endnote-type";
6507 		props[1] = "numeric";
6508 		props[2] = NULL;
6509 		if(!setProperties(props)) return false;
6510 		props[0] = "document-endnote-place-enddoc";
6511 		props[1] = "1";
6512 		props[2] = NULL;
6513 		if(!setProperties(props)) return false;
6514 		props[0] = "document-endnote-place-endsection";
6515 		props[1] = "0";
6516 		props[2] = NULL;
6517 		if(!setProperties(props)) return false;
6518 		props[0] = "document-endnote-initial";
6519 		props[1] = "1";
6520 		props[2] = NULL;
6521 		if(!setProperties(props)) return false;
6522 		props[0] = "document-endnote-restart-section";
6523 		props[1] = "0";
6524 		props[2] = NULL;
6525 		if(!setProperties(props)) return false;
6526 			// Footnotes
6527 		props[0] = "document-footnote-type";
6528 		props[1] = "numeric";
6529 		props[2] = NULL;
6530 		if(!setProperties(props)) return false;
6531 		props[0] = "document-footnote-initial";
6532 		props[1] = "1";
6533 		props[2] = NULL;
6534 		if(!setProperties(props)) return false;
6535 		props[0] = "document-footnote-restart-page";
6536 		props[1] = "0";
6537 		props[2] = NULL;
6538 		if(!setProperties(props)) return false;
6539 		props[0] = "document-footnote-restart-section";
6540 		props[1] = "0";
6541 		props[2] = NULL;
6542 		if(!setProperties(props)) return false;
6543 
6544 		// now overlay the attribs we were passed ...
6545 		bRet = setAttributes(ppAttr);
6546 	}
6547 	else if(ppAttr == NULL)
6548 	{
6549 		// we already have an AP, and have nothing to add to it
6550 		return true;
6551 	}
6552 	else
6553 	{
6554 		// have an AP and given something to add to it
6555 		// first, we need to take care of the top-xid attribute
6556 		const gchar * pXID = UT_getAttribute("top-xid", ppAttr);
6557 		if(pXID && *pXID)
6558 		{
6559 			UT_uint32 iXID = atoi(pXID);
6560 			m_pPieceTable->setXIDThreshold(iXID);
6561 		}
6562 
6563 		bRet = VARSET.mergeAP(PTC_AddFmt, m_indexAP, ppAttr, NULL, &m_indexAP, this);
6564 	}
6565 
6566 	return bRet;
6567 }
6568 
setAttributes(const gchar ** ppAttr)6569 bool PD_Document::setAttributes(const gchar ** ppAttr)
6570 {
6571 	return VARSET.mergeAP(PTC_AddFmt, m_indexAP, ppAttr, NULL, &m_indexAP, this);
6572 }
6573 
6574 
setProperties(const gchar ** ppProps)6575 bool PD_Document::setProperties(const gchar ** ppProps)
6576 {
6577 	return VARSET.mergeAP(PTC_AddFmt, m_indexAP, NULL, ppProps, &m_indexAP, this);
6578 }
6579 
6580 #undef VARSET
6581 
lockStyles(bool b)6582 void PD_Document::lockStyles(bool b)
6583 {
6584 	const gchar *attr[3];
6585 	const gchar n[] = "styles";
6586 	const gchar v1[] = "locked";
6587 	const gchar v2[] = "unlocked";
6588 
6589 	attr[0] = n;
6590 	attr[2] = NULL;
6591 
6592 	if(b)
6593 		attr[1] = v1;
6594 	else
6595 		attr[1] = v2;
6596 
6597 	setAttributes(attr);
6598 	m_bLockedStyles = b;
6599 }
6600 
6601 /*!
6602     Some exporters (RTF) need to know the visual direction at each
6603     position in the document as it is being exported. The problem is
6604     that visual direction is a property of the layout not of the
6605     document itself (I shall not make any comments about badly
6606     designed file formats here!). Since our document is not directly
6607     aware of any of its layouts, we have to find a listener for
6608     FL_DocLayout that is registered with this document (it does not
6609     matter if there are more FL_DocLayout listeners registered, the
6610     visual direction will be same for all, so we grab the first one),
6611     and from the listener we can get access to the layout, down to the
6612     runs which carry the information that we need.  Tomas, May 3, 2003
6613  */
exportGetVisDirectionAtPos(PT_DocPosition pos,UT_BidiCharType & type)6614 bool PD_Document::exportGetVisDirectionAtPos(PT_DocPosition pos, UT_BidiCharType &type)
6615 {
6616 	if(m_bLoading)
6617 		return true;
6618 
6619 	if(pos == m_iVDLastPos && m_pVDRun)
6620 	{
6621 		// we have all the info we need cached, so just use it
6622 		type = m_pVDRun->getVisDirection();
6623 		return true;
6624 	}
6625 	else if(pos < m_iVDLastPos)
6626 	{
6627 		// this is the worst-case scenario, we have to start from the
6628 		// beginning
6629 		m_iVDLastPos = pos;
6630 		if(!_exportInitVisDirection(pos))
6631 			return false;
6632 	}
6633 	else
6634 	{
6635 		// we can continue from where we left of the last time
6636 		m_iVDLastPos = pos;
6637 		if(!_exportFindVisDirectionRunAtPos(pos))
6638 			return false;
6639 	}
6640 
6641 	// make sure nothing has gone wrong here ...
6642 	UT_return_val_if_fail(m_pVDRun, false);
6643 
6644 	type = m_pVDRun->getVisDirection();
6645 	return true;
6646 }
6647 
_exportInitVisDirection(PT_DocPosition pos)6648 bool PD_Document::_exportInitVisDirection(PT_DocPosition pos)
6649 {
6650 	if(m_bLoading)
6651 		return true;
6652 	m_pVDBl = NULL;
6653 	m_pVDRun = NULL;
6654 
6655 	// find the first DocLayout listener
6656 	UT_uint32 count = m_vecListeners.getItemCount();
6657     fl_DocListener* pDocListener = NULL;
6658 
6659 	for(UT_uint32 i = 0; i < count; i++)
6660 	{
6661 		PL_Listener * pL = (PL_Listener *) m_vecListeners.getNthItem(i);
6662 		if(pL && pL->getType() == PTL_DocLayout)
6663 		{
6664 			pDocListener = (fl_DocListener*) pL;
6665 			break;
6666 		}
6667 	}
6668 
6669 	UT_return_val_if_fail(pDocListener, false);
6670 
6671 	const FL_DocLayout * pDL = pDocListener->getLayout();
6672 	UT_return_val_if_fail(pDL, false);
6673 
6674 
6675 	m_pVDBl = pDL->findBlockAtPosition(pos);
6676 	UT_return_val_if_fail(m_pVDBl, false);
6677 
6678 	UT_uint32 iOffset = pos - m_pVDBl->getPosition();
6679 	m_pVDRun = m_pVDBl->findRunAtOffset(iOffset);
6680 	UT_return_val_if_fail(m_pVDRun, false);
6681 	return true;
6682 }
6683 
_exportFindVisDirectionRunAtPos(PT_DocPosition pos)6684 bool PD_Document::_exportFindVisDirectionRunAtPos(PT_DocPosition pos)
6685 {
6686 	// this is similar to the above, except we will first try to use
6687 	// the cached info
6688 
6689 	if(m_pVDBl && m_pVDRun)
6690 	{
6691 		UT_uint32 iOffset = pos - m_pVDBl->getPosition();
6692 
6693 		//first see if the cached run matches (this will often be the
6694 		//case since this we typicaly crawl over the document position
6695 		//by position
6696 		if(m_pVDRun->getBlockOffset() <= iOffset
6697 		   && (m_pVDRun->getBlockOffset() + m_pVDRun->getLength()) > iOffset)
6698 		{
6699 			return true;
6700 		}
6701 
6702 		// now try to use the present block and any blocks that are
6703 		// chained with it
6704 		const fl_BlockLayout * pBL        = m_pVDBl;
6705 		fp_Run *         pRunResult = NULL;
6706 
6707 		while (1)
6708 		{
6709 			UT_sint32 iOffset2 = pos - pBL->getPosition();
6710 
6711 			if(iOffset2 < 0)
6712 				break;
6713 
6714 			pRunResult = pBL->findRunAtOffset((UT_uint32)iOffset2);
6715 
6716 			if(pRunResult)
6717 				break;
6718 
6719 			const fl_ContainerLayout * pCL = pBL->getNext();
6720 
6721 			if(pCL && pCL->getContainerType() == FL_CONTAINER_BLOCK)
6722 				pBL = reinterpret_cast<const fl_BlockLayout*>(pCL);
6723 			else
6724 				break;
6725 		}
6726 
6727 		if(pRunResult)
6728 		{
6729 			m_pVDRun = pRunResult;
6730 			m_pVDBl = pBL;
6731 			return true;
6732 		}
6733 	}
6734 
6735 	// if we got so far the offset is past the present
6736 	// block-chain, i.e., in a different section, we start from
6737 	// the beginning
6738 	return _exportInitVisDirection(pos);
6739 }
6740 
insertStruxBeforeFrag(pf_Frag * pF,PTStruxType pts,const gchar ** attributes,pf_Frag_Strux ** ppfs_ret)6741 bool PD_Document::insertStruxBeforeFrag(pf_Frag * pF, PTStruxType pts,
6742 										const gchar ** attributes, pf_Frag_Strux ** ppfs_ret)
6743 {
6744 	UT_return_val_if_fail (m_pPieceTable, false);
6745 
6746 	// can only be used while loading the document
6747 
6748 	if(pts == PTX_EndCell)
6749 	{
6750 		pf_Frag * pPrevFrag = pF->getPrev();
6751 		if(pPrevFrag && pPrevFrag->getType() == pf_Frag::PFT_Strux)
6752 		{
6753 			pf_Frag_Strux * pfs = static_cast<pf_Frag_Strux *>(pPrevFrag);
6754 			if(pfs->getStruxType() == PTX_SectionCell)
6755 			{
6756 				m_vecSuspectFrags.addItem(pPrevFrag);
6757 			}
6758 		}
6759 	}
6760 	updateStatus();
6761 	return m_pPieceTable->insertStruxBeforeFrag(pF,pts,attributes,ppfs_ret);
6762 }
6763 
insertSpanBeforeFrag(pf_Frag * pF,const UT_UCSChar * pbuf,UT_uint32 length)6764 bool PD_Document::insertSpanBeforeFrag(pf_Frag * pF, const UT_UCSChar * pbuf, UT_uint32 length)
6765 {
6766 	UT_return_val_if_fail (m_pPieceTable, false);
6767 	if(pF->getType() == pf_Frag::PFT_Strux)
6768 	{
6769 		pf_Frag_Strux * pfs = static_cast<pf_Frag_Strux *>(pF);
6770 		if((pfs->getStruxType() != PTX_Block) && (pfs->getStruxType() != PTX_EndFootnote) && (pfs->getStruxType() != PTX_EndEndnote) && (pfs->getStruxType() != PTX_EndAnnotation) && (pfs->getStruxType() != PTX_EndCell) )
6771 		{
6772 			//
6773 			// Append a block!
6774 			//
6775 			m_vecSuspectFrags.addItem(pF);
6776 			return true;
6777 		}
6778 	}
6779 
6780 	// can only be used while loading the document
6781 
6782 	// REMOVE UNDESIRABLE CHARACTERS ...
6783 	// we will remove all LRO, RLO, LRE, RLE, and PDF characters
6784 	// * at the moment we do not handle LRE/RLE
6785 	// * we replace LRO/RLO with our dir-override property
6786 
6787 	const gchar * attrs[] = {"props", NULL, NULL};
6788 	std::string s;
6789 
6790 	bool result = true;
6791 	const UT_UCS4Char * pStart = pbuf;
6792 
6793 	for(const UT_UCS4Char * p = pbuf; p < pbuf + length; p++)
6794 	{
6795 		switch(*p)
6796 		{
6797 			case UCS_LRO:
6798 				if((p - pStart) > 0)
6799 					result &= m_pPieceTable->insertSpanBeforeFrag(pF,pStart,p - pStart);
6800 
6801 				s = "dir-override:ltr";
6802 				attrs[1] = s.c_str();
6803 				result &= m_pPieceTable->appendFmt(&attrs[0]);
6804 				pStart = p + 1;
6805 				m_iLastDirMarker = *p;
6806 				break;
6807 
6808 			case UCS_RLO:
6809 				if((p - pStart) > 0)
6810 					result &= m_pPieceTable->insertSpanBeforeFrag(pF,pStart,p - pStart);
6811 
6812 				s = "dir-override:rtl";
6813 				attrs[1] = s.c_str();
6814 				result &= m_pPieceTable->appendFmt(&attrs[0]);
6815 
6816 				pStart = p + 1;
6817 				m_iLastDirMarker = *p;
6818 				break;
6819 
6820 			case UCS_PDF:
6821 				if((p - pStart) > 0)
6822 					result &= m_pPieceTable->insertSpanBeforeFrag(pF,pStart,p - pStart);
6823 
6824 				if((m_iLastDirMarker == UCS_RLO) || (m_iLastDirMarker == UCS_LRO))
6825 				{
6826 					s = "dir-override:";
6827 					attrs[1] = s.c_str();
6828 					result &= m_pPieceTable->appendFmt(&attrs[0]);
6829 				}
6830 
6831 				pStart = p + 1;
6832 				m_iLastDirMarker = *p;
6833 				break;
6834 
6835 			case UCS_LRE:
6836 			case UCS_RLE:
6837 				if((p - pStart) > 0)
6838 					result &= m_pPieceTable->insertSpanBeforeFrag(pF,pStart,p - pStart);
6839 
6840 				pStart = p + 1;
6841 				m_iLastDirMarker = *p;
6842 				break;
6843 		}
6844 	}
6845 
6846 	result &= m_pPieceTable->insertSpanBeforeFrag(pF,pStart,length - (pStart-pbuf));
6847 	return result;
6848 }
6849 
insertObjectBeforeFrag(pf_Frag * pF,PTObjectType pto,const gchar ** attributes)6850 bool PD_Document::insertObjectBeforeFrag(pf_Frag * pF, PTObjectType pto,
6851 										 const gchar ** attributes)
6852 {
6853 	UT_return_val_if_fail (m_pPieceTable, false);
6854 	if(pF->getType() == pf_Frag::PFT_Strux)
6855 	{
6856 		pf_Frag_Strux * pfs = static_cast<pf_Frag_Strux *>(pF);
6857 		if((pfs->getStruxType() != PTX_Block) && (pfs->getStruxType() != PTX_EndFootnote) && (pfs->getStruxType() != PTX_EndEndnote)  && (pfs->getStruxType() != PTX_EndAnnotation) )
6858 		{
6859 			//
6860 			// Append a block!
6861 			//
6862 			m_vecSuspectFrags.addItem(pF);
6863 			return true;
6864 		}
6865 	}
6866 
6867 	// can only be used while loading the document
6868 
6869 	return m_pPieceTable->insertObjectBeforeFrag(pF,pto,attributes);
6870 }
6871 
insertFmtMarkBeforeFrag(pf_Frag * pF)6872 bool PD_Document::insertFmtMarkBeforeFrag(pf_Frag * pF)
6873 {
6874 	UT_return_val_if_fail (m_pPieceTable, false);
6875 	if(pF->getType() == pf_Frag::PFT_Strux)
6876 	{
6877 		pf_Frag_Strux * pfs = static_cast<pf_Frag_Strux *>(pF);
6878 		if((pfs->getStruxType() != PTX_Block) && (pfs->getStruxType() != PTX_EndFootnote) && (pfs->getStruxType() != PTX_EndEndnote) && (pfs->getStruxType() != PTX_EndAnnotation) )
6879 		{
6880 			//
6881 			// Append a block!
6882 			//
6883 			m_vecSuspectFrags.addItem(pF);
6884 			return true;
6885 		}
6886 	}
6887 
6888 	// can only be used while loading the document
6889 
6890 	return m_pPieceTable->insertFmtMarkBeforeFrag(pF);
6891 }
6892 
changeStruxFormatNoUpdate(PTChangeFmt ptc,pf_Frag_Strux * sdh,const gchar ** attributes)6893 bool PD_Document::changeStruxFormatNoUpdate(PTChangeFmt ptc ,pf_Frag_Strux* sdh,const gchar ** attributes)
6894 {
6895 	return m_pPieceTable->changeStruxFormatNoUpdate(ptc ,sdh,attributes);
6896 }
6897 
6898 
6899 /*!
6900  * Change the attributes of an object without generating a Change Record.
6901  * Use with extreme care.
6902  */
changeObjectFormatNoUpdate(PTChangeFmt ptc,pf_Frag_Object * odh,const gchar ** attributes,const gchar ** properties)6903 bool PD_Document::changeObjectFormatNoUpdate(PTChangeFmt ptc ,pf_Frag_Object* odh,const gchar ** attributes,const gchar ** properties )
6904 {
6905 	pf_Frag_Object * pfo = odh;
6906 	return m_pPieceTable->changeObjectFormatNoUpdate(ptc ,pfo,attributes,properties);
6907 }
6908 
6909 /*!
6910  * Return Attribute Property Index associated with the pf_Frag_Object pointed
6911  * to by odh
6912  */
getAPIFromSOH(pf_Frag_Object * odh)6913 PT_AttrPropIndex  PD_Document::getAPIFromSOH(pf_Frag_Object* odh)
6914 {
6915 	pf_Frag_Object * pfo = odh;
6916 	return pfo->getIndexAP();
6917 }
6918 
insertFmtMarkBeforeFrag(pf_Frag * pF,const gchar ** attributes)6919 bool PD_Document::insertFmtMarkBeforeFrag(pf_Frag * pF, const gchar ** attributes)
6920 {
6921 	UT_return_val_if_fail (m_pPieceTable, false);
6922 
6923 	// can only be used while loading the document
6924 
6925 	return m_pPieceTable->insertFmtMarkBeforeFrag(pF,attributes);
6926 }
6927 
findFragOfType(pf_Frag::PFType type,UT_sint32 iSubtype,pf_Frag * pfStart) const6928 pf_Frag * PD_Document::findFragOfType(pf_Frag::PFType type, UT_sint32 iSubtype, pf_Frag * pfStart) const
6929 {
6930 	UT_return_val_if_fail(m_pPieceTable,NULL);
6931 
6932 	pf_Frag * pf = pfStart;
6933 
6934 	if(!pf)
6935 		pf = m_pPieceTable->getFragments().getFirst();
6936 
6937 	UT_return_val_if_fail(pf, NULL);
6938 
6939 	while(pf)
6940 	{
6941 		bool bBreak = true;
6942 		if(pf->getType() == type)
6943 		{
6944 			if(iSubtype < 0)
6945 				break;
6946 
6947 			switch(type)
6948 			{
6949 				// fragments with no subtypes
6950 				case pf_Frag::PFT_Text:
6951 				case pf_Frag::PFT_EndOfDoc:
6952 				case pf_Frag::PFT_FmtMark:
6953 					break;
6954 
6955 				case pf_Frag::PFT_Object:
6956 					{
6957 						const pf_Frag_Object * pfo = static_cast<const pf_Frag_Object*>(pf);
6958 						if((UT_sint32)pfo->getObjectType() != iSubtype)
6959 							bBreak = false;
6960 					}
6961 					break;
6962 
6963 				case pf_Frag::PFT_Strux:
6964 					{
6965 						const pf_Frag_Strux * pfs = static_cast<const pf_Frag_Strux*>(pf);
6966 						if((UT_sint32)pfs->getStruxType() != iSubtype)
6967 							bBreak = false;
6968 					}
6969 					break;
6970 
6971 				default:
6972 					UT_ASSERT_HARMLESS(UT_NOT_REACHED);
6973 			}
6974 
6975 			if(bBreak)
6976 				break;
6977 		}
6978 
6979 		pf = pf->getNext();
6980 	}
6981 
6982 	return pf;
6983 }
6984 
getLastFrag() const6985 pf_Frag * PD_Document::getLastFrag() const
6986 {
6987 	UT_return_val_if_fail(m_pPieceTable,NULL);
6988 	return m_pPieceTable->getFragments().getLast();
6989 }
6990 
6991 
6992 /*!
6993     force the document into being dirty and signal this to our listeners
6994 */
forceDirty()6995 void PD_Document::forceDirty()
6996 {
6997 	if(!isDirty())
6998 	{
6999 		_setForceDirty(true);
7000 
7001 		// now notify listeners ...
7002 		// this is necessary so that the save command is available after
7003 		// operations that only change m_bForcedDirty
7004 		signalListeners(PD_SIGNAL_DOCDIRTY_CHANGED);
7005 	}
7006 }
7007 
7008 
7009 /*!
7010     Returns true if the stylesheets of both documents are identical
7011 */
areDocumentStylesheetsEqual(const AD_Document & D) const7012 bool PD_Document::areDocumentStylesheetsEqual(const AD_Document &D) const
7013 {
7014 	if(D.getType() != ADDOCUMENT_ABIWORD)
7015 		return false;
7016 
7017 	PD_Document &d = (PD_Document &)D;
7018 	UT_return_val_if_fail(m_pPieceTable || d.m_pPieceTable, false);
7019 
7020 	const std::map<std::string,PD_Style*> & hS1 = m_pPieceTable->getAllStyles();
7021 	const std::map<std::string,PD_Style*> & hS2 = d.m_pPieceTable->getAllStyles();
7022 
7023 	if(hS1.size() != hS2.size())
7024 		return false;
7025 
7026 	UT_StringPtrMap hFmtMap;
7027 
7028 	for(std::map<std::string,PD_Style*>::const_iterator iter = hS1.begin();
7029 		iter != hS1.end(); ++iter)
7030 	{
7031 		const PD_Style * pS1, * pS2;
7032 		const std::string &key = iter->first;
7033 
7034 		pS1 = iter->second;
7035 
7036 		std::map<std::string,PD_Style*>::const_iterator iter2 = hS2.find(key);
7037 		if (iter2 == hS2.end())
7038 			return false;
7039 
7040 		pS2 = iter2->second;
7041 
7042 		PT_AttrPropIndex ap1 = pS1->getIndexAP();
7043 		PT_AttrPropIndex ap2 = pS2->getIndexAP();
7044 
7045 		// because the indexes are into different piecetables, we
7046 		// have to expand them
7047 		const PP_AttrProp * pAP1;
7048 		const PP_AttrProp * pAP2;
7049 
7050 		m_pPieceTable->getAttrProp(ap1, &pAP1);
7051 		d.m_pPieceTable->getAttrProp(ap2, &pAP2);
7052 
7053 		UT_return_val_if_fail(pAP1 && pAP2, false);
7054 
7055 		// must print all digits to make this unambigous
7056 		std::string s = UT_std_string_sprintf("%08x%08x", ap1, ap2);
7057 		bool bAreSame = hFmtMap.contains(s,NULL);
7058 
7059 		if(!bAreSame)
7060 		{
7061 			if(!pAP1->isEquivalent(pAP2))
7062 			{
7063 				return false;
7064 			}
7065 			else
7066 			{
7067 				hFmtMap.insert(s,NULL);
7068 			}
7069 		}
7070 	}
7071 
7072 	return true;
7073 }
7074 
7075 
7076 /*!
7077     carries out the actual change in PieceTable; called by
7078     acceptRejectRevision() and rejectAllHigherRevisions()
7079 
7080     this method operates on a fragment at a time, but if it
7081     results in deletion from PT, more fragments might be deleted
7082 */
_acceptRejectRevision(bool bReject,UT_uint32 iStart,UT_uint32 iEnd,const PP_Revision * pRev,PP_RevisionAttr & RevAttr,pf_Frag * pf,bool & bDeleted)7083 bool PD_Document::_acceptRejectRevision(bool bReject, UT_uint32 iStart, UT_uint32 iEnd,
7084 										const PP_Revision * pRev,
7085 										PP_RevisionAttr &RevAttr, pf_Frag * pf,
7086 										bool & bDeleted)
7087 {
7088 	UT_return_val_if_fail(pf && pRev, false);
7089 	bDeleted = false;
7090 
7091 	UT_uint32 iRealDeleteCount;
7092 	const gchar * ppAttr[3];
7093 	const gchar rev[] = "revision";
7094 	ppAttr[0] = rev;
7095 	ppAttr[1] = NULL;
7096 	ppAttr[2] = NULL;
7097 
7098 	UT_uint32 iAttrCount;
7099 	UT_uint32 iPropCount;
7100 
7101 	const gchar ** ppProps = NULL, ** ppAttr2 = NULL;
7102 	bool bRet = true;
7103 	UT_uint32 i;
7104 
7105 	// if the fragment is a strux that has a corresponding end element
7106 	// and we will be deleting itwe have to expand the deletion to the
7107 	// end of that element
7108 	UT_uint32 iEndDelete = iEnd;
7109 	PP_RevisionType iRevType = pRev->getType();
7110 
7111 	if(pf->getType() == pf_Frag::PFT_Strux &&
7112 	   (   (bReject &&  (iRevType == PP_REVISION_ADDITION_AND_FMT || iRevType == PP_REVISION_ADDITION))
7113 		|| (!bReject && (iRevType == PP_REVISION_DELETION))))
7114 	{
7115 		pf_Frag_Strux * pfs = (pf_Frag_Strux*)pf;
7116 		PTStruxType pst = PTX_Block;
7117 
7118 		switch(pfs->getStruxType())
7119 		{
7120 			case PTX_SectionEndnote:
7121 				pst = PTX_EndEndnote; break;
7122 			case PTX_SectionTable:
7123 				pst = PTX_EndTable; break;
7124 			case PTX_SectionCell:
7125 				pst = PTX_EndCell; break;
7126 			case PTX_SectionFootnote:
7127 				pst = PTX_EndFootnote; break;
7128 			case PTX_SectionAnnotation:
7129 				pst = PTX_EndAnnotation; break;
7130 		    case PTX_SectionMarginnote:
7131 				pst = PTX_EndMarginnote; break;
7132 			case PTX_SectionFrame:
7133 				pst = PTX_EndFrame; break;
7134 			case PTX_SectionTOC:
7135 				pst = PTX_EndTOC; break;
7136 
7137 			default: ; // do nothing
7138 		}
7139 
7140 		if(pst != PTX_Block)
7141 		{
7142 			pf_Frag * pf2 = pf->getNext();
7143 			while(pf2)
7144 			{
7145 				iEndDelete += pf2->getLength();
7146 				if(pf2->getType() == pf_Frag::PFT_Strux)
7147 				{
7148 					pf_Frag_Strux * pfs2 = (pf_Frag_Strux*)pf2;
7149 					if(pfs2->getStruxType() == pst)
7150 						break;
7151 				}
7152 
7153 				pf2 = pf2->getNext();
7154 			}
7155 		}
7156 	}
7157 
7158 	if(bReject)
7159 	{
7160 		switch(iRevType)
7161 		{
7162 			case PP_REVISION_ADDITION:
7163 			case PP_REVISION_ADDITION_AND_FMT:
7164 				{
7165 					// delete this fragment
7166 					bDeleted = true;
7167 
7168 					// since we need real delete, we need to step out
7169 					// of rev. marking mode for a moment ...
7170 					bool bMark = isMarkRevisions();
7171 					_setMarkRevisions(false);
7172 					bRet = deleteSpan(iStart,iEndDelete,NULL,iRealDeleteCount);
7173 					_setMarkRevisions(bMark);
7174 
7175 					if(!bRet)
7176 						bDeleted = false;
7177 
7178 					return bRet;
7179 				}
7180 
7181 			case PP_REVISION_DELETION:
7182 				// remove the revision (and any higher ones) from the attribute
7183 				RevAttr.removeAllHigherOrEqualIds(pRev->getId());
7184 				pRev = NULL;
7185 
7186 				ppAttr[0] = rev;
7187 				ppAttr[1] = RevAttr.getXMLstring();
7188 				ppAttr[2] = NULL;
7189 
7190 				if(pf->getType() == pf_Frag::PFT_Strux)
7191 				{
7192 					pf_Frag_Strux * pfs = static_cast<pf_Frag_Strux *>(pf);
7193 
7194 					// the changeStrux function tries to locate the strux which _contains_ the
7195 					// position we pass into it; however, iStart is the doc position of the actual
7196 					// strux, so we have to skip over the strux
7197 					return changeStruxFmt(PTC_AddFmt,iStart+1,iEnd,ppAttr,NULL, pfs->getStruxType());
7198 				}
7199 				else
7200 					return changeSpanFmt(PTC_AddFmt,iStart,iEnd,ppAttr,NULL);
7201 
7202 			case PP_REVISION_FMT_CHANGE:
7203 				// need to set a new revision attribute
7204 				// first remove current revision from pRevAttr
7205 				RevAttr.removeAllHigherOrEqualIds(pRev->getId());
7206 				pRev = NULL;
7207 
7208 				ppAttr[0] = rev;
7209 				ppAttr[1] = RevAttr.getXMLstring();
7210 				ppAttr[2] = NULL;
7211 
7212 				if(pf->getType() == pf_Frag::PFT_Strux)
7213 				{
7214 					pf_Frag_Strux * pfs = static_cast<pf_Frag_Strux *>(pf);
7215 					// the changeStrux function tries to locate the strux which _contains_ the
7216 					// position we pass into it; however, iStart is the doc position of the actual
7217 					// strux, so we have to skip over the strux
7218 					bRet &= changeStruxFmt(PTC_AddFmt,iStart+1,iEnd,ppAttr,ppProps, pfs->getStruxType());
7219 				}
7220 				else
7221 					bRet &= changeSpanFmt(PTC_AddFmt,iStart,iEnd,ppAttr,ppProps);
7222 
7223 				return bRet;
7224 
7225 
7226 			default:
7227 				UT_ASSERT_HARMLESS( UT_SHOULD_NOT_HAPPEN );
7228 				return false;
7229 		}
7230 	}
7231 	else
7232 	{
7233 		switch(iRevType)
7234 		{
7235 			case PP_REVISION_ADDITION:
7236 				// simply remove the revision attribute
7237 				if(pf->getType() == pf_Frag::PFT_Strux)
7238 				{
7239 					pf_Frag_Strux * pfs = static_cast<pf_Frag_Strux *>(pf);
7240 					// the changeStrux function tries to locate the strux which _contains_ the
7241 					// position we pass into it; however, iStart is the doc position of the actual
7242 					// strux, so we have to skip over the strux
7243 					return changeStruxFmt(PTC_RemoveFmt,iStart+1,iEnd,ppAttr,NULL, pfs->getStruxType());
7244 				}
7245 				else
7246 					return changeSpanFmt(PTC_RemoveFmt,iStart,iEnd,ppAttr,NULL);
7247 
7248 			case PP_REVISION_DELETION:
7249 				{
7250 					// delete this fragment
7251 					bDeleted = true;
7252 
7253 					// since we need real delete, we need to step out
7254 					// of rev. marking mode for a moment ...
7255 					bool bMark = isMarkRevisions();
7256 					_setMarkRevisions(false);
7257 					bRet = deleteSpan(iStart,iEndDelete,NULL,iRealDeleteCount);
7258 					_setMarkRevisions(bMark);
7259 
7260 					if(!bRet)
7261 						bDeleted = false;
7262 
7263 					return bRet;
7264 				}
7265 
7266 			case PP_REVISION_ADDITION_AND_FMT:
7267 				// overlay the formatting and remove the revision attribute
7268 				if(pf->getType() == pf_Frag::PFT_Strux)
7269 				{
7270 					pf_Frag_Strux * pfs = static_cast<pf_Frag_Strux *>(pf);
7271 					// the changeStrux function tries to locate the strux which _contains_ the
7272 					// position we pass into it; however, iStart is the doc position of the actual
7273 					// strux, so we have to skip over the strux
7274 					return changeStruxFmt(PTC_RemoveFmt,iStart+1,iEnd,ppAttr,NULL, pfs->getStruxType());
7275 				}
7276 				else
7277 					return changeSpanFmt(PTC_RemoveFmt,iStart,iEnd,ppAttr,NULL);
7278 
7279 			case PP_REVISION_FMT_CHANGE:
7280 				// overlay the formatting and remove this revision
7281 				// from the revision attribute
7282 				iPropCount = 0;
7283 				iAttrCount = 0;
7284 				ppProps = new const gchar *[2*pRev->getPropertyCount()+1];
7285 				ppAttr2 = new const gchar *[2*pRev->getAttributeCount()+3];
7286 
7287 				for(i = 0; i < pRev->getPropertyCount(); i++)
7288 				{
7289 					pRev->getNthProperty(i, ppProps[2*i],ppProps[2*i + 1]);
7290 
7291 					// we have to make copies of these because they might be deleted
7292 					// before we need them
7293 					ppProps[2*i] = (gchar*)g_strdup(ppProps[2*i]);
7294 					ppProps[2*i + 1] = (gchar*)g_strdup(ppProps[2*i + 1]);
7295 					iPropCount += 2; // these will need to be freed later ...
7296 				}
7297 
7298 				ppProps[2*i] = NULL;
7299 
7300 				for(i = 0; i < pRev->getAttributeCount(); i++)
7301 				{
7302 					pRev->getNthAttribute(i, ppAttr2[2*i],ppAttr2[2*i + 1]);
7303 
7304 					// we have to make copies of these because they might be deleted
7305 					// before we need them
7306 					ppAttr2[2*i] = (gchar*)g_strdup(ppAttr2[2*i]);
7307 					ppAttr2[2*i + 1] = (gchar*)g_strdup(ppAttr2[2*i + 1]);
7308 					iAttrCount += 2; // these will need to be freed later ...
7309 				}
7310 
7311 				if(pRev->getType() == PP_REVISION_ADDITION_AND_FMT)
7312 				{
7313 					ppAttr2[2*i] = NULL;
7314 				}
7315 				else
7316 				{
7317 					// need to set a new revision attribute
7318 					// first remove current revision from pRevAttr
7319 					RevAttr.removeAllHigherOrEqualIds(pRev->getId());
7320 					pRev = NULL;
7321 
7322 					ppAttr2[2*i] = rev;
7323 					ppAttr2[2*i + 1] = RevAttr.getXMLstring();
7324 					ppAttr2[2*i + 2] = NULL;
7325 
7326 					if(*ppAttr2[2*i + 1] == 0)
7327 					{
7328 						// no revision attribute left, which means we
7329 						// have to remove it by separate call to changeSpanFmt
7330 
7331 						// if this is the only attribute, we just set
7332 						// the whole thing to NULL
7333 						if(i == 0)
7334 						{
7335 							delete ppAttr2;
7336 							ppAttr2 = NULL;
7337 						}
7338 						else
7339 						{
7340 							// OK, there are some other attributes
7341 							// left, so we set the rev name to NULL
7342 							// and remove the formatting by a separate
7343 							// call to changeSpanFmt
7344 							ppAttr2[2*i] = NULL;
7345 						}
7346 
7347 						// now we use the ppAttr set to remove the
7348 						// revision attribute
7349 						if(pf->getType() == pf_Frag::PFT_Strux)
7350 						{
7351 							pf_Frag_Strux * pfs = static_cast<pf_Frag_Strux *>(pf);
7352 							// the changeStrux function tries to locate the strux which _contains_ the
7353 							// position we pass into it; however, iStart is the doc position of the actual
7354 							// strux, so we have to skip over the strux
7355 							bRet &= changeStruxFmt(PTC_RemoveFmt,iStart+1,iEnd,ppAttr,NULL, pfs->getStruxType());
7356 						}
7357 						else
7358 							bRet &= changeSpanFmt(PTC_RemoveFmt,iStart,iEnd,ppAttr,NULL);
7359 					}
7360 				}
7361 
7362 				UT_ASSERT_HARMLESS( ppAttr2 || ppProps );
7363 
7364 				if(pf->getType() == pf_Frag::PFT_Strux)
7365 				{
7366 					pf_Frag_Strux * pfs = static_cast<pf_Frag_Strux *>(pf);
7367 					// the changeStrux function tries to locate the strux which _contains_ the
7368 					// position we pass into it; however, iStart is the doc position of the actual
7369 					// strux, so we have to skip over the strux
7370 					bRet &= changeStruxFmt(PTC_AddFmt,iStart+1,iEnd,ppAttr2,ppProps, pfs->getStruxType());
7371 				}
7372 				else
7373 					bRet &= changeSpanFmt(PTC_AddFmt,iStart,iEnd,ppAttr2,ppProps);
7374 
7375 				for(i = 0; i < iPropCount; ++i)
7376 					g_free((gchar*)ppProps[i]);
7377 
7378 				for(i = 0; i < iAttrCount; ++i)
7379 					g_free((gchar*)ppAttr2[i]);
7380 
7381 				delete ppProps;
7382 				delete ppAttr2;
7383 
7384 				return bRet;
7385 
7386 			default:
7387 				UT_ASSERT_HARMLESS( UT_SHOULD_NOT_HAPPEN );
7388 		}
7389 	}
7390 
7391 	return false;
7392 }
7393 
acceptAllRevisions()7394 bool PD_Document::acceptAllRevisions()
7395 {
7396 	PD_DocIterator t(*this);
7397 	UT_return_val_if_fail(t.getStatus() == UTIter_OK, false);
7398 
7399 	notifyPieceTableChangeStart();
7400 
7401 	beginUserAtomicGlob();
7402 	while(t.getStatus() == UTIter_OK)
7403 	{
7404 		pf_Frag * pf = const_cast<pf_Frag *>(t.getFrag());
7405 
7406 		if(!pf)
7407 		{
7408 			UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
7409 			endUserAtomicGlob();
7410 			notifyPieceTableChangeEnd();
7411 			return false;
7412 		}
7413 
7414 		PT_AttrPropIndex API = pf->getIndexAP();
7415 
7416 		const PP_AttrProp * pAP = NULL;
7417 		m_pPieceTable->getAttrProp(API,&pAP);
7418 		if(!pAP)
7419 		{
7420 			UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
7421 			endUserAtomicGlob();
7422 			notifyPieceTableChangeEnd();
7423 			return false;
7424 		}
7425 
7426 		const gchar * pszRevision = NULL;
7427 		pAP->getAttribute("revision", pszRevision);
7428 
7429 		if(pszRevision == NULL)
7430 		{
7431 			// no revisions on this fragment
7432 			t += pf->getLength();
7433 			continue;
7434 		}
7435 
7436 		PP_RevisionAttr RevAttr(pszRevision);
7437 		RevAttr.pruneForCumulativeResult(this);
7438 		const PP_Revision * pRev = NULL;
7439 		if(RevAttr.getRevisionsCount())
7440 			pRev = RevAttr.getNthRevision(0);
7441 
7442 		if(!pRev)
7443 		{
7444 			// no revisions after pruning ???
7445 			t += pf->getLength();
7446 			continue;
7447 		}
7448 
7449 		UT_uint32 iStart = t.getPosition();
7450 		UT_uint32 iEnd   = iStart + pf->getLength();
7451 		bool bDeleted = false;
7452 
7453 		_acceptRejectRevision(false /*accept*/, iStart, iEnd, pRev, RevAttr, pf, bDeleted);
7454 
7455 		// advance -- the call to _acceptRejectRevision could have
7456 		// resulted in deletion and/or merging of fragments; we have
7457 		// to reset the iterator
7458 		if(bDeleted)
7459 			t.reset(iStart, NULL);
7460 		else
7461 			t.reset(iEnd, NULL);
7462 	}
7463 
7464 	// _acceptRejectRevison() function unfortunately leaves some unwanted fmt marks in the
7465 	// document; we will purge all fmt marks
7466 	purgeFmtMarks();
7467 
7468 	endUserAtomicGlob();
7469 	notifyPieceTableChangeEnd();
7470 	signalListeners(PD_SIGNAL_UPDATE_LAYOUT);
7471 	return true;
7472 }
7473 
rejectAllHigherRevisions(UT_uint32 iLevel)7474 bool PD_Document::rejectAllHigherRevisions(UT_uint32 iLevel)
7475 {
7476 	PD_DocIterator t(*this);
7477 	UT_return_val_if_fail(t.getStatus() == UTIter_OK, false);
7478 
7479 	const PP_Revision * pRev;
7480 
7481 	notifyPieceTableChangeStart();
7482 
7483 	beginUserAtomicGlob();
7484 	while(t.getStatus() == UTIter_OK)
7485 	{
7486 		pf_Frag * pf = const_cast<pf_Frag *>(t.getFrag());
7487 
7488 		if(!pf)
7489 		{
7490 			UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
7491 			endUserAtomicGlob();
7492 			notifyPieceTableChangeEnd();
7493 			return false;
7494 		}
7495 
7496 		PT_AttrPropIndex API = pf->getIndexAP();
7497 
7498 		const PP_AttrProp * pAP = NULL;
7499 		m_pPieceTable->getAttrProp(API,&pAP);
7500 		if(!pAP)
7501 		{
7502 			UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
7503 			endUserAtomicGlob();
7504 			notifyPieceTableChangeEnd();
7505 			return false;
7506 		}
7507 
7508 		const gchar * pszRevision = NULL;
7509 		pAP->getAttribute("revision", pszRevision);
7510 
7511 		if(pszRevision == NULL)
7512 		{
7513 			// no revisions on this fragment
7514 			t += pf->getLength();
7515 			continue;
7516 		}
7517 
7518 		PP_RevisionAttr RevAttr(pszRevision);
7519 		pRev = RevAttr.getLowestGreaterOrEqualRevision(iLevel+1);
7520 		if(!pRev)
7521 		{
7522 			// no higher revisions
7523 			t += pf->getLength();
7524 			continue;
7525 		}
7526 
7527 		UT_uint32 iStart = t.getPosition();
7528 		UT_uint32 iEnd   = iStart + pf->getLength();
7529 		bool bDeleted = false;
7530 
7531 		_acceptRejectRevision(true /*reject*/, iStart, iEnd, pRev, RevAttr, pf, bDeleted);
7532 
7533 		// advance -- the call to _acceptRejectRevision could have
7534 		// resulted in deletion and/or merging of fragments; we have
7535 		// to reset the iterator
7536 		if(bDeleted)
7537 			t.reset(iStart, NULL);
7538 		else
7539 			t.reset(iEnd, NULL);
7540 	}
7541 
7542 	// _acceptRejectRevison() function unfortunately leaves some unwanted fmt marks in the
7543 	// document; we will purge all fmt marks
7544 	purgeFmtMarks();
7545 
7546 	endUserAtomicGlob();
7547 	notifyPieceTableChangeEnd();
7548 	signalListeners(PD_SIGNAL_UPDATE_LAYOUT);
7549 	return true;
7550 }
7551 
7552 /*!
7553    accepts or reject top visible revision between document positions
7554    iStart and iEnd.
7555 
7556    \param bReject  true if revisions are to be rejected
7557    \param iPos1    document position to start at
7558    \param iPos2     document position to finish at
7559    \param iLevel   the highest revision level to accept
7560 
7561    \return         true on success
7562 
7563    NB: For each fragment this function removes the highest revision <=
7564        iLevel. For example, if iLevel is 3 and fragment contains
7565        revisions 1,2, 4, revision 2 will be removed.
7566 */
acceptRejectRevision(bool bReject,UT_uint32 iPos1,UT_uint32 iPos2,UT_uint32 iLevel)7567 bool PD_Document::acceptRejectRevision(bool bReject, UT_uint32 iPos1,
7568 									   UT_uint32 iPos2, UT_uint32 iLevel)
7569 {
7570 	UT_uint32 iPosStart = UT_MIN(iPos1, iPos2);
7571 	UT_uint32 iPosEnd   = UT_MAX(iPos1, iPos2);
7572 
7573 	PD_DocIterator t(*this, iPosStart);
7574 	UT_return_val_if_fail(t.getStatus() == UTIter_OK, false);
7575 
7576 
7577 	const PP_Revision * pSpecial;
7578 	const PP_Revision * pRev;
7579 	UT_uint32 iLenProcessed = 0;
7580 	bool bFirst = true;
7581 
7582 	notifyPieceTableChangeStart();
7583 
7584 	beginUserAtomicGlob();
7585 	while(t.getStatus() == UTIter_OK && iPosStart + iLenProcessed < iPosEnd)
7586 	{
7587 		pf_Frag * pf = const_cast<pf_Frag *>(t.getFrag());
7588 		if(!pf)
7589 		{
7590 			UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
7591 			endUserAtomicGlob();
7592 			notifyPieceTableChangeEnd();
7593 			return false;
7594 		}
7595 
7596 		UT_uint32 iFragLen = pf->getLength();
7597 
7598 		if(bFirst)
7599 		{
7600 			// we might be working only with a part of the frag
7601 			bFirst = false;
7602 			iFragLen -= (iPosStart - pf->getPos());
7603 		}
7604 
7605 		iLenProcessed += iFragLen;
7606 
7607 		PT_AttrPropIndex API = pf->getIndexAP();
7608 
7609 		const PP_AttrProp * pAP = NULL;
7610 		m_pPieceTable->getAttrProp(API,&pAP);
7611 		if(!pAP)
7612 		{
7613 			UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
7614 			endUserAtomicGlob();
7615 			notifyPieceTableChangeEnd();
7616 			return false;
7617 		}
7618 
7619 		const gchar * pszRevision = NULL;
7620 		pAP->getAttribute("revision", pszRevision);
7621 
7622 		if(pszRevision == NULL)
7623 		{
7624 			// no revisions on this fragment
7625 			t += iFragLen;
7626 			continue;
7627 		}
7628 
7629 		PP_RevisionAttr RevAttr(pszRevision);
7630 		pRev = RevAttr.getGreatestLesserOrEqualRevision(iLevel, &pSpecial);
7631 		if(!pRev)
7632 		{
7633 			// no visible revisions
7634 			t += iFragLen;
7635 			continue;
7636 		}
7637 
7638 		UT_uint32 iStart = t.getPosition();
7639 		UT_uint32 iEnd   = iStart + iFragLen;
7640 
7641 		bool bDeleted = false;
7642 		_acceptRejectRevision(bReject, iStart, iEnd, pRev, RevAttr, pf, bDeleted);
7643 
7644 		// advance -- the call to _acceptRejectRevision could have
7645 		// resulted in deletion and/or merging of fragments; we have
7646 		// to reset the iterator
7647 		if(bDeleted)
7648 			t.reset(iStart, NULL);
7649 		else
7650 			t.reset(iEnd, NULL);
7651 	}
7652 
7653 	endUserAtomicGlob();
7654 	notifyPieceTableChangeEnd();
7655 	signalListeners(PD_SIGNAL_UPDATE_LAYOUT);
7656 	return true;
7657 }
7658 
7659 
7660 /*!
7661   Clears out the revisions table if no revisions are left in the document
7662 */
purgeRevisionTable(bool bUnconditional)7663 void PD_Document::purgeRevisionTable(bool bUnconditional /* = false */)
7664 {
7665 	if(getRevisions().getItemCount() == 0)
7666 		return;
7667 
7668 	if(!bUnconditional)
7669 	{
7670 		UT_String sAPI;
7671 		UT_StringPtrMap hAPI;
7672 
7673 		PD_DocIterator t(*this);
7674 
7675 		// work our way thought the document looking for frags with
7676 		// revisions attributes ...
7677 		while(t.getStatus() == UTIter_OK)
7678 		{
7679 			const pf_Frag * pf = t.getFrag();
7680 			UT_return_if_fail(pf);
7681 
7682 			PT_AttrPropIndex api = pf->getIndexAP();
7683 
7684 			UT_String_sprintf(sAPI, "%08x", api);
7685 
7686 			if(!hAPI.contains(sAPI, NULL))
7687 			{
7688 				const PP_AttrProp * pAP;
7689 				UT_return_if_fail(getAttrProp(api, &pAP));
7690 				UT_return_if_fail(pAP);
7691 
7692 				const gchar * pVal;
7693 
7694 				if(pAP->getAttribute(PT_REVISION_ATTRIBUTE_NAME, pVal))
7695 					return;
7696 
7697 				// cache this api so we do not need to do this again if we
7698 				// come across it
7699 				hAPI.insert(sAPI,NULL);
7700 			}
7701 
7702 			t += pf->getLength();
7703 		}
7704 	}
7705 
7706 
7707 	// if we got this far, we have not found any revisions in the
7708 	// whole doc, clear out the table
7709 	UT_DEBUGMSG(("PD_Document::purgeRevisionTable(): clearing\n"));
7710 	_purgeRevisionTable();
7711 }
7712 
7713 
7714 /*!
7715     Starting to search this document at position pos where the two
7716     documents are known to become different, attepts to find location
7717     at which similarities resume
7718 
7719     \param pos: when called, should contain offset in present document
7720                 at which the difference starts; on successfult return
7721                 it will contain offset in present document where
7722                 similarities resume
7723 
7724     \param iOffset2: when called contains offset to be added to
7725                      position pos in order to correctly position start
7726                      of the search in document d; on return it
7727                      contains offset to be add to pos in order to
7728                      obtain correct location of the resumption of
7729                      similarites in document d
7730 
7731    \param iKnownLength: on return contains the minium guaranteed length of the similarity
7732 
7733    \param d the document to which this document is to be compared
7734 
7735    \return returns true if it succeeds; if no further similarities are
7736            found returns false
7737 */
findWhereSimilarityResumes(PT_DocPosition & pos,UT_sint32 & iOffset2,UT_uint32 & iKnownLength,const PD_Document & d) const7738 bool PD_Document::findWhereSimilarityResumes(PT_DocPosition &pos, UT_sint32 &iOffset2,
7739 											 UT_uint32 & iKnownLength,
7740 											 const PD_Document &d) const
7741 {
7742 	UT_return_val_if_fail(m_pPieceTable || d.m_pPieceTable, true);
7743 
7744 	//  scroll through the documents comparing contents
7745 	PD_DocIterator t1(*this, pos);
7746 	PD_DocIterator t2(d, pos + iOffset2);
7747 
7748 	// first, let's assume that the difference is an insertion in doc
7749 	// 2; we will take a few chars from doc 1 and try to locate them
7750 	// in doc 2
7751 
7752 	// this is a similarity threshold, very arbitrary ...  if we match
7753 	// iTry chars we will be happy if we do not match at least
7754 	// iMinOverlap we will give up. We will use variable step
7755 	UT_sint32 iTry = 128;
7756 	UT_sint32 iMinOverlap = 3;
7757 	UT_sint32 iStep = 128;
7758 	UT_sint32 i = 0;
7759 
7760 	UT_uint32 iFoundPos1 = 0;
7761 	UT_uint32 iFoundPos2 = 0;
7762 	UT_sint32 iFoundOffset1 = 0;
7763 	UT_sint32 iFoundOffset2 = 0;
7764 
7765 	for(i = iTry; i >= iMinOverlap; i -= iStep)
7766 	{
7767 		UT_uint32 pos1 = t1.getPosition();
7768 		UT_uint32 pos2 = t2.getPosition();
7769 
7770 		UT_uint32 iPos = t2.find(t1,i,true);
7771 
7772 		if(t2.getStatus() == UTIter_OK)
7773 		{
7774 			// we found what we were looking for
7775 			iFoundPos1 = pos1;
7776 			iFoundOffset1 = iPos - iFoundPos1;
7777 			break;
7778 		}
7779 		else
7780 		{
7781 			// we did not find our text, reset position ...
7782 			t2.setPosition(pos2);
7783 			t1.setPosition(pos1);
7784 
7785 			if(iStep > 1)
7786 				iStep /= 2;
7787 		}
7788 	}
7789 
7790 	// remember the length we found ...
7791 	UT_sint32 iLen1 = i >= iMinOverlap ? i : 0;
7792 
7793 	if(i == iTry)
7794 	{
7795 		// we found the whole iTry chunk, we will stop here ...
7796 		pos = iFoundPos1;
7797 		iOffset2 = iFoundOffset1;
7798 		iKnownLength = iTry;
7799 		return true;
7800 	}
7801 
7802 	// now do the same, but assuming our text is deleted from doc 2
7803 	t2.setPosition(pos);
7804 	t1.setPosition(pos + iOffset2);
7805 	iStep = 128;
7806 
7807 	for(i = iTry; i >= iMinOverlap; i -= iStep)
7808 	{
7809 		UT_uint32 pos1 = t1.getPosition();
7810 		UT_uint32 pos2 = t2.getPosition();
7811 
7812 		UT_uint32 iPos = t1.find(t2,i,true);
7813 
7814 		if(t1.getStatus() == UTIter_OK)
7815 		{
7816 			// we found what we were looking for
7817 			iFoundPos2 = iPos;
7818 			iFoundOffset2 = pos2 - iFoundPos2;
7819 			break;
7820 		}
7821 		else
7822 		{
7823 			// we did not find our text, reset position ...
7824 			t2.setPosition(pos2);
7825 			t1.setPosition(pos1);
7826 
7827 			if(iStep > 1)
7828 				iStep /= 2;
7829 		}
7830 	}
7831 
7832 	UT_sint32 iLen2 = i >= iMinOverlap ? i : 0;
7833 
7834 	if( !iLen1 && !iLen2)
7835 		return false;
7836 
7837 	// now we will go with whatever is longer
7838 	if(iLen1 >= iLen2)
7839 	{
7840 		pos = iFoundPos1;
7841 		iOffset2 = iFoundOffset1;
7842 		iKnownLength = iLen1;
7843 	}
7844 	else
7845 	{
7846 		pos = iFoundPos2;
7847 		iOffset2 = iFoundOffset2;
7848 		iKnownLength = iLen2;
7849 	}
7850 
7851 	return true;
7852 }
7853 
7854 
7855 /*!
7856     finds the position of the first difference in content between this
7857     document and document d, starting search at given position
7858 
7859     \param pos when called this variable should contian document
7860                offset at which to start searching; on success this
7861                variable will contain offset of the difference in
7862                present document
7863 
7864     \param iOffset2 when called contains offset to be added to pos to
7865                     locate identical position in document d
7866 
7867     \param d   the document to compare with
7868 
7869     \return    returns false if no difference was found, true otherwise
7870 */
findFirstDifferenceInContent(PT_DocPosition & pos,UT_sint32 & iOffset2,const PD_Document & d) const7871 bool PD_Document::findFirstDifferenceInContent(PT_DocPosition &pos, UT_sint32 &iOffset2,
7872 											   const PD_Document &d) const
7873 {
7874 	UT_return_val_if_fail(m_pPieceTable || d.m_pPieceTable, true);
7875 
7876 	//  scroll through the documents comparing contents
7877 	PD_DocIterator t1(*this, pos);
7878 	PD_DocIterator t2(d, pos + iOffset2);
7879 
7880 	while(t1.getStatus() == UTIter_OK && t2.getStatus() == UTIter_OK)
7881 	{
7882 		const pf_Frag * pf1 = t1.getFrag();
7883 		const pf_Frag * pf2 = t2.getFrag();
7884 
7885 		if(!pf1 || !pf2)
7886 		{
7887 			UT_ASSERT_HARMLESS( UT_SHOULD_NOT_HAPPEN );
7888 			return true;
7889 		}
7890 
7891 		if(pf1->getType() != pf2->getType())
7892 		{
7893 			pos = pf1->getPos();
7894 			return true;
7895 		}
7896 
7897 		UT_uint32 iFOffset1 = t1.getPosition() - pf1->getPos();
7898 		UT_uint32 iFOffset2 = t2.getPosition() - pf2->getPos();
7899 
7900 		UT_uint32 iLen1 = pf1->getLength() - iFOffset1;
7901 		UT_uint32 iLen2 = pf2->getLength() - iFOffset2;
7902 		UT_uint32 iLen  = UT_MIN(iLen1, iLen2);
7903 
7904 		if(   iLen1 == iLen2 && iFOffset1 == 0 && iFOffset2 == 0
7905 		   && pf1->getType() != pf_Frag::PFT_Text)
7906 		{
7907 			// completely overlapping non-text frags ..
7908 			if(!(pf1->isContentEqual(*pf2)))
7909 			{
7910 				pos = pf1->getPos();
7911 				return true;
7912 			}
7913 		}
7914 		else if(pf1->getType() != pf_Frag::PFT_Text)
7915 		{
7916 			// partially overlapping frags and not text
7917 			pos = pf1->getPos();
7918 			return true;
7919 		}
7920 		else
7921 		{
7922 			// we have two textual frags that overlap
7923 			// work our way along the overlap ...
7924 			for(UT_uint32 i = 0; i < iLen; ++i)
7925 			{
7926 				if(t1.getChar() != t2.getChar())
7927 				{
7928 					pos = t1.getPosition();
7929 					return true;
7930 				}
7931 
7932 				++t1;
7933 				++t2;
7934 			}
7935 
7936 			// we are already past the end of the shorter frag
7937 			continue;
7938 		}
7939 
7940 		// advance both iterators by the processed length
7941 		t1 += iLen;
7942 		t2 += iLen;
7943 	}
7944 
7945 	if(t1.getStatus() == UTIter_OK && t2.getStatus() != UTIter_OK)
7946 	{
7947 		// document two is shorter ...
7948 		pos = t1.getPosition();
7949 		return true;
7950 	}
7951 
7952 	if(t1.getStatus() != UTIter_OK && t2.getStatus() == UTIter_OK)
7953 	{
7954 		// document 1 is shorter
7955 		pos = t2.getPosition() - iOffset2;
7956 		return true;
7957 	}
7958 
7959 	// if we got this far, we found no differences at all ...
7960 	return false;
7961 }
7962 
setAutoRevisioning(bool autorev)7963 void PD_Document::setAutoRevisioning(bool autorev)
7964 {
7965 	AD_Document::setAutoRevisioning(autorev);
7966 
7967 	// TODO tell our listeners to redo layout ...
7968 	signalListeners(PD_SIGNAL_REFORMAT_LAYOUT);
7969 }
7970 
7971 
7972 
7973 /*!
7974     Returns true if the contents of the two documents are identical
7975     if the function returns false, pos contains the document position
7976     at which first difference was encountered
7977 */
areDocumentContentsEqual(const AD_Document & D,UT_uint32 & pos) const7978 bool PD_Document::areDocumentContentsEqual(const AD_Document &D, UT_uint32 &pos) const
7979 {
7980 	pos = 0;
7981 	if(D.getType() != ADDOCUMENT_ABIWORD)
7982 		return false;
7983 
7984 	PD_Document &d = (PD_Document &)D;
7985 	UT_return_val_if_fail(m_pPieceTable || d.m_pPieceTable, false);
7986 
7987 	// test the docs for length
7988 	UT_uint32 end1, end2;
7989 
7990 	pf_Frag * pf = m_pPieceTable->getFragments().getLast();
7991 
7992 	UT_return_val_if_fail(pf,false);
7993 
7994 	end1 = pf->getPos() + pf->getLength();
7995 
7996 	pf = d.m_pPieceTable->getFragments().getLast();
7997 
7998 	UT_return_val_if_fail(pf,false);
7999 
8000 	end2 = pf->getPos() + pf->getLength();
8001 
8002 	if(end1 != end2)
8003 	{
8004 		pos = UT_MIN(end1, end2);
8005 		return false;
8006 	}
8007 
8008 
8009 	//  scroll through the documents comparing contents
8010 	PD_DocIterator t1(*this);
8011 	PD_DocIterator t2(d);
8012 
8013 	while(t1.getStatus() == UTIter_OK && t2.getStatus() == UTIter_OK)
8014 	{
8015 		const pf_Frag * pf1 = t1.getFrag();
8016 		const pf_Frag * pf2 = t2.getFrag();
8017 
8018 		if(!pf1)
8019 		{
8020 			if(pf2)
8021 				pos = pf2->getPos();
8022 			else
8023 				pos = 0;
8024 
8025 			return false;
8026 		}
8027 
8028 		if(!pf2)
8029 		{
8030 			pos = pf1->getPos();
8031 			return false;
8032 		}
8033 
8034 
8035 		if(pf1->getType() != pf2->getType())
8036 		{
8037 			pos = pf1->getPos();
8038 			return false;
8039 		}
8040 
8041 
8042 		UT_uint32 iFOffset1 = t1.getPosition() - pf1->getPos();
8043 		UT_uint32 iFOffset2 = t2.getPosition() - pf2->getPos();
8044 
8045 		UT_uint32 iLen1 = pf1->getLength() - iFOffset1;
8046 		UT_uint32 iLen2 = pf2->getLength() - iFOffset2;
8047 		UT_uint32 iLen  = UT_MIN(iLen1, iLen2);
8048 
8049 		if(iLen1 == iLen2 && iFOffset1 == 0 && iFOffset2 == 0)
8050 		{
8051 			// these two frags overlap exactly, so we can just use the
8052 			// pf_Frag::isContentEqual() on them
8053 			if(!(pf1->isContentEqual(*pf2)))
8054 			{
8055 				// TODO -- this is not position of the difference, but
8056 				// of the start of the fragment (there difference
8057 				// could be inside)
8058 				pos = pf1->getPos();
8059 				return false;
8060 			}
8061 
8062 		}
8063 		else if(pf1->getType() != pf_Frag::PFT_Text)
8064 		{
8065 			// partially overlapping frags and not text
8066 			pos = pf1->getPos();
8067 			return false;
8068 		}
8069 		else
8070 		{
8071 			// we have two textual frags that overlap
8072 			// work our way along the overlap ...
8073 			for(UT_uint32 i = 0; i < iLen; ++i)
8074 			{
8075 				if(t1.getChar() != t2.getChar())
8076 				{
8077 					pos = t1.getPosition() + i;
8078 					return false;
8079 				}
8080 
8081 
8082 				++t1;
8083 				++t2;
8084 			}
8085 
8086 			// we are already past the end of the shorter frag
8087 			continue;
8088 		}
8089 
8090 		// advance both iterators by the processed length
8091 		t1 += iLen;
8092 		t2 += iLen;
8093 	}
8094 
8095 	if((t1.getStatus() == UTIter_OK && t2.getStatus() != UTIter_OK))
8096 	{
8097 		// documents are of different length ...
8098 		pos = t1.getPosition();
8099 		return false;
8100 	}
8101 
8102 	if((t1.getStatus() != UTIter_OK && t2.getStatus() == UTIter_OK))
8103 	{
8104 		// documents are of different length ...
8105 		pos = t2.getPosition();
8106 		return false;
8107 	}
8108 
8109 	return true;
8110 }
8111 
8112 /*!
8113     Compare the format of the this document to another document;
8114     returns true if document formats are identical
8115 
8116     If the function returns false, pos contains the document position
8117     at which first difference was encountered
8118 
8119     NB: If the document contents are known not to be equal, it makes no
8120     sense to call this function.
8121 */
areDocumentFormatsEqual(const AD_Document & D,UT_uint32 & pos) const8122 bool PD_Document::areDocumentFormatsEqual(const AD_Document &D, UT_uint32 &pos) const
8123 {
8124 	pos = 0;
8125 	if(D.getType() != ADDOCUMENT_ABIWORD)
8126 		return false;
8127 
8128 	PD_Document &d = (PD_Document &)D;
8129 	UT_return_val_if_fail(m_pPieceTable || d.m_pPieceTable, false);
8130 
8131 	//  scroll through the documents comparing fmt
8132 	PD_DocIterator t1(*this);
8133 	PD_DocIterator t2(d);
8134 
8135 	// in order to avoid repeated comparions of AP, we will store
8136 	// record of matching AP's
8137 	UT_StringPtrMap hFmtMap;
8138 
8139 	while(t1.getStatus() == UTIter_OK && t2.getStatus() == UTIter_OK)
8140 	{
8141 		// need to cmp contents
8142 		const pf_Frag * pf1 = t1.getFrag();
8143 		const pf_Frag * pf2 = t2.getFrag();
8144 
8145 		UT_return_val_if_fail(pf1 && pf2, false);
8146 
8147 		PT_AttrPropIndex ap1 = pf1->getIndexAP();
8148 		PT_AttrPropIndex ap2 = pf2->getIndexAP();
8149 
8150 		// because the indexes are into different piecetables, we
8151 		// have to expand them
8152 		const PP_AttrProp * pAP1;
8153 		const PP_AttrProp * pAP2;
8154 
8155 		m_pPieceTable->getAttrProp(ap1, &pAP1);
8156 		d.m_pPieceTable->getAttrProp(ap2, &pAP2);
8157 
8158 		UT_return_val_if_fail(pAP1 && pAP2, false);
8159 
8160 		UT_String s;
8161 		UT_String_sprintf(s,"%08x%08x", ap1, ap2);
8162 		bool bAreSame = hFmtMap.contains(s,NULL);
8163 
8164 		if(!bAreSame)
8165 		{
8166 			if(!pAP1->isEquivalent(pAP2))
8167 			{
8168 				pos = t1.getPosition();
8169 				return false;
8170 			}
8171 			else
8172 			{
8173 				hFmtMap.insert(s,NULL);
8174 			}
8175 		}
8176 
8177 		UT_uint32 iLen = UT_MIN(pf1->getLength(),pf2->getLength());
8178 		t1 += iLen;
8179 		t2 += iLen;
8180 	}
8181 
8182 	if((t1.getStatus() == UTIter_OK && t2.getStatus() != UTIter_OK))
8183 	{
8184 		// documents are of different length ...
8185 		pos = t1.getPosition();
8186 		return false;
8187 	}
8188 
8189 	if((t1.getStatus() != UTIter_OK && t2.getStatus() == UTIter_OK))
8190 	{
8191 		// documents are of different length ...
8192 		pos = t2.getPosition();
8193 		return false;
8194 	}
8195 
8196 	return true;
8197 }
8198 
setMarkRevisions(bool bMark)8199 void PD_Document::setMarkRevisions(bool bMark)
8200 {
8201 	if(isMarkRevisions() != bMark)
8202 	{
8203 		AD_Document::setMarkRevisions(bMark);
8204 	 	signalListeners(PD_SIGNAL_REVISION_MODE_CHANGED);
8205 	}
8206 }
8207 
8208 /*!
8209     This function crawls over the entire document and removes all fmt marks. It is principally meant
8210     to do PT clean up on import (for example, the structure of MS Word documents is such that we end
8211     up with a myriad of superfluous fmt marks in the document), and should be called by an importer
8212     at the end of import process.
8213 */
purgeFmtMarks()8214 bool PD_Document::purgeFmtMarks()
8215 {
8216 	return m_pPieceTable->purgeFmtMarks();
8217 }
8218 
8219 
getAttrProp(PT_AttrPropIndex apIndx,const PP_AttrProp ** ppAP,PP_RevisionAttr ** pRevisions,bool bShowRevisions,UT_uint32 iRevisionId,bool & bHiddenRevision) const8220 bool PD_Document::getAttrProp(PT_AttrPropIndex apIndx, const PP_AttrProp ** ppAP, PP_RevisionAttr ** pRevisions,
8221 							  bool bShowRevisions, UT_uint32 iRevisionId, bool &bHiddenRevision) const
8222 {
8223 	bool bRevisionAttrNeeded = pRevisions ? true : false;
8224 	PP_RevisionAttr * pRevAttr = NULL;
8225 	bHiddenRevision = false;
8226 
8227 	const PP_AttrProp * pAP = NULL;
8228 
8229 	if(!getAttrProp(apIndx,&pAP))
8230 		return false;
8231 
8232 	if(   pAP->getRevisedIndex() != 0xffffffff
8233 	   && pAP->getRevisionState().isEqual(iRevisionId, bShowRevisions, isMarkRevisions()))
8234 	{
8235 		// the revision has a valid index to an inflated AP, so we use it
8236 		bHiddenRevision = pAP->getRevisionHidden();
8237 
8238 		const gchar* pRevision = NULL;
8239 
8240 		if(bRevisionAttrNeeded && pAP->getAttribute("revision", pRevision))
8241 		{
8242 			*pRevisions = new PP_RevisionAttr(pRevision);
8243 			UT_return_val_if_fail(pRevisions, false);
8244 		}
8245 
8246 		PT_AttrPropIndex revAPI = pAP->getRevisedIndex();
8247 
8248 		getAttrProp(revAPI, ppAP);
8249 		return true;
8250 	}
8251 
8252 	const PP_AttrProp * pNewAP = explodeRevisions(pRevAttr, pAP, bShowRevisions, iRevisionId, bHiddenRevision);
8253 
8254 	if(pNewAP)
8255 	{
8256 		*ppAP = pNewAP;
8257 	}
8258 	else
8259 	{
8260 		*ppAP = pAP;
8261 	}
8262 
8263 	if(bRevisionAttrNeeded)
8264 	{
8265 		*pRevisions = pRevAttr;
8266 	}
8267 	else
8268 	{
8269 		delete pRevAttr;
8270 	}
8271 
8272 	return true;
8273 }
8274 
8275 
8276 /*!
8277     retrieves span AP corresponding to revision settings
8278 
8279     pRevisions : [out] the representation of the rev. attribute associated with the AP; if
8280     the caller does not need this, the pointer can be set to null
8281 */
getSpanAttrProp(pf_Frag_Strux * sdh,UT_uint32 offset,bool bLeftSide,const PP_AttrProp ** ppAP,PP_RevisionAttr ** pRevisions,bool bShowRevisions,UT_uint32 iRevisionId,bool & bHiddenRevision) const8282 bool PD_Document::getSpanAttrProp(pf_Frag_Strux* sdh, UT_uint32 offset, bool bLeftSide,
8283 								  const PP_AttrProp ** ppAP,
8284 								  PP_RevisionAttr ** pRevisions,
8285 								  bool bShowRevisions, UT_uint32 iRevisionId,
8286 								  bool &bHiddenRevision) const
8287 {
8288 	const PP_AttrProp *pAP = NULL;
8289 	bool bRevisionAttrNeeded = pRevisions ? true : false;
8290 	PP_RevisionAttr * pRevAttr = NULL;
8291 
8292 	if(!getSpanAttrProp(sdh,offset,bLeftSide,&pAP))
8293 		return false;
8294 
8295 	if(   pAP->getRevisedIndex() != 0xffffffff
8296 	   && pAP->getRevisionState().isEqual(iRevisionId, bShowRevisions, isMarkRevisions()))
8297 	{
8298 		// the revision has a valid index to an inflated AP, so we use it
8299 		bHiddenRevision = pAP->getRevisionHidden();
8300 
8301 		const gchar* pRevision = NULL;
8302 
8303 		// only do this if the pRevisions pointer is set to NULL
8304 		if(bRevisionAttrNeeded && pAP->getAttribute("revision", pRevision))
8305 		{
8306 			*pRevisions = new PP_RevisionAttr(pRevision);
8307 			UT_return_val_if_fail(pRevisions, false);
8308 		}
8309 
8310 		PT_AttrPropIndex revAPI = pAP->getRevisedIndex();
8311 
8312 		getAttrProp(revAPI, ppAP);
8313 		return true;
8314 	}
8315 
8316 	const PP_AttrProp * pNewAP = explodeRevisions(pRevAttr, pAP, bShowRevisions, iRevisionId, bHiddenRevision);
8317 
8318 	if(pNewAP)
8319 	{
8320 		*ppAP = pNewAP;
8321 	}
8322 	else
8323 	{
8324 		*ppAP = pAP;
8325 	}
8326 
8327 	if(bRevisionAttrNeeded)
8328 	{
8329 		*pRevisions = pRevAttr;
8330 	}
8331 	else
8332 	{
8333 		delete pRevAttr;
8334 	}
8335 
8336 
8337 	return true;
8338 }
8339 
_clearUndo()8340 void PD_Document::_clearUndo()
8341 {
8342 	UT_return_if_fail(m_pPieceTable);
8343 	m_pPieceTable->clearUndo();
8344 }
8345 
tellPTDoNotTweakPosition(bool b)8346 void PD_Document::tellPTDoNotTweakPosition(bool b)
8347 {
8348 	UT_return_if_fail( m_pPieceTable );
8349 	m_pPieceTable->setDoNotTweakPosition(b);
8350 }
8351 
getXID() const8352 UT_uint32 PD_Document::getXID() const
8353 {
8354 	return m_pPieceTable->getXID();
8355 }
8356 
getTopXID() const8357 UT_uint32 PD_Document::getTopXID() const
8358 {
8359 	return m_pPieceTable->getTopXID();
8360 }
8361 
fixMissingXIDs()8362 void PD_Document::fixMissingXIDs()
8363 {
8364 	m_pPieceTable->fixMissingXIDs();
8365 }
8366 
8367 /*!
8368     This function evaluates the xid value for the given frament and version level.
8369 
8370     The XID is a document-unique identifier of the frag; when we compare documents, we are
8371     interested not in document uniqueness but global uniqueness. We convert the
8372     document-unique xid to a globaly unique id by combining the xid with the UUID of
8373     document version: identical xid's in two documents represent identical elements if,
8374     and only if, the version UUIDs for the version of the document in which the element
8375     was created are identical. Therefore, as a part of the version record, we store the
8376     highest xid used in the document. This way we can determine in which version of the
8377     document the frag was created, based on its xid.
8378 
8379     Frags that have xid aboved the version threshold need to be treated as frags without xid.
8380 */
getFragXIDforVersion(const pf_Frag * pf,UT_uint32 iVersion) const8381 UT_uint32 PD_Document::getFragXIDforVersion(const pf_Frag * pf, UT_uint32 iVersion) const
8382 {
8383 	UT_return_val_if_fail( pf, 0 );
8384 
8385 	if(iVersion >= getDocVersion())
8386 	{
8387 		// all xid's valid
8388 		return pf->getXID();
8389 	}
8390 
8391 	const AD_VersionData * v = findHistoryRecord(iVersion);
8392 
8393 	if(!v)
8394 	{
8395 		// if there is no version record for this version, find the nearest lower version
8396 		for(UT_sint32 i = (UT_sint32)iVersion - 1; i > 0; --i)
8397 		{
8398 			v = findHistoryRecord(i);
8399 			if(v)
8400 				break;
8401 		}
8402 
8403 		if(!v)
8404 			return 0;
8405 	}
8406 
8407 
8408 	UT_uint32 iXid = pf->getXID();
8409 
8410 	if(iXid <= v->getTopXID())
8411 		return iXid;
8412 
8413 	// this frag's xid is above the version limit, i.e., this frag was inserted in a later
8414 	// version of the document, and its xid cannot be used in document matching for the
8415 	// given version level
8416 	return 0;
8417 }
8418 
8419 #include <sstream>
8420 
getDocumentRDF(void) const8421 PD_DocumentRDFHandle PD_Document::getDocumentRDF(void) const
8422 {
8423     return m_hDocumentRDF;
8424 }
8425 
8426 PD_XMLIDCreatorHandle
makeXMLIDCreator()8427 PD_Document::makeXMLIDCreator()
8428 {
8429     PD_XMLIDCreatorHandle ret( new PD_XMLIDCreator( this ));
8430     return ret;
8431 }
8432 
8433 
8434 class PD_XMLIDCreatorPrivate
8435 {
8436 public:
8437     std::set< std::string > m_cache;
8438     bool m_cacheIsVirgin;
8439 };
8440 
8441 
8442 
PD_XMLIDCreator(PD_Document * doc)8443 PD_XMLIDCreator::PD_XMLIDCreator( PD_Document* doc )
8444     : m_doc( doc )
8445     , m_impl( new PD_XMLIDCreatorPrivate() )
8446 {
8447     //
8448     // delay calling rebuildCache() for the first time until
8449     // the caller actually needs to use our methods.
8450     // No use = No cost.
8451     //
8452     m_impl->m_cacheIsVirgin = true;
8453 }
8454 
~PD_XMLIDCreator()8455 PD_XMLIDCreator::~PD_XMLIDCreator()
8456 {
8457     delete m_impl;
8458 }
8459 
8460 
8461 
8462 void
rebuildCache()8463 PD_XMLIDCreator::rebuildCache()
8464 {
8465     m_impl->m_cacheIsVirgin = false;
8466     std::set< std::string >& m_cache = m_impl->m_cache;
8467 
8468     m_cache.clear();
8469 
8470     //
8471     // walk the document and grab all the xmlid values
8472     //
8473     if( m_doc )
8474     {
8475         pt_PieceTable* m_pPieceTable = m_doc->getPieceTable();
8476 
8477         pf_Frag * pf = NULL;
8478         pf = m_pPieceTable->getFragments().getFirst();
8479         while(pf)
8480         {
8481             PT_AttrPropIndex api = pf->getIndexAP();
8482             const PP_AttrProp* pAP = 0;
8483             const gchar * v = 0;
8484 
8485             if( m_doc->getAttrProp( api, &pAP ))
8486             {
8487                 if( pAP->getAttribute(PT_XMLID, v) && v)
8488                 {
8489                     m_cache.insert( v );
8490                 }
8491             }
8492 
8493 
8494             pf = pf->getNext();
8495         }
8496     }
8497 
8498 	UT_DEBUGMSG(("PD_XMLIDCreator::rebuildCache() cache.sz:%lu \n", (long unsigned)m_cache.size() ));
8499 
8500 }
8501 
8502 // msvc doesn't like this
8503 // template <class STREAM>
8504 // STREAM& operator<<( STREAM& oss, const UT_UTF8String& s )
8505 // {
8506 //     oss << s.utf8_str();
8507 //     return oss;
8508 // }
8509 
8510 std::string
createUniqueXMLID(const std::string & desiredID,bool deepCopyRDF)8511 PD_XMLIDCreator::createUniqueXMLID( const std::string& desiredID, bool deepCopyRDF )
8512 {
8513 
8514     if( m_impl->m_cacheIsVirgin )
8515         rebuildCache();
8516 
8517     std::set< std::string >& m_cache = m_impl->m_cache;
8518     UT_DEBUGMSG(("createUniqueXMLID() desired:%s\n", desiredID.c_str() ));
8519 
8520     // It is not in use already, let them have their choice.
8521     if( !m_cache.count( desiredID ) )
8522     {
8523         UT_DEBUGMSG(("createUniqueXMLID() xmlid is not in use, returning desired:%s\n", desiredID.c_str() ));
8524         m_cache.insert( desiredID );
8525         return desiredID;
8526     }
8527 
8528     UT_DEBUGMSG(("createUniqueXMLID() xmlid is in use! desired:%s\n", desiredID.c_str() ));
8529 	UT_UUID* uuido = XAP_App::getApp()->getUUIDGenerator()->createUUID();
8530     UT_UTF8String uuid;
8531 	uuido->toString(uuid);
8532     delete uuido;
8533 
8534     std::string trimmedID = desiredID;
8535 
8536     //
8537     // Check to see if desiredID is already an ID which has x-ID-uuid
8538     // and if so, remove the old uuid so that we do not end up making
8539     // hugely long xml:id strings as copy and paste is repeated.
8540     //
8541     if( starts_with( desiredID, "x-" )
8542         && std::count( desiredID.begin(), desiredID.end(), '-' ) > 2 )
8543     {
8544         const int preambleLength = 2;
8545         int epos  = desiredID.find( '-', preambleLength );
8546         trimmedID = desiredID.substr( preambleLength, epos-preambleLength );
8547         UT_DEBUGMSG(("createUniqueXMLID() epos:%d trimmedID:%s desired:%s\n",
8548                      epos, trimmedID.c_str(), desiredID.c_str() ));
8549     }
8550 
8551     std::stringstream ss;
8552     ss << "x-" << trimmedID << "-" << uuid.utf8_str();
8553     std::string xmlid = ss.str();
8554     m_cache.insert( xmlid );
8555     UT_DEBUGMSG(("createUniqueXMLID() xmlid is in use! updated:%s\n", xmlid.c_str() ));
8556 
8557     // link RDF from the desired xml:id to the new xml:id
8558     m_doc->getDocumentRDF()->relinkRDFToNewXMLID( desiredID, xmlid, deepCopyRDF );
8559 
8560     return xmlid;
8561 }
8562 
8563 
8564