1 /* AbiWord
2 * Copyright (C) 2002 Tomas Frydrych <tomas@frydrych.uklinux.net>
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 * 02110-1301 USA.
18 */
19
20 #include "pp_Revision.h"
21 #include "pp_AttrProp.h"
22 #include "pd_Style.h"
23 #include "pd_Document.h"
24 #include "ut_debugmsg.h"
25 #include "ut_misc.h"
26 #include "ut_std_map.h"
27
28 //#include <limits.h>
29
30 #include <sstream>
31
32
PP_Revision(UT_uint32 Id,PP_RevisionType eType,const gchar * props,const gchar * attrs)33 PP_Revision::PP_Revision(UT_uint32 Id, PP_RevisionType eType, const gchar * props, const gchar * attrs):
34 m_iID(Id), m_eType(eType), m_bDirty(true)
35 {
36 if(!props && !attrs)
37 return;
38
39 const char * empty = "";
40
41 if(props)
42 {
43 char * pProps = g_strdup(props);
44 UT_return_if_fail (pProps);
45
46 char * p = strtok(pProps, ":");
47
48 while(p)
49 {
50 char * n = p;
51
52 // skip over spaces ...
53 while(n && *n == ' ')
54 ++n;
55
56 p = strtok(NULL, ";");
57
58 // if we have no p, that means the property is being removed ...
59 const char * v = p ? p : empty;
60 if(! strcmp(v, "-/-"))
61 v = empty;
62
63 if(n)
64 {
65 setProperty(n,v);
66 p = strtok(NULL,":");
67 }
68 else
69 {
70 // malformed property
71 UT_DEBUGMSG(("PP_Revision::PP_Revision: malformed props string [%s]\n", props));
72 // if we have not reached the end, we will keep trying ...
73 if(p)
74 p = strtok(NULL,":");
75 }
76 }
77
78 FREEP(pProps);
79 }
80
81 if(attrs)
82 {
83 char * pAttrs = g_strdup(attrs);
84
85 UT_ASSERT_HARMLESS(pAttrs);
86 if(!pAttrs)
87 {
88 UT_DEBUGMSG(("PP_Revision::PP_Revision: out of memory\n"));
89 return;
90 }
91
92 char * p = strtok(pAttrs, ":");
93
94 while(p)
95 {
96 char * n = p;
97 p = strtok(NULL, ";");
98
99 const char * v = p ? p : empty;
100 if(! strcmp(v, "-/-"))
101 v = empty;
102
103 if(n)
104 {
105 setAttribute(n,v);
106 p = strtok(NULL,":");
107 }
108 else
109 {
110 // malformed property
111 UT_DEBUGMSG(("PP_Revision::PP_Revision: malformed props string [%s]\n", props));
112 // if we have not reached the end, we will keep trying ...
113 if(p)
114 p = strtok(NULL,":");
115 }
116 }
117
118 FREEP(pAttrs);
119 }
120 }
121
PP_Revision(UT_uint32 Id,PP_RevisionType eType,const gchar ** props,const gchar ** attrs)122 PP_Revision::PP_Revision(UT_uint32 Id, PP_RevisionType eType, const gchar ** props, const gchar ** attrs):
123 m_iID(Id), m_eType(eType), m_bDirty(true)
124 {
125 if(!props && !attrs)
126 return;
127
128 if(props)
129 {
130 setProperties(props);
131 }
132
133 if(attrs)
134 {
135 setAttributes(attrs);
136 }
137 }
138
139 /*!
140 Sets attributes taking care of any nested revision attribute (which needs to be parsed
141 and combined with the current AP set.
142 */
setAttributes(const gchar ** attributes)143 bool PP_Revision::setAttributes(const gchar ** attributes)
144 {
145 if(!PP_AttrProp::setAttributes(attributes))
146 return false;
147
148 return _handleNestedRevAttr();
149 }
150
151
152
_handleNestedRevAttr()153 bool PP_Revision::_handleNestedRevAttr()
154 {
155 const gchar * pNestedRev = NULL;
156 getAttribute("revision", pNestedRev);
157
158 if(pNestedRev)
159 {
160 PP_RevisionAttr NestedAttr(pNestedRev);
161
162 // now remove "revision"
163 setAttribute("revision", NULL);
164 prune();
165
166 // overlay the attrs and props from the revision attribute
167 for(UT_uint32 i = 0; i < NestedAttr.getRevisionsCount(); ++i)
168 {
169 const PP_Revision * pRev = NestedAttr.getNthRevision(i);
170 UT_return_val_if_fail( pRev, false );
171
172 // ignore inserts and deletes
173 if(pRev->getType() == PP_REVISION_ADDITION || pRev->getType() == PP_REVISION_DELETION)
174 continue;
175
176 setProperties(pRev->getProperties());
177 setAttributes(pRev->getAttributes());
178 }
179
180 prune();
181 }
182
183 return true;
184 }
185
186
187 /*! converts the internal vector of properties into XML string */
getPropsString() const188 const gchar * PP_Revision::getPropsString() const
189 {
190 if(m_bDirty)
191 _refreshString();
192
193 return (const gchar*) m_sXMLProps.c_str();
194 }
195
196 /*! converts the internal vector of attributes into XML string */
getAttrsString() const197 const gchar * PP_Revision::getAttrsString() const
198 {
199 if(m_bDirty)
200 _refreshString();
201
202 return (const gchar*) m_sXMLAttrs.c_str();
203 }
204
_refreshString() const205 void PP_Revision::_refreshString() const
206 {
207 m_sXMLProps.clear();
208 m_sXMLAttrs.clear();
209
210 UT_uint32 i;
211 UT_uint32 iCount = getPropertyCount();
212 const gchar * n, *v;
213
214 for(i = 0; i < iCount; i++)
215 {
216 if(!getNthProperty(i,n,v))
217 {
218 // UT_ASSERT_HARMLESS( UT_SHOULD_NOT_HAPPEN );
219 continue;
220 }
221
222 if(!v || !*v) v = "-/-";
223
224 m_sXMLProps += n;
225 m_sXMLProps += ":";
226 m_sXMLProps += v;
227 if(i < iCount - 1)
228 m_sXMLProps += ";";
229 }
230
231 iCount = getAttributeCount();
232 for(i = 0; i < iCount; i++)
233 {
234 if(!getNthAttribute(i,n,v))
235 {
236 // UT_ASSERT_HARMLESS( UT_SHOULD_NOT_HAPPEN );
237 continue;
238 }
239
240 if(!v || !*v) v = "-/-";
241
242 m_sXMLAttrs += n;
243 m_sXMLAttrs += ":";
244 m_sXMLAttrs += v;
245 if(i < iCount - 1)
246 m_sXMLAttrs += ";";
247 }
248
249 m_bDirty = false;
250 }
251
toString() const252 std::string PP_Revision::toString() const
253 {
254 std::stringstream ret;
255 PP_RevisionType r_type = getType();
256
257 if(r_type == PP_REVISION_FMT_CHANGE)
258 ret << "!";
259
260 // print the id with appropriate sign
261 ret << (int)(getId()* ((r_type == PP_REVISION_DELETION)?-1:1));
262
263 if(r_type != PP_REVISION_DELETION)
264 {
265 // if we have no props but have attribs, we have to issue empty braces so as not to
266 // confuse attribs with props
267 if(hasProperties() || hasAttributes())
268 ret << "{";
269
270 if(hasProperties())
271 ret << getPropsString();
272
273 if(hasProperties() || hasAttributes())
274 ret << "}";
275
276 if(hasAttributes())
277 {
278 ret << "{" << getAttrsString() << "}";
279 }
280 }
281
282 return ret.str();
283 }
284
onlyContainsAbiwordChangeTrackingMarkup() const285 bool PP_Revision::onlyContainsAbiwordChangeTrackingMarkup() const
286 {
287 UT_DEBUGMSG(("onlyContainsAbiwordChangeTrackingMarkup(top) ac:%ld pc:%ld\n",
288 (long)getAttributeCount(), (long)getPropertyCount() ));
289
290 if( !getAttributeCount() )
291 return false;
292 if( getPropertyCount() )
293 return false;
294
295 bool ret = true;
296 UT_uint32 i;
297 UT_uint32 iCount = getAttributeCount();
298 const gchar * n, *v;
299
300 for(i = 0; i < iCount; i++)
301 {
302 if(!getNthAttribute(i,n,v))
303 {
304 // UT_ASSERT_HARMLESS( UT_SHOULD_NOT_HAPPEN );
305 continue;
306 }
307 UT_DEBUGMSG(("onlyContainsAbiwordChangeTrackingMarkup() n:%s\n", n ));
308
309 if( n != strstr( n, "abi-para" ) )
310 {
311 return false;
312 }
313 }
314
315 return ret;
316 }
317
318
operator ==(const PP_Revision & op2) const319 bool PP_Revision::operator == (const PP_Revision &op2) const
320 {
321 // this is quite involved, but we will start with the simple
322 // non-equality cases
323
324 if(getId() != op2.getId())
325 return false;
326
327 if(getType() != op2.getType())
328 return false;
329
330
331 // OK, so we have the same type and id, do we have the same props ???
332 UT_uint32 iPCount1 = getPropertyCount();
333 UT_uint32 iPCount2 = op2.getPropertyCount();
334 UT_uint32 iACount1 = getAttributeCount();
335 UT_uint32 iACount2 = op2.getAttributeCount();
336
337 if((iPCount1 != iPCount2) || (iACount1 != iACount2))
338 return false;
339
340 // now the lengthy comparison
341 UT_uint32 i;
342 const gchar * n;
343 const gchar * v1, * v2;
344
345 for(i = 0; i < iPCount1; i++)
346 {
347
348 getNthProperty(i,n,v1);
349 op2.getProperty(n,v2);
350
351 if(strcmp(v1,v2))
352 return false;
353 }
354
355 for(i = 0; i < iACount1; i++)
356 {
357
358 getNthAttribute(i,n,v1);
359 op2.getAttribute(n,v2);
360
361 if(strcmp(v1,v2))
362 return false;
363 }
364 return true;
365 }
366
367
368 // PP_Revision*
369 // PP_Revision::clone() const
370 // {
371 // PP_Revision* ret = new PP_Revision( *this );
372 // return ret;
373 // }
374
375
376 /************************************************************
377 ************************************************************/
378
379 /*! create class instance from an XML attribute string
380 */
PP_RevisionAttr(const gchar * r)381 PP_RevisionAttr::PP_RevisionAttr(const gchar * r):
382 m_pLastRevision(NULL)
383 {
384 _init(r);
385 }
386
387 /*! create class instance from a single revision data */
PP_RevisionAttr(UT_uint32 iId,PP_RevisionType eType,const gchar ** pAttrs,const gchar ** pProps)388 PP_RevisionAttr::PP_RevisionAttr(UT_uint32 iId, PP_RevisionType eType,
389 const gchar ** pAttrs, const gchar ** pProps)
390 {
391 PP_Revision * pRevision = new PP_Revision((UT_uint32)iId, eType, pProps, pAttrs);
392 m_vRev.addItem((void*)pRevision);
393 }
394
395
~PP_RevisionAttr()396 PP_RevisionAttr::~PP_RevisionAttr()
397 {
398 _clear();
399 }
400
401 /*! initialize instance with XML attribute string
402 */
setRevision(const gchar * r)403 void PP_RevisionAttr::setRevision(const gchar * r)
404 {
405 _clear();
406 _init(r);
407 }
408
409 void
setRevision(std::string & r)410 PP_RevisionAttr::setRevision(std::string& r)
411 {
412 setRevision( r.c_str() );
413 }
414
415
416 /*! destroys all internal data */
_clear()417 void PP_RevisionAttr::_clear()
418 {
419 for(UT_sint32 i = 0; i < m_vRev.getItemCount(); i++)
420 {
421 delete (PP_Revision *) m_vRev.getNthItem(i);
422 }
423
424 m_vRev.clear();
425 m_bDirty = true;
426 m_pLastRevision = NULL;
427 }
428
429
430 /*! parse given XML attribute string and fill the
431 instance with the data
432 */
_init(const gchar * r)433 void PP_RevisionAttr::_init(const gchar *r)
434 {
435 if(!r)
436 return;
437
438 // the string we are parsing looks like
439 // "+1,-2,!3{font-family: Times New Roman}"
440
441 // first duplicate the string so we can play with it ...
442 char * s = (char*) g_strdup(r);
443 char * end_s = s + strlen(s); // we need to remember where this
444 // string ends because we cannot use strtok(NULL,...)
445
446 UT_sint32 iId;
447 PP_RevisionType eType;
448 gchar * pProps, * pAttrs,
449 * cl_brace = 0, * op_brace = 0,
450 * cl_brace2 = 0;
451
452 char * t = strtok(s,",");
453
454 // we have to remember the end of this token for future calls to
455 // strtok since strtok is also used in the PP_Revision class and
456 // it screws us up, so we have to start always with explicit
457 // string
458 char * next_s = s;
459
460 while(t)
461 {
462 next_s = next_s + strlen(t) + 1; // 1 for the token separator
463
464 if(*t == '!')
465 {
466 eType = PP_REVISION_FMT_CHANGE;
467 t++;
468 }
469 else if(*t == '-')
470 {
471 eType = PP_REVISION_DELETION;
472 t++; // so we do not need to deal with sign later
473 }
474
475 else
476 eType = PP_REVISION_ADDITION; // this value is only
477 // temporary because this
478 // could equally be addition
479 // + format
480
481 cl_brace = strchr(t, '}');
482 op_brace = strchr(t, '{');
483
484 if(!cl_brace || !op_brace)
485 {
486 // no props
487 if(eType == PP_REVISION_FMT_CHANGE)
488 {
489 // malformed token, move onto the next one
490 UT_DEBUGMSG(("PP_RevisionAttr::_init: invalid ! token [%s]\n",t));
491 goto skip_this_token;
492 }
493 pProps = NULL;
494 pAttrs = NULL;
495 }
496 else
497 {
498 // OK this is a case where we have some props, i.e., it
499 // must be either fmt change or addition
500 if(eType == PP_REVISION_DELETION)
501 {
502 // malformed token, move onto the next one
503 UT_DEBUGMSG(("PP_RevisionAttr::_init: invalid - token [%s]\n",t));
504 goto skip_this_token;
505 }
506
507 // insert null as needed to be able to parse the id and props
508 *op_brace = 0;
509 *cl_brace = 0;
510 pProps = op_brace+1;
511
512 // now see if the props are followed by attributes
513 if(*(cl_brace + 1) == '{')
514 {
515 cl_brace2 = strchr(cl_brace + 2,'}');
516 if(cl_brace2)
517 {
518 pAttrs = cl_brace + 2;
519 *cl_brace2 = 0;
520 }
521 else
522 {
523 UT_DEBUGMSG(( "PP_RevisionAttr::_init: invalid token - [%s]\n", t ));
524 pAttrs = NULL;
525 }
526 }
527 else
528 pAttrs = NULL;
529
530 if(eType == PP_REVISION_ADDITION)
531 eType = PP_REVISION_ADDITION_AND_FMT;
532 }
533
534 // now we can retrieve the id
535 iId = atol(t);
536
537 {
538 PP_Revision * pRevision = new PP_Revision((UT_uint32)iId, eType, pProps, pAttrs);
539
540 m_vRev.addItem((void*)pRevision);
541 }
542
543 skip_this_token:
544 if(next_s < end_s)
545 t = strtok(next_s,",");
546 else
547 t = NULL;
548 }
549
550 FREEP(s);
551 m_bDirty = true;
552 m_iSuperfluous = 0;
553 m_pLastRevision = NULL;
554 }
555
556 /*!
557 changes the type of revision with id iId to eType; if revision
558 with that id is not present, returns false
559 */
changeRevisionType(UT_uint32 iId,PP_RevisionType eType)560 bool PP_RevisionAttr::changeRevisionType(UT_uint32 iId, PP_RevisionType eType)
561 {
562 for(UT_sint32 i = 0; i < m_vRev.getItemCount(); i++)
563 {
564 PP_Revision * r = (PP_Revision *)m_vRev.getNthItem(i);
565
566 if(iId == r->getId())
567 {
568 r->setType(eType);
569 m_bDirty = true;
570 return true;
571 }
572 }
573
574 return false;
575 }
576
changeRevisionId(UT_uint32 iOldId,UT_uint32 iNewId)577 bool PP_RevisionAttr::changeRevisionId(UT_uint32 iOldId, UT_uint32 iNewId)
578 {
579 UT_return_val_if_fail(iNewId >= iOldId, false);
580
581 for(UT_sint32 i = 0; i < m_vRev.getItemCount(); i++)
582 {
583 PP_Revision * r = (PP_Revision *)m_vRev.getNthItem(i);
584
585 if(iOldId == r->getId())
586 {
587 r->setId(iNewId);
588 m_bDirty = true;
589 return true;
590 }
591 }
592
593 return false;
594 }
595
596 /*!
597 this function removes any revisions that no-longer contribute to the cumulative effect
598 it is used in full-history mode when transfering attrs and props from the revision attribute
599 into the main attrs and props
600 */
pruneForCumulativeResult(PD_Document * pDoc)601 void PP_RevisionAttr::pruneForCumulativeResult(PD_Document * pDoc)
602 {
603 // first we are looking for any deletions which cancel anything below them
604 bool bDelete = false;
605 UT_sint32 i;
606 if(m_vRev.getItemCount() == 0)
607 {
608 return;
609 }
610
611 m_bDirty = true;
612
613 for(i = m_vRev.getItemCount()-1; i >=0; --i)
614 {
615 PP_Revision * r = (PP_Revision *)m_vRev.getNthItem(i);
616
617 if(!bDelete && r->getType() == PP_REVISION_DELETION)
618 {
619 bDelete = true;
620 continue; // we do not want the top revision deleted
621 }
622
623
624 if(bDelete)
625 {
626 delete r;
627 m_vRev.deleteNthItem(i);
628 }
629 }
630
631 // now we merge props and attrs in what is left
632 if(m_vRev.getItemCount() == 0)
633 {
634 return;
635 }
636
637 PP_Revision * r0 = (PP_Revision *)m_vRev.getNthItem(0);
638 UT_return_if_fail(r0);
639
640 for(i = 1; i < m_vRev.getItemCount(); ++i)
641 {
642 PP_Revision * r = (PP_Revision *)m_vRev.getNthItem(i);
643 UT_return_if_fail( r );
644
645 r0->setProperties(r->getProperties());
646 r0->setAttributes(r->getAttributes());
647
648 delete r;
649 m_vRev.deleteNthItem(i);
650 --i;
651 }
652
653 // explode the style if present
654 if(pDoc)
655 r0->explodeStyle(pDoc);
656
657 #if 0
658 // I do not think we should do this -- the emptiness indicates that the props and
659 // attributes should be removed from the format; if we remove them, we irreversibly
660 // lose this information
661
662 // get rid of any empty props and attrs
663 r0->prune();
664 #endif
665
666 // finally, remove the revision attribute if present
667 const gchar * v;
668 if(r0->getAttribute("revision", v))
669 r0->setAttribute("revision", NULL);
670
671 UT_ASSERT_HARMLESS( m_vRev.getItemCount() == 1 );
672 }
673
674
675 /*! return highest revision associated with this revision attribute
676 that has ID at most equal to id; the returned PP_Revision can be
677 used to determine whether this text should be displayed or hidden
678 in revision with the original id
679
680 \param UT_uint32 id : the id of this revision
681 \param PP_Revision ** ppR: location where to store pointer to one
682 of the special revisions in case return
683 value is NULL
684
685 \return : pointer to PP_Revision, or NULL if the revision
686 attribute should be ignored for the present level
687 */
688
689 // these are special instances of PP_Revision that are used to in the
690 // following function to handle special cases
691 static const PP_Revision s_del(0, PP_REVISION_DELETION, (gchar*)0, (gchar*)0);
692 static const PP_Revision s_add(0, PP_REVISION_ADDITION, (gchar*)0, (gchar*)0);
693
getGreatestLesserOrEqualRevision(UT_uint32 id,const PP_Revision ** ppR) const694 const PP_Revision * PP_RevisionAttr::getGreatestLesserOrEqualRevision(UT_uint32 id,
695 const PP_Revision ** ppR) const
696 {
697 if(ppR)
698 *ppR = NULL;
699
700 if(id == 0)
701 return getLastRevision();
702
703 const PP_Revision *r = NULL; // this will be the revision we are looking for
704 UT_uint32 r_id = 0;
705
706 const PP_Revision *m = NULL; // this will be the lowest revision present
707 UT_uint32 m_id = 0xFFFF;
708
709 for(UT_sint32 i = 0; i < m_vRev.getItemCount(); i++)
710 {
711 const PP_Revision * t = (const PP_Revision *) m_vRev.getNthItem(i);
712 UT_uint32 t_id = t->getId();
713
714 // the special case speedup - if we hit our id, then we can return immediately
715 if(t_id == id)
716 return t;
717
718 if(t_id < m_id)
719 {
720 m = t;
721 m_id = t_id;
722 }
723
724 if((t_id < id) && (t_id > r_id))
725 {
726 r = t;
727 r_id = t_id;
728 }
729 }
730
731 // now that we have the biggest revision with ID lesser or equal
732 // id, we have to deal with the special case when this is NULL
733 // i.e., this fragment only figures in revisions > id; the problem
734 // with NULL is that it is visible if the smallest revision ID is
735 // negative, and hidden in the opposite case -- we use the special
736 // static variables s_del and s_add to indicate what should
737 // be done
738
739 if(r == NULL && ppR)
740 {
741 if(!m)
742 {
743 //UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
744 // this happens when there was no revision attribute
745 return NULL;
746 }
747
748 if(m->getType() == PP_REVISION_DELETION)
749 *ppR = &s_del;
750 else if((m->getType() == PP_REVISION_ADDITION)
751 ||(m->getType() == PP_REVISION_ADDITION_AND_FMT))
752 *ppR = &s_add;
753 else // the initial revision was fmt change, so ignore it
754 *ppR = NULL;
755 }
756
757 return r;
758 }
759
getLowestGreaterOrEqualRevision(UT_uint32 id) const760 const PP_Revision * PP_RevisionAttr::getLowestGreaterOrEqualRevision(UT_uint32 id) const
761 {
762 if(id == 0)
763 return NULL;
764
765 const PP_Revision *r = NULL; // this will be the revision we are looking for
766 UT_uint32 r_id = PD_MAX_REVISION;
767
768 for(UT_sint32 i = 0; i < m_vRev.getItemCount(); i++)
769 {
770 const PP_Revision * t = (const PP_Revision *) m_vRev.getNthItem(i);
771 UT_uint32 t_id = t->getId();
772
773 // the special case speedup - if we hit our id, then we can return immediately
774 if(t_id == id)
775 return t;
776
777 if((t_id > id) && (t_id < r_id))
778 {
779 r = t;
780 r_id = t_id;
781 }
782 }
783
784 return r;
785 }
786
787
788 /*! finds the highest revision number in this attribute
789 */
getLastRevision() const790 const PP_Revision * PP_RevisionAttr::getLastRevision() const
791 {
792 // since this is rather involved, we will cache the result and
793 // use the cache if it is uptodate
794 if(m_pLastRevision)
795 return m_pLastRevision;
796
797 //const PP_Revision * r = NULL;
798 UT_uint32 r_id = 0;
799
800 for(UT_sint32 i = 0; i < m_vRev.getItemCount(); i++)
801 {
802 const PP_Revision * t = (const PP_Revision *)m_vRev.getNthItem(i);
803 UT_uint32 t_id = t->getId();
804
805 if(t_id > r_id)
806 {
807 r_id = t_id;
808 m_pLastRevision = t;
809 }
810 }
811
812 // UT_ASSERT_HARMLESS( m_pLastRevision );
813 // it is legal for this to be NULL -- it happens when the revision was pruned for
814 // cumulative effect and the last revision was a deletion.
815 return m_pLastRevision;
816 }
817
818
getHighestId() const819 UT_uint32 PP_RevisionAttr::getHighestId() const
820 {
821 UT_uint32 ret = 0;
822 for(UT_sint32 i = 0; i < m_vRev.getItemCount(); i++)
823 {
824 const PP_Revision * t = (const PP_Revision *)m_vRev.getNthItem(i);
825 ret = std::max( ret, t->getId() );
826 }
827 return ret;
828 }
829
830
831 /*!
832 find revision with id == iId; if revision is not found minId
833 contains the smallest id in this set greater than iId; if return value is and minId
834 is PD_MAX_REVISION then there are revisions preset
835 */
getRevisionWithId(UT_uint32 iId,UT_uint32 & minId) const836 const PP_Revision * PP_RevisionAttr::getRevisionWithId(UT_uint32 iId, UT_uint32 &minId) const
837 {
838 minId = PD_MAX_REVISION;
839
840 for(UT_sint32 i = 0; i < m_vRev.getItemCount(); i++)
841 {
842 const PP_Revision * t = (const PP_Revision *)m_vRev.getNthItem(i);
843 UT_uint32 t_id = t->getId();
844
845 if(t_id == iId)
846 {
847 return t;
848 }
849
850 if(minId > t_id && t_id > iId)
851 minId = t_id;
852 }
853
854 return NULL;
855 }
856
857
858 /*! given revision level id, this function returns true if given
859 segment of text is to be visible, false if it is to be hidden
860 */
isVisible(UT_uint32 id) const861 bool PP_RevisionAttr::isVisible(UT_uint32 id) const
862 {
863 if(id == 0)
864 {
865 // id 0 means show all revisions
866 return true;
867 }
868
869 const PP_Revision * pSpecial;
870 const PP_Revision * pR = getGreatestLesserOrEqualRevision(id, &pSpecial);
871
872 if(pR)
873 {
874 // found compliant revision ...
875 return true;
876 }
877
878
879 if(pSpecial)
880 {
881 // pSpecial is of the same type as the revision with the
882 // lowest id
883 PP_RevisionType eType = pSpecial->getType();
884
885 // deletions and fmt changes can be ignored; insertions need
886 // to be hidden
887 return ((eType != PP_REVISION_ADDITION) && (eType == PP_REVISION_ADDITION_AND_FMT));
888 }
889
890 // the revision with the lowest id is a change of format, this
891 // text has to remain visible
892 return true;
893 }
894
895
896 /*! adds id to the revision vector handling the special cases where id
897 is already present in this attribute.
898 */
addRevision(UT_uint32 iId,PP_RevisionType eType,const gchar ** pAttrs,const gchar ** pProps)899 void PP_RevisionAttr::addRevision(UT_uint32 iId, PP_RevisionType eType,
900 const gchar ** pAttrs, const gchar ** pProps)
901 {
902 UT_sint32 i;
903
904 for(i = 0; i < m_vRev.getItemCount(); i++)
905 {
906 PP_Revision * r = (PP_Revision*) m_vRev.getNthItem(i);
907 UT_uint32 r_id = r->getId();
908 PP_RevisionType r_type = r->getType();
909
910 if(iId != r_id)
911 continue;
912
913 if(eType != r_type)
914 {
915 // we are trying to add a revision id already in the vector
916 // but of a different -- this is legal, i.e., the
917 // editor just changed his mind
918 // we need to make distinction between different cases
919
920 if((eType == PP_REVISION_DELETION) && ( r_type == PP_REVISION_ADDITION
921 || r_type == PP_REVISION_ADDITION_AND_FMT))
922 {
923 // the editor originally inserted a new segment of
924 // text but now wants it out; we cannot just remove
925 // the id, because the original operation resulted in
926 // a new fragment in the piece table; rather we will
927 // mark this with '-' and will remember the superfluous
928 // id, so if queried later we can work out if this
929 // whole fragment should in fact go
930
931 delete r;
932 m_vRev.deleteNthItem(i);
933
934 m_iSuperfluous = iId;
935
936 const PP_Revision * pRevision = new PP_Revision(iId, eType, (gchar*)0, (gchar*)0);
937 m_vRev.addItem((void*)pRevision);
938 }
939 else if((eType == PP_REVISION_ADDITION) && (r_type == PP_REVISION_DELETION))
940 {
941 // in the opposite case, when the editor originally
942 // marked the text for removal and now wants it back,
943 // we just remove the attribute (which we have done)
944 // this also happens when we have been left with a
945 // superfluous deletion id in the vector; if that is
946 // the case we need to reset m_iSuperfluous, since
947 // this fragment can no more be superfluous
948
949 delete r;
950 m_vRev.deleteNthItem(i);
951
952 if(m_iSuperfluous == iId)
953 {
954 // the editor has had another change of heart
955 m_iSuperfluous = 0;
956 }
957 }
958 else if((eType == PP_REVISION_DELETION) && (r_type == PP_REVISION_FMT_CHANGE))
959 {
960 // this is the case when the editor changed
961 // formatting, but now wants the whole fragment out
962 // instead -- we simly replace the old revision with
963 // the new, since the original action did not result
964 // in inserting new text
965
966 delete r;
967 m_vRev.deleteNthItem(i);
968
969 const PP_Revision * pRevision = new PP_Revision(iId, eType, (gchar*)0, (gchar*)0);
970 m_vRev.addItem((void*)pRevision);
971 }
972 else if((eType == PP_REVISION_FMT_CHANGE) && (r_type == PP_REVISION_DELETION))
973 {
974 // originally the editor marked the text for deletion,
975 // but now he just wants a format change, in this case
976 // we just replace the old revision with the new
977
978 delete r;
979 m_vRev.deleteNthItem(i);
980
981 const PP_Revision * pRevision = new PP_Revision(iId, eType, pProps, pAttrs);
982
983 m_vRev.addItem((void*)pRevision);
984 }
985 else if((eType == PP_REVISION_FMT_CHANGE) && (r_type == PP_REVISION_ADDITION))
986 {
987 // the editor first added this fragment, and now wants
988 // to apply a format change on the top of that
989 // so we will keep the old revision record, but need
990 // to merge any existing props in the revision with
991 // the new ones
992 r->setProperties(pProps);
993 r->setAttributes(pAttrs);
994 }
995 else if((eType == PP_REVISION_FMT_CHANGE) && (r_type == PP_REVISION_ADDITION_AND_FMT))
996 {
997 // addition with a fmt change, just add our changes to it
998 r->setProperties(pProps);
999 r->setAttributes(pAttrs);
1000 }
1001
1002 m_bDirty = true;
1003 m_pLastRevision = NULL;
1004 return;
1005 }
1006 else //(eType == r_type)
1007 {
1008 // we are trying to add a type already in the vector; this is legal but makes sense only
1009 // if both are fmt changes
1010 if(!((eType == PP_REVISION_FMT_CHANGE) && (r_type == PP_REVISION_FMT_CHANGE)))
1011 return;
1012
1013 r->setProperties(pProps);
1014 r->setAttributes(pAttrs);
1015
1016 m_bDirty = true;
1017 m_pLastRevision = NULL;
1018 return;
1019 }
1020 }
1021
1022 // if we got here then the item is not in our vector so add it
1023 const PP_Revision * pRevision = new PP_Revision(iId, eType, pProps, pAttrs);
1024
1025 m_vRev.addItem((void*)pRevision);
1026 m_bDirty = true;
1027 m_pLastRevision = NULL;
1028 }
1029
1030
1031 /**
1032 * Logically Performs addRevision( iId, eType, 0, 0 ). This method is
1033 * mainly useful for loading an ODT+GCT file where you want to add and
1034 * delete revisions but don't actually care about the attrs/props for
1035 * that action.
1036 */
addRevision(UT_uint32 iId,PP_RevisionType eType)1037 void PP_RevisionAttr::addRevision(UT_uint32 iId, PP_RevisionType eType )
1038 {
1039 const gchar ** pAttrs = 0;
1040 const gchar ** pProps = 0;
1041 addRevision( iId, eType, pAttrs, pProps );
1042 }
1043
1044
1045 void
addRevision(const PP_Revision * r)1046 PP_RevisionAttr::addRevision( const PP_Revision* r )
1047 {
1048 std::stringstream ss;
1049 if(r->getType() & PP_REVISION_FMT_CHANGE)
1050 ss << "!";
1051
1052 ss << (r->getId() * ((r->getType() == PP_REVISION_DELETION)?-1:1));
1053
1054 if(r->hasProperties())
1055 {
1056 ss << "{" << r->getPropsString() << "}";
1057 }
1058 if(r->hasAttributes())
1059 {
1060 ss << "{" << r->getAttrsString() << "}";
1061 }
1062
1063 PP_RevisionAttr us( getXMLstring() );
1064 _clear();
1065 std::string tmp = (std::string)us.getXMLstring() + "," + ss.str();
1066 setRevision( tmp.c_str() );
1067 }
1068
mergeAttr(UT_uint32 iId,PP_RevisionType t,const gchar * pzName,const gchar * pzValue)1069 void PP_RevisionAttr::mergeAttr( UT_uint32 iId, PP_RevisionType t,
1070 const gchar* pzName, const gchar* pzValue )
1071 {
1072 PP_RevisionAttr ra;
1073 const gchar* ppAtts[10];
1074 ppAtts[0] = pzName;
1075 ppAtts[1] = pzValue;
1076 ppAtts[2] = 0;
1077 ra.addRevision(iId,t,ppAtts,NULL);
1078
1079 mergeAll( ra );
1080 }
1081
1082 /**
1083 * Do not replace the attribute/value if it exists in the given revision already.
1084 */
mergeAttrIfNotAlreadyThere(UT_uint32 iId,PP_RevisionType t,const gchar * pzName,const gchar * pzValue)1085 void PP_RevisionAttr::mergeAttrIfNotAlreadyThere( UT_uint32 iId,
1086 PP_RevisionType t,
1087 const gchar* pzName,
1088 const gchar* pzValue )
1089 {
1090 for(UT_sint32 i = 0; i < m_vRev.getItemCount(); i++)
1091 {
1092 const PP_Revision * tr = (const PP_Revision *)m_vRev.getNthItem(i);
1093 UT_uint32 tid = tr->getId();
1094
1095 if( tid == iId )
1096 {
1097 if( t == PP_REVISION_NONE || t == tr->getType() )
1098 {
1099 const gchar * tattrs = tr->getAttrsString();
1100 if( strstr( tattrs, pzName ))
1101 {
1102 return;
1103 }
1104 }
1105 }
1106 }
1107
1108 return mergeAttr( iId, t, pzName, pzValue );
1109 }
1110
1111
1112
1113 //
1114 // getId() getType() rev
1115 typedef std::map< std::pair< UT_uint32, PP_RevisionType >, const PP_Revision* > revidx_t;
1116
toIndex(const PP_RevisionAttr & ra)1117 static revidx_t toIndex( const PP_RevisionAttr& ra )
1118 {
1119 revidx_t ret;
1120 for( UT_uint32 i=0; i < ra.getRevisionsCount(); ++i )
1121 {
1122 const PP_Revision* r = ra.getNthRevision( i );
1123 ret[ std::make_pair( r->getId(), r->getType() ) ] = r;
1124 }
1125 return ret;
1126 }
1127
mergeAPStrings(const std::string & a,const std::string & b)1128 static std::string mergeAPStrings( const std::string& a, const std::string& b )
1129 {
1130 if( b.empty() )
1131 return a;
1132 if( a.empty() )
1133 return b;
1134 std::stringstream ss;
1135 ss << a << ";" << b;
1136 return ss.str();
1137 }
1138
1139
1140 #define DEBUG_MERGEALL false
1141
mergeAll(const PP_RevisionAttr & ra)1142 void PP_RevisionAttr::mergeAll( const PP_RevisionAttr& ra )
1143 {
1144 PP_RevisionAttr us( getXMLstring() );
1145 _clear();
1146 std::string tmp = (std::string)us.getXMLstring() + "," + ra.getXMLstring();
1147
1148 revidx_t oldidx = toIndex( us );
1149 revidx_t newidx = toIndex( ra );
1150
1151 /*
1152 * Iterate over the entries in the oldidx merging the data from
1153 * newidx if found whenever a entry from newidx is used it is
1154 * removed from newidx too. This way, we can then just iterate
1155 * over newidx to add the entries which are in newidx but not in
1156 * oldidx.
1157 */
1158 revidx_t output;
1159 for( revidx_t::iterator iter = oldidx.begin(); iter != oldidx.end(); ++iter )
1160 {
1161 const PP_Revision* r = iter->second;
1162 revidx_t::iterator niter = newidx.find( iter->first );
1163 // UT_DEBUGMSG(("ODTCT ra::merge() id:%d attrs:%s props:%s\n",
1164 // r->getId(), r->getAttrsString(), r->getPropsString() ));
1165
1166 /*
1167 * If there is an entry in oldidx and newidx then merge them
1168 */
1169 if( niter != newidx.end() )
1170 {
1171 const PP_Revision* nr = niter->second;
1172
1173 std::string attrs = mergeAPStrings( r->getAttrsString(), nr->getAttrsString() );
1174 std::string props = mergeAPStrings( r->getPropsString(), nr->getPropsString() );
1175 output[ iter->first ] = new PP_Revision( iter->first.first,
1176 iter->first.second,
1177 props.c_str(), attrs.c_str() );
1178 newidx.erase( niter );
1179 }
1180 else
1181 {
1182 /*
1183 * disregard entries without anything to tell
1184 */
1185 if( r->getType() != PP_REVISION_DELETION
1186 && !strlen(r->getAttrsString())
1187 && !strlen(r->getPropsString()) )
1188 {
1189 // UT_DEBUGMSG(("ODTCT ra::merge() rev as no attr/props, skipping old id:%d type:%d\n",
1190 // r->getId(), r->getType() ));
1191
1192 continue;
1193 }
1194
1195 /*
1196 * no matching entry in the newidx, just copy the data
1197 */
1198 output[ iter->first ] = new PP_Revision( iter->first.first,
1199 iter->first.second,
1200 r->getPropsString(),
1201 r->getAttrsString() );
1202 }
1203 }
1204
1205 /*
1206 * copy over new revisions which didn't have a matching entry in the oldidx
1207 */
1208 for( revidx_t::iterator iter = newidx.begin(); iter != newidx.end(); ++iter )
1209 {
1210 output[ iter->first ] = new PP_Revision( iter->first.first,
1211 iter->first.second,
1212 iter->second->getPropsString(),
1213 iter->second->getAttrsString() );
1214 }
1215
1216 /*
1217 * Build the XML string for the merged revision attribute from the output index
1218 */
1219 bool outputssVirgin = true;
1220 std::stringstream outputss;
1221 for( revidx_t::iterator iter = output.begin(); iter != output.end(); ++iter )
1222 {
1223 const PP_Revision* r = iter->second;
1224
1225 if( DEBUG_MERGEALL )
1226 {
1227 UT_DEBUGMSG(("ODTCT ra::merge() output id:%d t:%d attr:%s\n",
1228 r->getId(), r->getType(), r->getAttrsString() ));
1229 UT_DEBUGMSG(("ODTCT ra::merge() output id:%d t:%d prop:%s\n",
1230 r->getId(), r->getType(), r->getPropsString() ));
1231 }
1232
1233 if( outputssVirgin ) outputssVirgin = false;
1234 else outputss << ",";
1235
1236 outputss << r->toString();
1237 }
1238 UT_map_delete_all_second( output );
1239
1240
1241
1242 setRevision( outputss.str().c_str() );
1243
1244 if( DEBUG_MERGEALL )
1245 {
1246 UT_DEBUGMSG(("ODTCT ra::merge() outputss::%s\n", outputss.str().c_str() ));
1247 UT_DEBUGMSG(("ODTCT ra::merge() ret:%s\n", getXMLstring() ));
1248 }
1249 return;
1250 }
1251
1252
1253
1254
1255 /*! removes id from this revision, respecting the sign, i.e., it will
1256 not remove -5 if given 5
1257 */
removeRevisionIdWithType(UT_uint32 iId,PP_RevisionType eType)1258 void PP_RevisionAttr::removeRevisionIdWithType(UT_uint32 iId, PP_RevisionType eType)
1259 {
1260 for(UT_sint32 i = 0; i < m_vRev.getItemCount(); i++)
1261 {
1262 PP_Revision * r = (PP_Revision *)m_vRev.getNthItem(i);
1263
1264 if((iId == r->getId()) && (eType == r->getType()))
1265 {
1266 delete r;
1267 m_vRev.deleteNthItem(i);
1268 m_bDirty = true;
1269 m_pLastRevision = NULL;
1270 return;
1271 }
1272 }
1273 }
1274
1275 /*! removes id from the attribute disregarding sign, i.e.,
1276 if given 5 it will remove both -5 and +5
1277 */
removeRevisionIdTypeless(UT_uint32 iId)1278 void PP_RevisionAttr::removeRevisionIdTypeless(UT_uint32 iId)
1279 {
1280 for(UT_sint32 i = 0; i < m_vRev.getItemCount(); i++)
1281 {
1282 PP_Revision * r = (PP_Revision *)m_vRev.getNthItem(i);
1283
1284 if(iId == r->getId())
1285 {
1286 delete r;
1287 m_vRev.deleteNthItem(i);
1288 m_bDirty = true;
1289 m_pLastRevision = NULL;
1290 return;
1291 }
1292 }
1293 }
1294
1295 /*! removes pRev unconditionally from the attribute
1296 */
removeRevision(const PP_Revision * pRev)1297 void PP_RevisionAttr::removeRevision(const PP_Revision * pRev)
1298 {
1299 for(UT_sint32 i = 0; i < m_vRev.getItemCount(); i++)
1300 {
1301 PP_Revision * r = (PP_Revision *)m_vRev.getNthItem(i);
1302
1303 if(r == pRev)
1304 {
1305 delete r;
1306 m_vRev.deleteNthItem(i);
1307 m_bDirty = true;
1308 m_pLastRevision = NULL;
1309 return;
1310 }
1311 }
1312 }
1313
1314
1315 /*! removes all IDs from the attribute whose value is lesser or
1316 equal the given id
1317 */
removeAllLesserOrEqualIds(UT_uint32 iId)1318 void PP_RevisionAttr::removeAllLesserOrEqualIds(UT_uint32 iId)
1319 {
1320 for(UT_sint32 i = 0; i < m_vRev.getItemCount(); i++)
1321 {
1322 PP_Revision * r = (PP_Revision *)m_vRev.getNthItem(i);
1323
1324 if(iId >= r->getId())
1325 {
1326 delete r;
1327 m_vRev.deleteNthItem(i);
1328 --i; // the vector just shrunk
1329 }
1330 }
1331
1332 m_bDirty = true;
1333 m_pLastRevision = NULL;
1334 }
1335
1336 /*! removes all IDs from the attribute whose value is higher or
1337 equal the given id
1338 */
removeAllHigherOrEqualIds(UT_uint32 iId)1339 void PP_RevisionAttr::removeAllHigherOrEqualIds(UT_uint32 iId)
1340 {
1341 for(UT_sint32 i = 0; i < m_vRev.getItemCount(); i++)
1342 {
1343 PP_Revision * r = (PP_Revision *)m_vRev.getNthItem(i);
1344
1345 if(iId <= r->getId())
1346 {
1347 delete r;
1348 m_vRev.deleteNthItem(i);
1349 --i; // the vector just shrunk
1350 }
1351 }
1352
1353 m_bDirty = true;
1354 m_pLastRevision = NULL;
1355 }
1356
1357
1358 /*! create XML string from our vector
1359 */
_refreshString() const1360 void PP_RevisionAttr::_refreshString() const
1361 {
1362 // char buf[30];
1363 m_sXMLstring.clear();
1364 UT_uint32 iCount = m_vRev.getItemCount();
1365
1366 for(UT_uint32 i = 0; i < iCount; i++)
1367 {
1368 PP_Revision * r = (PP_Revision *)m_vRev.getNthItem(i);
1369
1370 if( !m_sXMLstring.empty() )
1371 m_sXMLstring += ",";
1372
1373 m_sXMLstring += r->toString();
1374
1375 // PP_Revision * r = (PP_Revision *)m_vRev.getNthItem(i);
1376 // PP_RevisionType r_type = r->getType();
1377
1378 // if(r_type == PP_REVISION_FMT_CHANGE)
1379 // m_sXMLstring += "!";
1380
1381 // // print the id with appropriate sign
1382 // sprintf(buf,"%d",r->getId()* ((r_type == PP_REVISION_DELETION)?-1:1));
1383 // m_sXMLstring += buf;
1384
1385 // if(r_type != PP_REVISION_DELETION)
1386 // {
1387 // // if we have no props but have attribs, we have to issue empty braces so as not to
1388 // // confuse attribs with props
1389 // if(r->hasProperties() || r->hasAttributes())
1390 // m_sXMLstring += "{";
1391
1392 // if(r->hasProperties())
1393 // m_sXMLstring += r->getPropsString();
1394
1395 // if(r->hasProperties() || r->hasAttributes())
1396 // m_sXMLstring += "}";
1397
1398 // if(r->hasAttributes())
1399 // {
1400 // m_sXMLstring += "{";
1401 // m_sXMLstring += r->getAttrsString();
1402 // m_sXMLstring += "}";
1403 // }
1404 // };
1405
1406 // if(i != iCount - 1)
1407 // {
1408 // //not the last itteration, append ','
1409 // m_sXMLstring += ",";
1410 // }
1411
1412 }
1413 m_bDirty = false;
1414 }
1415
1416
1417
1418 /*! get an gchar string representation of this revision
1419 */
getXMLstring() const1420 const gchar * PP_RevisionAttr::getXMLstring() const
1421 {
1422 if(m_bDirty)
1423 _refreshString();
1424
1425 return (const gchar*) m_sXMLstring.c_str();
1426 }
1427
1428 std::string
getXMLstringUpTo(UT_uint32 iId) const1429 PP_RevisionAttr::getXMLstringUpTo( UT_uint32 iId ) const
1430 {
1431 PP_RevisionAttr rat;
1432 rat.setRevision( getXMLstring() );
1433 UT_DEBUGMSG(("PP_RevisionAttr::getXMLstringUpTo() id:%d before:%s\n", iId, rat.getXMLstring() ));
1434 rat.removeAllHigherOrEqualIds( iId );
1435 UT_DEBUGMSG(("PP_RevisionAttr::getXMLstringUpTo() id:%d after:%s\n", iId, rat.getXMLstring() ));
1436 // PP_RevisionAttr rat;
1437 // const PP_Revision* r = 0;
1438 // for( int raIdx = 0;
1439 // raIdx < iId && (r = ra.getNthRevision( raIdx ));
1440 // raIdx++ )
1441 // {
1442 // rat.addRevision( r );
1443 // }
1444 return rat.getXMLstring();
1445 }
1446
1447
1448
1449 /*! returns true if the fragment marked by this attribute is
1450 superfluous, i.e, it was created in the process of the present
1451 revision but the editor has later changed his/her mind and decided
1452 it should go away
1453 */
isFragmentSuperfluous() const1454 bool PP_RevisionAttr::isFragmentSuperfluous() const
1455 {
1456 // the fragment is superfluous if the superfluous flag is set
1457 // and the fragment belongs only to a single revision level
1458 if(m_iSuperfluous != 0 && m_vRev.getItemCount() == 1)
1459 {
1460 UT_return_val_if_fail (((PP_Revision *)m_vRev.getNthItem(0))->getId() == m_iSuperfluous,false);
1461 return true;
1462 }
1463 else
1464 return false;
1465 }
1466
operator ==(const PP_RevisionAttr & op2) const1467 bool PP_RevisionAttr::operator== (const PP_RevisionAttr &op2) const
1468 {
1469 for(UT_sint32 i = 0; i < m_vRev.getItemCount(); i++)
1470 {
1471 const PP_Revision * r1 = (const PP_Revision *) m_vRev.getNthItem(i);
1472
1473 for(UT_sint32 j = 0; j < op2.m_vRev.getItemCount(); j++)
1474 {
1475 const PP_Revision * r2 = (const PP_Revision *) op2.m_vRev.getNthItem(j);
1476
1477 if(!(*r1 == *r2))
1478 return false;
1479 }
1480 }
1481 return true;
1482 }
1483
1484 // PP_RevisionAttr&
1485 // PP_RevisionAttr::operator=(const PP_RevisionAttr &rhs)
1486 // {
1487 // setRevision( rhs.getXMLstring() );
1488 // return *this;
1489 // }
1490
1491
1492 /*! returns true if after revision iId this fragment carries revised
1493 property pName, the value of which will be stored in pValue; see
1494 notes on PP_Revision::hasProperty(...)
1495 */
hasProperty(UT_uint32 iId,const gchar * pName,const gchar * & pValue) const1496 bool PP_RevisionAttr::hasProperty(UT_uint32 iId, const gchar * pName, const gchar * &pValue) const
1497 {
1498 const PP_Revision * s;
1499 const PP_Revision * r = getGreatestLesserOrEqualRevision(iId, &s);
1500
1501 if(r)
1502 return r->getProperty(pName, pValue);
1503
1504 return false;
1505 }
1506
1507 /*! returns true if after the last revision this fragment carries revised
1508 property pName, the value of which will be stored in pValue; see
1509 notes on PP_Revision::hasProperty(...)
1510 */
hasProperty(const gchar * pName,const gchar * & pValue) const1511 bool PP_RevisionAttr::hasProperty(const gchar * pName, const gchar * &pValue) const
1512 {
1513 const PP_Revision * r = getLastRevision();
1514 return r->getProperty(pName, pValue);
1515 }
1516
1517 /*! returns the type of cumulative revision up to iId represented by this attribute
1518 */
getType(UT_uint32 iId) const1519 PP_RevisionType PP_RevisionAttr::getType(UT_uint32 iId) const
1520 {
1521 const PP_Revision * s;
1522 const PP_Revision * r = getGreatestLesserOrEqualRevision(iId,&s);
1523
1524 if(!r)
1525 {
1526 // HACK need to return something
1527 return PP_REVISION_FMT_CHANGE;
1528 }
1529
1530 return r->getType();
1531 }
1532
1533 /*! returns the type of overall cumulative revision represented by this attribute
1534 */
getType() const1535 PP_RevisionType PP_RevisionAttr::getType() const
1536 {
1537 const PP_Revision * r = getLastRevision();
1538 return r->getType();
1539 }
1540
1541
getHighestRevisionNumberWithAttribute(const gchar * attrName) const1542 UT_uint32 PP_RevisionAttr::getHighestRevisionNumberWithAttribute( const gchar * attrName ) const
1543 {
1544 const PP_Revision* r = 0;
1545
1546 for( UT_uint32 raIdx = 0;
1547 raIdx < getRevisionsCount() && (r = getNthRevision( raIdx ));
1548 raIdx++ )
1549 {
1550 if( UT_getAttribute( r, attrName, 0 ))
1551 return r->getId();
1552 }
1553 return 0;
1554 }
1555
1556
UT_getAttribute(const PP_AttrProp * pAP,const char * name,const char * def)1557 const char* UT_getAttribute( const PP_AttrProp* pAP, const char* name, const char* def )
1558 {
1559 const gchar* pValue;
1560 bool ok;
1561
1562 ok = pAP->getAttribute( name, pValue );
1563 if (!ok)
1564 {
1565 pValue = def;
1566 }
1567 return pValue;
1568 }
1569
1570 const PP_Revision *
getLowestDeletionRevision() const1571 PP_RevisionAttr::getLowestDeletionRevision() const
1572 {
1573 if( !getRevisionsCount() )
1574 return 0;
1575
1576 UT_uint32 rmax = getRevisionsCount();
1577 const PP_Revision* last = getNthRevision( rmax-1 );
1578 if( last->getType() != PP_REVISION_DELETION )
1579 return 0;
1580
1581 for( long idx = rmax - 1; idx >= 0; --idx )
1582 {
1583 const PP_Revision* p = getNthRevision( idx );
1584 if( p->getType() != PP_REVISION_DELETION )
1585 {
1586 return last;
1587 }
1588 last = p;
1589 }
1590 return 0;
1591 }
1592
1593
UT_getLatestAttribute(const PP_AttrProp * pAP,const char * name,const char * def)1594 std::string UT_getLatestAttribute( const PP_AttrProp* pAP,
1595 const char* name,
1596 const char* def )
1597 {
1598 const char* t = 0;
1599 std::string ret = def;
1600 bool ok = false;
1601
1602 if( const char* revisionString = UT_getAttribute( pAP, "revision", 0 ))
1603 {
1604 PP_RevisionAttr ra( revisionString );
1605 const PP_Revision* r = 0;
1606
1607 for( int raIdx = ra.getRevisionsCount()-1;
1608 raIdx >= 0 && (r = ra.getNthRevision( raIdx ));
1609 --raIdx )
1610 {
1611 ok = r->getAttribute( name, t );
1612 if (ok)
1613 {
1614 ret = t;
1615 return ret;
1616 }
1617 }
1618 }
1619
1620 ok = pAP->getAttribute( name, t );
1621 if (ok)
1622 {
1623 ret = t;
1624 return ret;
1625 }
1626 ret = def;
1627
1628 return ret;
1629 }
1630