1 /************************************************************************/
2 /*									*/
3 /*  Buffer administration routines. Functionality related to the node	*/
4 /*  tree.								*/
5 /*									*/
6 /************************************************************************/
7 
8 #   include	"docBufConfig.h"
9 
10 #   include	<stdlib.h>
11 
12 #   include	<appDebugon.h>
13 
14 #   include	"docBuf.h"
15 #   include	"docNodeTree.h"
16 #   include	"docParaParticules.h"
17 
18 #   define	VALIDATE_TREE	0
19 
20 #if 0
21 
22 Schema to illustrate the numbering of paragraphs and the
23 administration that is kept for that purpose
24 =========================================================
25 
26 node->biLeftParagraphs is the number of paragraphs to the left
27 of this node in its immediate parent plus the number of paragraph
28 descendants. I.E: For nodes with children, the number of paragraphs
29 in the children is included in node->biLeftParagraphs. A paragraph
30 includes itself in its node->biLeftParagraphs.
31 
32 Numbers of the paragraphs and the value of node->biLeftParagraphs:
33 
34     1     2     3     4     5     6     7     8     9    10    11    12
35     +     +     +     +     +     +     +     +     +     +     +     +
36     1     2     3     4     1     2     3     4     5     1     2     3
37     |     |     |     |     |     |     |     |     |     |     |     |
38     4-----+-----+-----+     9-----+-----+-----+-----+     12----+-----+
39     |                       |                             |
40     12----------------------+-----------------------------+
41     |
42     *
43 
44 Deleting 8 yields:
45 
46     1     2     3     4     5     6     7           8     9    10    11
47     +     +     +     +     +     +     +           +     +     +     +
48     1     2     3     4     1     2     3           4     1     2     3
49     |     |     |     |     |     |     |           |     |     |     |
50     4-----+-----+-----+     8-----+-----+-----+-----+     11----+-----+
51     |                       |                             |
52     11----------------------+-----------------------------+
53     |
54     *
55 
56 So after deleting/inserting paragraphs: Descend to the root of the
57 tree. In every parent adapt node->biLeftParagraphs of all direct
58 children to the right of the child that we come from. Set
59 node->biLeftParagraphs of the parent to the value in its right most
60 child. Continue with the parent of the parent until we have reached
61 the root.
62 
63 NOTE: I am educated as a biologist. My trees have their root at the
64     bottom, not at the top like those of computer scientists that
65     turn the world upside down. (I must admit that the metaphor
66     of parents and descendants has the computer science orientation.)
67 
68 #endif
69 
70 /************************************************************************/
71 /*									*/
72 /*  Free a BufferItem.							*/
73 /*									*/
74 /************************************************************************/
75 
docCleanNode(BufferDocument * bd,DocumentTree * dt,BufferItem * node)76 static void docCleanNode(	BufferDocument *	bd,
77 				DocumentTree *		dt,
78 				BufferItem *		node )
79     {
80     int				i;
81 
82     for ( i= node->biChildCount- 1; i >= 0; i-- )
83 	{ docFreeNode( bd, dt, node->biChildren[i] ); }
84     if  ( node->biChildren )
85 	{ free( node->biChildren );	}
86 
87     switch( node->biLevel )
88 	{
89 	case DOClevBODY:
90 	    break;
91 
92 	case DOClevSECT:
93 	    {
94 	    SectHeadersFooters *	shf= node->biSectHeadersFooters;
95 
96 	    if  ( shf )
97 		{
98 		docCleanSectHeadersFooters( bd, shf );
99 		free( shf );
100 		}
101 
102 	    docCleanSectionProperties( &(node->biSectProperties) );
103 	    }
104 	    break;
105 
106 	case DOClevCELL:
107 	    break;
108 
109 	case DOClevROW:
110 	    docCleanRowProperties( &(node->biRowProperties) );
111 	    break;
112 
113 	case DOClevPARA:
114 	    docCleanParaNode( bd, dt, node );
115 	    break;
116 
117 	default:
118 	    /*FALLTHROUGH*/
119 	case DOClevOUT:
120 	    LDEB(node->biLevel);
121 	    break;
122 	}
123 
124     node->biLevel= DOClevOUT;
125     }
126 
docFreeNode(BufferDocument * bd,DocumentTree * dt,BufferItem * node)127 void docFreeNode(	BufferDocument *	bd,
128 			DocumentTree *		dt,
129 			BufferItem *		node )
130     {
131     docCleanNode( bd, dt, node );
132     free( node );
133     }
134 
docMakeNode(void)135 BufferItem * docMakeNode( void )
136     {
137     return (BufferItem *)malloc(sizeof(BufferItem));
138     }
139 
140 /************************************************************************/
141 /*									*/
142 /*  Initialise a BufferItem.						*/
143 /*									*/
144 /************************************************************************/
145 
docInitNode(BufferItem * node,BufferItem * parent,const BufferDocument * bd,int numberInParent,int level,int treeType)146 void docInitNode(	BufferItem *		node,
147 			BufferItem *		parent,
148 			const BufferDocument *	bd,
149 			int			numberInParent,
150 			int			level,
151 			int			treeType )
152     {
153     node->biChildren= (BufferItem **)0;
154     node->biChildCount= 0;
155     node->biLeftParagraphs= 0;
156 
157     switch( level )
158 	{
159 	case DOClevBODY:
160 	    break;
161 
162 	case DOClevSECT:
163 	    node->biSectHeadersFooters= (SectHeadersFooters *)malloc(
164 						sizeof(SectHeadersFooters) );
165 	    if  ( ! node->biSectHeadersFooters )
166 		{ XDEB(node->biSectHeadersFooters);			}
167 	    else{ docInitSectHeadersFooters( node->biSectHeadersFooters ); }
168 
169 	    docInitSectionProperties( &(node->biSectProperties) );
170 
171 	    if  ( bd )
172 		{ node->biSectDocumentGeometry= bd->bdProperties.dpGeometry; }
173 
174 	    docInitSelectionScope( &(node->biSectSelectionScope) );
175 
176 	    node->biSectSelectionScope.ssTreeType= treeType;
177 	    node->biSectSelectionScope.ssSectNr= numberInParent;
178 
179 	    break;
180 
181 	case DOClevCELL:
182 	    node->biCellTopInset= 0;
183 	    node->biCellRowspan= 1;
184 	    node->biCellMergedCellTopRow= -1;
185 	    node->biCellMergedCellTopCol= -1;
186 	    break;
187 
188 	case DOClevROW:
189 	    node->biRowTableHeaderRow= -1;
190 	    node->biRowTableFirst= -1;
191 	    node->biRowTablePast= -1;
192 	    node->biRowPrecededByHeader= 0;
193 	    node->biRowForTable= 0;
194 
195 	    node->biRowTopInset= 0;
196 
197 	    docInitRowProperties( &(node->biRowProperties) );
198 
199 	    docInitLayoutPosition( &(node->biRowBelowAllCellsPosition) );
200 	    docInitLayoutPosition( &(node->biRowAboveHeaderPosition) );
201 	    break;
202 
203 	case DOClevPARA:
204 	    docInitParaNode( node );
205 	    break;
206 
207 	default:
208 	    node->biLevel= DOClevOUT;
209 	    node->biParent= (BufferItem *)0;
210 	    LDEB(level); return;
211 	}
212 
213     node->biLevel= level;
214     node->biTreeType= treeType;
215     node->biParent= parent;
216     node->biNumberInParent= numberInParent;
217 
218     docInitLayoutPosition( &(node->biTopPosition) );
219     docInitLayoutPosition( &(node->biBelowPosition) );
220 
221     return;
222     }
223 
224 /************************************************************************/
225 /*									*/
226 /*  1)  Delete a series of nodes.					*/
227 /*  2)  Delete a node from its parent.					*/
228 /*									*/
229 /************************************************************************/
230 
docSectSetSelectionScopes(BufferItem * sectBi)231 static void docSectSetSelectionScopes(		BufferItem *	sectBi )
232     {
233     int				n= sectBi->biNumberInParent;
234     SectHeadersFooters *	shf= sectBi->biSectHeadersFooters;
235 
236     sectBi->biSectSelectionScope.ssSectNr= n;
237 
238     if  ( shf )
239 	{
240 	if  ( shf->shfFirstPageHeader.dtRoot )
241 	    {
242 	    shf->shfFirstPageHeader.dtRoot->
243 				biSectSelectionScope.ssOwnerSectNr= n;
244 	    }
245 	if  ( shf->shfLeftPageHeader.dtRoot )
246 	    {
247 	    shf->shfLeftPageHeader.dtRoot->
248 				biSectSelectionScope.ssOwnerSectNr= n;
249 	    }
250 	if  ( shf->shfRightPageHeader.dtRoot )
251 	    {
252 	    shf->shfRightPageHeader.dtRoot->
253 				biSectSelectionScope.ssOwnerSectNr= n;
254 	    }
255 
256 	if  ( shf->shfFirstPageFooter.dtRoot )
257 	    {
258 	    shf->shfFirstPageFooter.dtRoot->
259 				biSectSelectionScope.ssOwnerSectNr= n;
260 	    }
261 	if  ( shf->shfLeftPageFooter.dtRoot )
262 	    {
263 	    shf->shfLeftPageFooter.dtRoot->
264 				biSectSelectionScope.ssOwnerSectNr= n;
265 	    }
266 	if  ( shf->shfRightPageFooter.dtRoot )
267 	    {
268 	    shf->shfRightPageFooter.dtRoot->
269 				biSectSelectionScope.ssOwnerSectNr= n;
270 	    }
271 	}
272 
273     return;
274     }
275 
276 /************************************************************************/
277 /*									*/
278 /*  Paragraphs have been deleted from a group node.			*/
279 /*  Administration in the child array has been done. Fix		*/
280 /*  biLeftParagraphs in the node itself and its parents.		*/
281 /*									*/
282 /************************************************************************/
283 
docParagraphsDeleted(BufferItem * node,int paragraphsDeleted)284 static void docParagraphsDeleted(	BufferItem *	node,
285 					int		paragraphsDeleted )
286     {
287     while( node->biParent )
288 	{
289 	int		first;
290 	int		f;
291 
292 	first= node->biNumberInParent;
293 	node= node->biParent;
294 
295 	for ( f= first; f < node->biChildCount; f++ )
296 	    { node->biChildren[f]->biLeftParagraphs -= paragraphsDeleted;	}
297 	}
298 
299     node->biLeftParagraphs -= paragraphsDeleted;
300 
301     return;
302     }
303 
304 /*  1  */
docDeleteNodes(BufferDocument * bd,DocumentTree * dt,BufferItem * node,int first,int count)305 void docDeleteNodes(	BufferDocument *	bd,
306 			DocumentTree *		dt,
307 			BufferItem *		node,
308 			int			first,
309 			int			count )
310     {
311     int		n;
312     int		f;
313     int		c;
314     int		paragraphsDeleted= 0;
315 
316 #   if VALIDATE_TREE
317     if  ( docCheckNode( bd->bdBody.dtRoot ) )
318 	{ LDEB(2); docListNode( 0, bd->bdBody.dtRoot ); abort(); }
319 #   endif
320 
321     if  ( first > node->biChildCount )
322 	{
323 	LLDEB(first,node->biChildCount);
324 	first= node->biChildCount;
325 	}
326 
327     if  ( first+ count > node->biChildCount )
328 	{
329 	LLDEB(first+count,node->biChildCount);
330 	count= node->biChildCount- first;
331 	}
332 
333     if  ( count <= 0 )
334 	{ LDEB(count); return;	}
335     else{
336 	f= 0;
337 	if  ( first > 0 )
338 	    { f= node->biChildren[first- 1]->biLeftParagraphs;	}
339 
340 	c= node->biChildren[first+ count- 1]->biLeftParagraphs;
341 
342 	paragraphsDeleted= c- f;
343 	}
344 
345     n= first+ count- 1;
346     while( n >= first )
347 	{ docFreeNode( bd, dt, node->biChildren[n] ); n--; }
348 
349     node->biChildCount -= count;
350 
351     f= first;
352     while( f < node->biChildCount )
353 	{
354 	node->biChildren[f]= node->biChildren[f+ count];
355 	node->biChildren[f]->biNumberInParent= f;
356 	node->biChildren[f]->biLeftParagraphs -= paragraphsDeleted;
357 
358 	if  ( node->biChildren[f]->biLevel == DOClevSECT )
359 	    { docSectSetSelectionScopes( node->biChildren[f] ); }
360 
361 	f++;
362 	}
363 
364     docParagraphsDeleted( node, paragraphsDeleted );
365 
366 #   if VALIDATE_TREE
367     if  ( docCheckNode( bd->bdBody.dtRoot ) )
368 	{ LDEB(2); docListNode( 0, bd->bdBody.dtRoot ); abort(); }
369 #   endif
370 
371     return;
372     }
373 
374 /*  2  */
docDeleteNode(BufferDocument * bd,DocumentTree * dt,BufferItem * node)375 void docDeleteNode(	BufferDocument *	bd,
376 			DocumentTree *		dt,
377 			BufferItem *		node )
378     {
379     if  ( node->biParent )
380 	{
381 	docDeleteNodes( bd, dt, node->biParent, node->biNumberInParent, 1 );
382 	}
383     else{ docFreeNode( bd, dt, node );					}
384     }
385 
386 /************************************************************************/
387 /*									*/
388 /*  Delete the Document Tree Node that is the root of a document Tree	*/
389 /*  This is NOT the way to delete a document tree. Use			*/
390 /*  docEraseDocumentTree() to do so.					*/
391 /*									*/
392 /************************************************************************/
393 
docDeleteDocumentTree(BufferDocument * bd,DocumentTree * dt)394 void docDeleteDocumentTree(	BufferDocument *	bd,
395 				DocumentTree *		dt )
396     {
397     docFreeNode( bd, dt, dt->dtRoot );
398     dt->dtRoot= (BufferItem *)0;
399     }
400 
401 /************************************************************************/
402 /*									*/
403 /*  Paragraphs have been inserted into a group node.			*/
404 /*  Administration in the child array has been done. Fix		*/
405 /*  biLeftParagraphs in the node itself and its parents.		*/
406 /*									*/
407 /************************************************************************/
408 
docParagraphsInserted(BufferItem * node,int paragraphsInserted)409 static void docParagraphsInserted(	BufferItem *	node,
410 					int		paragraphsInserted )
411     {
412     while( node->biParent )
413 	{
414 	int		first;
415 	int		f;
416 
417 	first= node->biNumberInParent;
418 	node= node->biParent;
419 
420 	for ( f= first; f < node->biChildCount; f++ )
421 	    { node->biChildren[f]->biLeftParagraphs += paragraphsInserted; }
422 	}
423 
424     node->biLeftParagraphs += paragraphsInserted;
425 
426     return;
427     }
428 
429 /************************************************************************/
430 
docValidChildLevel(int parentLevel,int childLevel)431 int docValidChildLevel(		int		parentLevel,
432 				int		childLevel )
433     {
434     switch( parentLevel )
435 	{
436 	case DOClevBODY:
437 	    if  ( childLevel != DOClevSECT )
438 		{ return 0;	}
439 	    break;
440 
441 	case DOClevSECT:
442 	    if  ( childLevel != DOClevROW )
443 		{ return 0; }
444 	    break;
445 
446 	case DOClevROW:
447 	    if  ( childLevel != DOClevCELL )
448 		{ return 0; }
449 	    break;
450 
451 	case DOClevCELL:
452 	    if  ( childLevel != DOClevPARA && childLevel != DOClevROW )
453 		{ return 0; }
454 	    break;
455 
456 	default:
457 	    LLDEB(parentLevel,childLevel); return 0;
458 	}
459 
460     return 1;
461     }
462 
463 /************************************************************************/
464 /*									*/
465 /*  Add a new child to a parent.					*/
466 /*									*/
467 /************************************************************************/
468 
docInsertNode(const BufferDocument * bd,BufferItem * parent,int n,int level)469 BufferItem * docInsertNode(	const BufferDocument *	bd,
470 				BufferItem *		parent,
471 				int			n,
472 				int			level )
473     {
474     BufferItem *	rval= (BufferItem *)0;
475     BufferItem *	newBi= (BufferItem *)0;
476 
477     int			i;
478 
479     int			newSize;
480 
481     BufferItem **	freshChildren;
482 
483     int			paragraphsInserted;
484 
485 #   if VALIDATE_TREE
486     if  ( docCheckNode( bd->bdBody.dtRoot ) )
487 	{ LDEB(2); docListNode( 0, bd->bdBody.dtRoot ); abort(); }
488 #   endif
489 
490     if  ( ! docValidChildLevel( parent->biLevel, level ) )
491 	{ SSDEB(docLevelStr(parent->biLevel),docLevelStr(level)); goto ready; }
492 
493     if  ( n == -1 )
494 	{ n= parent->biChildCount;	}
495 
496     newSize= parent->biChildCount;
497 
498     if  ( newSize % 10 )
499 	{ newSize ++;		}
500     else{ newSize += 10;	}
501 
502     newSize *= sizeof(BufferItem *);
503 
504     freshChildren= (BufferItem **)realloc( parent->biChildren, newSize );
505     if  ( ! freshChildren )
506 	{ LLXDEB(parent->biChildCount,newSize,freshChildren); goto ready; }
507     parent->biChildren= freshChildren;
508 
509     newBi= (BufferItem *)malloc( sizeof(BufferItem) );
510     if  ( ! newBi )
511 	{ XDEB(newBi); goto ready;	}
512 
513     docInitNode( newBi, parent, bd, n, level, parent->biTreeType );
514 
515     if  ( n == 0 )
516 	{ newBi->biTopPosition= parent->biTopPosition;			}
517     else{ newBi->biTopPosition= freshChildren[n-1]->biBelowPosition;	}
518 
519     for ( i= parent->biChildCount; i > n; i-- )
520 	{
521 	freshChildren[i]= freshChildren[i-1];
522 
523 	freshChildren[i]->biNumberInParent= i;
524 
525 	if  ( freshChildren[i]->biLevel == DOClevSECT )
526 	    { docSectSetSelectionScopes( freshChildren[i] ); }
527 	}
528 
529     freshChildren[n]= newBi;
530     parent->biChildCount++;
531 
532     rval= newBi; newBi= (BufferItem *)0; /* steal */
533 
534     if  ( level == DOClevPARA )
535 	{
536 	paragraphsInserted= 1;
537 
538 	docSetParaTableNesting( rval );
539 
540 	if  ( n > 0 )
541 	    { rval->biLeftParagraphs= freshChildren[n-1]->biLeftParagraphs; }
542 
543 	for ( i= n; i < parent->biChildCount; i++ )
544 	    { parent->biChildren[i]->biLeftParagraphs += paragraphsInserted; }
545 
546 	docParagraphsInserted( parent, paragraphsInserted );
547 	}
548     else{
549 	if  ( n > 0 )
550 	    { rval->biLeftParagraphs= freshChildren[n-1]->biLeftParagraphs; }
551 	}
552 
553 #   if VALIDATE_TREE
554     if  ( docCheckNode( bd->bdBody.dtRoot ) )
555 	{ LDEB(2); docListNode( 0, bd->bdBody.dtRoot ); abort(); }
556 #   endif
557 
558   ready:
559 
560     if  ( newBi )
561 	{ free( newBi );	}
562 
563     return rval;
564     }
565 
566 /************************************************************************/
567 /*									*/
568 /*  Make an empty paragraph: Needed at several locations.		*/
569 /*									*/
570 /************************************************************************/
571 
docInsertEmptyParagraph(BufferDocument * bd,BufferItem * node,int textAttributeNumber)572 BufferItem * docInsertEmptyParagraph(
573 				BufferDocument *	bd,
574 				BufferItem *		node,
575 				int			textAttributeNumber )
576     {
577     if  ( node->biLevel < DOClevSECT )
578 	{ LDEB(node->biLevel); return (BufferItem *)0;	}
579 
580     if  ( node->biLevel < DOClevROW )
581 	{
582 	node= docInsertNode( bd, node, -1, DOClevROW );
583 	if  ( ! node )
584 	    { XDEB(node); return (BufferItem *)0;   }
585 	}
586 
587     if  ( node->biLevel < DOClevCELL )
588 	{
589 	node= docInsertNode( bd, node, -1, DOClevCELL );
590 	if  ( ! node )
591 	    { XDEB(node); return (BufferItem *)0;   }
592 	}
593 
594     if  ( node->biLevel < DOClevPARA )
595 	{
596 	node= docInsertNode( bd, node, -1, DOClevPARA );
597 	if  ( ! node )
598 	    { XDEB(node); return (BufferItem *)0;   }
599 	}
600     else{
601 	node= docInsertNode( bd, node->biParent, -1, DOClevPARA );
602 	if  ( ! node )
603 	    { XDEB(node); return (BufferItem *)0;   }
604 	}
605 
606     if  ( ! docInsertTextParticule( node, 0, 0, 0,
607 				    DOCkindSPAN, textAttributeNumber ) )
608 	{ LDEB(1); return (BufferItem *)0;	}
609 
610     return node;
611     }
612 
613 /************************************************************************/
614 /*									*/
615 /*  Insert a new row in a table.					*/
616 /*									*/
617 /************************************************************************/
618 
docInsertRowNode(BufferDocument * bd,BufferItem * sectBi,int n,const RowProperties * rp,int textAttributeNumber)619 BufferItem * docInsertRowNode(	BufferDocument *	bd,
620 				BufferItem *		sectBi,
621 				int			n,
622 				const RowProperties *	rp,
623 				int			textAttributeNumber )
624     {
625     int				col;
626 
627     BufferItem *		rval= (BufferItem *)0;
628     BufferItem *		rowBi= (BufferItem *)0;
629 
630     rowBi= docInsertNode( bd, sectBi, n, DOClevROW );
631     if  ( ! rowBi )
632 	{ XDEB(rowBi); goto ready;	}
633 
634     if  ( docCopyRowProperties( &(rowBi->biRowProperties), rp,
635 					(const DocumentAttributeMap *)0 ) )
636 	{ LDEB(1); goto ready; }
637 
638     for ( col= 0; col < rp->rpCellCount; col++ )
639 	{
640 	BufferItem *	paraBi;
641 
642 	paraBi= docInsertEmptyParagraph( bd, rowBi, textAttributeNumber );
643 	if  ( ! paraBi )
644 	    { XDEB(paraBi); goto ready; }
645 
646 	docSetParaTableNesting( paraBi );
647 	}
648 
649     rval= rowBi; rowBi= (BufferItem *)0; /* steal */
650 
651   ready:
652 
653     if  ( rowBi )
654 	{ docDeleteNode( bd, (DocumentTree *)0, rowBi );	}
655 
656     return rval;
657     }
658 
659 /************************************************************************/
660 /*									*/
661 /*  Split a node with childen.						*/
662 /*									*/
663 /*  1)  Insert a fresh node at the position of the node to be split.	*/
664 /*  2)  Copy various properties to the fresh node.			*/
665 /*  3)  Move the first n children to the fresh node.			*/
666 /*  4)  Paragraph number administration.				*/
667 /*  5)  Shift the children in the old node.				*/
668 /*									*/
669 /************************************************************************/
670 
docSplitGroupNode(BufferDocument * bd,BufferItem ** pNewBi,BufferItem * oldBi,int n)671 int docSplitGroupNode(			BufferDocument *	bd,
672 					BufferItem **		pNewBi,
673 					BufferItem *		oldBi,
674 					int			n )
675     {
676     BufferItem *	newBi;
677     int			i;
678     int			prev;
679 
680     /*  1  */
681     newBi= docInsertNode( bd, oldBi->biParent,
682 				oldBi->biNumberInParent, oldBi->biLevel );
683     if  ( ! newBi )
684 	{ XDEB(newBi); return -1;	}
685 
686     /*  2  */
687     switch( oldBi->biLevel )
688 	{
689 	case DOClevSECT:
690 	    if  ( docCopySectDescription( newBi, bd, oldBi, bd ) )
691 		{ LDEB(1); return -1;	}
692 	    break;
693 
694 	case DOClevCELL:
695 	    break;
696 
697 	case DOClevROW:
698 	    if  ( docCopyRowProperties( &(newBi->biRowProperties),
699 					&(oldBi->biRowProperties),
700 					(const DocumentAttributeMap *)0 ) )
701 		{ LDEB(1); return -1;	}
702 	    break;
703 
704 	default:
705 	    LDEB(oldBi->biLevel); return -1;
706 	}
707 
708     newBi->biChildren= (BufferItem **)malloc( n* sizeof(BufferItem *) );
709     if  ( ! newBi->biChildren )
710 	{ XDEB(newBi->biChildren); return -1;	}
711 
712     /*  3  */
713     for ( i= 0; i < n; i++ )
714 	{
715 	newBi->biChildren[i]= oldBi->biChildren[i];
716 	newBi->biChildren[i]->biParent= newBi;
717 	}
718 
719     /*  4  */
720     prev= 0;
721     if  ( newBi->biNumberInParent > 0 )
722 	{
723 	prev= newBi->biParent->biChildren[newBi->biNumberInParent-1]->
724 							biLeftParagraphs;
725 	}
726 
727     if  ( n == 0 )
728 	{ newBi->biLeftParagraphs= prev; }
729     else{
730 	newBi->biLeftParagraphs= prev+ newBi->biChildren[n-1]->biLeftParagraphs;
731 	}
732 
733     /*  4,5  */
734     newBi->biChildCount= n;
735     oldBi->biChildCount -= n;
736 
737     prev= 0;
738     for ( i= 0; i < oldBi->biChildCount; i++ )
739 	{
740 	BufferItem *	child= oldBi->biChildren[i+ n];
741 
742 	/*  5  */
743 	oldBi->biChildren[i]= child;
744 	child->biNumberInParent -= n;
745 
746 	if  ( oldBi->biChildren[i]->biLevel == DOClevPARA )
747 	    { prev++;	}
748 	else{
749 	    if  ( child->biChildCount > 0 )
750 		{
751 		prev += child->biChildren[child->biChildCount-1]->
752 							    biLeftParagraphs;
753 		}
754 	    }
755 
756 	oldBi->biChildren[i]->biLeftParagraphs= prev;
757 	}
758 
759     *pNewBi= newBi; return 0;
760     }
761 
762 /************************************************************************/
763 /*									*/
764 /*  Split a 'group node' I.E. a node that has children rather than	*/
765 /*  its own text.							*/
766 /*									*/
767 /*  The goal is to make sure that the split before position n in	*/
768 /*  splitNode is between two nodes with level level.			*/
769 /*									*/
770 /************************************************************************/
771 
docSplitGroupNodeAtLevel(BufferDocument * bd,BufferItem ** pBeforeNode,BufferItem ** pAfterNode,BufferItem * splitNode,int n,int level)772 int docSplitGroupNodeAtLevel(	BufferDocument *	bd,
773 				BufferItem **		pBeforeNode,
774 				BufferItem **		pAfterNode,
775 				BufferItem *		splitNode,
776 				int			n,
777 				int			level )
778     {
779     BufferItem *	beforeNode= (BufferItem *)0;
780     BufferItem *	afterNode= splitNode;
781     BufferItem *	node;
782 
783 #   if VALIDATE_TREE
784     SDEB(docLevelStr(parentBi->biLevel));
785     if  ( docCheckNode( bd->bdBody.dtRoot ) )
786 	{ LDEB(2); docListNode( 0, bd->bdBody.dtRoot ); abort(); }
787 #   endif
788 
789     if  ( ! splitNode )
790 	{ XDEB(splitNode); return -1;	}
791 
792     node= splitNode;
793     while( node && node->biLevel != level- 1 )
794 	{ node= node->biParent; }
795     if  ( ! node )
796 	{
797 	SSXDEB(docLevelStr(splitNode->biLevel),docLevelStr(level),node);
798 	return -1;
799 	}
800 
801     if  ( n < 0 || n >= splitNode->biChildCount )
802 	{ LLDEB(n,splitNode->biChildCount); return -1;	}
803 
804     afterNode= splitNode;
805     for (;;)
806 	{
807 	if  ( n > 0 || afterNode->biLevel == level )
808 	    {
809 	    if  ( docSplitGroupNode( bd, &beforeNode, afterNode, n ) )
810 		{ LDEB(n); return -1;	}
811 
812 	    if  ( afterNode->biLevel == level )
813 		{ break;	}
814 	    }
815 
816 	n= afterNode->biNumberInParent;
817 	afterNode= afterNode->biParent;
818 	}
819 
820 #   if VALIDATE_TREE
821     SDEB(docLevelStr(afterNode->biLevel));
822     if  ( docCheckNode( bd->bdBody.dtRoot ) )
823 	{ LDEB(2); docListNode( 0, bd->bdBody.dtRoot ); abort(); }
824 #   endif
825 
826     if  ( pBeforeNode )
827 	{ *pBeforeNode= beforeNode;	}
828     if  ( pAfterNode )
829 	{ *pAfterNode= afterNode;	}
830 
831     return 0;
832     }
833 
834 /************************************************************************/
835 /*									*/
836 /*  Merge the children of two nodes into the first one.			*/
837 /*									*/
838 /************************************************************************/
839 
docMergeGroupNodes(BufferItem * to,BufferItem * from)840 int docMergeGroupNodes(		BufferItem *	to,
841 				BufferItem *	from )
842     {
843     BufferItem **	freshChildren;
844     int			f;
845     int			t;
846     int			left;
847     int			prev;
848     int			paragraphsMoved;
849 
850     if  ( to == from )
851 	{ XXDEB(to,from); return -1;	}
852     if  ( to->biLevel != from->biLevel )
853 	{ LLDEB(to->biLevel,from->biLevel); return -1;	}
854 
855     if  ( from->biChildCount == 0 )
856 	{ return 0;	}
857 
858 #   if VALIDATE_TREE
859     SSDEB(docLevelStr(to->biLevel),docLevelStr(from->biLevel));
860     if  ( docCheckRootNode( to ) )
861 	{ LDEB(2); docListRootNode( 0, to ); abort(); }
862     if  ( docCheckRootNode( from ) )
863 	{ LDEB(2); docListRootNode( 0, from ); abort(); }
864 #   endif
865 
866     freshChildren= (BufferItem **)realloc( to->biChildren,
867 	    ( to->biChildCount+ from->biChildCount )* sizeof(BufferItem *) );
868     if  ( ! freshChildren )
869 	{
870 	LLXDEB(to->biChildCount,from->biChildCount,freshChildren);
871 	return -1;
872 	}
873     to->biChildren= freshChildren;
874 
875     if  ( from->biParent && from->biNumberInParent > 0 )
876 	{
877 	BufferItem *	prevBi;
878 
879 	prevBi= from->biParent->biChildren[from->biNumberInParent- 1];
880 	paragraphsMoved= from->biLeftParagraphs- prevBi->biLeftParagraphs;
881 	}
882     else{
883 	paragraphsMoved= from->biLeftParagraphs;
884 	}
885 
886     left= 0; prev= 0;
887     t= to->biChildCount;
888     if  ( t > 0 )
889 	{ left= to->biChildren[t- 1]->biLeftParagraphs; }
890     for ( f= 0; f < from->biChildCount; t++, f++ )
891 	{
892 	int	count;
893 
894 	freshChildren[t]= from->biChildren[f];
895 	freshChildren[t]->biParent= to;
896 	freshChildren[t]->biNumberInParent= t;
897 
898 	count= freshChildren[t]->biLeftParagraphs- prev;
899 	prev= freshChildren[t]->biLeftParagraphs;
900 	left += count;
901 	freshChildren[t]->biLeftParagraphs= left;
902 
903 	if  ( freshChildren[t]->biLevel == DOClevSECT )
904 	    { docSectSetSelectionScopes( freshChildren[t] ); }
905 	}
906 
907     to->biChildCount += from->biChildCount;
908     from->biChildCount= 0;
909 
910     docParagraphsInserted( to, paragraphsMoved );
911     docParagraphsDeleted( from, paragraphsMoved );
912 
913 #   if VALIDATE_TREE
914     SSDEB(docLevelStr(to->biLevel),docLevelStr(from->biLevel));
915     if  ( docCheckRootNode( to ) )
916 	{ LDEB(2); docListRootNode( 0, to ); abort(); }
917     if  ( docCheckRootNode( from ) )
918 	{ LDEB(2); docListRootNode( 0, from ); abort(); }
919 #   endif
920 
921     return 0;
922     }
923 
924 /************************************************************************/
925 /*									*/
926 /*  Return the number of a paragraph.					*/
927 /*									*/
928 /************************************************************************/
929 
docNumberOfParagraph(const BufferItem * node)930 int docNumberOfParagraph(	const BufferItem *	node )
931     {
932     int		n= 0;
933 
934     if  ( node->biLevel != DOClevPARA )
935 	{ SDEB(docLevelStr(node->biLevel)); return -1;	}
936 
937     n= node->biLeftParagraphs;
938 
939     while( node->biParent )
940 	{
941 	node= node->biParent;
942 
943 	if  ( node->biParent && node->biNumberInParent > 0 )
944 	    {
945 	    if  ( node->biNumberInParent >= node->biParent->biChildCount )
946 		{ LLDEB(node->biNumberInParent,node->biParent->biChildCount); }
947 
948 	    n += node->biParent->biChildren[
949 				node->biNumberInParent- 1]->biLeftParagraphs;
950 	    }
951 	}
952 
953     return n;
954     }
955 
docGetParagraphByNumber(const DocumentTree * dt,int paraNr)956 BufferItem * docGetParagraphByNumber(	const DocumentTree *	dt,
957 					int			paraNr )
958     {
959     BufferItem *	node= dt->dtRoot;
960 
961     if  ( paraNr < 1 )
962 	{ LDEB(paraNr); return (BufferItem *)0;	}
963 
964     while( node->biChildCount > 0 )
965 	{
966 	int		i;
967 
968 	for ( i= 0; i < node->biChildCount; i++ )
969 	    {
970 	    if  ( node->biChildren[i]->biLeftParagraphs >= paraNr )
971 		{ break;	}
972 	    }
973 
974 	if  ( i >= node->biChildCount )
975 	    {
976 	    /* LLSDEB(paraNr,node->biChildCount,docLevelStr(node->biLevel)); */
977 	    return (BufferItem *)0;
978 	    }
979 
980 	if  ( i > 0 )
981 	    { paraNr -= node->biChildren[i-1]->biLeftParagraphs;	}
982 
983 	node= node->biChildren[i];
984 	}
985 
986     if  ( node->biLevel != DOClevPARA )
987 	{ SDEB(docLevelStr(node->biLevel)); return (BufferItem *)0;	}
988 
989     if  ( paraNr != 1 )
990 	{ LDEB(paraNr); return (BufferItem *)0; }
991 
992     return node;
993     }
994 
docGetCommonParent(BufferItem * paraNode1,BufferItem * paraNode2)995 BufferItem * docGetCommonParent(	BufferItem *	paraNode1,
996 					BufferItem *	paraNode2 )
997     {
998     int			paraNr1= docNumberOfParagraph( paraNode1 );
999     int			paraNr2= docNumberOfParagraph( paraNode2 );
1000 
1001     BufferItem *	bi1= paraNode1;
1002     BufferItem *	bi2= paraNode2;
1003 
1004     if  ( paraNr1 < 1 || paraNr2 < paraNr1 )
1005 	{ LLDEB(paraNr1,paraNr2); return (BufferItem *)0;	}
1006 
1007     while( bi1->biParent )
1008 	{ bi1= bi1->biParent;	}
1009     while( bi2->biParent )
1010 	{ bi2= bi2->biParent;	}
1011     if  ( bi1 != bi2 )
1012 	{ XXDEB(bi1,bi2); return (BufferItem *)0;	}
1013 
1014     while( bi1->biChildCount > 0 )
1015 	{
1016 	int		i;
1017 
1018 	for ( i= 0; i < bi1->biChildCount; i++ )
1019 	    {
1020 	    if  ( bi1->biChildren[i]->biLeftParagraphs >= paraNr1 )
1021 		{ break;	}
1022 	    }
1023 
1024 	if  ( bi1->biChildren[i]->biLeftParagraphs < paraNr2 )
1025 	    { return bi1;	}
1026 
1027 	if  ( i >= bi1->biChildCount )
1028 	    {
1029 	    /* LLSDEB(paraNr,bi1->biChildCount,docLevelStr(bi1->biLevel)); */
1030 	    return (BufferItem *)0;
1031 	    }
1032 
1033 	if  ( i > 0 )
1034 	    {
1035 	    paraNr1 -= bi1->biChildren[i-1]->biLeftParagraphs;
1036 	    paraNr2 -= bi1->biChildren[i-1]->biLeftParagraphs;
1037 	    }
1038 
1039 	bi1= bi1->biChildren[i];
1040 	}
1041 
1042     if  ( bi1->biLevel != DOClevPARA )
1043 	{ SDEB(docLevelStr(bi1->biLevel)); return (BufferItem *)0;	}
1044 
1045     if  ( paraNr1 != 1 || paraNr2 != 1 )
1046 	{ LLDEB(paraNr1,paraNr2); return (BufferItem *)0; }
1047 
1048     return bi1;
1049     }
1050 
1051 /************************************************************************/
1052 /*									*/
1053 /*  Return the nearest parent of a BufferItem that is a real row.	*/
1054 /*  candidate row, cell.						*/
1055 /*									*/
1056 /************************************************************************/
1057 
docGetRowNode(BufferItem * node)1058 BufferItem * docGetRowNode(		BufferItem *		node )
1059     {
1060     while( node && ! docIsRowNode( node ) )
1061 	{ node= node->biParent;	}
1062 
1063     return node;
1064     }
1065 
docGetRowLevelNode(BufferItem * node)1066 BufferItem * docGetRowLevelNode(	BufferItem *		node )
1067     {
1068     while( node					&&
1069 	   node->biLevel != DOClevROW		)
1070 	{ node= node->biParent;	}
1071 
1072     return node;
1073     }
1074 
docGetCellNode(BufferItem * node)1075 BufferItem * docGetCellNode(		BufferItem *		node )
1076     {
1077     while( node					&&
1078 	   node->biLevel != DOClevCELL		)
1079 	{ node= node->biParent;	}
1080 
1081     return node;
1082     }
1083 
1084 /************************************************************************/
1085 /*									*/
1086 /*  Return the nearest parent of a BufferItem that is a section.	*/
1087 /*									*/
1088 /************************************************************************/
1089 
docGetSectNode(BufferItem * node)1090 BufferItem * docGetSectNode(		BufferItem *		node )
1091     {
1092     while( node					&&
1093 	   node->biLevel != DOClevSECT		)
1094 	{ node= node->biParent;	}
1095 
1096     return node;
1097     }
1098 
1099 /************************************************************************/
1100 /*									*/
1101 /*  Calculate table nesting: Follow the path to the root and count	*/
1102 /*  the number of (table) rows that we traverse.			*/
1103 /*									*/
1104 /************************************************************************/
1105 
docTableNesting(const BufferItem * node)1106 int docTableNesting(		const BufferItem *	node )
1107     {
1108     int			tableNesting= 0;
1109 
1110     while( node )
1111 	{
1112 	if  ( docIsRowNode( node ) )
1113 	    { tableNesting++;	}
1114 
1115 	node= node->biParent;
1116 	}
1117 
1118     return tableNesting;
1119     }
1120 
docRowNesting(const BufferItem * node)1121 int docRowNesting(		const BufferItem *	node )
1122     {
1123     int			rowNesting= 0;
1124 
1125     while( node )
1126 	{
1127 	if  ( node->biLevel == DOClevROW )
1128 	    { rowNesting++;	}
1129 
1130 	node= node->biParent;
1131 	}
1132 
1133     return rowNesting;
1134     }
1135 
docSetParaTableNesting(BufferItem * paraBi)1136 void docSetParaTableNesting(		BufferItem *	paraBi )
1137     {
1138     if  ( paraBi->biLevel != DOClevPARA )
1139 	{ SDEB(docLevelStr(paraBi->biLevel)); return;	}
1140 
1141     paraBi->biParaTableNesting= docTableNesting( paraBi );
1142     }
1143 
1144 /************************************************************************/
1145 /*									*/
1146 /*  Change the kind of tree for a node and all its children.		*/
1147 /*									*/
1148 /************************************************************************/
1149 
docSetTreeTypeOfNode(BufferItem * node,int treeType)1150 void docSetTreeTypeOfNode(	BufferItem *		node,
1151 				int			treeType )
1152     {
1153     int		i;
1154 
1155     node->biTreeType= treeType;
1156 
1157     if  ( node->biLevel == DOClevSECT )
1158 	{ node->biSectSelectionScope.ssTreeType= treeType; }
1159 
1160     for ( i= 0; i < node->biChildCount; i++ )
1161 	{ docSetTreeTypeOfNode( node->biChildren[i], treeType );	}
1162 
1163     return;
1164     }
1165 
1166 /************************************************************************/
1167 /*									*/
1168 /*  Is a node at the extremity of a parent node.			*/
1169 /*									*/
1170 /************************************************************************/
1171 
docNodeAtExtremity(int * pAtExtremity,const BufferItem * parentNode,const BufferItem * paraNode,int after)1172 int docNodeAtExtremity(	int *				pAtExtremity,
1173 			const BufferItem *		parentNode,
1174 			const BufferItem *		paraNode,
1175 			int				after )
1176     {
1177     if  ( after )
1178 	{
1179 	DocumentPosition	dpTail;
1180 
1181 	if  ( docTailPosition( &dpTail, (BufferItem *)parentNode ) )
1182 	    { LDEB(1); return -1;	}
1183 
1184 	*pAtExtremity= ( paraNode == dpTail.dpNode );
1185 	}
1186     else{
1187 	DocumentPosition	dpHead;
1188 
1189 	if  ( docHeadPosition( &dpHead, (BufferItem *)parentNode ) )
1190 	    { LDEB(1); return -1;	}
1191 
1192 	*pAtExtremity= ( paraNode == dpHead.dpNode );
1193 	}
1194 
1195     return 0;
1196     }
1197 
1198