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